aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/util')
-rw-r--r--src/main/kotlin/util/FirmFormatters.kt4
-rw-r--r--src/main/kotlin/util/MC.kt5
-rw-r--r--src/main/kotlin/util/SBData.kt4
-rw-r--r--src/main/kotlin/util/SkyBlockIsland.kt63
-rw-r--r--src/main/kotlin/util/SkyblockId.kt6
-rw-r--r--src/main/kotlin/util/compatloader/CompatLoader.kt2
-rw-r--r--src/main/kotlin/util/compatloader/CompatMeta.kt48
-rw-r--r--src/main/kotlin/util/data/MultiFileDataHolder.kt63
-rw-r--r--src/main/kotlin/util/mc/FirmamentDataComponentTypes.kt24
-rw-r--r--src/main/kotlin/util/mc/asFakeServer.kt37
-rw-r--r--src/main/kotlin/util/regex.kt7
-rw-r--r--src/main/kotlin/util/render/TintedOverlayTexture.kt44
-rw-r--r--src/main/kotlin/util/skyblock/SkyBlockItems.kt5
-rw-r--r--src/main/kotlin/util/textutil.kt2
14 files changed, 281 insertions, 33 deletions
diff --git a/src/main/kotlin/util/FirmFormatters.kt b/src/main/kotlin/util/FirmFormatters.kt
index acb7102..a660f51 100644
--- a/src/main/kotlin/util/FirmFormatters.kt
+++ b/src/main/kotlin/util/FirmFormatters.kt
@@ -13,6 +13,7 @@ import kotlin.math.roundToInt
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import net.minecraft.text.Text
+import net.minecraft.util.math.BlockPos
object FirmFormatters {
@@ -131,4 +132,7 @@ object FirmFormatters {
return if (boolean == trueIsGood) text.lime() else text.red()
}
+ fun formatPosition(position: BlockPos): Text {
+ return Text.literal("x: ${position.x}, y: ${position.y}, z: ${position.z}")
+ }
}
diff --git a/src/main/kotlin/util/MC.kt b/src/main/kotlin/util/MC.kt
index 215d2a8..c1a5e65 100644
--- a/src/main/kotlin/util/MC.kt
+++ b/src/main/kotlin/util/MC.kt
@@ -7,11 +7,13 @@ import net.minecraft.client.gui.hud.InGameHud
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.client.network.ClientPlayerEntity
+import net.minecraft.client.render.GameRenderer
import net.minecraft.client.render.WorldRenderer
import net.minecraft.client.render.item.ItemRenderer
import net.minecraft.client.world.ClientWorld
import net.minecraft.entity.Entity
import net.minecraft.item.Item
+import net.minecraft.item.ItemStack
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
import net.minecraft.registry.BuiltinRegistries
import net.minecraft.registry.RegistryKeys
@@ -85,6 +87,7 @@ object MC {
inline val resourceManager get() = (instance.resourceManager as ReloadableResourceManagerImpl)
inline val itemRenderer: ItemRenderer get() = instance.itemRenderer
inline val worldRenderer: WorldRenderer get() = instance.worldRenderer
+ inline val gameRenderer: GameRenderer get() = instance.gameRenderer
inline val networkHandler get() = player?.networkHandler
inline val instance get() = MinecraftClient.getInstance()
inline val keyboard get() = instance.keyboard
@@ -96,6 +99,7 @@ object MC {
inline val soundManager get() = instance.soundManager
inline val player: ClientPlayerEntity? get() = TestUtil.unlessTesting { instance.player }
inline val camera: Entity? get() = instance.cameraEntity
+ inline val stackInHand: ItemStack get() = player?.inventory?.mainHandStack ?: ItemStack.EMPTY
inline val guiAtlasManager get() = instance.guiAtlasManager
inline val world: ClientWorld? get() = TestUtil.unlessTesting { instance.world }
inline val playerName: String? get() = player?.name?.unformattedString
@@ -109,6 +113,7 @@ object MC {
val defaultRegistries: RegistryWrapper.WrapperLookup by lazy { BuiltinRegistries.createWrapperLookup() }
inline val currentOrDefaultRegistries get() = currentRegistries ?: defaultRegistries
val defaultItems: RegistryWrapper.Impl<Item> by lazy { defaultRegistries.getOrThrow(RegistryKeys.ITEM) }
+ var currentTick = 0
var lastWorld: World? = null
get() {
field = world ?: field
diff --git a/src/main/kotlin/util/SBData.kt b/src/main/kotlin/util/SBData.kt
index b2f9449..1a4734c 100644
--- a/src/main/kotlin/util/SBData.kt
+++ b/src/main/kotlin/util/SBData.kt
@@ -31,6 +31,10 @@ object SBData {
val hypixelTimeZone = ZoneId.of("US/Eastern")
private var hasReceivedProfile = false
var locraw: Locraw? = null
+
+ /**
+ * The current server location the player is in. This will be null outside of SkyBlock.
+ */
val skyblockLocation: SkyBlockIsland? get() = locraw?.skyblockLocation
val hasValidLocraw get() = locraw?.server !in listOf("limbo", null)
val isOnSkyblock get() = locraw?.gametype == "SKYBLOCK"
diff --git a/src/main/kotlin/util/SkyBlockIsland.kt b/src/main/kotlin/util/SkyBlockIsland.kt
index a86543c..e7f955a 100644
--- a/src/main/kotlin/util/SkyBlockIsland.kt
+++ b/src/main/kotlin/util/SkyBlockIsland.kt
@@ -1,4 +1,3 @@
-
package moe.nea.firmament.util
import kotlinx.serialization.KSerializer
@@ -13,33 +12,41 @@ import moe.nea.firmament.repo.RepoManager
@Serializable(with = SkyBlockIsland.Serializer::class)
class SkyBlockIsland
private constructor(
- val locrawMode: String,
+ val locrawMode: String,
) {
- object Serializer : KSerializer<SkyBlockIsland> {
- override val descriptor: SerialDescriptor
- get() = PrimitiveSerialDescriptor("SkyBlockIsland", PrimitiveKind.STRING)
-
- override fun deserialize(decoder: Decoder): SkyBlockIsland {
- return forMode(decoder.decodeString())
- }
-
- override fun serialize(encoder: Encoder, value: SkyBlockIsland) {
- encoder.encodeString(value.locrawMode)
- }
- }
- companion object {
- private val allIslands = mutableMapOf<String, SkyBlockIsland>()
- fun forMode(mode: String): SkyBlockIsland = allIslands.computeIfAbsent(mode, ::SkyBlockIsland)
- val HUB = forMode("hub")
- val PRIVATE_ISLAND = forMode("dynamic")
- val RIFT = forMode("rift")
- val MINESHAFT = forMode("mineshaft")
- val GARDEN = forMode("garden")
- val DUNGEON = forMode("dungeon")
- }
-
- val userFriendlyName
- get() = RepoManager.neuRepo.constants.islands.areaNames
- .getOrDefault(locrawMode, locrawMode)
+ object Serializer : KSerializer<SkyBlockIsland> {
+ override val descriptor: SerialDescriptor
+ get() = PrimitiveSerialDescriptor("SkyBlockIsland", PrimitiveKind.STRING)
+
+ override fun deserialize(decoder: Decoder): SkyBlockIsland {
+ return forMode(decoder.decodeString())
+ }
+
+ override fun serialize(encoder: Encoder, value: SkyBlockIsland) {
+ encoder.encodeString(value.locrawMode)
+ }
+ }
+
+ companion object {
+ private val allIslands = mutableMapOf<String, SkyBlockIsland>()
+ fun forMode(mode: String): SkyBlockIsland = allIslands.computeIfAbsent(mode, ::SkyBlockIsland)
+ val HUB = forMode("hub")
+ val DWARVEN_MINES = forMode("dwarven_mines")
+ val CRYSTAL_HOLLOWS = forMode("crystal_hollows")
+ val CRIMSON_ISLE = forMode("crimson_isle")
+ val PRIVATE_ISLAND = forMode("dynamic")
+ val RIFT = forMode("rift")
+ val MINESHAFT = forMode("mineshaft")
+ val GARDEN = forMode("garden")
+ val DUNGEON = forMode("dungeon")
+ val NIL = forMode("_")
+ }
+
+ val hasCustomMining
+ get() = RepoManager.miningData.customMiningAreas[this]?.isSpecialMining ?: false
+
+ val userFriendlyName
+ get() = RepoManager.neuRepo.constants.islands.areaNames
+ .getOrDefault(locrawMode, locrawMode)
}
diff --git a/src/main/kotlin/util/SkyblockId.kt b/src/main/kotlin/util/SkyblockId.kt
index a99afda..a31255c 100644
--- a/src/main/kotlin/util/SkyblockId.kt
+++ b/src/main/kotlin/util/SkyblockId.kt
@@ -34,7 +34,7 @@ import moe.nea.firmament.util.json.DashlessUUIDSerializer
*/
@JvmInline
@Serializable
-value class SkyblockId(val neuItem: String) {
+value class SkyblockId(val neuItem: String) : Comparable<SkyblockId> {
val identifier
get() = Identifier.of("skyblockitem",
neuItem.lowercase().replace(";", "__")
@@ -48,6 +48,10 @@ value class SkyblockId(val neuItem: String) {
return neuItem
}
+ override fun compareTo(other: SkyblockId): Int {
+ return neuItem.compareTo(other.neuItem)
+ }
+
/**
* A bazaar stock item id, as returned by the HyPixel bazaar api endpoint.
* These are not equivalent to the in-game ids, or the NEU repo ids, and in fact, do not refer to items, but instead
diff --git a/src/main/kotlin/util/compatloader/CompatLoader.kt b/src/main/kotlin/util/compatloader/CompatLoader.kt
index 6b60e87..d1073af 100644
--- a/src/main/kotlin/util/compatloader/CompatLoader.kt
+++ b/src/main/kotlin/util/compatloader/CompatLoader.kt
@@ -6,7 +6,7 @@ import kotlin.reflect.KClass
import kotlin.streams.asSequence
import moe.nea.firmament.Firmament
-abstract class CompatLoader<T : Any>(val kClass: Class<T>) {
+open class CompatLoader<T : Any>(val kClass: Class<T>) {
constructor(kClass: KClass<T>) : this(kClass.java)
val loader: ServiceLoader<T> = ServiceLoader.load(kClass)
diff --git a/src/main/kotlin/util/compatloader/CompatMeta.kt b/src/main/kotlin/util/compatloader/CompatMeta.kt
new file mode 100644
index 0000000..cf63645
--- /dev/null
+++ b/src/main/kotlin/util/compatloader/CompatMeta.kt
@@ -0,0 +1,48 @@
+package moe.nea.firmament.util.compatloader
+
+import java.util.ServiceLoader
+import moe.nea.firmament.events.subscription.SubscriptionList
+import moe.nea.firmament.init.AutoDiscoveryPlugin
+import moe.nea.firmament.util.ErrorUtil
+
+/**
+ * Declares the compat meta interface for the current source set.
+ * This is used by [CompatLoader], [SubscriptionList], and [AutoDiscoveryPlugin]. Annotate a [ICompatMeta] object with
+ * this.
+ */
+annotation class CompatMeta
+
+interface ICompatMetaGen {
+ fun owns(className: String): Boolean
+ val meta: ICompatMeta
+}
+
+interface ICompatMeta {
+ fun shouldLoad(): Boolean
+
+ companion object {
+ val allMetas = ServiceLoader
+ .load(ICompatMetaGen::class.java)
+ .toList()
+
+ fun shouldLoad(className: String): Boolean {
+ // TODO: replace this with a more performant package lookup
+ val meta = if (ErrorUtil.aggressiveErrors) {
+ val fittingMetas = allMetas.filter { it.owns(className) }
+ require(fittingMetas.size == 1) { "Orphaned or duplicate owned class $className (${fittingMetas.map { it.meta }}). Consider adding a @CompatMeta object." }
+ fittingMetas.single()
+ } else {
+ allMetas.firstOrNull { it.owns(className) }
+ }
+ return meta?.meta?.shouldLoad() ?: true
+ }
+ }
+}
+
+object CompatHelper {
+ fun isOwnedByPackage(className: String, vararg packages: String): Boolean {
+ // TODO: create package lookup structure once
+ val packageName = className.substringBeforeLast('.')
+ return packageName in packages
+ }
+}
diff --git a/src/main/kotlin/util/data/MultiFileDataHolder.kt b/src/main/kotlin/util/data/MultiFileDataHolder.kt
new file mode 100644
index 0000000..94c6f05
--- /dev/null
+++ b/src/main/kotlin/util/data/MultiFileDataHolder.kt
@@ -0,0 +1,63 @@
+package moe.nea.firmament.util.data
+
+import kotlinx.serialization.KSerializer
+import kotlin.io.path.createDirectories
+import kotlin.io.path.deleteExisting
+import kotlin.io.path.exists
+import kotlin.io.path.extension
+import kotlin.io.path.listDirectoryEntries
+import kotlin.io.path.nameWithoutExtension
+import kotlin.io.path.readText
+import kotlin.io.path.writeText
+import moe.nea.firmament.Firmament
+
+abstract class MultiFileDataHolder<T>(
+ val dataSerializer: KSerializer<T>,
+ val configName: String
+) { // TODO: abstract this + ProfileSpecificDataHolder
+ val configDirectory = Firmament.CONFIG_DIR.resolve(configName)
+ private var allData = readValues()
+ protected fun readValues(): MutableMap<String, T> {
+ if (!configDirectory.exists()) {
+ configDirectory.createDirectories()
+ }
+ val profileFiles = configDirectory.listDirectoryEntries()
+ return profileFiles
+ .filter { it.extension == "json" }
+ .mapNotNull {
+ try {
+ it.nameWithoutExtension to Firmament.json.decodeFromString(dataSerializer, it.readText())
+ } catch (e: Exception) { /* Expecting IOException and SerializationException, but Kotlin doesn't allow multi catches*/
+ IDataHolder.badLoads.add(configName)
+ Firmament.logger.error(
+ "Exception during loading of multi file data holder $it ($configName). This will reset that profiles config.",
+ e
+ )
+ null
+ }
+ }.toMap().toMutableMap()
+ }
+
+ fun save() {
+ if (!configDirectory.exists()) {
+ configDirectory.createDirectories()
+ }
+ val c = allData
+ configDirectory.listDirectoryEntries().forEach {
+ if (it.nameWithoutExtension !in c.mapKeys { it.toString() }) {
+ it.deleteExisting()
+ }
+ }
+ c.forEach { (name, value) ->
+ val f = configDirectory.resolve("$name.json")
+ f.writeText(Firmament.json.encodeToString(dataSerializer, value))
+ }
+ }
+
+ fun list(): Map<String, T> = allData
+ val validPathRegex = "[a-zA-Z0-9_][a-zA-Z0-9\\-_.]*".toPattern()
+ fun insert(name: String, value: T) {
+ require(validPathRegex.matcher(name).matches()) { "Not a valid name: $name" }
+ allData[name] = value
+ }
+}
diff --git a/src/main/kotlin/util/mc/FirmamentDataComponentTypes.kt b/src/main/kotlin/util/mc/FirmamentDataComponentTypes.kt
index 012f52e..0866665 100644
--- a/src/main/kotlin/util/mc/FirmamentDataComponentTypes.kt
+++ b/src/main/kotlin/util/mc/FirmamentDataComponentTypes.kt
@@ -1,12 +1,15 @@
package moe.nea.firmament.util.mc
import com.mojang.serialization.Codec
+import io.netty.buffer.ByteBuf
import net.minecraft.component.ComponentType
+import net.minecraft.network.codec.PacketCodec
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ClientInitEvent
+import moe.nea.firmament.repo.MiningRepoData
object FirmamentDataComponentTypes {
@@ -26,11 +29,32 @@ object FirmamentDataComponentTypes {
)
}
+ fun <T> errorCodec(message: String): PacketCodec<in ByteBuf, T> =
+ object : PacketCodec<ByteBuf, T> {
+ override fun decode(buf: ByteBuf?): T? {
+ error(message)
+ }
+
+ override fun encode(buf: ByteBuf?, value: T?) {
+ error(message)
+ }
+ }
+
+ fun <T, B : ComponentType.Builder<T>> B.neverEncode(message: String = "This element should never be encoded or decoded"): B {
+ packetCodec(errorCodec(message))
+ codec(null)
+ return this
+ }
+
val IS_BROKEN = register<Boolean>(
"is_broken"
) {
it.codec(Codec.BOOL.fieldOf("is_broken").codec())
}
+ val CUSTOM_MINING_BLOCK_DATA = register<MiningRepoData.CustomMiningBlock>("custom_mining_block") {
+ it.neverEncode()
+ }
+
}
diff --git a/src/main/kotlin/util/mc/asFakeServer.kt b/src/main/kotlin/util/mc/asFakeServer.kt
new file mode 100644
index 0000000..d3811bd
--- /dev/null
+++ b/src/main/kotlin/util/mc/asFakeServer.kt
@@ -0,0 +1,37 @@
+package moe.nea.firmament.util.mc
+
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
+import net.minecraft.server.command.CommandOutput
+import net.minecraft.server.command.ServerCommandSource
+import net.minecraft.text.Text
+
+fun FabricClientCommandSource.asFakeServer(): ServerCommandSource {
+ val source = this
+ return ServerCommandSource(
+ object : CommandOutput {
+ override fun sendMessage(message: Text?) {
+ source.player.sendMessage(message, false)
+ }
+
+ override fun shouldReceiveFeedback(): Boolean {
+ return true
+ }
+
+ override fun shouldTrackOutput(): Boolean {
+ return true
+ }
+
+ override fun shouldBroadcastConsoleToOps(): Boolean {
+ return true
+ }
+ },
+ source.position,
+ source.rotation,
+ null,
+ 0,
+ "FakeServerCommandSource",
+ Text.literal("FakeServerCommandSource"),
+ null,
+ source.player
+ )
+}
diff --git a/src/main/kotlin/util/regex.kt b/src/main/kotlin/util/regex.kt
index a44435c..f239810 100644
--- a/src/main/kotlin/util/regex.kt
+++ b/src/main/kotlin/util/regex.kt
@@ -16,12 +16,13 @@ import kotlin.time.Duration.Companion.seconds
inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? =
regex.matchEntire(this)?.let(block)
-inline fun <T> Pattern.useMatch(string: String, block: Matcher.() -> T): T? {
+inline fun <T> Pattern.useMatch(string: String?, block: Matcher.() -> T): T? {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
- return matcher(string)
- .takeIf(Matcher::matches)
+ return string
+ ?.let(this::matcher)
+ ?.takeIf(Matcher::matches)
?.let(block)
}
diff --git a/src/main/kotlin/util/render/TintedOverlayTexture.kt b/src/main/kotlin/util/render/TintedOverlayTexture.kt
new file mode 100644
index 0000000..a02eccc
--- /dev/null
+++ b/src/main/kotlin/util/render/TintedOverlayTexture.kt
@@ -0,0 +1,44 @@
+package moe.nea.firmament.util.render
+
+import com.mojang.blaze3d.platform.GlConst
+import com.mojang.blaze3d.systems.RenderSystem
+import me.shedaniel.math.Color
+import net.minecraft.client.render.OverlayTexture
+import net.minecraft.util.math.ColorHelper
+import moe.nea.firmament.util.ErrorUtil
+
+class TintedOverlayTexture : OverlayTexture() {
+ companion object {
+ val size = 16
+ }
+
+ private var lastColor: Color? = null
+ fun setColor(color: Color): TintedOverlayTexture {
+ val image = ErrorUtil.notNullOr(texture.image, "Disposed TintedOverlayTexture written to") { return this }
+ if (color == lastColor) return this
+ lastColor = color
+
+ for (i in 0..<size) {
+ for (j in 0..<size) {
+ if (i < 8) {
+ image.setColorArgb(j, i, 0xB2FF0000.toInt())
+ } else {
+ val k = ((1F - j / 15F * 0.75F) * 255F).toInt()
+ image.setColorArgb(j, i, ColorHelper.withAlpha(k, color.color))
+ }
+ }
+ }
+
+ RenderSystem.activeTexture(GlConst.GL_TEXTURE1)
+ texture.bindTexture()
+ texture.setFilter(false, false)
+ texture.setClamp(true)
+ image.upload(0,
+ 0, 0,
+ 0, 0,
+ image.width, image.height,
+ false)
+ RenderSystem.activeTexture(GlConst.GL_TEXTURE0)
+ return this
+ }
+}
diff --git a/src/main/kotlin/util/skyblock/SkyBlockItems.kt b/src/main/kotlin/util/skyblock/SkyBlockItems.kt
index cfd8429..ca2b17b 100644
--- a/src/main/kotlin/util/skyblock/SkyBlockItems.kt
+++ b/src/main/kotlin/util/skyblock/SkyBlockItems.kt
@@ -8,4 +8,9 @@ object SkyBlockItems {
val DIAMOND = SkyblockId("DIAMOND")
val ANCESTRAL_SPADE = SkyblockId("ANCESTRAL_SPADE")
val REFORGE_ANVIL = SkyblockId("REFORGE_ANVIL")
+ val SLICE_OF_BLUEBERRY_CAKE = SkyblockId("SLICE_OF_BLUEBERRY_CAKE")
+ val SLICE_OF_CHEESECAKE = SkyblockId("SLICE_OF_CHEESECAKE")
+ val SLICE_OF_GREEN_VELVET_CAKE = SkyblockId("SLICE_OF_GREEN_VELVET_CAKE")
+ val SLICE_OF_RED_VELVET_CAKE = SkyblockId("SLICE_OF_RED_VELVET_CAKE")
+ val SLICE_OF_STRAWBERRY_SHORTCAKE = SkyblockId("SLICE_OF_STRAWBERRY_SHORTCAKE")
}
diff --git a/src/main/kotlin/util/textutil.kt b/src/main/kotlin/util/textutil.kt
index c295ae0..806f61e 100644
--- a/src/main/kotlin/util/textutil.kt
+++ b/src/main/kotlin/util/textutil.kt
@@ -2,6 +2,7 @@ package moe.nea.firmament.util
import java.util.Optional
import net.minecraft.text.ClickEvent
+import net.minecraft.text.HoverEvent
import net.minecraft.text.MutableText
import net.minecraft.text.OrderedText
import net.minecraft.text.PlainTextContent
@@ -126,6 +127,7 @@ fun MutableText.darkGrey() = withColor(Formatting.DARK_GRAY)
fun MutableText.red() = withColor(Formatting.RED)
fun MutableText.white() = withColor(Formatting.WHITE)
fun MutableText.bold(): MutableText = styled { it.withBold(true) }
+fun MutableText.hover(text: Text): MutableText = styled {it.withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, text))}
fun MutableText.clickCommand(command: String): MutableText {