Skip to content

Commit e9e8311

Browse files
committed
Add sorting sounds
1 parent 4ca3dfb commit e9e8311

File tree

15 files changed

+247
-62
lines changed

15 files changed

+247
-62
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Fixed version metadata
66
- Fixed a bug causing multiplayer sort rate to be used in singleplayer
7+
- Added optional sorting sounds
78

89
## 1.1.3-beta.1
910

common/src/main/java/dev/terminalmc/clientsort/ClientSort.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import dev.terminalmc.clientsort.util.ModLogger;
2424
import net.minecraft.client.KeyMapping;
2525
import net.minecraft.client.Minecraft;
26+
import net.minecraft.resources.ResourceLocation;
2627

2728
import static dev.terminalmc.clientsort.util.Localization.translationKey;
2829

@@ -52,10 +53,15 @@ public static void onConfigSaved(Config config) {
5253
options.shiftSortMode = SortMode.SORT_MODES.get(options.shiftSortModeStr);
5354
options.ctrlSortMode = SortMode.SORT_MODES.get(options.ctrlSortModeStr);
5455
options.altSortMode = SortMode.SORT_MODES.get(options.altSortModeStr);
56+
options.sortSoundLoc = ResourceLocation.tryParse(options.sortSound);
57+
setInteractionManagerTickRate(config);
58+
}
59+
60+
public static void setInteractionManagerTickRate(Config config) {
5561
if (Minecraft.getInstance().getSingleplayerServer() == null) {
56-
InteractionManager.setTickRate(options.interactionRateServer);
62+
InteractionManager.setTickRate(config.options.interactionRateServer);
5763
} else {
58-
InteractionManager.setTickRate(options.interactionRateClient);
64+
InteractionManager.setTickRate(config.options.interactionRateClient);
5965
}
6066
}
6167
}

common/src/main/java/dev/terminalmc/clientsort/config/Config.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.gson.GsonBuilder;
2121
import dev.terminalmc.clientsort.ClientSort;
2222
import dev.terminalmc.clientsort.inventory.sort.SortMode;
23+
import net.minecraft.resources.ResourceLocation;
2324
import org.jetbrains.annotations.NotNull;
2425
import org.jetbrains.annotations.Nullable;
2526

@@ -107,6 +108,26 @@ public String lowerName() {
107108

108109
public static final boolean optimizedCreativeSortingDefault = true;
109110
public boolean optimizedCreativeSorting = optimizedCreativeSortingDefault;
111+
112+
// Sounds
113+
public static final boolean soundEnabledDefault = false;
114+
public boolean soundEnabled = soundEnabledDefault;
115+
116+
public static final String sortSoundDefault = "minecraft:block.note_block.xylophone";
117+
public String sortSound = sortSoundDefault;
118+
public transient @Nullable ResourceLocation sortSoundLoc = null;
119+
120+
public static final int soundRateDefault = 1;
121+
public int soundRate = soundRateDefault;
122+
123+
public static final float soundMinPitchDefault = 0.5F;
124+
public float soundMinPitch = soundMinPitchDefault;
125+
126+
public static final float soundMaxPitchDefault = 2.0F;
127+
public float soundMaxPitch = soundMaxPitchDefault;
128+
129+
public static final float soundVolumeDefault = 0.2F;
130+
public float soundVolume = soundVolumeDefault;
110131
}
111132

112133
// Cleanup

common/src/main/java/dev/terminalmc/clientsort/gui/screen/ClothScreenProvider.java

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
import me.shedaniel.clothconfig2.api.ConfigBuilder;
2323
import me.shedaniel.clothconfig2.api.ConfigCategory;
2424
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
25+
import net.minecraft.client.Minecraft;
2526
import net.minecraft.client.gui.screens.Screen;
2627
import net.minecraft.network.chat.Component;
28+
import net.minecraft.resources.ResourceLocation;
2729

2830
import java.util.Optional;
2931

30-
import static dev.terminalmc.clientsort.util.mod.Localization.localized;
32+
import static dev.terminalmc.clientsort.util.Localization.localized;
3133

3234
public class ClothScreenProvider {
3335
/**
@@ -149,6 +151,80 @@ else if (val > 100) return Optional.of(
149151
})
150152
.build());
151153

154+
ConfigCategory sound = builder.getOrCreateCategory(localized("option", "sound"));
155+
156+
sound.addEntry(eb.startBooleanToggle(localized("option", "soundEnabled"),
157+
options.soundEnabled)
158+
.setDefaultValue(Config.Options.soundEnabledDefault)
159+
.setSaveConsumer(val -> options.soundEnabled = val)
160+
.build());
161+
162+
sound.addEntry(eb.startStrField(localized("option", "sortSound"),
163+
options.sortSound)
164+
.setDefaultValue(Config.Options.sortSoundDefault)
165+
.setSaveConsumer(val -> options.sortSound = val)
166+
.setErrorSupplier(val -> {
167+
if (ResourceLocation.tryParse(val) == null) return Optional.of(
168+
localized("option", "error.resourceLocation.parse"));
169+
else return Optional.empty();
170+
})
171+
.build());
172+
173+
sound.addEntry(eb.startIntField(localized("option", "soundRate"),
174+
options.soundRate)
175+
.setTooltip(localized("option", "soundRate.tooltip"))
176+
.setErrorSupplier(val -> {
177+
if (val < 1) return Optional.of(
178+
localized("option", "error.low"));
179+
else if (val > 100) return Optional.of(
180+
localized("option", "error.high"));
181+
else return Optional.empty();
182+
})
183+
.setDefaultValue(Config.Options.soundRateDefault)
184+
.setSaveConsumer(val -> options.soundRate = val)
185+
.build());
186+
187+
sound.addEntry(eb.startFloatField(localized("option", "soundMinPitch"),
188+
options.soundMinPitch)
189+
.setTooltip(localized("option", "soundMinPitch.tooltip"))
190+
.setErrorSupplier(val -> {
191+
if (val < 0.5) return Optional.of(
192+
localized("option", "error.low"));
193+
else if (val > options.soundMaxPitch) return Optional.of(
194+
localized("option", "error.high"));
195+
else return Optional.empty();
196+
})
197+
.setDefaultValue(Config.Options.soundMinPitchDefault)
198+
.setSaveConsumer(val -> options.soundMinPitch = val)
199+
.build());
200+
201+
sound.addEntry(eb.startFloatField(localized("option", "soundMaxPitch"),
202+
options.soundMaxPitch)
203+
.setTooltip(localized("option", "soundMaxPitch.tooltip"))
204+
.setErrorSupplier(val -> {
205+
if (val < options.soundMinPitch) return Optional.of(
206+
localized("option", "error.low"));
207+
else if (val > 2) return Optional.of(
208+
localized("option", "error.high"));
209+
else return Optional.empty();
210+
})
211+
.setDefaultValue(Config.Options.soundMaxPitchDefault)
212+
.setSaveConsumer(val -> options.soundMaxPitch = val)
213+
.build());
214+
215+
sound.addEntry(eb.startFloatField(localized("option", "soundVolume"),
216+
options.soundVolume)
217+
.setErrorSupplier(val -> {
218+
if (val < 0.0F) return Optional.of(
219+
localized("option", "error.low"));
220+
else if (val > 1.0F) return Optional.of(
221+
localized("option", "error.high"));
222+
else return Optional.empty();
223+
})
224+
.setDefaultValue(Config.Options.soundVolumeDefault)
225+
.setSaveConsumer(val -> options.soundVolume = val)
226+
.build());
227+
152228
return builder.build();
153229
}
154230
}

common/src/main/java/dev/terminalmc/clientsort/gui/screen/ConfigScreenProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import net.minecraft.client.gui.screens.Screen;
2424
import net.minecraft.network.chat.CommonComponents;
2525

26-
import static dev.terminalmc.clientsort.util.mod.Localization.localized;
26+
import static dev.terminalmc.clientsort.util.Localization.localized;
2727

2828
/**
2929
* Wraps the config screen implementation and provides a backup screen for

common/src/main/java/dev/terminalmc/clientsort/inventory/ContainerScreenHelper.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ public static <T extends AbstractContainerScreen<?>> ContainerScreenHelper<T> of
4747
return new ContainerScreenHelper<>(screen, clickEventFactory);
4848
}
4949

50-
public InteractionManager.InteractionEvent createClickEvent(Slot slot, int action, ClickType actionType) {
51-
return clickEventFactory.create(slot, action, actionType);
50+
public InteractionManager.InteractionEvent createClickEvent(Slot slot, int action, ClickType actionType, boolean playSound) {
51+
return clickEventFactory.create(slot, action, actionType, playSound);
5252
}
5353

5454
public boolean isHotbarSlot(Slot slot) {
@@ -63,9 +63,9 @@ public int getScope(Slot slot, boolean preferSmallerScopes) {
6363
if (!slot.mayPlace(ItemStack.EMPTY)) {
6464
// Removed checks:
6565
// slot.container == null
66-
// (always false)
66+
// (always false)
6767
// ((ISlot) slot).mouseWheelie_getIndexInInv() >= slot.container.getContainerSize()
68-
// (prevents compatibility with Traveler's Backpack)
68+
// (prevents compatibility with Traveler's Backpack)
6969
return INVALID_SCOPE;
7070
}
7171
if (screen instanceof EffectRenderingInventoryScreen) {

common/src/main/java/dev/terminalmc/clientsort/inventory/sort/InventorySorter.java

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717

1818
package dev.terminalmc.clientsort.inventory.sort;
1919

20+
import dev.terminalmc.clientsort.ClientSort;
2021
import dev.terminalmc.clientsort.compat.itemlocks.ItemLocksWrapper;
21-
import dev.terminalmc.clientsort.config.Config;
2222
import dev.terminalmc.clientsort.inventory.ContainerScreenHelper;
2323
import dev.terminalmc.clientsort.network.InteractionManager;
24+
import dev.terminalmc.clientsort.util.SoundUtil;
2425
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
2526
import net.minecraft.world.inventory.ClickType;
2627
import net.minecraft.world.inventory.Slot;
@@ -31,6 +32,8 @@
3132
import java.util.Arrays;
3233
import java.util.BitSet;
3334

35+
import static dev.terminalmc.clientsort.config.Config.options;
36+
3437
public class InventorySorter {
3538
private final ContainerScreenHelper<? extends AbstractContainerScreen<?>> screenHelper;
3639
private final AbstractContainerScreen<?> containerScreen;
@@ -74,7 +77,7 @@ private void combineStacks() {
7477
if (stack.isEmpty()) continue;
7578
int stackSize = stack.getCount();
7679
if (stackSize >= stack.getItem().getDefaultMaxStackSize()) continue;
77-
clickEvents.add(screenHelper.createClickEvent(inventorySlots[i], 0, ClickType.PICKUP));
80+
clickEvents.add(screenHelper.createClickEvent(inventorySlots[i], 0, ClickType.PICKUP, false));
7881
for (int j = 0; j < i; j++) {
7982
ItemStack targetStack = stacks[j];
8083
if (targetStack.isEmpty()) continue;
@@ -84,7 +87,7 @@ private void combineStacks() {
8487
delta = Math.min(delta, stackSize);
8588
stackSize -= delta;
8689
targetStack.setCount(targetStack.getCount() + delta);
87-
clickEvents.add(screenHelper.createClickEvent(inventorySlots[j], 0, ClickType.PICKUP));
90+
clickEvents.add(screenHelper.createClickEvent(inventorySlots[j], 0, ClickType.PICKUP, false));
8891
if (stackSize <= 0) break;
8992
}
9093
}
@@ -96,7 +99,7 @@ private void combineStacks() {
9699
InteractionManager.triggerSend(InteractionManager.TriggerType.GUI_CONFIRM);
97100
clickEvents.clear();
98101
if (stackSize > 0) {
99-
InteractionManager.push(screenHelper.createClickEvent(inventorySlots[i], 0, ClickType.PICKUP));
102+
InteractionManager.push(screenHelper.createClickEvent(inventorySlots[i], 0, ClickType.PICKUP, false));
100103
stack.setCount(stackSize);
101104
} else {
102105
stacks[i] = ItemStack.EMPTY;
@@ -116,11 +119,37 @@ public void sort(SortMode sortMode) {
116119
}
117120

118121
sortIds = sortMode.sort(sortIds, stacks, new SortContext(containerScreen, Arrays.asList(inventorySlots)));
119-
120-
this.sortOnClient(sortIds);
122+
123+
boolean playSound = options().soundEnabled && options().soundVolume > 0;
124+
if (playSound) SoundUtil.reset(getSoundCount());
125+
126+
this.sortOnClient(sortIds, playSound);
127+
}
128+
129+
private int getSoundCount() {
130+
// We want the pitch to reach maximum as sorting finishes, so we
131+
// do a quick calculation to estimate the rough number of operations
132+
int stackCount = 0;
133+
for (ItemStack stack : stacks) {
134+
if (stack != ItemStack.EMPTY) {
135+
stackCount++;
136+
}
137+
}
138+
int compaction = 0;
139+
for (int i = 0; i < stackCount; i++) {
140+
if (stacks[i] == ItemStack.EMPTY) {
141+
compaction++;
142+
}
143+
}
144+
int size = stackCount + compaction;
145+
146+
// Roughly compensate for swaps requiring multiple operations
147+
size += size / 15;
148+
149+
return size;
121150
}
122151

123-
protected void sortOnClient(int[] sortedIds) {
152+
protected void sortOnClient(int[] sortedIds, boolean playSound) {
124153
ItemStack currentStack;
125154
final int slotCount = stacks.length;
126155

@@ -167,7 +196,7 @@ protected void sortOnClient(int[] sortedIds) {
167196
Item temp = backingStacks[sortedIds[i]];
168197
backingStacks[sortedIds[i]] = carriedItem;
169198
carriedItem = temp;
170-
InteractionManager.push(screenHelper.createClickEvent(inventorySlots[sortedIds[i]], 0, ClickType.PICKUP));
199+
InteractionManager.push(screenHelper.createClickEvent(inventorySlots[sortedIds[i]], 0, ClickType.PICKUP, playSound));
171200
doneSlashEmpty.set(slotCount + sortedIds[i]); // Mark the origin slot as empty (because we picked the stack up, duh)
172201
currentStack = stacks[sortedIds[i]]; // Save the stack we're currently working with
173202
Slot workingSlot = inventorySlots[sortedIds[i]]; // A slot that we can use when fiddling around with swapping stacks
@@ -191,11 +220,11 @@ protected void sortOnClient(int[] sortedIds) {
191220
temp = backingStacks[id];
192221
backingStacks[id] = carriedItem;
193222
carriedItem = temp;
194-
InteractionManager.push(screenHelper.createClickEvent(workingSlot, 0, ClickType.PICKUP));
195-
InteractionManager.push(screenHelper.createClickEvent(targetSlot, 0, ClickType.PICKUP));
196-
InteractionManager.push(screenHelper.createClickEvent(workingSlot, 0, ClickType.PICKUP));
197-
InteractionManager.push(screenHelper.createClickEvent(targetSlot, 0, ClickType.PICKUP));
198-
InteractionManager.push(screenHelper.createClickEvent(workingSlot, 0, ClickType.PICKUP));
223+
InteractionManager.push(screenHelper.createClickEvent(workingSlot, 0, ClickType.PICKUP, playSound));
224+
InteractionManager.push(screenHelper.createClickEvent(targetSlot, 0, ClickType.PICKUP, playSound));
225+
InteractionManager.push(screenHelper.createClickEvent(workingSlot, 0, ClickType.PICKUP, playSound));
226+
InteractionManager.push(screenHelper.createClickEvent(targetSlot, 0, ClickType.PICKUP, playSound));
227+
InteractionManager.push(screenHelper.createClickEvent(workingSlot, 0, ClickType.PICKUP, playSound));
199228

200229
currentStack = stacks[id];
201230
doneSlashEmpty.set(id); // mark the current target as done
@@ -206,15 +235,15 @@ protected void sortOnClient(int[] sortedIds) {
206235

207236
// swap the current stack with the target stack
208237
if (
209-
Config.options().lmbBundle
238+
options().lmbBundle
210239
&& (
211240
(backingStacks[id] instanceof BundleItem && !(carriedItem instanceof AirItem))
212241
|| (carriedItem instanceof BundleItem && !(backingStacks[id] instanceof AirItem))
213242
)
214243
) {
215-
InteractionManager.push(screenHelper.createClickEvent(inventorySlots[id], 1, ClickType.PICKUP));
244+
InteractionManager.push(screenHelper.createClickEvent(inventorySlots[id], 1, ClickType.PICKUP, playSound));
216245
} else {
217-
InteractionManager.push(screenHelper.createClickEvent(inventorySlots[id], 0, ClickType.PICKUP));
246+
InteractionManager.push(screenHelper.createClickEvent(inventorySlots[id], 0, ClickType.PICKUP, playSound));
218247
}
219248
temp = backingStacks[id];
220249
backingStacks[id] = carriedItem;

common/src/main/java/dev/terminalmc/clientsort/mixin/MixinAbstractContainerScreen.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import dev.terminalmc.clientsort.inventory.sort.InventorySorter;
2525
import dev.terminalmc.clientsort.inventory.sort.SortMode;
2626
import dev.terminalmc.clientsort.network.InteractionManager;
27+
import dev.terminalmc.clientsort.util.SoundUtil;
2728
import dev.terminalmc.clientsort.util.inject.IContainerScreen;
2829
import dev.terminalmc.clientsort.util.inject.ISlot;
2930
import net.minecraft.client.Minecraft;
@@ -101,10 +102,11 @@ private void onKeyPressed(int keyCode, int scanCode, int modifiers,
101102
@SuppressWarnings({"ConstantConditions", "unchecked"})
102103
@Unique
103104
private final Supplier<ContainerScreenHelper<AbstractContainerScreen<AbstractContainerMenu>>> clientSort$screenHelper = Suppliers.memoize(
104-
() -> ContainerScreenHelper.of((AbstractContainerScreen<AbstractContainerMenu>) (Object) this, (slot, data, slotActionType) -> new InteractionManager.CallbackEvent(() -> {
105+
() -> ContainerScreenHelper.of((AbstractContainerScreen<AbstractContainerMenu>) (Object) this, (slot, data, slotActionType, sound) -> new InteractionManager.CallbackEvent(() -> {
105106
slotClicked(slot, ((ISlot) slot).mouseWheelie_getIdInContainer(), data, slotActionType);
107+
if (sound) SoundUtil.play();
106108
return InteractionManager.TICK_WAITER;
107-
}, true))
109+
}))
108110
);
109111

110112
@SuppressWarnings("ConstantConditions")

common/src/main/java/dev/terminalmc/clientsort/mixin/MixinClientPacketListener.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package dev.terminalmc.clientsort.mixin;
1919

2020
import dev.terminalmc.clientsort.ClientSort;
21+
import dev.terminalmc.clientsort.config.Config;
2122
import dev.terminalmc.clientsort.network.InteractionManager;
2223
import net.minecraft.client.Minecraft;
2324
import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl;
@@ -32,8 +33,6 @@
3233
import org.spongepowered.asm.mixin.injection.Inject;
3334
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
3435

35-
import static dev.terminalmc.clientsort.config.Config.options;
36-
3736
@Mixin(ClientPacketListener.class)
3837
public abstract class MixinClientPacketListener extends ClientCommonPacketListenerImpl {
3938
protected MixinClientPacketListener(Minecraft client, Connection connection, CommonListenerCookie connectionState) {
@@ -43,11 +42,7 @@ protected MixinClientPacketListener(Minecraft client, Connection connection, Com
4342
@Inject(method = "handleLogin", at = @At("HEAD"))
4443
private void onLogin(ClientboundLoginPacket packet, CallbackInfo ci) {
4544
ClientSort.searchOrderUpdated = false;
46-
if (Minecraft.getInstance().getSingleplayerServer() == null) {
47-
InteractionManager.setTickRate(options().interactionRateServer);
48-
} else {
49-
InteractionManager.setTickRate(options().interactionRateClient);
50-
}
45+
ClientSort.setInteractionManagerTickRate(Config.get());
5146
}
5247

5348
@Inject(method = "handleSetCarriedItem", at = @At("HEAD"))

common/src/main/java/dev/terminalmc/clientsort/network/ClickEventFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@
2121
import net.minecraft.world.inventory.Slot;
2222

2323
public interface ClickEventFactory {
24-
InteractionManager.InteractionEvent create(Slot slot, int action, ClickType slotActionType);
24+
InteractionManager.InteractionEvent create(Slot slot, int action, ClickType slotActionType, boolean playSound);
2525
}

0 commit comments

Comments
 (0)