Skip to content

Commit a522ab7

Browse files
authored
feat: Notebot (#6439)
1 parent f653ee4 commit a522ab7

File tree

26 files changed

+1397
-21
lines changed

26 files changed

+1397
-21
lines changed

src/main/kotlin/net/ccbluex/liquidbounce/features/module/ClientModule.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,13 @@ open class ClientModule(
121121
*/
122122
open fun onRegistration() {}
123123

124-
override fun onEnabledValueRegistration(value: Value<Boolean>) =
124+
final override fun onEnabledValueRegistration(value: Value<Boolean>) =
125125
super.onEnabledValueRegistration(value).also { value ->
126126
// Might not include the enabled state of the module depending on the category
127127
if (category == Category.MISC || category == Category.FUN || category == Category.RENDER) {
128128
if (this is ModuleAntiBot) {
129129
return@also
130130
}
131-
132131
value.doNotIncludeAlways()
133132
}
134133
}.notAnOption().onChanged { newState ->

src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import net.ccbluex.liquidbounce.features.module.modules.exploit.dupe.ModuleDupe
4444
import net.ccbluex.liquidbounce.features.module.modules.exploit.phase.ModulePhase
4545
import net.ccbluex.liquidbounce.features.module.modules.exploit.servercrasher.ModuleServerCrasher
4646
import net.ccbluex.liquidbounce.features.module.modules.`fun`.*
47+
import net.ccbluex.liquidbounce.features.module.modules.`fun`.notebot.ModuleNotebot
4748
import net.ccbluex.liquidbounce.features.module.modules.misc.*
4849
import net.ccbluex.liquidbounce.features.module.modules.misc.antibot.ModuleAntiBot
4950
import net.ccbluex.liquidbounce.features.module.modules.misc.betterchat.ModuleBetterChat
@@ -250,6 +251,7 @@ object ModuleManager : EventListener, Iterable<ClientModule> by modules {
250251
// Fun
251252
ModuleDankBobbing,
252253
ModuleDerp,
254+
ModuleNotebot,
253255
ModuleSkinDerp,
254256
ModuleHandDerp,
255257
ModuleTwerk,
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/*
2+
* This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce)
3+
*
4+
* Copyright (c) 2015 - 2025 CCBlueX
5+
*
6+
* LiquidBounce is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* LiquidBounce is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with LiquidBounce. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
package net.ccbluex.liquidbounce.features.module.modules.`fun`.notebot
20+
21+
import kotlinx.coroutines.Dispatchers
22+
import kotlinx.coroutines.withContext
23+
import net.ccbluex.liquidbounce.config.types.nesting.Configurable
24+
import net.ccbluex.liquidbounce.event.events.PacketEvent
25+
import net.ccbluex.liquidbounce.event.handler
26+
import net.ccbluex.liquidbounce.event.tickHandler
27+
import net.ccbluex.liquidbounce.features.module.Category
28+
import net.ccbluex.liquidbounce.features.module.ClientModule
29+
import net.ccbluex.liquidbounce.features.module.modules.`fun`.notebot.nbs.InstrumentNote
30+
import net.ccbluex.liquidbounce.features.module.modules.`fun`.notebot.nbs.NbsLoader
31+
import net.ccbluex.liquidbounce.features.module.modules.`fun`.notebot.nbs.NbsNoteBlock
32+
import net.ccbluex.liquidbounce.features.module.modules.`fun`.notebot.nbs.SongData
33+
import net.ccbluex.liquidbounce.features.module.modules.world.packetmine.ModulePacketMine
34+
import net.ccbluex.liquidbounce.render.engine.type.Color4b
35+
import net.ccbluex.liquidbounce.utils.aiming.RotationsConfigurable
36+
import net.ccbluex.liquidbounce.utils.client.*
37+
import net.minecraft.block.enums.NoteBlockInstrument
38+
import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket
39+
import net.minecraft.text.MutableText
40+
import net.minecraft.util.Formatting
41+
import net.minecraft.util.math.MathHelper
42+
import java.util.*
43+
44+
/**
45+
* Notebot Module
46+
*
47+
* Automatically plays note block songs from NBS files.
48+
*
49+
* @author ccetl
50+
*/
51+
object ModuleNotebot : ClientModule("Notebot", Category.FUN, disableOnQuit = true) {
52+
53+
private val song = file("Song", supportedExtensions = setOf("nbs"))
54+
private val pianoOnly by boolean("PianoOnly", false)
55+
val reuseBlocks by boolean("ReuseBlocks", true).onChanged { enabled = false }
56+
val range by float("Range", 6f, 1f..6f)
57+
val rotationsConfigurable = RotationsConfigurable(this)
58+
val ignoreOpenInventory by boolean("IgnoreOpenInventory", true)
59+
60+
private object StartDelay : Configurable("StartDelay") {
61+
val test by int("Test", 0, 0..20, "ticks")
62+
val tune by int("Tune", 0, 0..20, "ticks")
63+
val play by int("Play", 2, 0..20, "ticks")
64+
}
65+
66+
init {
67+
tree(StartDelay)
68+
}
69+
70+
val renderer = tree(NotebotRenderer)
71+
72+
var engine: NotebotEngine? = null
73+
private set
74+
75+
@Suppress("unused")
76+
private val tickHandler = tickHandler {
77+
engine?.onTick(this)
78+
}
79+
80+
@Suppress("unused")
81+
private val packetHandler = handler<PacketEvent> { event ->
82+
if (event.packet is PlaySoundS2CPacket) {
83+
this.engine?.handleSoundPacket(event.packet)
84+
}
85+
}
86+
87+
override suspend fun enabledEffect() {
88+
val messageMetadata = MessageMetadata(id = "M${this.name}#loaded", remove = false)
89+
mc.inGameHud.chatHud.removeMessage(messageMetadata.id)
90+
91+
if (!checkRequirements()) {
92+
this.enabled = false
93+
return
94+
}
95+
96+
val songData = loadSongData()
97+
98+
if (songData == null) {
99+
this.enabled = false
100+
return
101+
}
102+
103+
val blocksAndRequirements = NotebotScanner.scanBlocksAndCheckRequirements(songData)
104+
105+
if (!blocksAndRequirements.validateRequirements()) {
106+
blocksAndRequirements.printRequirements()
107+
this.enabled = false
108+
return
109+
}
110+
111+
this.setRenderedBlocks(blocksAndRequirements.availableBlocks.flatMap { it.value })
112+
113+
showSongInfo(songData, messageMetadata)
114+
115+
this.engine = NotebotEngine(songData, blocksAndRequirements)
116+
chat(message("startTesting").formatted(Formatting.GREEN), this)
117+
}
118+
119+
fun setRenderedBlocks(blocks: List<NoteBlockTracker>) {
120+
renderer.clearSilently()
121+
122+
blocks.forEach {
123+
renderer.addBlock(it.pos, false)
124+
}
125+
126+
renderer.updateAll()
127+
}
128+
129+
private suspend fun loadSongData(): SongData? {
130+
chat(message("startLoading").formatted(Formatting.GREEN), this)
131+
132+
val songData = withContext(Dispatchers.IO) {
133+
NbsLoader.load(song.absoluteFile)
134+
}
135+
136+
return songData
137+
}
138+
139+
private fun checkRequirements(): Boolean {
140+
return when {
141+
!inGame -> {
142+
chat(markAsError(message("notInGame")), this)
143+
false
144+
}
145+
146+
player.isCreative -> {
147+
chat(markAsError(message("inCreative")), this)
148+
false
149+
}
150+
151+
ModulePacketMine.enabled -> {
152+
chat(markAsError(message("packetMineEnabled")), this)
153+
false
154+
}
155+
156+
else -> true
157+
}
158+
}
159+
160+
private fun showSongInfo(
161+
songData: SongData,
162+
messageMetadata: MessageMetadata
163+
) {
164+
chat(
165+
regular(message("songInfoName", variable(songData.name))),
166+
messageMetadata
167+
)
168+
chat(
169+
regular(message("songInfoTicksPerGameTick", variable(songData.songTicksPerGameTick.toString()))),
170+
messageMetadata
171+
)
172+
chat(
173+
regular(message("songInfoTickLength", variable(songData.songTickLength.toString()))),
174+
messageMetadata
175+
)
176+
chat(
177+
regular(message("songInfoTotalNotes", variable(songData.nbs.noteBlocks.size.toString()))),
178+
messageMetadata
179+
)
180+
}
181+
182+
override fun onDisabled() {
183+
removeProgressMessage()
184+
185+
renderer.reset()
186+
}
187+
188+
private val progressMessageMetadata = MessageMetadata(id = "M$name#progress", remove = false)
189+
190+
private fun removeProgressMessage() {
191+
mc.inGameHud.chatHud.removeMessage(progressMessageMetadata.id)
192+
}
193+
194+
fun sendNewProgressMessage(name: MutableText, progress: Int, total: Int) {
195+
removeProgressMessage()
196+
197+
val percent = (progress.toDouble() / total.toDouble() * 100.0).toInt()
198+
chat(
199+
variable(name.copy())
200+
.append(regular(" ["))
201+
.append(textLoadingBar(percent))
202+
.append(regular("] "))
203+
.append(variable(percent.toString()))
204+
.append(regular("%")),
205+
metadata = progressMessageMetadata
206+
)
207+
}
208+
209+
fun getPlayedNote(note: NbsNoteBlock): InstrumentNote {
210+
val noteValue = MathHelper.clamp(note.key - 33, 0, 24)
211+
val instrument = if (!this.pianoOnly) {
212+
note.instrument.toInt()
213+
} else {
214+
0
215+
}
216+
217+
return InstrumentNote(instrument, noteValue)
218+
}
219+
220+
fun getRequiredInstruments(songData: SongData): EnumSet<NoteBlockInstrument> {
221+
if (pianoOnly) {
222+
return EnumSet.of(NoteBlockInstrument.HARP)
223+
}
224+
225+
return songData.nbs.noteBlocks
226+
.mapTo(EnumSet.noneOf(NoteBlockInstrument::class.java)) {
227+
InstrumentNote.getInstrumentEnumFromId(it.instrument.toInt())
228+
}
229+
}
230+
231+
enum class NotebotStage(
232+
val stageStartDelay: () -> Int,
233+
val blockColor: () -> Color4b,
234+
val blockOutlineColor: () -> Color4b
235+
) {
236+
TEST(StartDelay::test, NotebotRenderer::testColor, NotebotRenderer::outlineTestColor),
237+
TUNE(StartDelay::tune, NotebotRenderer::tuneColor, NotebotRenderer::outlineTuneColor),
238+
PLAY(StartDelay::play, NotebotRenderer::colorSetting, NotebotRenderer::outlineColorSetting)
239+
}
240+
241+
interface NotebotStageHandler {
242+
val handledStage: NotebotStage
243+
244+
fun onTick(engine: NotebotEngine)
245+
}
246+
}

0 commit comments

Comments
 (0)