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

Commit

Permalink
support custom tick rates, preparation for lagslayer hud fix
Browse files Browse the repository at this point in the history
  • Loading branch information
homchom committed Mar 8, 2024
1 parent 3e46190 commit d407688
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 60 deletions.
5 changes: 4 additions & 1 deletion src/main/java/io/github/homchom/recode/Power.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.homchom.recode
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

Expand All @@ -25,7 +26,8 @@ interface PowerSink {
class Power(
extent: PowerSink? = null,
private val onEnable: PowerCallback? = null,
private val onDisable: PowerCallback? = null
private val onDisable: PowerCallback? = null,
startEnabled: Boolean = false
) : PowerSink, CoroutineScope {
private var charge = 0

Expand All @@ -41,6 +43,7 @@ class Power(

init {
extent?.use(this)
if (startEnabled) runBlocking { up() }
}

suspend fun up() = updateCharge { it + 1 }
Expand Down
20 changes: 10 additions & 10 deletions src/main/java/io/github/homchom/recode/event/BufferedEvent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package io.github.homchom.recode.event

import com.google.common.cache.CacheBuilder
import io.github.homchom.recode.PowerSink
import io.github.homchom.recode.game.currentTick
import kotlinx.coroutines.flow.MutableStateFlow
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit
import kotlin.time.toJavaDuration

// TODO: replace stableInterval with a tick utility
Expand All @@ -14,21 +14,21 @@ import kotlin.time.toJavaDuration
/**
* Creates a [BufferedCustomEvent] that runs asynchronously and caches the result. [BufferedCustomEvent.stabilize]
* should be called once during each critical section of this event's execution; if this is done, the result will
* be cached roughly on [stableInterval].
* be cached roughly on [stableTickInterval] (in ticks).
*
* @param keySelector A function that transforms [I] into a hashable key type [K] for the buffer.
* @param contextGenerator A function that transforms [I] into the context type [T] when needed.
* @param cacheDuration How long event contexts should stay in the cache after being written before eviction.
*/
fun <T, R : Any, I, K : Any> createBufferedEvent(
resultCapture: (T) -> R,
stableInterval: Duration,
stableTickInterval: Int,
keySelector: (I) -> K,
contextGenerator: (I) -> T,
cacheDuration: Duration = 1.seconds
): BufferedCustomEvent<T, R, I> {
val delegate = createEvent(resultCapture)
return BufferedFlowEvent(delegate, stableInterval, keySelector, contextGenerator, cacheDuration)
return BufferedFlowEvent(delegate, stableTickInterval, keySelector, contextGenerator, cacheDuration)
}

/**
Expand All @@ -46,7 +46,7 @@ interface BufferedCustomEvent<T, R, I> : ResultListenable<T, R?> {

private class BufferedFlowEvent<T, R : Any, I, K : Any>(
private val delegate: CustomEvent<T, R>,
stableInterval: Duration,
stableTickInterval: Int,
private val keySelector: (I) -> K,
private val contextGenerator: (I) -> T,
cacheDuration: Duration = 1.seconds
Expand All @@ -65,17 +65,17 @@ private class BufferedFlowEvent<T, R : Any, I, K : Any>(
var passIndex = -1
var runIndex = 0

private var prevStamp = 0L
private var prevTick = 0L

fun stamp() {
val now = System.currentTimeMillis()
val now = currentTick
if (++passIndex == passes) {
val elapsed = (now - prevStamp).toInt().coerceAtLeast(1)
passes = stableInterval.toInt(DurationUnit.MILLISECONDS) / elapsed
val elapsed = (now - prevTick).toInt().coerceAtLeast(1)
passes = stableTickInterval / elapsed
passIndex = 0
}
runIndex = passIndex
prevStamp = now
prevTick = now
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/io/github/homchom/recode/game/GameEvents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import io.github.homchom.recode.event.createValidatedEvent
import io.github.homchom.recode.event.wrapFabricEvent
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents.ClientStopping
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents.EndTick
import net.minecraft.network.protocol.game.ClientboundSoundPacket

val AfterClientTickEvent = wrapFabricEvent(ClientTickEvents.END_CLIENT_TICK) { EndTick(it) }

val PlaySoundEvent = createValidatedEvent<ClientboundSoundPacket>()

val QuitGameEvent = wrapFabricEvent(ClientLifecycleEvents.CLIENT_STOPPING) { ClientStopping(it) }
48 changes: 45 additions & 3 deletions src/main/java/io/github/homchom/recode/game/GameTime.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,50 @@
package io.github.homchom.recode.game

import kotlin.time.Duration.Companion.milliseconds
import io.github.homchom.recode.Power
import io.github.homchom.recode.event.listen
import io.github.homchom.recode.event.listenEach
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.takeWhile

/**
* @return this integer as a [kotlin.time.Duration] in ticks, where 20 ticks = 1 second.
* The current client tick.
*
* @see waitTicks
* @see AfterClientTickEvent
*/
val Int.ticks get() = milliseconds * 50
val currentTick get() = TickRecorder.currentTick

/**
* Suspends until [AfterClientTickEvent] runs [ticks] times. Unlike [kotlinx.coroutines.delay], this is
* event-based and respects tick rates.
*/
suspend fun waitTicks(ticks: Int) = AfterClientTickEvent.notifications.take(ticks).collect()

private object TickRecorder {
var currentTick = 0L

private val power = Power()

init {
power.listenEach(AfterClientTickEvent) { currentTick++ }
}
}

class TickCountdown(private val duration: Int, private val onFinish: () -> Unit = {}) {
val isActive get() = counter > 0

private var counter = 0
private val power = Power(startEnabled = true)

fun wind() {
val launch = !isActive
counter = duration
if (launch) power.listen(AfterClientTickEvent) {
takeWhile { --counter > 0 }
}.invokeOnCompletion { exception ->
counter = 0
if (exception == null) onFinish()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.github.homchom.recode.hypercube.state.DF;
import io.github.homchom.recode.hypercube.state.PlotMode;
import io.github.homchom.recode.mod.config.Config;
import io.github.homchom.recode.mod.features.LagslayerHUD;
import io.github.homchom.recode.mod.features.StateOverlayHandler;
import io.github.homchom.recode.mod.features.commands.CodeSearcher;
import net.minecraft.client.Minecraft;
Expand All @@ -20,7 +19,7 @@
public class MInGameHUD {
@Inject(method = "renderEffects", at = @At("RETURN"))
private void renderEffects(GuiGraphics guiGraphics, CallbackInfo ci) {
LagslayerHUD.onRender(guiGraphics);
//LagslayerHUD.onRender(guiGraphics);

Minecraft mc = Minecraft.getInstance();
Font tr = mc.font;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
package io.github.homchom.recode.multiplayer

import io.github.homchom.recode.RecodeDispatcher
import io.github.homchom.recode.game.ticks
import io.github.homchom.recode.game.waitTicks
import io.github.homchom.recode.mc
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

// https://github.com/PaperMC/Velocity/issues/909
Expand All @@ -22,7 +21,7 @@ fun unsafelySendCommand(command: String) {
do {
val next = queue.removeFirst()
mc.player?.connection?.sendUnsignedCommand(next) ?: break
delay(1.ticks)
waitTicks(1)
} while (queue.isNotEmpty())
} finally {
queue.clear()
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/io/github/homchom/recode/render/RenderDucks.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.homchom.recode.render

import net.minecraft.core.SectionPos
import net.minecraft.world.level.block.entity.BlockEntity

/**
* An [net.minecraft.client.renderer.LevelRenderer] that is augmented by recode.
*/
@Suppress("FunctionName")
interface DRecodeLevelRenderer {
/**
* @returns A filtered list of block entities that should still be rendered.
*/
fun `recode$runBlockEntityEvents`(
blockEntities: Collection<BlockEntity>,
sectionPos: SectionPos?
): List<BlockEntity>

/**
* Gets and returns the RGBA hex of [blockEntity]'s outline, or `null` if it will not be outlined.
*/
fun `recode$getBlockEntityOutlineColor`(blockEntity: BlockEntity): Int?

/**
* Processes all unprocessed entity and block entity outlines.
*/
fun `recode$processOutlines`(partialTick: Float)
}
31 changes: 7 additions & 24 deletions src/main/java/io/github/homchom/recode/render/RenderEvents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ package io.github.homchom.recode.render
import com.mojang.blaze3d.systems.RenderSystem
import io.github.homchom.recode.Power
import io.github.homchom.recode.event.*
import io.github.homchom.recode.game.ticks
import io.github.homchom.recode.mc
import io.github.homchom.recode.util.Case
import io.github.homchom.recode.util.math.MixedInt
import io.github.homchom.recode.util.std.mapToArray
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents.BeforeBlockOutline
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.core.SectionPos
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.phys.HitResult
Expand Down Expand Up @@ -41,7 +42,7 @@ val OutlineBlockEntitiesEvent =
.filter { it.outlineColor != null }
.associate { it.blockEntity.blockPos to it.outlineColor!! }
},
stableInterval = 3.ticks,
stableTickInterval = 3,
keySelector = { Case(it.sectionPos) },
contextGenerator = { input ->
input.blockEntities.mapToArray(::BlockEntityOutlineContext)
Expand All @@ -64,26 +65,8 @@ data class BlockEntityOutlineContext(
data class Input(val blockEntities: Collection<BlockEntity>, val sectionPos: SectionPos?)
}

/**
* An [net.minecraft.client.renderer.LevelRenderer] that is augmented by recode.
*/
@Suppress("FunctionName")
interface DRecodeLevelRenderer {
/**
* @returns A filtered list of block entities that should still be rendered.
*/
fun `recode$runBlockEntityEvents`(
blockEntities: Collection<BlockEntity>,
sectionPos: SectionPos?
): List<BlockEntity>

/**
* Gets and returns the RGBA hex of [blockEntity]'s outline, or `null` if it will not be outlined.
*/
fun `recode$getBlockEntityOutlineColor`(blockEntity: BlockEntity): Int?
val AfterRenderHudEvent = wrapFabricEvent(HudRenderCallback.EVENT) { listener ->
HudRenderCallback { guiGraphics, tickDelta -> listener(HudRenderContext(guiGraphics, tickDelta)) }
}

/**
* Processes all unprocessed entity and block entity outlines.
*/
fun `recode$processOutlines`(partialTick: Float)
}
data class HudRenderContext(val guiGraphics: GuiGraphics, val tickDelta: Float)
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@

import com.google.common.collect.Lists;
import io.github.homchom.recode.mod.config.Config;
import io.github.homchom.recode.render.*;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextColor;
import org.w3c.dom.css.RGBColor;


import java.util.List;
import java.util.function.Predicate;
Expand Down
38 changes: 28 additions & 10 deletions src/main/java/io/github/homchom/recode/ui/text/StyledString.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,55 @@ import net.kyori.adventure.text.ComponentIteratorType
import net.kyori.adventure.text.ComponentLike
import net.kyori.adventure.text.TextComponent
import net.kyori.adventure.text.format.Style
import kotlin.math.max
import kotlin.math.min

data class StyledChar(val char: Char, val style: Style)

class StyledString private constructor(
private val text: Component,
override val size: Int
) : AbstractCollection<StyledChar>(), ComponentLike by text {
val components = text.asFlatSequence().map { it as TextComponent }

constructor(vararg contents: Pair<String, StyleWrapper>) : this(
text {
for ((string, style) in contents) literal(string, style)
}.asComponent(),
contents.sumOf { it.first.length }
)

constructor(text: Component) : this(
text.apply {
for (component in iterator(ComponentIteratorType.DEPTH_FIRST)) {
if (component !is TextComponent) {
constructor(text: ComponentLike) : this(
text.asComponent().also { component ->
for (comp in component) {
if (comp !is TextComponent) {
throw IllegalArgumentException("StyledString must only consist of TextComponents")
}
}
},
text.iterable(ComponentIteratorType.DEPTH_FIRST)
text.asComponent().iterable(ComponentIteratorType.DEPTH_FIRST)
.sumOf { (it as TextComponent).content().length }
)

override fun iterator() = iterator {
for (component in text.iterator(ComponentIteratorType.DEPTH_FIRST)) {
for (char in (component as TextComponent).content()) {
yield(StyledChar(char, component.style()))
override fun iterator() = components
.flatMap { component ->
component.content().map { StyledChar(it, component.style()) }
}
.iterator()

fun substring(startIndex: Int, endIndex: Int = size) = StyledString(text {
var index = 0
for (component in components) {
if (index >= endIndex) return@text
val nextIndex = index + component.content().length
if (nextIndex > startIndex) {
val substring = component.content().substring(
max(0, startIndex - index),
min(component.content().length, endIndex - index)
)
literal(substring, style(component.style()))
}
index = nextIndex
}
}
})
}

This file was deleted.

Loading

1 comment on commit d407688

@homchom
Copy link
Owner Author

@homchom homchom commented on d407688 Mar 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also comments out LagSlayer HUD so people don't have to explicitly disable it, for now

Please sign in to comment.