fix: Improve exception logging for entity widgets

This commit is contained in:
Linnea Gräf
2024-11-13 15:44:04 +01:00
parent 57cdcb21ec
commit 59566bcab8
4 changed files with 69 additions and 50 deletions

View File

@@ -10,9 +10,10 @@ import net.minecraft.client.gui.Element
import net.minecraft.client.gui.ParentElement
import net.minecraft.entity.LivingEntity
import moe.nea.firmament.gui.entity.EntityRenderer
import moe.nea.firmament.util.ErrorUtil
class EntityWidget(val entity: LivingEntity, val point: Point) : WidgetWithBounds() {
class EntityWidget(val entity: LivingEntity?, val point: Point) : WidgetWithBounds() {
override fun children(): List<Element> {
return emptyList()
}
@@ -22,9 +23,9 @@ class EntityWidget(val entity: LivingEntity, val point: Point) : WidgetWithBound
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
try {
if (!hasErrored)
EntityRenderer.renderEntity(entity, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat())
EntityRenderer.renderEntity(entity!!, context, point.x, point.y, mouseX.toFloat(), mouseY.toFloat())
} catch (ex: Exception) {
EntityRenderer.logger.error("Failed to render constructed entity: $entity", ex)
ErrorUtil.softError("Failed to render constructed entity: $entity", ex)
hasErrored = true
}
if (hasErrored) {

View File

@@ -39,44 +39,42 @@ class SBMobDropRecipe(override val neuRecipe: NEUMobDropRecipe) : SBRecipe() {
} else {
EntityRenderer.applyModifiers(source, listOf())
}
if (entity != null) {
val level = display.neuRecipe.level
val fullMobName =
if (level > 0) Text.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name)
else Text.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name)
val tt = mutableListOf<Text>()
tt.add((fullMobName))
tt.add(Text.literal(""))
if (display.neuRecipe.coins > 0) {
tt.add(Text.stringifiedTranslatable("firmament.recipe.mobs.coins", display.neuRecipe.coins))
}
if (display.neuRecipe.combatExperience > 0) {
tt.add(
Text.stringifiedTranslatable(
"firmament.recipe.mobs.combat",
display.neuRecipe.combatExperience
)
)
}
if (display.neuRecipe.enchantingExperience > 0) {
tt.add(
Text.stringifiedTranslatable(
"firmament.recipe.mobs.exp",
display.neuRecipe.enchantingExperience
)
)
}
if (display.neuRecipe.extra != null)
display.neuRecipe.extra.mapTo(tt) { Text.literal(it) }
if (tt.size == 2)
tt.removeAt(1)
add(
Widgets.withTooltip(
EntityWidget(entity, Point(bounds.minX + 5, bounds.minY + 15)),
tt
val level = display.neuRecipe.level
val fullMobName =
if (level > 0) Text.translatable("firmament.recipe.mobs.name", level, display.neuRecipe.name)
else Text.translatable("firmament.recipe.mobs.name.nolevel", display.neuRecipe.name)
val tt = mutableListOf<Text>()
tt.add((fullMobName))
tt.add(Text.literal(""))
if (display.neuRecipe.coins > 0) {
tt.add(Text.stringifiedTranslatable("firmament.recipe.mobs.coins", display.neuRecipe.coins))
}
if (display.neuRecipe.combatExperience > 0) {
tt.add(
Text.stringifiedTranslatable(
"firmament.recipe.mobs.combat",
display.neuRecipe.combatExperience
)
)
}
if (display.neuRecipe.enchantingExperience > 0) {
tt.add(
Text.stringifiedTranslatable(
"firmament.recipe.mobs.exp",
display.neuRecipe.enchantingExperience
)
)
}
if (display.neuRecipe.extra != null)
display.neuRecipe.extra.mapTo(tt) { Text.literal(it) }
if (tt.size == 2)
tt.removeAt(1)
add(
Widgets.withTooltip(
EntityWidget(entity, Point(bounds.minX + 5, bounds.minY + 15)),
tt
)
)
add(
Widgets.createLabel(Point(bounds.minX + 15, bounds.minY + 5), Text.literal(display.neuRecipe.name))
.leftAligned()

View File

@@ -3,7 +3,6 @@ package moe.nea.firmament.gui.entity
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import org.apache.logging.log4j.LogManager
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.atan
@@ -15,8 +14,8 @@ import net.minecraft.entity.LivingEntity
import net.minecraft.entity.SpawnReason
import net.minecraft.util.Identifier
import net.minecraft.world.World
import moe.nea.firmament.util.ErrorUtil
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.assertNotNullOr
import moe.nea.firmament.util.iterate
import moe.nea.firmament.util.openFirmamentResource
import moe.nea.firmament.util.render.enableScissorWithTranslation
@@ -76,18 +75,15 @@ object EntityRenderer {
"name" to ModifyName,
)
val logger = LogManager.getLogger("Firmament.Entity")
fun applyModifiers(entityId: String, modifiers: List<JsonObject>): LivingEntity? {
val entityType = assertNotNullOr(entityIds[entityId]) {
logger.error("Could not create entity with id $entityId")
val entityType = ErrorUtil.notNullOr(entityIds[entityId], "Could not create entity with id $entityId") {
return null
}
var entity = entityType()
var entity = ErrorUtil.catch("") { entityType() }.or { return null }
for (modifierJson in modifiers) {
val modifier = assertNotNullOr(modifierJson["type"]?.asString?.let(entityModifiers::get)) {
logger.error("Unknown modifier $modifierJson")
return null
}
val modifier = ErrorUtil.notNullOr(
modifierJson["type"]?.asString?.let(entityModifiers::get),
"Could not create entity with id $entityId. Failed to apply modifier $modifierJson") { return null }
entity = modifier.apply(entity, modifierJson)
}
return entity
@@ -95,8 +91,7 @@ object EntityRenderer {
fun constructEntity(info: JsonObject): LivingEntity? {
val modifiers = (info["modifiers"] as JsonArray?)?.map { it.asJsonObject } ?: emptyList()
val entityType = assertNotNullOr(info["entity"]?.asString) {
logger.error("Missing entity type on entity object")
val entityType = ErrorUtil.notNullOr(info["entity"]?.asString, "Missing entity type on entity object") {
return null
}
return applyModifiers(entityType, modifiers)

View File

@@ -37,6 +37,31 @@ object ErrorUtil {
else Firmament.logger.error(message)
}
class Catch<T> private constructor(val value: T?, val exc: Throwable?) {
inline fun or(block: (exc: Throwable) -> T): T {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (exc != null) return block(exc)
@Suppress("UNCHECKED_CAST")
return value as T
}
companion object {
fun <T> fail(exception: Throwable): Catch<T> = Catch(null, exception)
fun <T> succeed(value: T): Catch<T> = Catch(value, null)
}
}
inline fun <T> catch(message: String, block: () -> T): Catch<T> {
try {
return Catch.succeed(block())
} catch (exc: Throwable) {
softError(message, exc)
return Catch.fail(exc)
}
}
inline fun <T : Any> notNullOr(nullable: T?, message: String, orElse: () -> T): T {
contract {
callsInPlace(orElse, InvocationKind.AT_MOST_ONCE)