Fairy souls
This commit is contained in:
@@ -4,3 +4,11 @@
|
|||||||
|
|
||||||
### Building your own
|
### Building your own
|
||||||
|
|
||||||
|
Use Java 17.
|
||||||
|
|
||||||
|
This depends on [neurepoparsing](https://git.nea.moe/nea/neurepoparsing/). Please clone that repository and make it available in your local maven repository using `./gradlew publishToMavenLocal`. This will be automated at a later stage.
|
||||||
|
|
||||||
|
|
||||||
|
Afterwards, running `./gradlew build` will create a mod jar in `build/libs`
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ plugins {
|
|||||||
id("dev.architectury.loom") version "0.12.0.+"
|
id("dev.architectury.loom") version "0.12.0.+"
|
||||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||||
id("moe.nea.licenseextractificator") version "fffc76c"
|
id("moe.nea.licenseextractificator") version "fffc76c"
|
||||||
id("com.github.eutro.hierarchical-lang") version "1.1.3"
|
id("io.github.juuxel.loom-quiltflower") version "1.7.3"
|
||||||
id("io.github.juuxel.loom-quiltflower") version "1.7.2"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loom {
|
loom {
|
||||||
@@ -24,6 +23,12 @@ loom {
|
|||||||
property("notenoughupdates.debug", "true")
|
property("notenoughupdates.debug", "true")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
runs {
|
||||||
|
named("client") {
|
||||||
|
vmArg("-XX:+AllowEnhancedClassRedefinition")
|
||||||
|
vmArg("-XX:HotswapAgent=fatjar")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@@ -59,12 +64,16 @@ dependencies {
|
|||||||
modImplementation("net.fabricmc:fabric-loader:${project.property("fabric_loader_version")}")
|
modImplementation("net.fabricmc:fabric-loader:${project.property("fabric_loader_version")}")
|
||||||
modApi("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_api_version")}")
|
modApi("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_api_version")}")
|
||||||
modImplementation("net.fabricmc:fabric-language-kotlin:${project.property("fabric_kotlin_version")}")
|
modImplementation("net.fabricmc:fabric-language-kotlin:${project.property("fabric_kotlin_version")}")
|
||||||
|
modApi("dev.architectury:architectury:6.2.46")
|
||||||
|
|
||||||
// Actual dependencies
|
// Actual dependencies
|
||||||
modCompileOnly("me.shedaniel:RoughlyEnoughItems-api:${rootProject.property("rei_version")}")
|
modCompileOnly("me.shedaniel:RoughlyEnoughItems-api:${rootProject.property("rei_version")}") {
|
||||||
|
exclude(module = "architectury")
|
||||||
|
exclude(module = "architectury-fabric")
|
||||||
|
}
|
||||||
shadowMe("io.github.moulberry:neurepoparser:0.0.1")
|
shadowMe("io.github.moulberry:neurepoparser:0.0.1")
|
||||||
shadowMe("com.github.hypfvieh:dbus-java-core:4.1.0")
|
shadowMe("com.github.hypfvieh:dbus-java-core:${rootProject.property("dbus_java_version")}")
|
||||||
shadowMe("com.github.hypfvieh:dbus-java-transport-native-unixsocket:4.1.0")
|
shadowMe("com.github.hypfvieh:dbus-java-transport-native-unixsocket:${rootProject.property("dbus_java_version")}")
|
||||||
fun ktor(mod: String) = "io.ktor:ktor-$mod-jvm:${project.property("ktor_version")}"
|
fun ktor(mod: String) = "io.ktor:ktor-$mod-jvm:${project.property("ktor_version")}"
|
||||||
|
|
||||||
transInclude(implementation(ktor("client-core"))!!)
|
transInclude(implementation(ktor("client-core"))!!)
|
||||||
@@ -74,7 +83,11 @@ dependencies {
|
|||||||
modImplementation(include("io.github.cottonmc:LibGui:${project.property("libgui_version")}")!!)
|
modImplementation(include("io.github.cottonmc:LibGui:${project.property("libgui_version")}")!!)
|
||||||
|
|
||||||
// Dev environment preinstalled mods
|
// Dev environment preinstalled mods
|
||||||
modRuntimeOnly("me.shedaniel:RoughlyEnoughItems-fabric:${project.property("rei_version")}")
|
modRuntimeOnly("dev.architectury:architectury-fabric:6.2.46")
|
||||||
|
modRuntimeOnly("me.shedaniel:RoughlyEnoughItems-fabric:${project.property("rei_version")}") {
|
||||||
|
exclude(module = "architectury")
|
||||||
|
exclude(module = "architectury-fabric")
|
||||||
|
}
|
||||||
modRuntimeOnly("me.djtheredstoner:DevAuth-fabric:${project.property("devauth_version")}")
|
modRuntimeOnly("me.djtheredstoner:DevAuth-fabric:${project.property("devauth_version")}")
|
||||||
modRuntimeOnly("maven.modrinth:modmenu:${project.property("modmenu_version")}")
|
modRuntimeOnly("maven.modrinth:modmenu:${project.property("modmenu_version")}")
|
||||||
|
|
||||||
@@ -126,7 +139,7 @@ tasks.processResources {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
filesMatching("**/lang/*.json") {
|
filesMatching("**/lang/*.json") {
|
||||||
flattenJson(this)
|
// flattenJson(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ archives_base_name=notenoughupdates
|
|||||||
mod_version=1.0.0
|
mod_version=1.0.0
|
||||||
maven_group=moe.nea.notenoughupdates
|
maven_group=moe.nea.notenoughupdates
|
||||||
|
|
||||||
architectury_version=5.10.33
|
fabric_loader_version=0.14.9
|
||||||
|
fabric_api_version=0.60.0+1.19.2
|
||||||
fabric_loader_version=0.14.8
|
|
||||||
fabric_api_version=0.58.0+1.19
|
|
||||||
fabric_kotlin_version=1.8.2+kotlin.1.7.10
|
fabric_kotlin_version=1.8.2+kotlin.1.7.10
|
||||||
yarn_version=1.19.2+build.9
|
yarn_version=1.19.2+build.9
|
||||||
|
|
||||||
libgui_version=6.0.0+1.19
|
libgui_version=6.1.0+1.19
|
||||||
rei_version=9.1.518
|
rei_version=9.1.537
|
||||||
devauth_version=1.0.0
|
devauth_version=1.0.0
|
||||||
modmenu_version=4.0.4
|
modmenu_version=4.0.6
|
||||||
ktor_version=2.0.3
|
ktor_version=2.0.3
|
||||||
|
|
||||||
|
dbus_java_version=4.2.0
|
||||||
|
|||||||
@@ -5,12 +5,8 @@ import io.ktor.client.*
|
|||||||
import io.ktor.client.plugins.*
|
import io.ktor.client.plugins.*
|
||||||
import io.ktor.client.plugins.contentnegotiation.*
|
import io.ktor.client.plugins.contentnegotiation.*
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
import kotlinx.coroutines.*
|
import java.nio.file.Files
|
||||||
import kotlinx.serialization.json.Json
|
import java.nio.file.Path
|
||||||
import moe.nea.notenoughupdates.commands.registerNeuCommand
|
|
||||||
import moe.nea.notenoughupdates.dbus.NEUDbusObject
|
|
||||||
import moe.nea.notenoughupdates.repo.RepoManager
|
|
||||||
import moe.nea.notenoughupdates.util.ConfigHolder
|
|
||||||
import net.fabricmc.api.ClientModInitializer
|
import net.fabricmc.api.ClientModInitializer
|
||||||
import net.fabricmc.api.ModInitializer
|
import net.fabricmc.api.ModInitializer
|
||||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback
|
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback
|
||||||
@@ -19,13 +15,18 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
|
|||||||
import net.fabricmc.loader.api.FabricLoader
|
import net.fabricmc.loader.api.FabricLoader
|
||||||
import net.fabricmc.loader.api.Version
|
import net.fabricmc.loader.api.Version
|
||||||
import net.fabricmc.loader.api.metadata.ModMetadata
|
import net.fabricmc.loader.api.metadata.ModMetadata
|
||||||
import net.minecraft.command.CommandRegistryAccess
|
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder
|
import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder
|
||||||
import java.nio.file.Files
|
import kotlinx.coroutines.*
|
||||||
import java.nio.file.Path
|
import kotlinx.serialization.json.Json
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
import net.minecraft.command.CommandRegistryAccess
|
||||||
|
import moe.nea.notenoughupdates.commands.registerNeuCommand
|
||||||
|
import moe.nea.notenoughupdates.dbus.NEUDbusObject
|
||||||
import moe.nea.notenoughupdates.features.FeatureManager
|
import moe.nea.notenoughupdates.features.FeatureManager
|
||||||
|
import moe.nea.notenoughupdates.repo.RepoManager
|
||||||
|
import moe.nea.notenoughupdates.util.SBData
|
||||||
|
import moe.nea.notenoughupdates.util.config.IConfigHolder
|
||||||
|
|
||||||
object NotEnoughUpdates : ModInitializer, ClientModInitializer {
|
object NotEnoughUpdates : ModInitializer, ClientModInitializer {
|
||||||
const val MOD_ID = "notenoughupdates"
|
const val MOD_ID = "notenoughupdates"
|
||||||
@@ -59,7 +60,6 @@ object NotEnoughUpdates : ModInitializer, ClientModInitializer {
|
|||||||
.build()
|
.build()
|
||||||
val coroutineScope =
|
val coroutineScope =
|
||||||
CoroutineScope(EmptyCoroutineContext + CoroutineName("NotEnoughUpdates")) + SupervisorJob(globalJob)
|
CoroutineScope(EmptyCoroutineContext + CoroutineName("NotEnoughUpdates")) + SupervisorJob(globalJob)
|
||||||
val coroutineScopeIo = coroutineScope + Dispatchers.IO + SupervisorJob(globalJob)
|
|
||||||
|
|
||||||
private fun registerCommands(
|
private fun registerCommands(
|
||||||
dispatcher: CommandDispatcher<FabricClientCommandSource>,
|
dispatcher: CommandDispatcher<FabricClientCommandSource>,
|
||||||
@@ -72,8 +72,9 @@ object NotEnoughUpdates : ModInitializer, ClientModInitializer {
|
|||||||
override fun onInitialize() {
|
override fun onInitialize() {
|
||||||
dbusConnection.requestBusName("moe.nea.notenoughupdates")
|
dbusConnection.requestBusName("moe.nea.notenoughupdates")
|
||||||
dbusConnection.exportObject(NEUDbusObject)
|
dbusConnection.exportObject(NEUDbusObject)
|
||||||
ConfigHolder.registerEvents()
|
IConfigHolder.registerEvents()
|
||||||
RepoManager.initialize()
|
RepoManager.initialize()
|
||||||
|
SBData.init()
|
||||||
FeatureManager.autoload()
|
FeatureManager.autoload()
|
||||||
ClientCommandRegistrationCallback.EVENT.register(this::registerCommands)
|
ClientCommandRegistrationCallback.EVENT.register(this::registerCommands)
|
||||||
ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping {
|
ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping {
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ package moe.nea.notenoughupdates.commands
|
|||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher
|
import com.mojang.brigadier.CommandDispatcher
|
||||||
import io.github.cottonmc.cotton.gui.client.CottonClientScreen
|
import io.github.cottonmc.cotton.gui.client.CottonClientScreen
|
||||||
import moe.nea.notenoughupdates.gui.repoGui
|
|
||||||
import moe.nea.notenoughupdates.repo.RepoManager
|
|
||||||
import moe.nea.notenoughupdates.util.ScreenUtil.setScreenLater
|
|
||||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.notenoughupdates.gui.repoGui
|
||||||
|
import moe.nea.notenoughupdates.repo.RepoManager
|
||||||
|
import moe.nea.notenoughupdates.util.SBData
|
||||||
|
import moe.nea.notenoughupdates.util.ScreenUtil.setScreenLater
|
||||||
|
|
||||||
|
|
||||||
fun neuCommand() = literal("neu") {
|
fun neuCommand() = literal("neu") {
|
||||||
@@ -27,6 +28,23 @@ fun neuCommand() = literal("neu") {
|
|||||||
setScreenLater(CottonClientScreen(repoGui()))
|
setScreenLater(CottonClientScreen(repoGui()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
thenLiteral("dev") {
|
||||||
|
val sbData = thenLiteral("sbdata") {
|
||||||
|
thenExecute {
|
||||||
|
source.sendFeedback(Text.translatable("notenoughupdates.sbinfo.profile", SBData.profileCuteName))
|
||||||
|
val locrawInfo = SBData.locraw
|
||||||
|
if (locrawInfo == null) {
|
||||||
|
source.sendFeedback(Text.translatable("notenoughupdates.sbinfo.nolocraw"))
|
||||||
|
} else {
|
||||||
|
source.sendFeedback(Text.translatable("notenoughupdates.sbinfo.server", locrawInfo.server))
|
||||||
|
source.sendFeedback(Text.translatable("notenoughupdates.sbinfo.gametype", locrawInfo.gametype))
|
||||||
|
source.sendFeedback(Text.translatable("notenoughupdates.sbinfo.mode", locrawInfo.mode))
|
||||||
|
source.sendFeedback(Text.translatable("notenoughupdates.sbinfo.map", locrawInfo.map))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,36 @@
|
|||||||
package moe.nea.notenoughupdates.events
|
package moe.nea.notenoughupdates.events
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that can be fired by a [NEUEventBus].
|
||||||
|
*
|
||||||
|
* Typically, that event bus is implemented as a companion object
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* class SomeEvent : NEUEvent() {
|
||||||
|
* companion object : NEUEventBus<SomeEvent>()
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
abstract class NEUEvent {
|
abstract class NEUEvent {
|
||||||
|
/**
|
||||||
|
* A [NEUEvent] that can be [cancelled]
|
||||||
|
*/
|
||||||
abstract class Cancellable : NEUEvent() {
|
abstract class Cancellable : NEUEvent() {
|
||||||
|
/**
|
||||||
|
* Cancels this is event.
|
||||||
|
*
|
||||||
|
* @see cancelled
|
||||||
|
*/
|
||||||
|
fun cancel() {
|
||||||
|
cancelled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this event is cancelled.
|
||||||
|
*
|
||||||
|
* Cancelled events will bypass handlers unless otherwise specified and will prevent the action that this
|
||||||
|
* event was originally fired for.
|
||||||
|
*/
|
||||||
var cancelled: Boolean = false
|
var cancelled: Boolean = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
package moe.nea.notenoughupdates.events
|
package moe.nea.notenoughupdates.events
|
||||||
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pubsub event bus.
|
||||||
|
*
|
||||||
|
* [subscribe] to events [publish]ed on this event bus.
|
||||||
|
* Subscriptions may not necessarily be delivered in the order or registering.
|
||||||
|
*/
|
||||||
open class NEUEventBus<T : NEUEvent> {
|
open class NEUEventBus<T : NEUEvent> {
|
||||||
data class Handler<T>(val invocation: (T) -> Unit, val receivesCancelled: Boolean)
|
data class Handler<T>(val invocation: (T) -> Unit, val receivesCancelled: Boolean)
|
||||||
|
|
||||||
@@ -17,7 +24,11 @@ open class NEUEventBus<T : NEUEvent> {
|
|||||||
fun publish(event: T): T {
|
fun publish(event: T): T {
|
||||||
for (function in toHandle) {
|
for (function in toHandle) {
|
||||||
if (function.receivesCancelled || event !is NEUEvent.Cancellable || !event.cancelled) {
|
if (function.receivesCancelled || event !is NEUEvent.Cancellable || !event.cancelled) {
|
||||||
function.invocation(event)
|
try {
|
||||||
|
function.invocation(event)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
NotEnoughUpdates.logger.error("Caught exception during processing event $event", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return event
|
return event
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package moe.nea.notenoughupdates.events
|
||||||
|
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.notenoughupdates.util.unformattedString
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event gets published whenever the client receives a chat message from the server.
|
||||||
|
*/
|
||||||
|
data class ServerChatLineReceivedEvent(val text: Text) : NEUEvent.Cancellable() {
|
||||||
|
companion object : NEUEventBus<ServerChatLineReceivedEvent>()
|
||||||
|
|
||||||
|
val unformattedString = text.unformattedString
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package moe.nea.notenoughupdates.events
|
||||||
|
|
||||||
|
import moe.nea.notenoughupdates.util.Locraw
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event gets published whenever `/locraw` is queried and HyPixel returns a location different to the old one.
|
||||||
|
*
|
||||||
|
* **N.B.:** This event may get fired multiple times while on the server (for example, first to null, then to the
|
||||||
|
* correct location).
|
||||||
|
*/
|
||||||
|
data class SkyblockServerUpdateEvent(val oldLocraw: Locraw?, val newLocraw: Locraw?) : NEUEvent() {
|
||||||
|
companion object : NEUEventBus<SkyblockServerUpdateEvent>()
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package moe.nea.notenoughupdates.events
|
||||||
|
|
||||||
|
import net.minecraft.client.render.Camera
|
||||||
|
import net.minecraft.client.render.GameRenderer
|
||||||
|
import net.minecraft.client.render.LightmapTextureManager
|
||||||
|
import net.minecraft.client.util.math.MatrixStack
|
||||||
|
import net.minecraft.util.math.Matrix4f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is called after all world rendering is done, but before any GUI rendering (including hand) has been done.
|
||||||
|
*/
|
||||||
|
data class WorldRenderLastEvent(
|
||||||
|
val matrices: MatrixStack,
|
||||||
|
val tickDelta: Float,
|
||||||
|
val renderBlockOutline: Boolean,
|
||||||
|
val camera: Camera,
|
||||||
|
val gameRenderer: GameRenderer,
|
||||||
|
val lightmapTextureManager: LightmapTextureManager,
|
||||||
|
val positionMatrix: Matrix4f,
|
||||||
|
) : NEUEvent() {
|
||||||
|
companion object : NEUEventBus<WorldRenderLastEvent>()
|
||||||
|
}
|
||||||
@@ -1,27 +1,42 @@
|
|||||||
package moe.nea.notenoughupdates.features
|
package moe.nea.notenoughupdates.features
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.serializer
|
import kotlinx.serialization.serializer
|
||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
import moe.nea.notenoughupdates.features.world.FairySouls
|
import moe.nea.notenoughupdates.features.world.FairySouls
|
||||||
import moe.nea.notenoughupdates.util.ConfigHolder
|
import moe.nea.notenoughupdates.util.config.ConfigHolder
|
||||||
|
|
||||||
object FeatureManager : ConfigHolder<FeatureManager.Config>(serializer(), "features", ::Config) {
|
object FeatureManager : ConfigHolder<FeatureManager.Config>(serializer(), "features", ::Config) {
|
||||||
|
@Serializable
|
||||||
data class Config(
|
data class Config(
|
||||||
val enabledFeatures: MutableMap<String, Boolean> = mutableMapOf()
|
val enabledFeatures: MutableMap<String, Boolean> = mutableMapOf()
|
||||||
)
|
)
|
||||||
|
|
||||||
private val features = mutableMapOf<String, NEUFeature>()
|
private val features = mutableMapOf<String, NEUFeature>()
|
||||||
|
|
||||||
|
private var hasAutoloaded = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
autoload()
|
||||||
|
}
|
||||||
|
|
||||||
fun autoload() {
|
fun autoload() {
|
||||||
loadFeature(FairySouls)
|
synchronized(this) {
|
||||||
|
if (hasAutoloaded) return
|
||||||
|
loadFeature(FairySouls)
|
||||||
|
hasAutoloaded = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadFeature(feature: NEUFeature) {
|
fun loadFeature(feature: NEUFeature) {
|
||||||
if (feature.identifier in features) {
|
synchronized(features) {
|
||||||
NotEnoughUpdates.logger.error("Double registering feature ${feature.identifier}. Ignoring second instance $feature")
|
if (feature.identifier in features) {
|
||||||
return
|
NotEnoughUpdates.logger.error("Double registering feature ${feature.identifier}. Ignoring second instance $feature")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
features[feature.identifier] = feature
|
||||||
|
feature.onLoad()
|
||||||
}
|
}
|
||||||
features[feature.identifier] = feature
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isEnabled(identifier: String): Boolean? =
|
fun isEnabled(identifier: String): Boolean? =
|
||||||
|
|||||||
@@ -1,12 +1,103 @@
|
|||||||
package moe.nea.notenoughupdates.features.world
|
package moe.nea.notenoughupdates.features.world
|
||||||
|
|
||||||
|
import io.github.moulberry.repo.data.Coordinate
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import moe.nea.notenoughupdates.events.ServerChatLineReceivedEvent
|
||||||
|
import moe.nea.notenoughupdates.events.SkyblockServerUpdateEvent
|
||||||
|
import moe.nea.notenoughupdates.events.WorldRenderLastEvent
|
||||||
import moe.nea.notenoughupdates.features.NEUFeature
|
import moe.nea.notenoughupdates.features.NEUFeature
|
||||||
|
import moe.nea.notenoughupdates.repo.RepoManager
|
||||||
|
import moe.nea.notenoughupdates.util.MC
|
||||||
|
import moe.nea.notenoughupdates.util.SBData
|
||||||
|
import moe.nea.notenoughupdates.util.config.ProfileSpecificConfigHolder
|
||||||
|
import moe.nea.notenoughupdates.util.render.RenderBlockContext.Companion.renderBlocks
|
||||||
|
import moe.nea.notenoughupdates.util.unformattedString
|
||||||
|
|
||||||
|
val Coordinate.blockPos: BlockPos
|
||||||
|
get() = BlockPos(x, y, z)
|
||||||
|
|
||||||
|
object FairySouls : NEUFeature,
|
||||||
|
ProfileSpecificConfigHolder<FairySouls.Config>(serializer(), "fairy-souls.json", ::Config) {
|
||||||
|
@Serializable
|
||||||
|
data class Config(
|
||||||
|
val foundSouls: MutableMap<String, MutableSet<Int>> = mutableMapOf()
|
||||||
|
)
|
||||||
|
|
||||||
object FairySouls : NEUFeature {
|
|
||||||
override val name: String get() = "Fairy Souls"
|
override val name: String get() = "Fairy Souls"
|
||||||
override val identifier: String get() = "fairy-souls"
|
override val identifier: String get() = "fairy-souls"
|
||||||
|
|
||||||
override fun onLoad() {
|
val playerReach = 5
|
||||||
|
val playerReachSquared = playerReach * playerReach
|
||||||
|
|
||||||
|
var currentLocationName: String? = null
|
||||||
|
var currentLocationSouls: List<Coordinate> = emptyList()
|
||||||
|
var currentMissingSouls: List<Coordinate> = emptyList()
|
||||||
|
|
||||||
|
fun updateMissingSouls() {
|
||||||
|
currentMissingSouls = emptyList()
|
||||||
|
val c = config ?: return
|
||||||
|
val fi = c.foundSouls[currentLocationName] ?: setOf()
|
||||||
|
val cms = currentLocationSouls.toMutableList()
|
||||||
|
fi.asSequence().sortedDescending().filter { it in cms.indices }.forEach { cms.removeAt(it) }
|
||||||
|
currentMissingSouls = cms
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateWorldSouls() {
|
||||||
|
currentLocationSouls = emptyList()
|
||||||
|
val loc = currentLocationName ?: return
|
||||||
|
currentLocationSouls = RepoManager.neuRepo.constants.fairySouls.soulLocations[loc] ?: return
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findNearestClickableSoul(): Coordinate? {
|
||||||
|
val player = MC.player ?: return null
|
||||||
|
val pos = player.pos
|
||||||
|
val location = SBData.skyblockLocation ?: return null
|
||||||
|
val soulLocations: List<Coordinate> =
|
||||||
|
RepoManager.neuRepo.constants.fairySouls.soulLocations[location] ?: return null
|
||||||
|
return soulLocations
|
||||||
|
.map { it to it.blockPos.getSquaredDistance(pos) }
|
||||||
|
.filter { it.second < playerReachSquared }
|
||||||
|
.minByOrNull { it.second }
|
||||||
|
?.first
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun markNearestSoul() {
|
||||||
|
val nearestSoul = findNearestClickableSoul() ?: return
|
||||||
|
val c = config ?: return
|
||||||
|
val loc = currentLocationName ?: return
|
||||||
|
val idx = currentLocationSouls.indexOf(nearestSoul)
|
||||||
|
c.foundSouls.computeIfAbsent(loc) { mutableSetOf() }.add(idx)
|
||||||
|
markDirty()
|
||||||
|
updateMissingSouls()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onLoad() {
|
||||||
|
SkyblockServerUpdateEvent.subscribe {
|
||||||
|
currentLocationName = it.newLocraw?.skyblockLocation
|
||||||
|
updateWorldSouls()
|
||||||
|
updateMissingSouls()
|
||||||
|
}
|
||||||
|
ServerChatLineReceivedEvent.subscribe {
|
||||||
|
when (it.text.unformattedString) {
|
||||||
|
"You have already found that Fairy Soul!" -> {
|
||||||
|
markNearestSoul()
|
||||||
|
}
|
||||||
|
|
||||||
|
"SOUL! You found a Fairy Soul!" -> {
|
||||||
|
markNearestSoul()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WorldRenderLastEvent.subscribe {
|
||||||
|
renderBlocks(it.camera) {
|
||||||
|
color(1F, 1F, 0F, 0.8F)
|
||||||
|
currentMissingSouls.forEach {
|
||||||
|
block(it.blockPos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment
|
|||||||
import io.github.cottonmc.cotton.gui.widget.data.Insets
|
import io.github.cottonmc.cotton.gui.widget.data.Insets
|
||||||
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment
|
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment
|
||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
import moe.nea.notenoughupdates.util.ConfigHolder
|
import moe.nea.notenoughupdates.util.config.ConfigHolder
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package moe.nea.notenoughupdates.mixins
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin
|
||||||
|
import org.spongepowered.asm.mixin.injection.At
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo
|
||||||
|
import net.minecraft.client.network.message.MessageHandler
|
||||||
|
import net.minecraft.network.message.MessageType
|
||||||
|
import net.minecraft.network.message.SignedMessage
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.notenoughupdates.events.ServerChatLineReceivedEvent
|
||||||
|
|
||||||
|
@Mixin(MessageHandler::class)
|
||||||
|
class MixinMessageHandler {
|
||||||
|
@Inject(method = ["onChatMessage"], at = [At("HEAD")], cancellable = true)
|
||||||
|
fun onOnChatMessage(message: SignedMessage, params: MessageType.Parameters, ci: CallbackInfo) {
|
||||||
|
val decoratedText = params.applyChatDecoration(message.unsignedContent.orElse(message.content))
|
||||||
|
val event = ServerChatLineReceivedEvent(decoratedText)
|
||||||
|
if (ServerChatLineReceivedEvent.publish(event).cancelled) {
|
||||||
|
ci.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = ["onGameMessage"], at = [At("HEAD")], cancellable = true)
|
||||||
|
fun onOnGameMessage(message: Text, overlay: Boolean, ci: CallbackInfo) {
|
||||||
|
if (!overlay) {
|
||||||
|
val event = ServerChatLineReceivedEvent(message)
|
||||||
|
if (ServerChatLineReceivedEvent.publish(event).cancelled) {
|
||||||
|
ci.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package moe.nea.notenoughupdates.mixins
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin
|
||||||
|
import org.spongepowered.asm.mixin.injection.At
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo
|
||||||
|
import net.minecraft.client.render.Camera
|
||||||
|
import net.minecraft.client.render.GameRenderer
|
||||||
|
import net.minecraft.client.render.LightmapTextureManager
|
||||||
|
import net.minecraft.client.render.WorldRenderer
|
||||||
|
import net.minecraft.client.util.math.MatrixStack
|
||||||
|
import net.minecraft.util.math.Matrix4f
|
||||||
|
import moe.nea.notenoughupdates.events.WorldRenderLastEvent
|
||||||
|
|
||||||
|
@Mixin(WorldRenderer::class)
|
||||||
|
class MixinWorldRenderer {
|
||||||
|
|
||||||
|
@Inject(
|
||||||
|
method = ["render"],
|
||||||
|
at = [At("INVOKE", target = "renderChunkDebugInfo", shift = At.Shift.AFTER)],
|
||||||
|
)
|
||||||
|
fun onWorldRenderLast(
|
||||||
|
matrices: MatrixStack,
|
||||||
|
tickDelta: Float,
|
||||||
|
arg2: Long,
|
||||||
|
renderBlockOutline: Boolean,
|
||||||
|
camera: Camera,
|
||||||
|
gameRenderer: GameRenderer,
|
||||||
|
lightmapTextureManager: LightmapTextureManager,
|
||||||
|
positionMatrix: Matrix4f,
|
||||||
|
ci: CallbackInfo
|
||||||
|
) {
|
||||||
|
val event = WorldRenderLastEvent(
|
||||||
|
matrices, tickDelta, renderBlockOutline,
|
||||||
|
camera, gameRenderer, lightmapTextureManager,
|
||||||
|
positionMatrix
|
||||||
|
)
|
||||||
|
WorldRenderLastEvent.publish(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import kotlinx.serialization.serializer
|
|||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
import moe.nea.notenoughupdates.NotEnoughUpdates.logger
|
import moe.nea.notenoughupdates.NotEnoughUpdates.logger
|
||||||
import moe.nea.notenoughupdates.hud.ProgressBar
|
import moe.nea.notenoughupdates.hud.ProgressBar
|
||||||
import moe.nea.notenoughupdates.util.ConfigHolder
|
import moe.nea.notenoughupdates.util.config.ConfigHolder
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
|
||||||
import net.minecraft.client.MinecraftClient
|
import net.minecraft.client.MinecraftClient
|
||||||
import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
|
import net.minecraft.network.packet.s2c.play.SynchronizeRecipesS2CPacket
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
package moe.nea.notenoughupdates.util
|
|
||||||
|
|
||||||
import java.io.IOException
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
|
|
||||||
import kotlinx.serialization.KSerializer
|
|
||||||
import kotlinx.serialization.SerializationException
|
|
||||||
import kotlin.io.path.exists
|
|
||||||
import kotlin.io.path.readText
|
|
||||||
import kotlin.io.path.writeText
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import net.minecraft.client.MinecraftClient
|
|
||||||
import net.minecraft.server.command.CommandOutput
|
|
||||||
import net.minecraft.text.Text
|
|
||||||
import moe.nea.notenoughupdates.NotEnoughUpdates
|
|
||||||
import moe.nea.notenoughupdates.events.ScreenOpenEvent
|
|
||||||
|
|
||||||
abstract class ConfigHolder<T>(
|
|
||||||
val serializer: KSerializer<T>,
|
|
||||||
val name: String,
|
|
||||||
val default: () -> T
|
|
||||||
) {
|
|
||||||
|
|
||||||
var config: T
|
|
||||||
private set
|
|
||||||
|
|
||||||
init {
|
|
||||||
config = readValueOrDefault()
|
|
||||||
putConfig(this::class, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
val file: Path get() = NotEnoughUpdates.CONFIG_DIR.resolve("$name.json")
|
|
||||||
|
|
||||||
protected fun readValueOrDefault(): T {
|
|
||||||
if (file.exists())
|
|
||||||
try {
|
|
||||||
return NotEnoughUpdates.json.decodeFromString(
|
|
||||||
serializer,
|
|
||||||
file.readText()
|
|
||||||
)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
badLoads.add(name)
|
|
||||||
NotEnoughUpdates.logger.error(
|
|
||||||
"IO exception during loading of config file $name. This will reset this config.",
|
|
||||||
e
|
|
||||||
)
|
|
||||||
} catch (e: SerializationException) {
|
|
||||||
badLoads.add(name)
|
|
||||||
NotEnoughUpdates.logger.error(
|
|
||||||
"Serialization exception during loading of config file $name. This will reset this config.",
|
|
||||||
e
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return default()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeValue(t: T) {
|
|
||||||
file.writeText(NotEnoughUpdates.json.encodeToString(serializer, t))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun save() {
|
|
||||||
writeValue(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun load() {
|
|
||||||
config = readValueOrDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markDirty() {
|
|
||||||
Companion.markDirty(this::class)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private var badLoads: MutableList<String> = CopyOnWriteArrayList()
|
|
||||||
private val allConfigs: MutableMap<KClass<out ConfigHolder<*>>, ConfigHolder<*>> = mutableMapOf()
|
|
||||||
private val dirty: MutableSet<KClass<out ConfigHolder<*>>> = mutableSetOf()
|
|
||||||
|
|
||||||
private fun <T : ConfigHolder<K>, K> putConfig(kClass: KClass<T>, inst: ConfigHolder<K>) {
|
|
||||||
allConfigs[kClass] = inst
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : ConfigHolder<K>, K> markDirty(kClass: KClass<T>) {
|
|
||||||
if (kClass !in allConfigs) {
|
|
||||||
NotEnoughUpdates.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'ConfigHolder'")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dirty.add(kClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun performSaves() {
|
|
||||||
val toSave = dirty.toList().also {
|
|
||||||
dirty.clear()
|
|
||||||
}
|
|
||||||
for (it in toSave) {
|
|
||||||
val obj = allConfigs[it]
|
|
||||||
if (obj == null) {
|
|
||||||
NotEnoughUpdates.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
obj.save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun warnForResetConfigs(player: CommandOutput) {
|
|
||||||
if (badLoads.isNotEmpty()) {
|
|
||||||
player.sendMessage(
|
|
||||||
Text.literal(
|
|
||||||
"The following configs have been reset: ${badLoads.joinToString(", ")}. " +
|
|
||||||
"This can be intentional, but probably isn't."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
badLoads.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun registerEvents() {
|
|
||||||
ScreenOpenEvent.subscribe { event ->
|
|
||||||
performSaves()
|
|
||||||
val p = MinecraftClient.getInstance().player
|
|
||||||
if (p != null) {
|
|
||||||
warnForResetConfigs(p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping {
|
|
||||||
performSaves()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
8
src/main/kotlin/moe/nea/notenoughupdates/util/Locraw.kt
Normal file
8
src/main/kotlin/moe/nea/notenoughupdates/util/Locraw.kt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package moe.nea.notenoughupdates.util
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Locraw(val server: String, val gametype: String? = null, val mode: String? = null, val map: String? = null) {
|
||||||
|
val skyblockLocation = if (gametype == "SKYBLOCK") mode else null
|
||||||
|
}
|
||||||
7
src/main/kotlin/moe/nea/notenoughupdates/util/MC.kt
Normal file
7
src/main/kotlin/moe/nea/notenoughupdates/util/MC.kt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package moe.nea.notenoughupdates.util
|
||||||
|
|
||||||
|
import net.minecraft.client.MinecraftClient
|
||||||
|
|
||||||
|
object MC {
|
||||||
|
inline val player get() = MinecraftClient.getInstance().player
|
||||||
|
}
|
||||||
63
src/main/kotlin/moe/nea/notenoughupdates/util/SBData.kt
Normal file
63
src/main/kotlin/moe/nea/notenoughupdates/util/SBData.kt
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package moe.nea.notenoughupdates.util
|
||||||
|
|
||||||
|
import dev.architectury.event.events.client.ClientPlayerEvent
|
||||||
|
import kotlinx.serialization.SerializationException
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlin.time.TimeSource
|
||||||
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
|
import moe.nea.notenoughupdates.events.ServerChatLineReceivedEvent
|
||||||
|
import moe.nea.notenoughupdates.events.SkyblockServerUpdateEvent
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTime::class)
|
||||||
|
object SBData {
|
||||||
|
val profileRegex = "(?:Your profile was changed to: |You are playing on profile: )(.+)".toRegex()
|
||||||
|
var profileCuteName: String? = null
|
||||||
|
|
||||||
|
private var lastLocrawSent: TimeSource.Monotonic.ValueTimeMark? = null
|
||||||
|
private val locrawRoundtripTime: Duration = 5.seconds
|
||||||
|
var locraw: Locraw? = null
|
||||||
|
val skyblockLocation get() = locraw?.skyblockLocation
|
||||||
|
|
||||||
|
|
||||||
|
fun init() {
|
||||||
|
ServerChatLineReceivedEvent.subscribe { event ->
|
||||||
|
val profileMatch = profileRegex.matchEntire(event.unformattedString)
|
||||||
|
if (profileMatch != null) {
|
||||||
|
profileCuteName = profileMatch.groupValues[1]
|
||||||
|
}
|
||||||
|
if (event.unformattedString.startsWith("{")) {
|
||||||
|
val lLS = lastLocrawSent
|
||||||
|
if (tryReceiveLocraw(event.unformattedString) && lLS != null && lLS.elapsedNow() < locrawRoundtripTime) {
|
||||||
|
lastLocrawSent = null
|
||||||
|
event.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientPlayerEvent.CLIENT_PLAYER_JOIN.register(ClientPlayerEvent.ClientPlayerJoin {
|
||||||
|
locraw = null
|
||||||
|
sendLocraw()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryReceiveLocraw(unformattedString: String): Boolean = try {
|
||||||
|
val lastLocraw = locraw
|
||||||
|
locraw = NotEnoughUpdates.json.decodeFromString<Locraw>(unformattedString)
|
||||||
|
SkyblockServerUpdateEvent.publish(SkyblockServerUpdateEvent(lastLocraw, locraw))
|
||||||
|
true
|
||||||
|
} catch (e: SerializationException) {
|
||||||
|
false
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendLocraw() {
|
||||||
|
lastLocrawSent = TimeSource.Monotonic.markNow()
|
||||||
|
MC.player?.sendCommand("locraw")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package moe.nea.notenoughupdates.util.config
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
import kotlin.io.path.writeText
|
||||||
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
|
|
||||||
|
abstract class ConfigHolder<T>(
|
||||||
|
val serializer: KSerializer<T>,
|
||||||
|
val name: String,
|
||||||
|
val default: () -> T
|
||||||
|
) : IConfigHolder<T> {
|
||||||
|
|
||||||
|
|
||||||
|
final override var config: T
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
config = readValueOrDefault()
|
||||||
|
IConfigHolder.putConfig(this::class, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val file: Path get() = NotEnoughUpdates.CONFIG_DIR.resolve("$name.json")
|
||||||
|
|
||||||
|
protected fun readValueOrDefault(): T {
|
||||||
|
if (file.exists())
|
||||||
|
try {
|
||||||
|
return NotEnoughUpdates.json.decodeFromString(
|
||||||
|
serializer,
|
||||||
|
file.readText()
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {/* Expecting IOException and SerializationException, but Kotlin doesn't allow multi catches*/
|
||||||
|
IConfigHolder.badLoads.add(name)
|
||||||
|
NotEnoughUpdates.logger.error(
|
||||||
|
"Exception during loading of config file $name. This will reset this config.",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return default()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeValue(t: T) {
|
||||||
|
file.writeText(NotEnoughUpdates.json.encodeToString(serializer, t))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save() {
|
||||||
|
writeValue(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun load() {
|
||||||
|
config = readValueOrDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun markDirty() {
|
||||||
|
IConfigHolder.markDirty(this::class)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package moe.nea.notenoughupdates.util.config
|
||||||
|
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import net.minecraft.client.MinecraftClient
|
||||||
|
import net.minecraft.server.command.CommandOutput
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
|
import moe.nea.notenoughupdates.events.ScreenOpenEvent
|
||||||
|
|
||||||
|
interface IConfigHolder<T> {
|
||||||
|
companion object {
|
||||||
|
internal var badLoads: MutableList<String> = CopyOnWriteArrayList()
|
||||||
|
private val allConfigs: MutableMap<KClass<out IConfigHolder<*>>, IConfigHolder<*>> = mutableMapOf()
|
||||||
|
private val dirty: MutableSet<KClass<out IConfigHolder<*>>> = mutableSetOf()
|
||||||
|
|
||||||
|
internal fun <T : IConfigHolder<K>, K> putConfig(kClass: KClass<T>, inst: IConfigHolder<K>) {
|
||||||
|
allConfigs[kClass] = inst
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : IConfigHolder<K>, K> markDirty(kClass: KClass<T>) {
|
||||||
|
if (kClass !in allConfigs) {
|
||||||
|
NotEnoughUpdates.logger.error("Tried to markDirty '${kClass.qualifiedName}', which isn't registered as 'IConfigHolder'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dirty.add(kClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun performSaves() {
|
||||||
|
val toSave = dirty.toList().also {
|
||||||
|
dirty.clear()
|
||||||
|
}
|
||||||
|
for (it in toSave) {
|
||||||
|
val obj = allConfigs[it]
|
||||||
|
if (obj == null) {
|
||||||
|
NotEnoughUpdates.logger.error("Tried to save '${it}', which isn't registered as 'ConfigHolder'")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun warnForResetConfigs(player: CommandOutput) {
|
||||||
|
if (badLoads.isNotEmpty()) {
|
||||||
|
player.sendMessage(
|
||||||
|
Text.literal(
|
||||||
|
"The following configs have been reset: ${badLoads.joinToString(", ")}. " +
|
||||||
|
"This can be intentional, but probably isn't."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
badLoads.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerEvents() {
|
||||||
|
ScreenOpenEvent.subscribe { event ->
|
||||||
|
performSaves()
|
||||||
|
val p = MinecraftClient.getInstance().player
|
||||||
|
if (p != null) {
|
||||||
|
warnForResetConfigs(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientLifecycleEvents.CLIENT_STOPPING.register(ClientLifecycleEvents.ClientStopping {
|
||||||
|
performSaves()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val config: T
|
||||||
|
fun save()
|
||||||
|
fun markDirty()
|
||||||
|
fun load()
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package moe.nea.notenoughupdates.util.config
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
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.notenoughupdates.NotEnoughUpdates
|
||||||
|
import moe.nea.notenoughupdates.util.SBData
|
||||||
|
|
||||||
|
abstract class ProfileSpecificConfigHolder<S>(
|
||||||
|
private val configSerializer: KSerializer<S>,
|
||||||
|
val configName: String,
|
||||||
|
private val configDefault: () -> S
|
||||||
|
) : IConfigHolder<S?> {
|
||||||
|
|
||||||
|
var allConfigs: MutableMap<String, S>
|
||||||
|
|
||||||
|
override val config: S?
|
||||||
|
get() = SBData.profileCuteName?.let {
|
||||||
|
allConfigs.computeIfAbsent(it) { configDefault() }
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
allConfigs = readValues()
|
||||||
|
readValues()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val configDirectory: Path get() = NotEnoughUpdates.CONFIG_DIR.resolve("profiles")
|
||||||
|
|
||||||
|
private fun readValues(): MutableMap<String, S> {
|
||||||
|
if (!configDirectory.exists()) {
|
||||||
|
configDirectory.createDirectories()
|
||||||
|
}
|
||||||
|
val profileFiles = configDirectory.listDirectoryEntries()
|
||||||
|
return profileFiles
|
||||||
|
.filter { it.extension == "json" }
|
||||||
|
.mapNotNull {
|
||||||
|
try {
|
||||||
|
it.nameWithoutExtension to NotEnoughUpdates.json.decodeFromString(configSerializer, it.readText())
|
||||||
|
} catch (e: Exception) { /* Expecting IOException and SerializationException, but Kotlin doesn't allow multi catches*/
|
||||||
|
IConfigHolder.badLoads.add(configName)
|
||||||
|
NotEnoughUpdates.logger.error(
|
||||||
|
"Exception during loading of profile specific config file $it ($configName). This will reset that profiles config.",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}.toMap().toMutableMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save() {
|
||||||
|
if (!configDirectory.exists()) {
|
||||||
|
configDirectory.createDirectories()
|
||||||
|
}
|
||||||
|
val c = allConfigs
|
||||||
|
configDirectory.listDirectoryEntries().forEach {
|
||||||
|
if (it.nameWithoutExtension !in c) {
|
||||||
|
it.deleteExisting()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.forEach { (name, value) ->
|
||||||
|
val f = configDirectory.resolve("$name.json")
|
||||||
|
f.writeText(NotEnoughUpdates.json.encodeToString(configSerializer, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun markDirty() {
|
||||||
|
IConfigHolder.markDirty(this::class)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun load() {
|
||||||
|
allConfigs = readValues()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package moe.nea.notenoughupdates.util.render
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem
|
||||||
|
import net.minecraft.client.gl.VertexBuffer
|
||||||
|
import net.minecraft.client.render.BufferBuilder
|
||||||
|
import net.minecraft.client.render.Camera
|
||||||
|
import net.minecraft.client.render.GameRenderer
|
||||||
|
import net.minecraft.client.render.Tessellator
|
||||||
|
import net.minecraft.client.render.VertexFormat
|
||||||
|
import net.minecraft.client.render.VertexFormats
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.util.math.Vec3d
|
||||||
|
|
||||||
|
class RenderBlockContext(val tesselator: Tessellator, val camPos: Vec3d) {
|
||||||
|
val buffer = tesselator.buffer
|
||||||
|
fun color(red: Float, green: Float, blue: Float, alpha: Float) {
|
||||||
|
RenderSystem.setShaderColor(red, green, blue, alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun block(blockPos: BlockPos) {
|
||||||
|
val matrixStack = RenderSystem.getModelViewStack()
|
||||||
|
matrixStack.push()
|
||||||
|
matrixStack.translate(blockPos.x - camPos.x, blockPos.y - camPos.y, blockPos.z - camPos.z)
|
||||||
|
RenderSystem.applyModelViewMatrix()
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionColorShader)
|
||||||
|
buildCube(buffer)
|
||||||
|
tesselator.draw()
|
||||||
|
matrixStack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun buildCube(buf: BufferBuilder) {
|
||||||
|
buf.begin(VertexFormat.DrawMode.TRIANGLES, VertexFormats.POSITION_COLOR)
|
||||||
|
buf.fixedColor(255, 255, 255, 255)
|
||||||
|
buf.vertex(0.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(0.0, 0.0, 1.0).next()
|
||||||
|
buf.vertex(0.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(1.0, 1.0, 0.0).next()
|
||||||
|
buf.vertex(0.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(0.0, 1.0, 0.0).next()
|
||||||
|
buf.vertex(1.0, 0.0, 1.0).next()
|
||||||
|
buf.vertex(0.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(1.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(1.0, 1.0, 0.0).next()
|
||||||
|
buf.vertex(1.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(0.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(0.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(0.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(0.0, 1.0, 0.0).next()
|
||||||
|
buf.vertex(1.0, 0.0, 1.0).next()
|
||||||
|
buf.vertex(0.0, 0.0, 1.0).next()
|
||||||
|
buf.vertex(0.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(0.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(0.0, 0.0, 1.0).next()
|
||||||
|
buf.vertex(1.0, 0.0, 1.0).next()
|
||||||
|
buf.vertex(1.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(1.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(1.0, 1.0, 0.0).next()
|
||||||
|
buf.vertex(1.0, 0.0, 0.0).next()
|
||||||
|
buf.vertex(1.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(1.0, 0.0, 1.0).next()
|
||||||
|
buf.vertex(1.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(1.0, 1.0, 0.0).next()
|
||||||
|
buf.vertex(0.0, 1.0, 0.0).next()
|
||||||
|
buf.vertex(1.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(0.0, 1.0, 0.0).next()
|
||||||
|
buf.vertex(0.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(1.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(0.0, 1.0, 1.0).next()
|
||||||
|
buf.vertex(1.0, 0.0, 1.0).next()
|
||||||
|
buf.unfixColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun renderBlocks(camera: Camera, block: RenderBlockContext. () -> Unit) {
|
||||||
|
RenderSystem.disableDepthTest()
|
||||||
|
RenderSystem.disableTexture()
|
||||||
|
RenderSystem.enableBlend()
|
||||||
|
RenderSystem.defaultBlendFunc()
|
||||||
|
|
||||||
|
val ctx = RenderBlockContext(Tessellator.getInstance(), camera.pos)
|
||||||
|
block(ctx)
|
||||||
|
|
||||||
|
VertexBuffer.unbind()
|
||||||
|
RenderSystem.enableDepthTest()
|
||||||
|
RenderSystem.enableTexture()
|
||||||
|
RenderSystem.disableBlend()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
70
src/main/kotlin/moe/nea/notenoughupdates/util/textutil.kt
Normal file
70
src/main/kotlin/moe/nea/notenoughupdates/util/textutil.kt
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package moe.nea.notenoughupdates.util
|
||||||
|
|
||||||
|
import net.minecraft.text.LiteralTextContent
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import net.minecraft.text.TextContent
|
||||||
|
import moe.nea.notenoughupdates.NotEnoughUpdates
|
||||||
|
|
||||||
|
|
||||||
|
class TextMatcher(text: Text) {
|
||||||
|
data class State(
|
||||||
|
var iterator: MutableList<Text>,
|
||||||
|
var currentText: Text?,
|
||||||
|
var offset: Int,
|
||||||
|
var textContent: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
var state = State(
|
||||||
|
mutableListOf(text),
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun pollChunk(): Boolean {
|
||||||
|
val firstOrNull = state.iterator.removeFirstOrNull() ?: return false
|
||||||
|
state.offset = 0
|
||||||
|
state.currentText = firstOrNull
|
||||||
|
state.textContent = when (val content = firstOrNull.content) {
|
||||||
|
is LiteralTextContent -> content.string
|
||||||
|
TextContent.EMPTY -> ""
|
||||||
|
else -> {
|
||||||
|
NotEnoughUpdates.logger.warn("TextContent of type ${content.javaClass} not understood.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.iterator.addAll(0, firstOrNull.siblings)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pollChunks(): Boolean {
|
||||||
|
while (state.offset !in state.textContent.indices) {
|
||||||
|
if (!pollChunk()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pollChar(): Char? {
|
||||||
|
if (!pollChunks()) return null
|
||||||
|
return state.textContent[state.offset++]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun expectString(string: String): Boolean {
|
||||||
|
var found = ""
|
||||||
|
while (found.length < string.length) {
|
||||||
|
if (!pollChunks()) return false
|
||||||
|
val takeable = state.textContent.drop(state.offset).take(string.length - found.length)
|
||||||
|
state.offset += takeable.length
|
||||||
|
found += takeable
|
||||||
|
}
|
||||||
|
return found == string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val Text.unformattedString
|
||||||
|
get() = string.replace("§.".toRegex(), "")
|
||||||
|
|
||||||
@@ -1,26 +1,22 @@
|
|||||||
{
|
{
|
||||||
"notenoughupdates": {
|
"notenoughupdates.repo.reload.network": "Trying to redownload the repository",
|
||||||
"repo": {
|
"notenoughupdates.repo.reload.disk": "Reloading repository from disk. This may lag a bit.",
|
||||||
"reload": {
|
"notenoughupdates.repo.cache": "Recaching items",
|
||||||
"network": "Trying to redownload the repository",
|
"notenoughupdates.repo.brokenitem": "Failed to render item: %s",
|
||||||
"disk": "Reloading repository from disk. This may lag a bit."
|
"notenoughupdates.gui.repo.title": "NotEnoughUpdates Repo Settings",
|
||||||
},
|
"notenoughupdates.gui.repo.autoupdate": "Auto Update",
|
||||||
"cache": "Recaching items",
|
"notenoughupdates.gui.repo.username": "Repo Username",
|
||||||
"brokenitem": "Failed to render item: %s"
|
"notenoughupdates.gui.repo.hint.username": "NotEnoughUpdates",
|
||||||
},
|
"notenoughupdates.gui.repo.reponame": "Repo Name",
|
||||||
"gui": {
|
"notenoughupdates.gui.repo.hint.reponame": "NotEnoughUpdates-REPO",
|
||||||
"repo": {
|
"notenoughupdates.gui.repo.branch": "Repo Branch",
|
||||||
"title": "NotEnoughUpdates Repo Settings",
|
"notenoughupdates.gui.repo.hint.branch": "dangerous",
|
||||||
"autoupdate": "Auto Update",
|
"notenoughupdates.gui.repo.reset": "Reset",
|
||||||
"username": "Repo Username",
|
"notenoughupdates.gui.repo.reset.label": "Reset to Defaults",
|
||||||
"hint.username": "NotEnoughUpdates",
|
"notenoughupdates.sbinfo.nolocraw": "No locraw data available",
|
||||||
"reponame": "Repo Name",
|
"notenoughupdates.sbinfo.profile": "Current profile cutename: %s",
|
||||||
"hint.reponame": "NotEnoughUpdates-REPO",
|
"notenoughupdates.sbinfo.server": "Locraw Server: %s",
|
||||||
"branch": "Repo Branch",
|
"notenoughupdates.sbinfo.gametype": "Locraw Gametype: %s",
|
||||||
"hint.branch": "dangerous",
|
"notenoughupdates.sbinfo.mode": "Locraw Mode: %s",
|
||||||
"reset": "Reset",
|
"notenoughupdates.sbinfo.map": "Locraw Map: %s"
|
||||||
"reset.label": "Reset to Defaults"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/main/resources/hotswap-agent.properties
Normal file
2
src/main/resources/hotswap-agent.properties
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
disabledPlugins=Log4j2,Proxy
|
||||||
|
LOGGER=info
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
{
|
{
|
||||||
"required": true,
|
"required": true,
|
||||||
"package": "moe.nea.notenoughupdates.mixins",
|
"package": "moe.nea.notenoughupdates.mixins",
|
||||||
"compatibilityLevel": "JAVA_16",
|
"compatibilityLevel": "JAVA_16",
|
||||||
"client": [
|
"client": [
|
||||||
"MixinMinecraft"
|
"MixinMessageHandler",
|
||||||
],
|
"MixinMinecraft",
|
||||||
"mixins": [
|
"MixinWorldRenderer"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"mixins": [
|
||||||
"defaultRequire": 1
|
],
|
||||||
}
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user