Skip to content

Commit ef6e1c6

Browse files
authored
Merge branch 'object-Object:main' into main
2 parents e011140 + f5a3d1a commit ef6e1c6

File tree

16 files changed

+397
-98
lines changed

16 files changed

+397
-98
lines changed

CHANGELOG.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,28 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Pydantic's HISTORY.md](https://github.com/pydantic/pydantic/blob/main/HISTORY.md), and this project *mostly* adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
## [UNRELEASED]
7+
## `0.5.0+1.20.1` - 2025-09-22
88

99
### Added
1010

11-
- Added keyboard shortcuts to the Splicing Table, configurable in HexDebug's client config menu.
11+
- Added keyboard shortcuts for almost all of the buttons in the Splicing Table, configurable in HexDebug's client config menu.
12+
- Added several new keyboard-only actions to the Splicing Table, mostly related to manipulating the active selection:
13+
- Move Cursor Left/Right
14+
- Expand Selection Left/Right
15+
- Move Selection Left/Right
16+
- Backspace
17+
- Added a tag (`hexdebug:splicing_table/media_blacklist`) to prevent specific items from being used to refill the Splicing Table's media buffer (eg. [Oneironaut](https://oneironaut.hexxy.media)'s Inexhaustible Phial).
18+
19+
### Changed
20+
1221
- The Splicing Table now works with Media Purification from [Hexpose](https://miyucomics.github.io/hexpose) (requires at least build [2e90cf5b](https://github.com/miyucomics/hexpose/commit/2e90cf5babb7677a26ca211bd66ac2777e5518cd), or any version after 1.0.0).
1322

1423
### Fixed
1524

1625
- Fixed an issue where newly placed splicing tables would default to having the first iota selected.
26+
- Fixed several bugs/exploits related to the Splicing Table's media buffer ([#40](https://github.com/object-Object/HexDebug/pull/40), [#41](https://github.com/object-Object/HexDebug/pull/41), [#44](https://github.com/object-Object/HexDebug/pull/44), [#45](https://github.com/object-Object/HexDebug/pull/45)).
27+
- Fixed Lodestone Reflection not working in the Mindsplice Table ([#42](https://github.com/object-Object/HexDebug/pull/42)).
28+
- Fixed a bug in the Splicing Table where the Select All button was enabled when the list was empty and the left edge was selected.
1729

1830
## `0.4.0+1.20.1` - 2025-09-21
1931

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package gay.object.hexdebug.api;
2+
3+
import gay.object.hexdebug.HexDebug;
4+
import net.minecraft.core.registries.Registries;
5+
import net.minecraft.tags.TagKey;
6+
import net.minecraft.world.item.Item;
7+
8+
@SuppressWarnings("SameParameterValue")
9+
public final class HexDebugTags {
10+
public static final class Items {
11+
public static final TagKey<Item> SPLICING_TABLE_MEDIA_BLACKLIST = create("splicing_table/media_blacklist");
12+
13+
private static TagKey<Item> create(String name) {
14+
return TagKey.create(Registries.ITEM, HexDebug.id(name));
15+
}
16+
}
17+
}

Common/src/main/java/gay/object/hexdebug/mixin/HexDebugMixinConfigPlugin.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package gay.object.hexdebug.mixin;
22

3-
import dev.architectury.platform.Platform;
43
import gay.object.hexdebug.HexDebug;
54
import org.objectweb.asm.tree.ClassNode;
65
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
@@ -28,10 +27,6 @@ public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
2827
}
2928
return shouldApply;
3029
}
31-
if (mixinClassName.startsWith("gay.object.hexdebug.mixin.interop.")) {
32-
var id = mixinClassName.substring("gay.object.hexdebug.mixin.interop.".length()).split("\\.", 2)[0];
33-
return Platform.isModLoaded(id);
34-
}
3530
return true;
3631
}
3732

Common/src/main/java/gay/object/hexdebug/mixin/interop/emi/MixinEmiScreenManager.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,24 @@
44
import com.llamalad7.mixinextras.expression.Expression;
55
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
66
import com.llamalad7.mixinextras.sugar.Local;
7-
import dev.emi.emi.screen.EmiScreenManager;
7+
import gay.object.hexdebug.config.HexDebugClientConfig;
88
import gay.object.hexdebug.gui.splicing.SplicingTableScreen;
99
import net.minecraft.client.Minecraft;
1010
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.Pseudo;
1112
import org.spongepowered.asm.mixin.injection.At;
1213

13-
@Mixin(value = EmiScreenManager.class, remap = false)
14+
@Pseudo
15+
@Mixin(targets = "dev.emi.emi.screen.EmiScreenManager", remap = false)
1416
public abstract class MixinEmiScreenManager {
1517
@Definition(id = "keyCode", local = @Local(name = "keyCode", type = int.class))
1618
@Expression("keyCode == 89")
1719
@ModifyExpressionValue(method = "keyPressed", at = @At(value = "MIXINEXTRAS:EXPRESSION"), require = 0)
1820
private static boolean hexdebug$cancelHardcodedKeybindInSplicingTable(boolean original) {
19-
if (Minecraft.getInstance().screen instanceof SplicingTableScreen) {
21+
if (
22+
HexDebugClientConfig.getConfig().getSplicingTableKeybinds().getEnabled()
23+
&& Minecraft.getInstance().screen instanceof SplicingTableScreen
24+
) {
2025
return false;
2126
}
2227
return original;

Common/src/main/kotlin/gay/object/hexdebug/blocks/splicing/SplicingTableBlockEntity.kt

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import at.petrak.hexcasting.api.utils.extractMedia
1818
import at.petrak.hexcasting.api.utils.getInt
1919
import at.petrak.hexcasting.api.utils.getList
2020
import at.petrak.hexcasting.xplat.IXplatAbstractions
21+
import gay.`object`.hexdebug.api.HexDebugTags
2122
import gay.`object`.hexdebug.blocks.base.BaseContainer
2223
import gay.`object`.hexdebug.blocks.base.ContainerDataDelegate
2324
import gay.`object`.hexdebug.blocks.base.ContainerDataLongDelegate
@@ -58,12 +59,16 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) :
5859

5960
var listStack by SplicingTableItemSlot.LIST.delegate
6061
var clipboardStack by SplicingTableItemSlot.CLIPBOARD.delegate
61-
var mediaStack by SplicingTableItemSlot.MEDIA.delegate
62-
private set
62+
private var mediaStack by SplicingTableItemSlot.MEDIA.delegate
6363
private var staffStack by SplicingTableItemSlot.STAFF.delegate
6464

6565
private val listHolder get() = IXplatAbstractions.INSTANCE.findDataHolder(listStack)
6666
val clipboardHolder get() = IXplatAbstractions.INSTANCE.findDataHolder(clipboardStack)
67+
val mediaHolder get() =
68+
mediaStack
69+
.takeIf { !it.`is`(HexDebugTags.Items.SPLICING_TABLE_MEDIA_BLACKLIST) }
70+
?.let { HexAPI.instance().findMediaHolder(it) }
71+
?.takeIf { it.canProvide() }
6772

6873
private val containerData = SimpleContainerData(SplicingTableDataSlot.size)
6974

@@ -89,6 +94,7 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) :
8994
private set
9095

9196
private var castingCooldown = 0
97+
private var isCurrentlyCasting = false
9298

9399
val analogOutputSignal get() = if (!listStack.isEmpty) 15 else 0
94100

@@ -250,7 +256,7 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) :
250256
if (writeList(list)) {
251257
shouldConsumeMedia = true
252258
selection = Selection.edge(typedSelection.start + 1)?.also {
253-
makeEdgeVisible(it.index)
259+
makeEdgeVisible(it)
254260
}
255261
pushUndoState(
256262
list = Some(list),
@@ -346,7 +352,16 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) :
346352
val env = SplicingTableCastEnv(player, this)
347353
val vm = CastingVM.empty(env)
348354

349-
val clientView = vm.queueExecuteAndWrapIotas(instrs, level)
355+
// prevent refilling while casting
356+
isCurrentlyCasting = true
357+
val clientView = try {
358+
vm.queueExecuteAndWrapIotas(instrs, level)
359+
} finally {
360+
isCurrentlyCasting = false
361+
}
362+
363+
refillMedia()
364+
sync()
350365

351366
if (clientView.resolutionType.success) {
352367
ParticleSpray(blockPos.center, Vec3(0.0, 1.5, 0.0), 0.4, Math.PI / 3, 30)
@@ -413,19 +428,31 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) :
413428
refillMedia()
414429
}
415430

416-
fun refillMedia() {
417-
val mediaHolder = HexAPI.instance().findMediaHolder(mediaStack) ?: return
418-
while (media < maxMedia) {
419-
val cost = maxMedia - media
431+
private fun refillMedia() {
432+
if (isCurrentlyCasting || media >= maxMedia) return
433+
val mediaHolder = mediaHolder ?: return
434+
435+
// for static media items (eg. dust), extract media from one item at a time
436+
// this is necessary to avoid wasting media while still getting as much as possible out of the stack
437+
val unitCost = HexAPI.instance()
438+
.findMediaHolder(mediaStack.copyWithCount(1))
439+
?.withdrawMedia(-1, true)
440+
?: media
441+
442+
// extract media at most once for each item in the stack
443+
// prevents big looping in case someone thinks they're funny
444+
for (i in 0 until mediaStack.count) {
445+
val cost = min(maxMedia - media, unitCost)
446+
if (cost <= 0) return
420447

421448
// avoid wasting media if the item is too large
422-
if (extractMedia(mediaStack, cost = cost, simulate = true) !in 1..cost) return
449+
if (extractMedia(mediaHolder, cost = cost, simulate = true) !in 1..cost) return
423450

424-
// avoid an infinite loop - stop looping as soon as we stop adding media
425451
val extracted = extractMedia(mediaHolder, cost = cost)
426-
if (extracted < 1) return
427-
428452
media += extracted
453+
454+
// stop looping as soon as we stop adding media
455+
if (extracted <= 1) return // the Inexhaustible Phial thinks it's funny.
429456
}
430457
}
431458

Common/src/main/kotlin/gay/object/hexdebug/casting/eval/SplicingTableCastEnv.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import at.petrak.hexcasting.api.casting.eval.vm.CastingImage
77
import at.petrak.hexcasting.api.pigment.FrozenPigment
88
import at.petrak.hexcasting.api.utils.extractMedia
99
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
10+
import gay.`object`.hexdebug.blocks.splicing.SplicingTableBlock
1011
import gay.`object`.hexdebug.blocks.splicing.SplicingTableBlockEntity
1112
import gay.`object`.hexdebug.config.HexDebugServerConfig
1213
import net.minecraft.core.BlockPos
1314
import net.minecraft.core.Direction
1415
import net.minecraft.server.level.ServerPlayer
1516
import net.minecraft.sounds.SoundSource
1617
import net.minecraft.world.InteractionHand
17-
import net.minecraft.world.level.block.state.properties.BlockStateProperties
1818
import net.minecraft.world.phys.Vec3
1919
import kotlin.math.min
2020

@@ -26,7 +26,7 @@ class SplicingTableCastEnv(
2626

2727
val blockPos: BlockPos get() = table.blockPos
2828

29-
val facing: Direction get() = table.blockState.getValue(BlockStateProperties.FACING)
29+
val facing: Direction get() = table.blockState.getValue(SplicingTableBlock.FACING)
3030

3131
override fun postExecution(result: CastResult) {
3232
super.postExecution(result)
@@ -35,7 +35,6 @@ class SplicingTableCastEnv(
3535

3636
override fun postCast(image: CastingImage) {
3737
super.postCast(image)
38-
table.sync() // TODO: is this necessary?
3938
sound.sound?.let {
4039
world.playSound(null, blockPos, it, SoundSource.PLAYERS, 1f, 1f)
4140
}
@@ -57,15 +56,11 @@ class SplicingTableCastEnv(
5756

5857
// then, pull straight from the item in the media slot
5958
if (costLeft > 0) {
60-
HexAPI.instance().findMediaHolder(table.mediaStack)?.let {
59+
table.mediaHolder?.let {
6160
costLeft -= extractMedia(it, cost = costLeft, simulate = simulate)
6261
}
6362
}
6463

65-
if (!simulate) {
66-
table.refillMedia()
67-
}
68-
6964
return costLeft
7065
}
7166

Common/src/main/kotlin/gay/object/hexdebug/config/HexDebugClientConfig.kt

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import at.petrak.hexcasting.api.utils.asTranslatedComponent
44
import com.mojang.blaze3d.platform.InputConstants
55
import gay.`object`.hexdebug.HexDebug
66
import gay.`object`.hexdebug.gui.splicing.SplicingTableScreen
7+
import gay.`object`.hexdebug.splicing.SplicingTableAction
78
import me.shedaniel.autoconfig.AutoConfig
89
import me.shedaniel.autoconfig.ConfigData
910
import me.shedaniel.autoconfig.ConfigHolder
@@ -37,12 +38,12 @@ object HexDebugClientConfig {
3738
entryBuilder.startModifierKeyCodeField(
3839
when (config) {
3940
is ClientConfig.SplicingTableKeybinds,
40-
is ClientConfig.EnlightenedSplicingTableKeybinds -> {
41-
SplicingTableScreen.buttonText(field.name.camelToSnakeCase())
41+
is ClientConfig.SplicingTableKeybinds.Enlightened -> {
42+
SplicingTableScreen.buttonText(field.name.camelToSnakeCase(), null)
4243
}
4344
else -> i18n.asTranslatedComponent
4445
},
45-
getUnsafely(field, config, ConfigModifierKey(InputConstants.UNKNOWN.name)).inner,
46+
getUnsafely(field, config, ConfigModifierKey()).inner,
4647
)
4748
.setModifierDefaultValue { (getUnsafely(field, defaults) as ConfigModifierKey).inner }
4849
.setModifierSaveConsumer { setUnsafely(field, config, ConfigModifierKey(it)) }
@@ -99,27 +100,95 @@ object HexDebugClientConfig {
99100
@CollapsibleObject
100101
val splicingTableKeybinds = SplicingTableKeybinds()
101102

102-
@Tooltip
103-
@CollapsibleObject
104-
val enlightenedSplicingTableKeybinds = EnlightenedSplicingTableKeybinds()
105-
103+
@Suppress("MemberVisibilityCanBePrivate")
106104
class SplicingTableKeybinds {
105+
@Tooltip
106+
val enabled: Boolean = true
107+
108+
@Tooltip
109+
val overrideVanillaArrowKeys = true
110+
111+
val viewLeft = ConfigModifierKey(InputConstants.KEY_UP)
112+
val viewLeftPage = ConfigModifierKey(InputConstants.KEY_PAGEUP)
113+
val viewLeftFull = ConfigModifierKey(InputConstants.KEY_HOME)
114+
val viewRight = ConfigModifierKey(InputConstants.KEY_DOWN)
115+
val viewRightPage = ConfigModifierKey(InputConstants.KEY_PAGEDOWN)
116+
val viewRightFull = ConfigModifierKey(InputConstants.KEY_END)
117+
118+
val cursorLeft = ConfigModifierKey(InputConstants.KEY_LEFT)
119+
val cursorRight = ConfigModifierKey(InputConstants.KEY_RIGHT)
120+
121+
val expandSelectionLeft = ConfigModifierKey(InputConstants.KEY_LEFT, shift = true)
122+
val expandSelectionRight = ConfigModifierKey(InputConstants.KEY_RIGHT, shift = true)
123+
val moveSelectionLeft = ConfigModifierKey(InputConstants.KEY_LEFT, ctrl = true, shift = true)
124+
val moveSelectionRight = ConfigModifierKey(InputConstants.KEY_RIGHT, ctrl = true, shift = true)
125+
107126
val selectNone = ConfigModifierKey(InputConstants.KEY_A, ctrl = true, shift = true)
108127
val selectAll = ConfigModifierKey(InputConstants.KEY_A, ctrl = true)
128+
109129
val undo = ConfigModifierKey(InputConstants.KEY_Z, ctrl = true)
110130
val redo = ConfigModifierKey(InputConstants.KEY_Y, ctrl = true)
131+
111132
val nudgeLeft = ConfigModifierKey(InputConstants.KEY_LEFT, ctrl = true)
112133
val nudgeRight = ConfigModifierKey(InputConstants.KEY_RIGHT, ctrl = true)
134+
113135
val duplicate = ConfigModifierKey(InputConstants.KEY_D, ctrl = true)
114136
val delete = ConfigModifierKey(InputConstants.KEY_DELETE)
137+
val backspace = ConfigModifierKey(InputConstants.KEY_BACKSPACE)
138+
115139
val cut = ConfigModifierKey(InputConstants.KEY_X, ctrl = true)
116140
val copy = ConfigModifierKey(InputConstants.KEY_C, ctrl = true)
117141
val pasteSplat = ConfigModifierKey(InputConstants.KEY_V, ctrl = true)
118142
val pasteVerbatim = ConfigModifierKey(InputConstants.KEY_V, ctrl = true, shift = true)
119-
}
120143

121-
class EnlightenedSplicingTableKeybinds {
122-
val cast = ConfigModifierKey(InputConstants.KEY_RETURN, ctrl = true)
144+
@Tooltip
145+
@CollapsibleObject(startExpanded = true)
146+
val enlightened = Enlightened()
147+
148+
fun getActionMap() = mapOf(
149+
SplicingTableAction.VIEW_LEFT to viewLeft,
150+
SplicingTableAction.VIEW_LEFT_PAGE to viewLeftPage,
151+
SplicingTableAction.VIEW_LEFT_FULL to viewLeftFull,
152+
SplicingTableAction.VIEW_RIGHT to viewRight,
153+
SplicingTableAction.VIEW_RIGHT_PAGE to viewRightPage,
154+
SplicingTableAction.VIEW_RIGHT_FULL to viewRightFull,
155+
SplicingTableAction.CURSOR_LEFT to cursorLeft,
156+
SplicingTableAction.CURSOR_RIGHT to cursorRight,
157+
SplicingTableAction.EXPAND_SELECTION_LEFT to expandSelectionLeft,
158+
SplicingTableAction.EXPAND_SELECTION_RIGHT to expandSelectionRight,
159+
SplicingTableAction.MOVE_SELECTION_LEFT to moveSelectionLeft,
160+
SplicingTableAction.MOVE_SELECTION_RIGHT to moveSelectionRight,
161+
SplicingTableAction.SELECT_NONE to selectNone,
162+
SplicingTableAction.SELECT_ALL to selectAll,
163+
SplicingTableAction.UNDO to undo,
164+
SplicingTableAction.REDO to redo,
165+
SplicingTableAction.NUDGE_LEFT to nudgeLeft,
166+
SplicingTableAction.NUDGE_RIGHT to nudgeRight,
167+
SplicingTableAction.DUPLICATE to duplicate,
168+
SplicingTableAction.DELETE to delete,
169+
SplicingTableAction.BACKSPACE to backspace,
170+
SplicingTableAction.CUT to cut,
171+
SplicingTableAction.COPY to copy,
172+
SplicingTableAction.PASTE_SPLAT to pasteSplat,
173+
SplicingTableAction.PASTE_VERBATIM to pasteVerbatim,
174+
)
175+
176+
fun getActionForKey(keyCode: Int, scanCode: Int): SplicingTableAction? {
177+
for ((action, key) in getActionMap().entries) {
178+
if (key.inner.matchesKey(keyCode, scanCode)) {
179+
return action
180+
}
181+
}
182+
return null
183+
}
184+
185+
fun getKeyForAction(action: SplicingTableAction): ConfigModifierKey? {
186+
return getActionMap()[action]
187+
}
188+
189+
class Enlightened {
190+
val cast = ConfigModifierKey(InputConstants.KEY_RETURN, ctrl = true)
191+
}
123192
}
124193
}
125194
}
@@ -165,8 +234,6 @@ data class ConfigModifierKey(
165234
Modifier.of(alt, ctrl, shift),
166235
)
167236
}
168-
169-
fun matchesKey(keyCode: Int, scanCode: Int) = inner.matchesKey(keyCode, scanCode)
170237
}
171238

172239
// https://stackoverflow.com/a/60010299

Common/src/main/kotlin/gay/object/hexdebug/gui/splicing/SplicingTableMenu.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package gay.`object`.hexdebug.gui.splicing
22

33
import at.petrak.hexcasting.api.mod.HexTags
44
import at.petrak.hexcasting.api.utils.isMediaItem
5+
import gay.`object`.hexdebug.api.HexDebugTags
56
import gay.`object`.hexdebug.blocks.base.ContainerDataDelegate
67
import gay.`object`.hexdebug.blocks.base.ContainerDataLongDelegate
78
import gay.`object`.hexdebug.blocks.base.ContainerDataSelectionDelegate
@@ -80,7 +81,7 @@ class SplicingTableMenu(
8081
mayPlace = ::isIotaHolder
8182
}
8283
mediaSlot = addTableSlot(SplicingTableItemSlot.MEDIA, 205, 169) {
83-
mayPlace = ::isMediaItem
84+
mayPlace = { isMediaItem(it) && !it.`is`(HexDebugTags.Items.SPLICING_TABLE_MEDIA_BLACKLIST) }
8485
}
8586
staffSlot = addTableSlot(SplicingTableItemSlot.STAFF, -20, 169) {
8687
maxStackSize = 1

0 commit comments

Comments
 (0)