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

Commit

Permalink
screen events, more documentation, cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
homchom committed Jun 24, 2024
1 parent e69423e commit 3407ad7
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ private class TrialCoroutineScope(
coroutineScope.launch(coroutineContext, CoroutineStart.UNDISPATCHED) {
channel.consumeEach { test(it) ?: throw TrialScopeException() }
}
yield() // fast-fail
yield() // fast fail
}

override fun <R : Any> instant(value: R?) = TrialResult(value)
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/io/github/homchom/recode/game/GameTime.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@file:JvmName("GameTime")

package io.github.homchom.recode.game

import io.github.homchom.recode.Power
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.github.homchom.recode.hypercube

import io.github.homchom.recode.game.getCompoundOrNull
import net.minecraft.nbt.CompoundTag

val CompoundTag.publicBukkitValues get() = getCompoundOrNull("PublicBukkitValues")
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ package io.github.homchom.recode.hypercube
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.google.gson.JsonSyntaxException
import io.github.homchom.recode.game.getCompoundOrNull
import io.github.homchom.recode.game.getStringOrNull
import io.github.homchom.recode.game.lore
import io.github.homchom.recode.logError
import net.kyori.adventure.text.Component
import net.minecraft.world.item.ItemStack

fun ItemStack.dfValueMeta(): DFValueMeta? {
val raw = tag
?.getCompoundOrNull("PublicBukkitValues")
val raw = tag?.publicBukkitValues
?.getStringOrNull("hypercube:varitem")
?: return null

Expand Down
105 changes: 100 additions & 5 deletions src/main/java/io/github/homchom/recode/hypercube/state/DFState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ package io.github.homchom.recode.hypercube.state

import io.github.homchom.recode.hypercube.MAIN_ARROW
import io.github.homchom.recode.hypercube.SUPPORT_ARROW
import io.github.homchom.recode.hypercube.state.DFState.AtSpawn
import io.github.homchom.recode.hypercube.state.DFState.OnPlot
import io.github.homchom.recode.hypercube.state.SupportSession.Helping
import io.github.homchom.recode.hypercube.state.SupportSession.Requested
import io.github.homchom.recode.mc
import io.github.homchom.recode.multiplayer.username
import io.github.homchom.recode.ui.text.equalsPlain
Expand All @@ -22,6 +26,9 @@ import net.minecraft.client.player.LocalPlayer
import net.minecraft.core.BlockPos
import net.minecraft.world.item.ItemStack

/**
* Whether this [ServerData]'s IP address matches `mcdiamondfire.com`.
*/
val ServerData?.ipMatchesDF get(): Boolean {
val regex = regex {
modify(RegexModifier.IgnoreCase)
Expand All @@ -41,6 +48,10 @@ val ServerData?.ipMatchesDF get(): Boolean {
return this?.ip?.matches(regex) ?: false
}

/**
* DiamondFire-related state, including the player's [node] and [permissions], among other things. Additional
* state can be obtained by smart-casting to the subclasses of `DFState`, i.e. [AtSpawn] and [OnPlot].
*/
sealed interface DFState {
val permissions: Deferred<PermissionGroup>

Expand Down Expand Up @@ -68,8 +79,14 @@ sealed interface DFState {
}
}

/**
* @return a new [DFState] derived from this one and [session].
*/
fun withSession(session: SupportSession?): DFState

/**
* [DFState] for when a player is at spawn.
*/
data class AtSpawn(
override val node: Node,
override val permissions: Deferred<PermissionGroup>,
Expand All @@ -81,6 +98,9 @@ sealed interface DFState {
override fun hashCode() = super.hashCode()
}

/**
* [DFState] for when a player is on a plot.
*/
data class OnPlot(
override val node: Node,
val mode: PlotMode,
Expand All @@ -96,10 +116,19 @@ sealed interface DFState {
}
}

fun DFState?.isOnPlot(plot: Plot) = this is DFState.OnPlot && this.plot == plot
/**
* @return Whether this [DFState] is non-null and refers to being on [plot].
*/
fun DFState?.isOnPlot(plot: Plot) = this is OnPlot && this.plot == plot

fun DFState?.isInMode(mode: PlotMode.ID) = this is DFState.OnPlot && this.mode.id == mode
/**
* @return Whether this [DFState] is non-null and refers to being on a plot in [mode].
*/
fun DFState?.isInMode(mode: PlotMode.ID) = this is OnPlot && this.mode.id == mode

/**
* A DiamondFire node.
*/
@JvmInline
value class Node(private val id: String) {
val displayName get() = when {
Expand All @@ -115,12 +144,19 @@ value class Node(private val id: String) {
}
}

/**
* @return The [Node] with the given name, as shown in `/locate`.
*/
fun nodeByName(name: String): Node {
val node = name.removePrefix("Node ")
val id = node.toIntOrNull()?.run { "node$node" } ?: node.replaceFirstChar(Char::lowercase)
val id = node.toIntOrNull()?.run { "node$node" }
?: node.replaceFirstChar(Char::lowercase)
return Node(id)
}

/**
* A DiamondFire plot.
*/
data class Plot(
val name: String,
val owner: String,
Expand All @@ -135,9 +171,23 @@ private val playModeRegex = regex {
period
}

/**
* State associated with any of the modes that a player can be in on a [Plot].
*
* @property id A [PlotMode.ID] to the mode itself.
*
* @see PlotMode.Play
* @see PlotMode.Build
* @see PlotMode.Dev
*/
sealed interface PlotMode {
val id: ID

/**
* An identifier for a [PlotMode]. Unlike `PlotMode`, the subtypes of `ID` are singletons and do not
* require associated state. This type and its `companion object` also both implement `Matcher` to match
* any mode switch [Component] to a [PlotMode.MatchResult].
*/
sealed interface ID : Matcher<Component, MatchResult> {
val descriptor: String

Expand All @@ -150,10 +200,16 @@ sealed interface PlotMode {
}
}

/**
* @see PlotMode.ID
*/
data class MatchResult(val id: ID, val plotName: String?, val plotOwner: String?) {
constructor(id: ID) : this(id, null, null)
}

/**
* A combined [PlotMode] and [PlotMode.ID] representing `/mode play`.
*/
data object Play : PlotMode, ID {
override val id get() = this

Expand All @@ -167,6 +223,9 @@ sealed interface PlotMode {
}
}

/**
* A combined [PlotMode] and [PlotMode.ID] representing `/mode build`.
*/
data object Build : PlotMode, ID {
override val id get() = this

Expand All @@ -178,7 +237,10 @@ sealed interface PlotMode {
}
}

data class Dev(val buildCorner: BlockPos, val referenceBookCopy: ItemStack) : PlotMode {
/**
* A [PlotMode] representing `/mode dev`.
*/
data class Dev(val buildCorner: BlockPos, private val referenceBookCopy: ItemStack) : PlotMode {
override val id get() = ID

constructor(player: LocalPlayer) : this(
Expand All @@ -187,12 +249,25 @@ sealed interface PlotMode {
.setY(49)
.move(10, 0, -10)
.immutable(),
player.inventory.getItem(17).copy()
player.inventory.items[REFERENCE_BOOK_SLOT].copy()
)

/**
* @return A fresh copy of the Reference Book.
*/
fun referenceBookCopy(): ItemStack = referenceBookCopy.copy()

/**
* A [PlotMode.ID] representing `/mode dev`.
*/
companion object ID : PlotMode.ID {
override val descriptor = "coding"

/**
* The default slot of the Reference Book in a non-compact inventory.
*/
const val REFERENCE_BOOK_SLOT = 17

override fun match(input: Component): MatchResult? {
val id = takeIf { input.equalsPlain("$MAIN_ARROW You are now in dev mode.") }
return id?.let(::MatchResult)
Expand All @@ -201,12 +276,27 @@ sealed interface PlotMode {
}
}

/**
* An `enum` representing the two types of support session a DiamondFire player can be in. This type
* and its `companion object` also both implement `Matcher` to match any support session commencement
* [Component] to a `SupportSession`.
*
* @see Requested
* @see Helping
*/
enum class SupportSession : Matcher<Component, SupportSession> {
/**
* A [SupportSession] in which the player requested support.
*/
Requested {
override fun match(input: Component) = takeIf { input.equalsPlain(
"You have requested code support.\nIf you wish to leave the queue, use /support cancel."
) }
},

/**
* A [SupportSession] in which the player is helping another player.
*/
Helping {
private val regex = dynamicRegex { username: String ->
str("[SUPPORT] $username entered a session with ")
Expand All @@ -223,6 +313,11 @@ enum class SupportSession : Matcher<Component, SupportSession> {
companion object : Matcher<Component, SupportSession> by matcherOf(entries)
}

/**
* DiamondFire state contained in a `/locate` message.
*
* @see DFState
*/
sealed interface LocateState {
val node: Node

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ object DFStateDetectors : StateListenable<Case<DFState?>> by eventGroup {
trial(teleportEvent, Unit) t@{ _, _ ->
enforceOnDF()

val gameMenuStack = mc.player!!.inventory.getItem(4) // middle hotbar slot
val gameMenuStack = mc.player!!.inventory.items[4] // middle hotbar slot
if (gameMenuStack.item != Items.EMERALD) return@t null // faster fail
if ("◇ Game Menu ◇" !in gameMenuStack.hoverName.string) return@t null

Expand Down
16 changes: 5 additions & 11 deletions src/main/java/io/github/homchom/recode/mixin/ConditionalMixins.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,8 @@ import net.fabricmc.loader.api.FabricLoader
*/
internal annotation class MixinConditional(val modID: String, val disjointWith: Array<String> = [])

/**
* Whether a mixin with this annotation should be applied.
*
* @see MixinConditional
*/
internal val MixinConditional.passes: Boolean
get() {
if (!FabricLoader.getInstance().isModLoaded(modID)) return false
if (disjointWith.any(FabricLoader.getInstance()::isModLoaded)) return false
return true
}
internal val MixinConditional.shouldApplyMixin get() = when {
!FabricLoader.getInstance().isModLoaded(modID) -> false
disjointWith.any(FabricLoader.getInstance()::isModLoaded) -> false
else -> true
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class MixinPluginRecode : IMixinConfigPlugin {
?: return true
annotations.filterIsInstance<MixinConditional>()
.singleOrNull()
?.run { return passes }
?.run { return shouldApplyMixin }

return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import io.github.homchom.recode.ModConstants;
import io.github.homchom.recode.mod.config.LegacyConfig;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import io.github.homchom.recode.render.ScreenEvents;
import io.github.homchom.recode.render.ScreenInitContext;
import net.fabricmc.fabric.api.client.screen.v1.Screens;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractWidget;
Expand All @@ -21,11 +22,13 @@

public class LegacyAfterScreenInitEvent {
public LegacyAfterScreenInitEvent() {
ScreenEvents.AFTER_INIT.register(this::afterScreenInit);
ScreenEvents.getAfterInitScreenEvent().register(this::afterScreenInit);
}

private void afterScreenInit(Minecraft mc, Screen screen, int scaledWidth, int scaledHeight) {
if (screen instanceof TitleScreen) afterTitleScreenInit(mc, screen);
private void afterScreenInit(ScreenInitContext context) {
if (context.getScreen() instanceof TitleScreen) {
afterTitleScreenInit(context.getClient(), context.getScreen());
}
}

private void afterTitleScreenInit(Minecraft mc, Screen screen) {
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/io/github/homchom/recode/render/ScreenEvents.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@file:JvmName("ScreenEvents")

package io.github.homchom.recode.render

import io.github.homchom.recode.event.wrapFabricEvent
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents.*
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen

val BeforeInitScreenEvent = wrapFabricEvent(BEFORE_INIT) { listener ->
BeforeInit { client, screen, scaledWidth, scaledHeight ->
listener(ScreenInitContext(client, screen, scaledWidth, scaledHeight))
}
}

val AfterInitScreenEvent = wrapFabricEvent(AFTER_INIT) { listener ->
AfterInit { client, screen, scaledWidth, scaledHeight ->
listener(ScreenInitContext(client, screen, scaledWidth, scaledHeight))
}
}

data class ScreenInitContext(
val client: Minecraft,
val screen: Screen,
val scaledWidth: Int,
val scaledHeight: Int
)

fun Screen.afterRender() = wrapFabricEvent(afterRender(this)) { listener ->
AfterRender { screen, drawContext, mouseX, mouseY, tickDelta ->
listener(ScreenRenderContext(screen, drawContext, mouseX, mouseY, tickDelta))
}
}

data class ScreenRenderContext(
val screen: Screen,
val guiGraphics: GuiGraphics,
val mouseX: Int,
val mouseY: Int,
val tickDelta: Float
)

fun Screen.onRemove() = wrapFabricEvent(remove(this), ::Remove)
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
package io.github.homchom.recode.util

/**
* Subtypes of this interface expect to be hashable as keys of any applicable collection, e.g. [Map] and [Set].
*/
interface KeyHashable {
override operator fun equals(other: Any?): Boolean
override fun hashCode(): Int
}

/**
* A wrapper for a [value] of type [T] that can be unboxed with [invoke].
*/
Expand Down
Loading

0 comments on commit 3407ad7

Please sign in to comment.