diff options
Diffstat (limited to 'src/main/kotlin/util')
-rw-r--r-- | src/main/kotlin/util/FirmFormatters.kt | 4 | ||||
-rw-r--r-- | src/main/kotlin/util/MC.kt | 5 | ||||
-rw-r--r-- | src/main/kotlin/util/SBData.kt | 4 | ||||
-rw-r--r-- | src/main/kotlin/util/SkyBlockIsland.kt | 63 | ||||
-rw-r--r-- | src/main/kotlin/util/SkyblockId.kt | 6 | ||||
-rw-r--r-- | src/main/kotlin/util/compatloader/CompatLoader.kt | 2 | ||||
-rw-r--r-- | src/main/kotlin/util/compatloader/CompatMeta.kt | 48 | ||||
-rw-r--r-- | src/main/kotlin/util/data/MultiFileDataHolder.kt | 63 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/FirmamentDataComponentTypes.kt | 24 | ||||
-rw-r--r-- | src/main/kotlin/util/mc/asFakeServer.kt | 37 | ||||
-rw-r--r-- | src/main/kotlin/util/regex.kt | 7 | ||||
-rw-r--r-- | src/main/kotlin/util/render/TintedOverlayTexture.kt | 44 | ||||
-rw-r--r-- | src/main/kotlin/util/skyblock/SkyBlockItems.kt | 5 | ||||
-rw-r--r-- | src/main/kotlin/util/textutil.kt | 2 |
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 { |