Skip to content
Merged
112 changes: 112 additions & 0 deletions src/main/kotlin/cn/elytra/mod/bandit/client/VeinMiningHUD.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cn.elytra.mod.bandit.client

import cn.elytra.mod.bandit.common.player_data.VeinMiningNoticeType
import cn.elytra.mod.bandit.common.util.HasUnlocalizedName
import cn.elytra.mod.bandit.mining.BlockFilterRegistry
import cn.elytra.mod.bandit.mining.ExecutorGeneratorRegistry
Expand All @@ -24,6 +25,17 @@ object VeinMiningHUD {
private val executors: CircleList<Pair<Int, VeinMiningExecutorGenerator>> = ExecutorGeneratorCircleList
private val blockFilters: CircleList<Pair<Int, VeinMiningBlockFilter>> = BlockFilterCircleList

data class HudNotice(
Comment thread
Taskeren marked this conversation as resolved.
val id: Long, // 唯一 ID
var noticeType: Int = 0,
var startTick: Long,
var fadeDelay: Int = 0, // 延迟渐隐 tick
var fadeTicks: Int = 20, // 渐隐时长
var extraData: Map<String, Int> = emptyMap(), // 额外数据
var isEnded: Boolean = false
)
private val notices = mutableListOf<HudNotice>()

/**
* Reference to the active menu, used to either show the menu and handle the mouse scroll inputs.
*/
Expand Down Expand Up @@ -78,6 +90,106 @@ object VeinMiningHUD {
}
}

fun pushNotice(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice 这部分,动词用 Add 和 Remove 会不会比较直观一点。

以及,internal

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add和remove给我的感觉更像是直接对notice进行添加或移除,并没有进行其他操作,直接对存在与否进行判定。push和end感觉结束的没那么直接,还有动作发生,是一个流动的状态。

noticeType: Int,
id: Long,
extraData: Map<String, Int> = emptyMap(),
fadeDelay: Int = 0,
fadeTicks: Int = 0
): Long {
val isEnd = !(fadeDelay == 0 && fadeTicks == 0)
var now: Long = 0
if (isEnd) {
now = Minecraft.getMinecraft().theWorld.totalWorldTime
}
notices += HudNotice(
id = id,
noticeType = noticeType,
extraData = extraData,
startTick = now,
fadeDelay = fadeDelay,
fadeTicks = fadeTicks,
isEnded = isEnd
)
VeinMiningHandlerClient.noticeActive = true
return id
}

fun endNotice(id: Long, fadeDelay: Int = 0, fadeTicks: Int = 20) {
if (fadeTicks == 0 && fadeDelay == 0) {
notices.removeIf { it.id == id }
return
}
val now = Minecraft.getMinecraft().theWorld.totalWorldTime
notices.find { it.id == id && !it.isEnded }?.apply {
isEnded = true
startTick = now
this.fadeDelay = fadeDelay
this.fadeTicks = fadeTicks
}
}

private fun getActiveNotices(): List<Pair<HudNotice, Float>> {
val now = Minecraft.getMinecraft().theWorld.totalWorldTime
val iter = notices.iterator()
val result = mutableListOf<Pair<HudNotice, Float>>()

while (iter.hasNext()) {
val n = iter.next()
val age = (now - n.startTick).toInt()

val alpha = when {
!n.isEnded -> 1f
age <= n.fadeDelay -> 1f
age < n.fadeDelay + n.fadeTicks -> 1f - (age - n.fadeDelay) / n.fadeTicks.toFloat()
else -> {
iter.remove()
continue
}
}

result += n to alpha
}

if (result.isEmpty()) VeinMiningHandlerClient.noticeActive = false

return result
}

internal fun renderNotice() {
val mc = Minecraft.getMinecraft()
if(mc.currentScreen == null && mc.theWorld != null) {
glDisable(GL_RESCALE_NORMAL)
glDisable(GL_LIGHTING)

glDisable(GL_DEPTH_TEST)

var y = marginY + 45 // list height + font height
for ((notice, alpha) in getActiveNotices()) {
val color = (alpha * 255).toInt() shl 24 or 0xFFFFFF
val text = when(VeinMiningNoticeType.fromId(notice.noticeType)) {
VeinMiningNoticeType.TASK_STARTING -> I18n.format("bandit.message.task-starting")
VeinMiningNoticeType.TASK_HALT_HINT -> I18n.format("bandit.message.task-halt-hint")
VeinMiningNoticeType.TASK_STOP_KEY_RELEASE -> I18n.format("bandit.message.task-stop.key-release")
VeinMiningNoticeType.TASK_DONE -> {
val statBlocksMined = notice.extraData["statBlocksMined"] ?: 0
val statItemDropped = notice.extraData["statItemDropped"] ?: 0
I18n.format(
"bandit.message.task-done",
statBlocksMined,
statItemDropped
)
}
null -> ""
}
drawStringWithMargin(text, marginX, y, color)
y += 9 // font height
}

glEnable(GL_RESCALE_NORMAL)
}
}

private fun getPosXWithMargin(margin: Int, str: String): Int {
val mc = Minecraft.getMinecraft()
val textRender = mc.fontRenderer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ object VeinMiningHandlerClient {
var veinMiningBlockFilterId = 0

var keyPressed = false
var noticeActive = false

val statusKey =
KeyBinding("keybinding.bandit.trigger", Keyboard.KEY_NONE, "keybinding.bandit.category")
Expand Down Expand Up @@ -101,6 +102,9 @@ object VeinMiningHandlerClient {
if(keyPressed) {
VeinMiningHUD.render()
}
if(noticeActive) {
VeinMiningHUD.renderNotice()
}
}

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,48 @@ object BanditCommand : CommandBase() {
}
}

override fun addTabCompletionOptions(sender: ICommandSender?, args: Array<out String?>?): List<String?>? {
if (args.isNullOrEmpty()) return super.addTabCompletionOptions(sender, args)
return when (args.size) {

// ===== 第一级:/bandit <这里> =====
1 -> getListOfStringsMatchingLastWord(
args,
"stop",
"help",
"drop_pos",
"drop_timing",
"stop_on_release",
"block-filter",
"executor-generator",
)

// ===== 第二级:/bandit <sub> <这里> =====
2 -> when (args[0]) {
"executor-generator", "executor" -> listOf("[Leave it blank to view the prompt]")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

指令补全应该直接给出补全的具体内容

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

()补全id号咩?如果带解析器名的提示目前的handler处理不了的,只提示数字的话感觉没有意义

"block-filter", "filter" -> listOf("[Leave it blank to view the prompt]")
"drop_pos" ->
getListOfStringsMatchingLastWord(
args,
*getValidEnumValues<DropPosition>().toTypedArray()
)
"drop_timing" ->
getListOfStringsMatchingLastWord(
args,
*getValidEnumValues<DropTiming>().toTypedArray()
)
"stop_on_release" ->
getListOfStringsMatchingLastWord(
args,
"true",
"false"
)
else -> super.addTabCompletionOptions(sender, args)
}
else -> super.addTabCompletionOptions(sender, args)
}
}

private fun ICommandSender.withEntityPlayer(block: (EntityPlayerMP) -> Unit) {
if(this is EntityPlayerMP) {
block(this)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cn.elytra.mod.bandit.common.player_data

/**
* 连锁挖掘通知类型
*/
enum class VeinMiningNoticeType(val id: Int) {
/**
* 任务开始
*/
TASK_STARTING(1),

/**
* 提示可以停止任务
*/
TASK_HALT_HINT(2),

/**
* 按键释放导致停止
*/
TASK_STOP_KEY_RELEASE(3),

/**
* 任务完成(需要统计数据)
*/
TASK_DONE(4);

companion object {
fun fromId(id: Int): VeinMiningNoticeType? {
return entries.firstOrNull { it.id == id }
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.server.MinecraftServer
import net.minecraft.util.ChatComponentTranslation
import net.minecraft.util.MovingObjectPosition
import java.util.*
import kotlin.coroutines.cancellation.CancellationException
Expand Down Expand Up @@ -297,31 +296,44 @@ data class VeinMiningPlayerData(

BanditMod.logger.info("Executing Vein Mining #${executionId}")
BanditMod.logger.debug("Executing Vein Mining #${executionId} at (${pos.x}, ${pos.y}, ${pos.z} @ ${world.provider.dimensionId}) ref ${blockAndMeta.first.unlocalizedName} @ ${blockAndMeta.second} te ${blockTileEntity?.toString() ?: "null"}")
player.addChatMessage(ChatComponentTranslation("bandit.message.task-starting"))
player.addChatMessage(ChatComponentTranslation("bandit.message.task-halt-hint"))
val notices = mutableListOf<Long>()
notices.add(BanditNetwork.pushSimpleNoticeToClient(player.asMP, VeinMiningNoticeType.TASK_STARTING))
notices.add(BanditNetwork.pushSimpleNoticeToClient(player.asMP, VeinMiningNoticeType.TASK_HALT_HINT))

val job = BanditCoroutines.VeinMiningScope.launch(start = CoroutineStart.LAZY) {
world.playSoundEffect(player.posX, player.posY, player.posZ, "note.harp", 3.0F, 1.0F)
executor()
}
job.invokeOnCompletion {
BanditMod.logger.info("VeinMining #${executionId} has ended")
if(it != null) {
when(it) {
is KeyReleaseCancellation -> {
getPlayer().addChatMessage(ChatComponentTranslation("bandit.message.task-stop.key-release"))
}

else -> BanditMod.logger.warn("Job was cancelled because of an throwable", it)
for (noticeId in notices) {
BanditNetwork.endNoticeToClient(player.asMP, noticeId, fadeDelay = 0, fadeTicks = 0)
}

when(it) {
is KeyReleaseCancellation -> {
BanditNetwork.pushSimpleNoticeToClient(player.asMP,
noticeType = VeinMiningNoticeType.TASK_STOP_KEY_RELEASE,
fadeDelay = 10,
fadeTicks = 20
)
}

else -> BanditMod.logger.warn("Job was cancelled because of an throwable", it)
}
player.addChatMessage(
ChatComponentTranslation(
"bandit.message.task-done",
context.statBlocksMined.get(),
context.statItemDropped.values.sum()
)

// 完成:发送任务完成通知,40tick后渐隐,渐隐20tick
BanditNetwork.pushCompletionNoticeToClient(
player.asMP,
extraData = mapOf(
"statBlocksMined" to context.statBlocksMined.get(),
"statItemDropped" to context.statItemDropped.values.sum()
),
fadeDelay = 40,
fadeTicks = 20
)

// play sound effects
world.playSoundEffect(player.posX, player.posY, player.posZ, "note.harp", 3.0F, 3.5F)
// clear caches
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import cn.elytra.mod.bandit.common.mining.VeinMiningContext
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
import kotlinx.coroutines.flow.flowOn
import kotlin.math.max
import kotlin.math.min

class LargeScanExecutorGenerator(
private val radiusXZ: Int,
Expand All @@ -22,18 +24,16 @@ class LargeScanExecutorGenerator(
for (x in x - radiusXZ until x + radiusXZ) {
for (z in z - radiusXZ until z + radiusXZ) {
// y iteration reversed from up to down
for (y in y + radiusY downTo y - radiusY) {
// FIX: the block can possibly not loaded, causing a CME when not loading it in the server thread.
// this may impact on the performance of executing large-scan.
withContext(BanditCoroutines.ServerThreadDispatcher) {
if (isBlockMatchingVein(context, BlockPos(x, y, z))) {
emit(BlockPos(x, y, z))
}
for (y in max((y + radiusY), 256) downTo min((y - radiusY), 0)) {
if (isBlockMatchingVein(context, BlockPos(x, y, z))) {
emit(BlockPos(x, y, z))
}
}
}
}
}
// FIX: the block can possibly not loaded, causing a CME when not loading it in the server thread.
// this may impact on the performance of executing large-scan.
}.flowOn(BanditCoroutines.ServerThreadDispatcher)

override fun getUnlocalizedName(): String = "bandit.executor.large-scan"
}
Loading