1.21.3 WIP

This commit is contained in:
Linnea Gräf
2024-11-03 01:24:24 +01:00
parent 646843ba3b
commit 22f0cc59a2
105 changed files with 2854 additions and 2646 deletions

View File

@@ -13,8 +13,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlin.math.min
import net.minecraft.client.gui.screen.ChatScreen
import net.minecraft.client.render.RenderLayer
import net.minecraft.client.texture.NativeImage
import net.minecraft.client.texture.NativeImageBackedTexture
import net.minecraft.scoreboard.ScoreboardCriterion.RenderType
import net.minecraft.text.ClickEvent
import net.minecraft.text.HoverEvent
import net.minecraft.text.Style
@@ -28,6 +30,7 @@ import moe.nea.firmament.events.ScreenRenderPostEvent
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.render.drawTexture
import moe.nea.firmament.util.transformEachRecursively
import moe.nea.firmament.util.unformattedString

View File

@@ -37,10 +37,10 @@ object DeveloperFeatures : FirmamentFeature {
builder.directory(gradleDir.toFile())
builder.inheritIO()
val process = builder.start()
MC.player?.sendMessage(Text.translatable("firmament.dev.resourcerebuild.start"))
MC.sendChat(Text.translatable("firmament.dev.resourcerebuild.start"))
val startTime = TimeMark.now()
process.toHandle().onExit().thenApply {
MC.player?.sendMessage(Text.stringifiedTranslatable(
MC.sendChat(Text.stringifiedTranslatable(
"firmament.dev.resourcerebuild.done",
startTime.passedTime()))
Unit

View File

@@ -9,6 +9,7 @@ import net.minecraft.entity.Entity
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.nbt.NbtOps
import net.minecraft.text.Text
import net.minecraft.text.TextCodecs
import net.minecraft.util.hit.BlockHitResult
@@ -54,15 +55,13 @@ object PowerUserTools : FirmamentFeature {
var lastCopiedStack: Pair<ItemStack, Text>? = null
set(value) {
field = value
if (value != null)
lastCopiedStackViewTime = true
if (value != null) lastCopiedStackViewTime = true
}
var lastCopiedStackViewTime = false
@Subscribe
fun resetLastCopiedStack(event: TickEvent) {
if (!lastCopiedStackViewTime)
lastCopiedStack = null
if (!lastCopiedStackViewTime) lastCopiedStack = null
lastCopiedStackViewTime = false
}
@@ -154,13 +153,13 @@ object PowerUserTools : FirmamentFeature {
}
ClipboardUtils.setTextContent(skullTexture.toString())
lastCopiedStack =
Pair(
item,
Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString())
)
Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString()))
println("Copied skull id: $skullTexture")
} else if (it.matches(TConfig.copyItemStack)) {
ClipboardUtils.setTextContent(item.encode(MC.currentOrDefaultRegistries).toPrettyString())
ClipboardUtils.setTextContent(
ItemStack.CODEC
.encodeStart(MC.currentOrDefaultRegistries.getOps(NbtOps.INSTANCE), item)
.orThrow.toPrettyString())
lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack"))
}
}

View File

@@ -104,12 +104,13 @@ object AncestralSpadeSolver : SubscriptionOwner {
if (!isEnabled()) return
RenderInWorldContext.renderInWorld(event) {
nextGuess?.let {
color(1f, 1f, 0f, 0.5f)
tinyBlock(it, 1f)
tinyBlock(it, 1f, 0x80FFFFFF.toInt())
// TODO: replace this
color(1f, 1f, 0f, 1f)
tracer(it, lineWidth = 3f)
}
if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) {
// TODO: replace this // TODO: add toggle
color(0f, 1f, 0f, 0.7f)
line(particlePositions)
}

View File

@@ -1,6 +1,6 @@
package moe.nea.firmament.features.diana
import me.shedaniel.math.Color
import kotlin.time.Duration.Companion.seconds
import net.minecraft.particle.ParticleTypes
import net.minecraft.util.math.BlockPos
@@ -20,125 +20,125 @@ import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorl
object NearbyBurrowsSolver : SubscriptionOwner {
private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20)
private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500)
private var lastBlockClick: BlockPos? = null
private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20)
private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500)
private var lastBlockClick: BlockPos? = null
enum class BurrowType {
START, MOB, TREASURE
}
enum class BurrowType {
START, MOB, TREASURE
}
val burrows = mutableMapOf<BlockPos, BurrowType>()
val burrows = mutableMapOf<BlockPos, BurrowType>()
@Subscribe
fun onChatEvent(event: ProcessChatEvent) {
val lastClickedBurrow = lastBlockClick ?: return
if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") ||
event.unformattedString.startsWith(" ☠ You were killed by") ||
event.unformattedString.startsWith("You finished the Griffin burrow chain!")
) {
markAsDug(lastClickedBurrow)
burrows.remove(lastClickedBurrow)
}
}
@Subscribe
fun onChatEvent(event: ProcessChatEvent) {
val lastClickedBurrow = lastBlockClick ?: return
if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") ||
event.unformattedString.startsWith(" ☠ You were killed by") ||
event.unformattedString.startsWith("You finished the Griffin burrow chain!")
) {
markAsDug(lastClickedBurrow)
burrows.remove(lastClickedBurrow)
}
}
fun wasRecentlyDug(blockPos: BlockPos): Boolean {
val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast()
return lastDigTime.passedTime() < 10.seconds
}
fun wasRecentlyDug(blockPos: BlockPos): Boolean {
val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast()
return lastDigTime.passedTime() < 10.seconds
}
fun markAsDug(blockPos: BlockPos) {
recentlyDugBurrows[blockPos] = TimeMark.now()
}
fun markAsDug(blockPos: BlockPos) {
recentlyDugBurrows[blockPos] = TimeMark.now()
}
fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean {
val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast()
return lastEnchantTime.passedTime() < 4.seconds
}
fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean {
val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast()
return lastEnchantTime.passedTime() < 4.seconds
}
fun markAsEnchanted(blockPos: BlockPos) {
recentEnchantParticles[blockPos] = TimeMark.now()
}
fun markAsEnchanted(blockPos: BlockPos) {
recentEnchantParticles[blockPos] = TimeMark.now()
}
@Subscribe
fun onParticles(event: ParticleSpawnEvent) {
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
@Subscribe
fun onParticles(event: ParticleSpawnEvent) {
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
val position: BlockPos = event.position.toBlockPos().down()
val position: BlockPos = event.position.toBlockPos().down()
if (wasRecentlyDug(position)) return
if (wasRecentlyDug(position)) return
val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f)
val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f)
if (event.particleEffect.type == ParticleTypes.ENCHANT) {
if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) {
markAsEnchanted(position)
}
return
}
if (event.particleEffect.type == ParticleTypes.ENCHANT) {
if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) {
markAsEnchanted(position)
}
return
}
if (!wasRecentlyEnchanted(position)) return
if (!wasRecentlyEnchanted(position)) return
if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT
&& event.count == 4
&& event.speed == 0.01F
&& event.offset.y == 0.1f
&& isEven50Spread
) {
burrows[position] = BurrowType.START
}
if (event.particleEffect.type == ParticleTypes.CRIT
&& event.count == 3
&& event.speed == 0.01F
&& event.offset.y == 0.1F
&& isEven50Spread
) {
burrows[position] = BurrowType.MOB
}
if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA
&& event.count == 2
&& event.speed == 0.01F
&& event.offset.y == 0.1F
&& event.offset.x == 0.35F && event.offset.z == 0.35f
) {
burrows[position] = BurrowType.TREASURE
}
}
if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT
&& event.count == 4
&& event.speed == 0.01F
&& event.offset.y == 0.1f
&& isEven50Spread
) {
burrows[position] = BurrowType.START
}
if (event.particleEffect.type == ParticleTypes.CRIT
&& event.count == 3
&& event.speed == 0.01F
&& event.offset.y == 0.1F
&& isEven50Spread
) {
burrows[position] = BurrowType.MOB
}
if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA
&& event.count == 2
&& event.speed == 0.01F
&& event.offset.y == 0.1F
&& event.offset.x == 0.35F && event.offset.z == 0.35f
) {
burrows[position] = BurrowType.TREASURE
}
}
@Subscribe
fun onRender(event: WorldRenderLastEvent) {
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
renderInWorld(event) {
for ((location, burrow) in burrows) {
when (burrow) {
BurrowType.START -> color(.2f, .8f, .2f, 0.4f)
BurrowType.MOB -> color(0.3f, 0.4f, 0.9f, 0.4f)
BurrowType.TREASURE -> color(1f, 0.7f, 0.2f, 0.4f)
}
block(location)
}
}
}
@Subscribe
fun onRender(event: WorldRenderLastEvent) {
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
renderInWorld(event) {
for ((location, burrow) in burrows) {
val color = when (burrow) {
BurrowType.START -> Color.ofRGBA(.2f, .8f, .2f, 0.4f)
BurrowType.MOB -> Color.ofRGBA(0.3f, 0.4f, 0.9f, 0.4f)
BurrowType.TREASURE -> Color.ofRGBA(1f, 0.7f, 0.2f, 0.4f)
}
block(location, color.color)
}
}
}
@Subscribe
fun onSwapWorld(worldReadyEvent: WorldReadyEvent) {
burrows.clear()
recentEnchantParticles.clear()
recentlyDugBurrows.clear()
lastBlockClick = null
}
@Subscribe
fun onSwapWorld(worldReadyEvent: WorldReadyEvent) {
burrows.clear()
recentEnchantParticles.clear()
recentlyDugBurrows.clear()
lastBlockClick = null
}
fun onBlockClick(blockPos: BlockPos) {
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
burrows.remove(blockPos)
lastBlockClick = blockPos
}
fun onBlockClick(blockPos: BlockPos) {
if (!DianaWaypoints.TConfig.nearbyWaypoints) return
burrows.remove(blockPos)
lastBlockClick = blockPos
}
override val delegateFeature: FirmamentFeature
get() = DianaWaypoints
override val delegateFeature: FirmamentFeature
get() = DianaWaypoints
}
fun Position.toBlockPos(): BlockPos {
return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z))
return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z))
}

View File

@@ -69,7 +69,7 @@ object CraftingOverlay : FirmamentFeature {
if (!slot.hasStack()) {
val itemStack = SBItemStack(expectedItem)?.asImmutableItemStack() ?: return
event.context.drawItem(itemStack, event.slot.x, event.slot.y)
event.context.drawItemInSlot(
event.context.drawStackOverlay(
MC.font,
itemStack,
event.slot.x,

View File

@@ -4,6 +4,7 @@ package moe.nea.firmament.features.inventory
import java.awt.Color
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.render.RenderLayer
import net.minecraft.item.ItemStack
import net.minecraft.util.Formatting
import net.minecraft.util.Identifier
@@ -16,6 +17,7 @@ import moe.nea.firmament.util.MC
import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.collections.lastNotNullOfOrNull
import moe.nea.firmament.util.collections.memoizeIdentity
import moe.nea.firmament.util.render.drawGuiTexture
import moe.nea.firmament.util.unformattedString
object ItemRarityCosmetics : FirmamentFeature {
@@ -43,10 +45,10 @@ object ItemRarityCosmetics : FirmamentFeature {
"SUPREME" to Formatting.DARK_RED,
).mapValues {
val c = Color(it.value.colorValue!!)
Triple(c.red / 255F, c.green / 255F, c.blue / 255F)
c.rgb
}
private fun getSkyblockRarity0(itemStack: ItemStack): Triple<Float, Float, Float>? {
private fun getSkyblockRarity0(itemStack: ItemStack): Int? {
return itemStack.loreAccordingToNbt.lastNotNullOfOrNull {
val entry = it.unformattedString
rarityToColor.entries.find { (k, v) -> k in entry }?.value
@@ -57,13 +59,13 @@ object ItemRarityCosmetics : FirmamentFeature {
fun drawItemStackRarity(drawContext: DrawContext, x: Int, y: Int, item: ItemStack) {
val (r, g, b) = getSkyblockRarity(item) ?: return
drawContext.drawSprite(
val rgb = getSkyblockRarity(item) ?: return
drawContext.drawGuiTexture(
RenderLayer::getGuiTextured,
Identifier.of("firmament:item_rarity_background"),
x, y,
0,
16, 16,
MC.guiAtlasManager.getSprite(Identifier.of("firmament:item_rarity_background")),
r, g, b, 1F
rgb
)
}

View File

@@ -7,6 +7,7 @@ import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.petData
import moe.nea.firmament.util.render.drawGuiTexture
import moe.nea.firmament.util.useMatch
object PetFeatures : FirmamentFeature {
@@ -28,9 +29,9 @@ object PetFeatures : FirmamentFeature {
val stack = event.slot.stack
if (stack.petData?.active == true)
petMenuTitle.useMatch(MC.screenName ?: return) {
event.context.drawSprite(
event.context.drawGuiTexture(
event.slot.x, event.slot.y, 0, 16, 16,
MC.guiAtlasManager.getSprite(Identifier.of("firmament:selected_pet_background"))
Identifier.of("firmament:selected_pet_background")
)
}
}

View File

@@ -35,6 +35,7 @@ import moe.nea.firmament.util.mc.ScreenUtil.getSlotByIndex
import moe.nea.firmament.util.mc.SlotUtils.swapWithHotBar
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.render.GuiRenderLayers
import moe.nea.firmament.util.render.drawLine
import moe.nea.firmament.util.skyblockUUID
import moe.nea.firmament.util.unformattedString
@@ -211,6 +212,11 @@ object SlotLocking : FirmamentFeature {
}
if (it.matches(TConfig.slotBind)) {
storedLockingSlot = null
val boundSlots = DConfig.data?.boundSlots ?: return
if (slot != null)
boundSlots.entries.removeIf {
it.value == slot.index || it.key == slot.index
}
}
}
@@ -331,24 +337,22 @@ object SlotLocking : FirmamentFeature {
val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())
val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
if (isSlotLocked || isUUIDLocked) {
RenderSystem.disableDepthTest()
it.context.drawSprite(
it.slot.x, it.slot.y, 0,
it.context.drawGuiTexture(
GuiRenderLayers.GUI_TEXTURED_NO_DEPTH,
when {
isSlotLocked ->
(Identifier.of("firmament:slot_locked"))
isUUIDLocked ->
(Identifier.of("firmament:uuid_locked"))
else ->
error("unreachable")
},
it.slot.x, it.slot.y,
16, 16,
MC.guiAtlasManager.getSprite(
when {
isSlotLocked ->
(Identifier.of("firmament:slot_locked"))
isUUIDLocked ->
(Identifier.of("firmament:uuid_locked"))
else ->
error("unreachable")
}
)
-1
)
RenderSystem.enableDepthTest()
}
}
}

View File

@@ -18,6 +18,7 @@ import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.collections.memoize
import moe.nea.firmament.util.render.drawGuiTexture
@Serializable
data class InventoryButton(
@@ -54,13 +55,13 @@ data class InventoryButton(
}
fun render(context: DrawContext) {
context.drawSprite(
context.drawGuiTexture(
0,
0,
0,
dimensions.width,
dimensions.height,
MC.guiAtlasManager.getSprite(Identifier.of("firmament:inventory_button_background"))
Identifier.of("firmament:inventory_button_background")
)
context.drawItem(getItem(), 1, 1)
}

View File

@@ -84,7 +84,6 @@ class InventoryButtonEditor(
context.matrices.push()
context.matrices.translate(0f, 0f, -10f)
context.fill(lastGuiRect.minX, lastGuiRect.minY, lastGuiRect.maxX, lastGuiRect.maxY, -1)
context.setShaderColor(1f, 1f, 1f, 1f)
context.matrices.pop()
for (button in buttons) {
val buttonPosition = button.getBounds(lastGuiRect)

View File

@@ -40,6 +40,7 @@ sealed interface StorageBackingHandle {
* representable as a [StorageBackingHandle], meaning another screen is open, for example the enderchest icon
* selection screen.
*/
@OptIn(ExperimentalContracts::class)
fun fromScreen(screen: Screen?): StorageBackingHandle? {
contract {
returnsNotNull() implies (screen != null)

View File

@@ -21,6 +21,7 @@ import moe.nea.firmament.util.MoulConfigUtils.clickMCComponentInPlace
import moe.nea.firmament.util.MoulConfigUtils.drawMCComponentInPlace
import moe.nea.firmament.util.assertTrueOr
import moe.nea.firmament.util.customgui.customGui
import moe.nea.firmament.util.render.drawGuiTexture
class StorageOverlayScreen : Screen(Text.literal("")) {
@@ -162,13 +163,11 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
context.drawGuiTexture(upperBackgroundSprite,
measurements.x,
measurements.y,
0,
measurements.overviewWidth,
measurements.overviewHeight)
context.drawGuiTexture(playerInventorySprite,
measurements.playerX,
measurements.playerY,
0,
PLAYER_WIDTH,
PLAYER_HEIGHT)
}
@@ -188,7 +187,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
items.withIndex().forEach { (index, item) ->
val (x, y) = getPlayerInventorySlotPosition(index)
context.drawItem(item, x, y, 0)
context.drawItemInSlot(textRenderer, item, x, y)
context.drawStackOverlay(textRenderer, item, x, y)
}
}
@@ -357,7 +356,7 @@ class StorageOverlayScreen : Screen(Text.literal("")) {
val slotY = (index / 9) * SLOT_SIZE + y + 4 + textRenderer.fontHeight + 1
if (slots == null) {
context.drawItem(stack, slotX, slotY)
context.drawItemInSlot(textRenderer, stack, slotX, slotY)
context.drawStackOverlay(textRenderer, stack, slotX, slotY)
} else {
val slot = slots[index]
slot.x = slotX - slotOffset.x

View File

@@ -111,7 +111,7 @@ class StorageOverviewScreen() : Screen(Text.empty()) {
context.fill(x, y, x + 18, y + 18, 0x40808080.toInt())
}
context.drawItem(stack, x + 1, y + 1)
context.drawItemInSlot(MC.font, stack, x + 1, y + 1)
context.drawStackOverlay(MC.font, stack, x + 1, y + 1)
}
}

View File

@@ -1,6 +1,6 @@
package moe.nea.firmament.features.mining
import net.minecraft.util.Identifier
import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.SlotRenderEvents
import moe.nea.firmament.gui.config.ManagedConfig
@@ -19,10 +19,8 @@ object CommissionFeatures {
if (!Config.highlightCompletedCommissions) return
if (MC.screenName != "Commissions") return
val stack = event.slot.stack
if(stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) {
event.highlight(
MC.guiAtlasManager.getSprite(Identifier.of("firmament:completed_commission_background"))
)
if (stack.loreAccordingToNbt.any { it.unformattedString == "COMPLETED" }) {
event.highlight(Firmament.identifier("completed_commission_background"))
}
}
}

View File

@@ -9,9 +9,7 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.Items
import net.minecraft.screen.GenericContainerScreenHandler
import net.minecraft.screen.ScreenHandler
import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.text.Text
import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe
@@ -31,6 +29,7 @@ import moe.nea.firmament.util.customgui.customGui
import moe.nea.firmament.util.mc.CommonTextures
import moe.nea.firmament.util.mc.SlotUtils.clickRightMouseButton
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.render.drawGuiTexture
import moe.nea.firmament.util.unformattedString
import moe.nea.firmament.util.useMatch
@@ -81,7 +80,7 @@ object HotmPresets {
override fun render(drawContext: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
drawContext.drawGuiTexture(
CommonTextures.genericWidget(),
bounds.x, bounds.y, 0,
bounds.x, bounds.y,
bounds.width,
bounds.height,
)
@@ -191,7 +190,7 @@ object HotmPresets {
if (hotmInventoryName == MC.screenName
&& event.slot.stack.displayNameAccordingToNbt.unformattedString in highlightedPerks
) {
event.highlight(MC.guiAtlasManager.getSprite(Firmament.identifier("hotm_perk_preset")))
event.highlight((Firmament.identifier("hotm_perk_preset")))
}
}

View File

@@ -1,11 +1,30 @@
package moe.nea.firmament.features.texturepack
import net.fabricmc.fabric.api.renderer.v1.model.WrapperBakedModel as WrapperBakedModelFabric
import net.minecraft.client.render.model.BakedModel
import net.minecraft.client.render.model.WrapperBakedModel
import moe.nea.firmament.util.ErrorUtil
interface BakedModelExtra {
companion object {
@JvmStatic
fun cast(originalModel: BakedModel): BakedModelExtra? {
var p = originalModel
for (i in 0..256) {
p = when (p) {
is BakedModelExtra -> return p
is WrapperBakedModel -> p.wrapped
is WrapperBakedModelFabric -> WrapperBakedModelFabric.unwrap(p)
else -> break
}
}
ErrorUtil.softError("Could not find a baked model for $originalModel")
return null
}
}
var tintOverrides_firmament: TintOverrides?
fun getHeadModel_firmament(): BakedModel?
fun setHeadModel_firmament(headModel: BakedModel?)
fun setHeadModel_firmament(headModel: BakedModel?)
}

View File

@@ -244,7 +244,7 @@ object CustomBlockTextures {
.flatMap { it.lookup.values }
.flatten()
.mapTo(mutableSetOf()) { it.replacement.blockModelIdentifier }
.forEach { event.addNonItemModel(it) }
.forEach { event.addNonItemModel(it, it.id) }
}
private fun prepare(manager: ResourceManager): BakedReplacements {
@@ -263,7 +263,7 @@ object CustomBlockTextures {
val island = SkyBlockIsland.forMode(mode)
val islandMpa = map.getOrPut(island, ::mutableMapOf)
for ((blockId, replacement) in json.replacements) {
val block = MC.defaultRegistries.getWrapperOrThrow(RegistryKeys.BLOCK)
val block = MC.defaultRegistries.getOrThrow(RegistryKeys.BLOCK)
.getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId))
.getOrNull()
if (block == null) {

View File

@@ -3,13 +3,13 @@
package moe.nea.firmament.features.texturepack
import java.util.Optional
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers
import kotlin.jvm.optionals.getOrNull
import net.minecraft.item.ArmorMaterial
import net.minecraft.item.ItemStack
import net.minecraft.item.equipment.EquipmentModel
import net.minecraft.resource.ResourceManager
import net.minecraft.resource.SinglePreparationResourceReloader
import net.minecraft.util.Identifier
@@ -17,90 +17,139 @@ import net.minecraft.util.profiler.Profiler
import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.FinalizeResourceManagerEvent
import moe.nea.firmament.events.subscription.SubscriptionOwner
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger
import moe.nea.firmament.util.IdentifierSerializer
import moe.nea.firmament.util.collections.WeakCache
import moe.nea.firmament.util.skyBlockId
object CustomGlobalArmorOverrides : SubscriptionOwner {
@Serializable
data class ArmorOverride(
@SerialName("item_ids")
val itemIds: List<String>,
val layers: List<ArmorOverrideLayer>,
val overrides: List<ArmorOverrideOverride> = listOf(),
) {
@Transient
val bakedLayers = bakeLayers(layers)
}
object CustomGlobalArmorOverrides {
@Serializable
data class ArmorOverride(
@SerialName("item_ids")
val itemIds: List<String>,
val layers: List<ArmorOverrideLayer>? = null,
val model: Identifier? = null,
val overrides: List<ArmorOverrideOverride> = listOf(),
) {
@Transient
lateinit var modelIdentifier: Identifier
fun bake() {
modelIdentifier = bakeModel(model, layers)
overrides.forEach { it.bake() }
}
fun bakeLayers(layers: List<ArmorOverrideLayer>): List<ArmorMaterial.Layer> {
return layers.map { ArmorMaterial.Layer(it.identifier, it.suffix, it.tint) }
}
init {
require(layers != null || model != null) { "Either model or layers must be specified for armor override" }
require(layers == null || model == null) { "Can't specify both model and layers for armor override" }
}
}
@Serializable
data class ArmorOverrideLayer(
val tint: Boolean = false,
val identifier: Identifier,
val suffix: String = "",
)
@Serializable
data class ArmorOverrideLayer(
val tint: Boolean = false,
val identifier: Identifier,
val suffix: String = "",
)
@Serializable
data class ArmorOverrideOverride(
val predicate: FirmamentModelPredicate,
val layers: List<ArmorOverrideLayer>,
) {
@Transient
val bakedLayers = bakeLayers(layers)
}
@Serializable
data class ArmorOverrideOverride(
val predicate: FirmamentModelPredicate,
val layers: List<ArmorOverrideLayer>? = null,
val model: Identifier? = null,
) {
init {
require(layers != null || model != null) { "Either model or layers must be specified for armor override override" }
require(layers == null || model == null) { "Can't specify both model and layers for armor override override" }
}
override val delegateFeature: FirmamentFeature
get() = CustomSkyBlockTextures
@Transient
lateinit var modelIdentifier: Identifier
fun bake() {
modelIdentifier = bakeModel(model, layers)
}
}
val overrideCache = WeakCache.memoize<ItemStack, Optional<List<ArmorMaterial.Layer>>>("ArmorOverrides") { stack ->
val id = stack.skyBlockId ?: return@memoize Optional.empty()
val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
for (suboverride in override.overrides) {
if (suboverride.predicate.test(stack)) {
return@memoize Optional.of(suboverride.bakedLayers)
}
}
return@memoize Optional.of(override.bakedLayers)
}
@JvmStatic
fun overrideArmor(stack: ItemStack): List<ArmorMaterial.Layer>? {
if (!CustomSkyBlockTextures.TConfig.enableArmorOverrides) return null
return overrideCache.invoke(stack).getOrNull()
}
val overrideCache = WeakCache.memoize<ItemStack, Optional<Identifier>>("ArmorOverrides") { stack ->
val id = stack.skyBlockId ?: return@memoize Optional.empty()
val override = overrides[id.neuItem] ?: return@memoize Optional.empty()
for (suboverride in override.overrides) {
if (suboverride.predicate.test(stack)) {
return@memoize Optional.of(suboverride.modelIdentifier)
}
}
return@memoize Optional.of(override.modelIdentifier)
}
var overrides: Map<String, ArmorOverride> = mapOf()
var overrides: Map<String, ArmorOverride> = mapOf()
private var bakedOverrides: MutableMap<Identifier, EquipmentModel> = mutableMapOf()
private val sentinelFirmRunning = AtomicInteger()
@Subscribe
fun onStart(event: FinalizeResourceManagerEvent) {
event.resourceManager.registerReloader(object :
SinglePreparationResourceReloader<Map<String, ArmorOverride>>() {
override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> {
val overrideFiles = manager.findResources("overrides/armor_models") {
it.namespace == "firmskyblock" && it.path.endsWith(".json")
}
val overrides = overrideFiles.mapNotNull {
Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex ->
logger.error("Failed to load armor texture override at ${it.key}", ex)
null
}
}
val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } }
.toMap()
return associatedMap
}
private fun bakeModel(model: Identifier?, layers: List<ArmorOverrideLayer>?): Identifier {
require(model == null || layers == null)
if (model != null) {
return model
} else if (layers != null) {
val idNumber = sentinelFirmRunning.incrementAndGet()
val identifier = Identifier.of("firmament:sentinel/$idNumber")
val equipmentLayers = layers.map {
EquipmentModel.Layer(
it.identifier, if (it.tint) {
Optional.of(EquipmentModel.Dyeable(Optional.empty()))
} else {
Optional.empty()
},
false
)
}
bakedOverrides[identifier] = EquipmentModel(
mapOf(
EquipmentModel.LayerType.HUMANOID to equipmentLayers,
EquipmentModel.LayerType.HUMANOID_LEGGINGS to equipmentLayers,
)
)
return identifier
} else {
error("Either model or layers must be non null")
}
}
override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) {
overrides = prepared
}
})
}
@Subscribe
fun onStart(event: FinalizeResourceManagerEvent) {
event.resourceManager.registerReloader(object :
SinglePreparationResourceReloader<Map<String, ArmorOverride>>() {
override fun prepare(manager: ResourceManager, profiler: Profiler): Map<String, ArmorOverride> {
val overrideFiles = manager.findResources("overrides/armor_models") {
it.namespace == "firmskyblock" && it.path.endsWith(".json")
}
val overrides = overrideFiles.mapNotNull {
Firmament.tryDecodeJsonFromStream<ArmorOverride>(it.value.inputStream).getOrElse { ex ->
logger.error("Failed to load armor texture override at ${it.key}", ex)
null
}
}
val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } }
.toMap()
return associatedMap
}
override fun apply(prepared: Map<String, ArmorOverride>, manager: ResourceManager, profiler: Profiler) {
bakedOverrides.clear()
prepared.forEach { it.value.bake() }
overrides = prepared
}
})
}
@JvmStatic
fun overrideArmor(itemStack: ItemStack): Optional<Identifier> {
return overrideCache.invoke(itemStack)
}
@JvmStatic
fun overrideArmorLayer(id: Identifier): EquipmentModel? {
return bakedOverrides[id]
}
}

View File

@@ -146,7 +146,7 @@ object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalText
it.overrides
.asSequence()
.filter { it.predicate.test(stack) }
.map { models.modelManager.getModel(ModelIdentifier(it.model, "inventory")) }
.map { models.getModel(it.model) }
.firstOrNull()
}
.intoOptional()

View File

@@ -1,9 +1,11 @@
package moe.nea.firmament.features.texturepack
import net.minecraft.client.render.model.Baker
import net.minecraft.util.Identifier
interface JsonUnbakedModelFirmExtra {
fun storeExtraBaker_firmament(baker: Baker)
fun setHeadModel_firmament(identifier: Identifier?)
fun getHeadModel_firmament(): Identifier?

View File

@@ -3,7 +3,6 @@ package moe.nea.firmament.features.texturepack
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import moe.nea.firmament.util.ErrorUtil
import moe.nea.firmament.util.assertNotNullOr
data class TintOverrides(
val layerMap: Map<Int, TintOverride> = mapOf()
@@ -14,21 +13,22 @@ data class TintOverrides(
val EMPTY = TintOverrides()
private val threadLocal = object : ThreadLocal<TintOverrides>() {}
fun enter(overrides: TintOverrides?) {
ErrorUtil.softCheck("Double entered tintOverrides") {
threadLocal.get() == null
}
ErrorUtil.softCheck("Double entered tintOverrides",
threadLocal.get() == null)
threadLocal.set(overrides ?: EMPTY)
}
fun exit(overrides: TintOverrides?) {
ErrorUtil.softCheck("Exited with non matching enter tintOverrides") {
threadLocal.get() == (overrides ?: EMPTY)
}
ErrorUtil.softCheck("Exited with non matching enter tintOverrides",
threadLocal.get() == (overrides ?: EMPTY))
threadLocal.remove()
}
fun getCurrentOverrides() =
assertNotNullOr(threadLocal.get(), "Got current tintOverrides without entering") { EMPTY }
fun getCurrentOverrides(): TintOverrides {
return ErrorUtil.notNullOr(threadLocal.get(), "Got current tintOverrides without entering") {
EMPTY
}
}
fun parse(jsonObject: JsonObject): TintOverrides {
val map = mutableMapOf<Int, TintOverride>()

View File

@@ -6,6 +6,7 @@ import io.github.moulberry.repo.data.Coordinate
import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer
import net.minecraft.text.Text
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ProcessChatEvent
@@ -98,9 +99,8 @@ object FairySouls : FirmamentFeature {
fun onWorldRender(it: WorldRenderLastEvent) {
if (!TConfig.displaySouls) return
renderInWorld(it) {
color(1F, 1F, 0F, 0.8F)
currentMissingSouls.forEach {
block(it.blockPos)
block(it.blockPos, 0x80FFFF00.toInt())
}
color(1f, 0f, 1f, 1f)
currentLocationSouls.forEach {

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.features.world
import com.mojang.brigadier.arguments.IntegerArgumentType
@@ -12,6 +10,7 @@ import kotlin.collections.set
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.seconds
import net.minecraft.command.argument.BlockPosArgumentType
import net.minecraft.server.command.CommandOutput
import net.minecraft.server.command.ServerCommandSource
import net.minecraft.text.Text
import net.minecraft.util.math.BlockPos
@@ -35,263 +34,273 @@ import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.render.RenderInWorldContext
object Waypoints : FirmamentFeature {
override val identifier: String
get() = "waypoints"
override val identifier: String
get() = "waypoints"
object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc
val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds }
val showIndex by toggle("show-index") { true }
val skipToNearest by toggle("skip-to-nearest") { false }
// TODO: look ahead size
}
object TConfig : ManagedConfig(identifier, Category.MINING) { // TODO: add to misc
val tempWaypointDuration by duration("temp-waypoint-duration", 0.seconds, 1.hours) { 30.seconds }
val showIndex by toggle("show-index") { true }
val skipToNearest by toggle("skip-to-nearest") { false }
// TODO: look ahead size
}
data class TemporaryWaypoint(
val pos: BlockPos,
val postedAt: TimeMark,
)
data class TemporaryWaypoint(
val pos: BlockPos,
val postedAt: TimeMark,
)
override val config get() = TConfig
override val config get() = TConfig
val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>()
val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern()
val temporaryPlayerWaypointList = mutableMapOf<String, TemporaryWaypoint>()
val temporaryPlayerWaypointMatcher = "(?i)x: (-?[0-9]+),? y: (-?[0-9]+),? z: (-?[0-9]+)".toPattern()
val waypoints = mutableListOf<BlockPos>()
var ordered = false
var orderedIndex = 0
val waypoints = mutableListOf<BlockPos>()
var ordered = false
var orderedIndex = 0
@Serializable
data class ColeWeightWaypoint(
val x: Int,
val y: Int,
val z: Int,
val r: Int = 0,
val g: Int = 0,
val b: Int = 0,
)
@Serializable
data class ColeWeightWaypoint(
val x: Int,
val y: Int,
val z: Int,
val r: Int = 0,
val g: Int = 0,
val b: Int = 0,
)
@Subscribe
fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) {
if (waypoints.isEmpty()) return
RenderInWorldContext.renderInWorld(event) {
if (!ordered) {
waypoints.withIndex().forEach {
color(0f, 0.3f, 0.7f, 0.5f)
block(it.value)
color(1f, 1f, 1f, 1f)
if (TConfig.showIndex)
withFacingThePlayer(it.value.toCenterPos()) {
text(Text.literal(it.index.toString()))
}
}
} else {
orderedIndex %= waypoints.size
val firstColor = Color.ofRGBA(0, 200, 40, 180)
color(firstColor)
tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f)
waypoints.withIndex().toList()
.wrappingWindow(orderedIndex, 3)
.zip(
listOf(
firstColor,
Color.ofRGBA(180, 200, 40, 150),
Color.ofRGBA(180, 80, 20, 140),
)
)
.reversed()
.forEach { (waypoint, col) ->
val (index, pos) = waypoint
color(col)
block(pos)
color(1f, 1f, 1f, 1f)
if (TConfig.showIndex)
withFacingThePlayer(pos.toCenterPos()) {
text(Text.literal(index.toString()))
}
}
}
}
}
@Subscribe
fun onRenderOrderedWaypoints(event: WorldRenderLastEvent) {
if (waypoints.isEmpty()) return
RenderInWorldContext.renderInWorld(event) {
if (!ordered) {
waypoints.withIndex().forEach {
block(it.value, 0x800050A0.toInt())
if (TConfig.showIndex)
withFacingThePlayer(it.value.toCenterPos()) {
text(Text.literal(it.index.toString()))
}
}
} else {
orderedIndex %= waypoints.size
val firstColor = Color.ofRGBA(0, 200, 40, 180)
color(firstColor)
tracer(waypoints[orderedIndex].toCenterPos(), lineWidth = 3f)
waypoints.withIndex().toList()
.wrappingWindow(orderedIndex, 3)
.zip(
listOf(
firstColor,
Color.ofRGBA(180, 200, 40, 150),
Color.ofRGBA(180, 80, 20, 140),
)
)
.reversed()
.forEach { (waypoint, col) ->
val (index, pos) = waypoint
block(pos, col.color)
if (TConfig.showIndex)
withFacingThePlayer(pos.toCenterPos()) {
text(Text.literal(index.toString()))
}
}
}
}
}
@Subscribe
fun onTick(event: TickEvent) {
if (waypoints.isEmpty() || !ordered) return
orderedIndex %= waypoints.size
val p = MC.player?.pos ?: return
if (TConfig.skipToNearest) {
orderedIndex =
(waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size
} else {
if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) {
orderedIndex = (orderedIndex + 1) % waypoints.size
}
}
}
@Subscribe
fun onTick(event: TickEvent) {
if (waypoints.isEmpty() || !ordered) return
orderedIndex %= waypoints.size
val p = MC.player?.pos ?: return
if (TConfig.skipToNearest) {
orderedIndex =
(waypoints.withIndex().minBy { it.value.getSquaredDistance(p) }.index + 1) % waypoints.size
} else {
if (waypoints[orderedIndex].isWithinDistance(p, 3.0)) {
orderedIndex = (orderedIndex + 1) % waypoints.size
}
}
}
@Subscribe
fun onProcessChat(it: ProcessChatEvent) {
val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString)
if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) {
temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint(
BlockPos(
matcher.group(1).toInt(),
matcher.group(2).toInt(),
matcher.group(3).toInt(),
),
TimeMark.now()
)
}
}
@Subscribe
fun onProcessChat(it: ProcessChatEvent) {
val matcher = temporaryPlayerWaypointMatcher.matcher(it.unformattedString)
if (it.nameHeuristic != null && TConfig.tempWaypointDuration > 0.seconds && matcher.find()) {
temporaryPlayerWaypointList[it.nameHeuristic] = TemporaryWaypoint(
BlockPos(
matcher.group(1).toInt(),
matcher.group(2).toInt(),
matcher.group(3).toInt(),
),
TimeMark.now()
)
}
}
@Subscribe
fun onCommand(event: CommandEvent.SubCommand) {
event.subcommand("waypoint") {
thenArgument("pos", BlockPosArgumentType.blockPos()) { pos ->
thenExecute {
val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer())
waypoints.add(position)
source.sendFeedback(
Text.stringifiedTranslatable(
"firmament.command.waypoint.added",
position.x,
position.y,
position.z
)
)
}
}
}
event.subcommand("waypoints") {
thenLiteral("clear") {
thenExecute {
waypoints.clear()
source.sendFeedback(Text.translatable("firmament.command.waypoint.clear"))
}
}
thenLiteral("toggleordered") {
thenExecute {
ordered = !ordered
if (ordered) {
val p = MC.player?.pos ?: Vec3d.ZERO
orderedIndex =
waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0
}
source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered"))
}
}
thenLiteral("skip") {
thenExecute {
if (ordered && waypoints.isNotEmpty()) {
orderedIndex = (orderedIndex + 1) % waypoints.size
source.sendFeedback(Text.translatable("firmament.command.waypoint.skip"))
} else {
source.sendError(Text.translatable("firmament.command.waypoint.skip.error"))
}
}
}
thenLiteral("remove") {
thenArgument("index", IntegerArgumentType.integer(0)) { indexArg ->
thenExecute {
val index = get(indexArg)
if (index in waypoints.indices) {
waypoints.removeAt(index)
source.sendFeedback(Text.stringifiedTranslatable(
"firmament.command.waypoint.remove",
index))
} else {
source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error"))
}
}
}
}
thenLiteral("import") {
thenExecute {
val contents = ClipboardUtils.getTextContents()
val data = try {
Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents)
} catch (ex: Exception) {
Firmament.logger.error("Could not load waypoints from clipboard", ex)
source.sendError(Text.translatable("firmament.command.waypoint.import.error"))
return@thenExecute
}
waypoints.clear()
data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) }
source.sendFeedback(
Text.stringifiedTranslatable(
"firmament.command.waypoint.import",
data.size
)
)
}
}
}
}
@Subscribe
fun onCommand(event: CommandEvent.SubCommand) {
event.subcommand("waypoint") {
thenArgument("pos", BlockPosArgumentType.blockPos()) { pos ->
thenExecute {
val position = pos.get(this).toAbsoluteBlockPos(source.asFakeServer())
waypoints.add(position)
source.sendFeedback(
Text.stringifiedTranslatable(
"firmament.command.waypoint.added",
position.x,
position.y,
position.z
)
)
}
}
}
event.subcommand("waypoints") {
thenLiteral("clear") {
thenExecute {
waypoints.clear()
source.sendFeedback(Text.translatable("firmament.command.waypoint.clear"))
}
}
thenLiteral("toggleordered") {
thenExecute {
ordered = !ordered
if (ordered) {
val p = MC.player?.pos ?: Vec3d.ZERO
orderedIndex =
waypoints.withIndex().minByOrNull { it.value.getSquaredDistance(p) }?.index ?: 0
}
source.sendFeedback(Text.translatable("firmament.command.waypoint.ordered.toggle.$ordered"))
}
}
thenLiteral("skip") {
thenExecute {
if (ordered && waypoints.isNotEmpty()) {
orderedIndex = (orderedIndex + 1) % waypoints.size
source.sendFeedback(Text.translatable("firmament.command.waypoint.skip"))
} else {
source.sendError(Text.translatable("firmament.command.waypoint.skip.error"))
}
}
}
thenLiteral("remove") {
thenArgument("index", IntegerArgumentType.integer(0)) { indexArg ->
thenExecute {
val index = get(indexArg)
if (index in waypoints.indices) {
waypoints.removeAt(index)
source.sendFeedback(Text.stringifiedTranslatable(
"firmament.command.waypoint.remove",
index))
} else {
source.sendError(Text.stringifiedTranslatable("firmament.command.waypoint.remove.error"))
}
}
}
}
thenLiteral("import") {
thenExecute {
val contents = ClipboardUtils.getTextContents()
val data = try {
Firmament.json.decodeFromString<List<ColeWeightWaypoint>>(contents)
} catch (ex: Exception) {
Firmament.logger.error("Could not load waypoints from clipboard", ex)
source.sendError(Text.translatable("firmament.command.waypoint.import.error"))
return@thenExecute
}
waypoints.clear()
data.mapTo(waypoints) { BlockPos(it.x, it.y, it.z) }
source.sendFeedback(
Text.stringifiedTranslatable(
"firmament.command.waypoint.import",
data.size
)
)
}
}
}
}
@Subscribe
fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) {
temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration }
if (temporaryPlayerWaypointList.isEmpty()) return
RenderInWorldContext.renderInWorld(event) {
color(1f, 1f, 0f, 1f)
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
block(waypoint.pos)
}
color(1f, 1f, 1f, 1f)
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
val skin =
MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player }
?.skinTextures
?.texture
withFacingThePlayer(waypoint.pos.toCenterPos()) {
waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player))
if (skin != null) {
matrixStack.translate(0F, -20F, 0F)
// Head front
texture(
skin, 16, 16,
1 / 8f, 1 / 8f,
2 / 8f, 2 / 8f,
)
// Head overlay
texture(
skin, 16, 16,
5 / 8f, 1 / 8f,
6 / 8f, 2 / 8f,
)
}
}
}
}
}
@Subscribe
fun onRenderTemporaryWaypoints(event: WorldRenderLastEvent) {
temporaryPlayerWaypointList.entries.removeIf { it.value.postedAt.passedTime() > TConfig.tempWaypointDuration }
if (temporaryPlayerWaypointList.isEmpty()) return
RenderInWorldContext.renderInWorld(event) {
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
block(waypoint.pos, 0xFFFFFF00.toInt())
}
temporaryPlayerWaypointList.forEach { (player, waypoint) ->
val skin =
MC.networkHandler?.listedPlayerListEntries?.find { it.profile.name == player }
?.skinTextures
?.texture
withFacingThePlayer(waypoint.pos.toCenterPos()) {
waypoint(waypoint.pos, Text.stringifiedTranslatable("firmament.waypoint.temporary", player))
if (skin != null) {
matrixStack.translate(0F, -20F, 0F)
// Head front
texture(
skin, 16, 16,
1 / 8f, 1 / 8f,
2 / 8f, 2 / 8f,
)
// Head overlay
texture(
skin, 16, 16,
5 / 8f, 1 / 8f,
6 / 8f, 2 / 8f,
)
}
}
}
}
}
@Subscribe
fun onWorldReady(event: WorldReadyEvent) {
temporaryPlayerWaypointList.clear()
}
@Subscribe
fun onWorldReady(event: WorldReadyEvent) {
temporaryPlayerWaypointList.clear()
}
}
fun <E> List<E>.wrappingWindow(startIndex: Int, windowSize: Int): List<E> {
val result = ArrayList<E>(windowSize)
if (startIndex + windowSize < size) {
result.addAll(subList(startIndex, startIndex + windowSize))
} else {
result.addAll(subList(startIndex, size))
result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex)))
}
return result
val result = ArrayList<E>(windowSize)
if (startIndex + windowSize < size) {
result.addAll(subList(startIndex, startIndex + windowSize))
} else {
result.addAll(subList(startIndex, size))
result.addAll(subList(0, minOf(windowSize - (size - startIndex), startIndex)))
}
return result
}
fun FabricClientCommandSource.asFakeServer(): ServerCommandSource {
val source = this
return ServerCommandSource(
source.player,
source.position,
source.rotation,
null,
0,
"FakeServerCommandSource",
Text.literal("FakeServerCommandSource"),
null,
source.player
)
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
)
}