feat(redstoner): ajouter les tris du terminal #139

Merged
koka merged 3 commits from feature/storage-terminal-sorting into main 2026-09-01 16:47:19 +00:00
11 changed files with 212 additions and 15 deletions
+6
View File
@@ -6,6 +6,12 @@ Ce journal suit les **versions du modpack**. Lorsqu'un seul module change, sa ve
- Depuis `26.2.0-alpha.6`, le pack et les modules ont des versions indépendantes. I Like To Move It et Only Fun deviennent les cinquième et sixième modules actifs dans l'alpha 46.
- Les versions ci-dessous ont été reconstituées à partir des JAR archivés, des packs locaux et du code source actuel. Les dates n'étant pas enregistrées de manière fiable, elles ne sont pas inventées.
## Non publié
Module modifié : **Red-Stoner `0.0.0-alpha.16`**.
- ajoute au terminal les tris alphabétique, par quantité croissante et par quantité décroissante, appliqués explicitement sans déplacer les objets sous le curseur lors des rafraîchissements périodiques ;
## `26.2.0-alpha.228`
Modules modifiés : **Sanctuary `0.0.0-alpha.132`** et **Red-Stoner `0.0.0-alpha.15`**.
+3 -1
View File
@@ -24,6 +24,8 @@ Les descriptions FR/EN/RU sont déclarées dans chaque `fabric.mod.json` et dans
Version courante du pack distribué : **`26.2.0-alpha.228`**. Cette release distribue Red-Stoner **`0.0.0-alpha.15`** et Sanctuary **`0.0.0-alpha.132`** ; les huit autres modules conservent leur version issue de l'alpha.227.
Le prochain module Red-Stoner **`0.0.0-alpha.16`** ajoute au terminal les tris alphabétique, par quantité croissante et par quantité décroissante. Le tri ne change que sur demande explicite et les rafraîchissements périodiques conservent lordre ainsi que les objets sous le curseur.
Lalpha.228 reconstruit la Backroom sur cinq niveaux empilés entre Y -64 et Y 383. Le monde devient entièrement noir et sans monstres ; larrivée est signalée par une torche récupérable. Les nouveaux chunks composent seuils modulaires, bibliothèques et bureaux, ville dense avec autoroutes et parcs, couloirs techniques, puis une grille 3D et un labyrinthe connecté dont certains trous mènent au vide. Red-Stoner corrige aussi le focus du terminal et permet de retrouver une boîte de Shulker par son contenu. Les chunks déjà générés et les sauvegardes existantes restent compatibles.
Lalpha.227 ajoute la dimension persistante `sanctuary:backroom`. On y entre accroupi devant un lit et on nen ressort que par un lit. L’équipement dentrée est transféré atomiquement vers la Mailbox, tandis que les objets perdus ou abandonnés dans la dimension alimentent des coffres renouvelables sans duplication. Les anciennes sauvegardes sont migrées vers Sanctuary data v14 sans réinitialisation ; le protocole réseau reste en version 24.
@@ -44,7 +46,7 @@ Lalpha.220 transforme le terminal de stockage en véritable menu de conteneur
Lalpha.219 porte l'île finie Minecraft Indev dans `sanctuary:alpha` : 128 × 128 × 64 blocs déterministes, plages, cavernes, minerais, fluides, végétation et maison historique, le tout borné sans chunks parasites. Le cadre 4 × 5 en cobblestone moussue est maintenant actif et crée une paire persistante à une arrivée sûre hors de la maison. Ambiance peut enfin composer localement Golden Days Base puis Alpha uniquement dans cette dimension, sans redistribuer leurs textures et en restaurant exactement la pile précédente. Voir le [contrat Alpha](sanctuary/ALPHA.md), le [contrat des portails](iliketomoveit/PORTALS.md) et le [guide Golden Days](ambiance/GOLDEN_DAYS.md).
Lalpha.218 ajoute le terminal de stockage Red-Stoner : cette demi-dalle verticale se fixe sur un coffre, un coffre piégé ou un baril et donne accès, par pages défilantes de 54 types, à un réseau contigu borné à 128 conteneurs chargés. Les retraits et dépôts sont validés par le serveur, les composants ditems restent strictement séparés et le terminal endgame est exclu du Shop, du Black Market et des récompenses Only Fun. La recherche retrouve aussi une boîte de Shulker daprès son contenu direct ; la boîte reste affichée dans la grille afin que son aperçu vanilla soit consultable sans modifier les règles de retrait.
Lalpha.218 ajoute le terminal de stockage Red-Stoner : cette demi-dalle verticale se fixe sur un coffre, un coffre piégé ou un baril et donne accès, par pages défilantes de 54 types, à un réseau contigu borné à 128 conteneurs chargés. Les retraits et dépôts sont validés par le serveur, les composants ditems restent strictement séparés et le terminal endgame est exclu du Shop, du Black Market et des récompenses Only Fun. La recherche retrouve aussi une boîte de Shulker daprès son contenu direct ; la boîte reste affichée dans la grille afin que son aperçu vanilla soit consultable sans modifier les règles de retrait. Un bouton permet enfin de trier explicitement les résultats par nom, quantité croissante ou quantité décroissante.
Lalpha.217 tentait de compenser la hauteur trop grave du disque blanc par un facteur OpenAL de `4/3`. Cette compensation est retirée par lalpha.221, qui corrige directement la conversion stéréo vers mono.
+51 -11
View File
@@ -3692,7 +3692,7 @@ tasks.register("verifyMirrorLavenderDrawerRelease") {
def drawerRecipe = file("redstoner/src/main/resources/data/redstoner/recipe/drawer.json")
if (rootProject.pack_version != "26.2.0-alpha.228"
|| rootProject.anotherworld_version != "0.0.0-alpha.57"
|| rootProject.redstoner_version != "0.0.0-alpha.15"
|| rootProject.redstoner_version != "0.0.0-alpha.16"
|| release.pack_version != rootProject.pack_version || index.versionId != rootProject.pack_version
|| migration.source?.pack_version != "26.2.0-alpha.191"
|| migration.source?.modules != [anotherworld: "0.0.0-alpha.48", redstoner: "0.0.0-alpha.9"]
@@ -4294,6 +4294,10 @@ project(":redstoner") {
"screen.redstoner.storage_terminal.search",
"screen.redstoner.storage_terminal.no_results",
"screen.redstoner.storage_terminal.summary",
"screen.redstoner.storage_terminal.sort",
"screen.redstoner.storage_terminal.sort.alphabetical",
"screen.redstoner.storage_terminal.sort.count_ascending",
"screen.redstoner.storage_terminal.sort.count_descending",
"screen.redstoner.storage_terminal.error.invalid_terminal",
"screen.redstoner.storage_terminal.error.disconnected",
"screen.redstoner.storage_terminal.error.too_large",
@@ -4310,7 +4314,7 @@ project(":redstoner") {
"src/main/resources/data/redstoner/recipe/controller.json"))
def terminalRecipe = new JsonSlurper().parse(file(
"src/main/resources/data/redstoner/recipe/storage_terminal.json"))
if (project.version.toString() != "0.0.0-alpha.15"
if (project.version.toString() != "0.0.0-alpha.16"
|| manifest.custom?.sanctuary26?.data_version != 3
|| manifest.custom?.sanctuary26?.network_protocol != 7
|| terminalMigration.source?.pack_version != "26.2.0-alpha.217"
@@ -6659,7 +6663,7 @@ tasks.register("verifyAlpha218Release") {
"file = \"mods/${id}-${version}.jar\""
}
if (rootProject.pack_version != "26.2.0-alpha.228"
|| rootProject.redstoner_version != "0.0.0-alpha.15"
|| rootProject.redstoner_version != "0.0.0-alpha.16"
|| currentModules.findAll { id, version ->
id != "redstoner" && id != "sanctuary" && id != "onlyfun" && id != "iliketomoveit"
}.any {
@@ -6848,7 +6852,7 @@ tasks.register("verifyAlpha220Release") {
def networking = file(
"redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/network/RedStonerNetworking.java").text
if (rootProject.pack_version != "26.2.0-alpha.228"
|| rootProject.redstoner_version != "0.0.0-alpha.15"
|| rootProject.redstoner_version != "0.0.0-alpha.16"
|| migration.source?.pack_version != "26.2.0-alpha.219"
|| migration.source?.modules != [redstoner: "0.0.0-alpha.11"]
|| migration.target?.pack_version != "26.2.0-alpha.220"
@@ -6883,7 +6887,7 @@ tasks.register("verifyAlpha220Release") {
|| !inventory.contains("for (Container container : containers)")
|| !screen.contains("extends AbstractContainerScreen<StorageTerminalMenu>")
|| !screen.contains("textures/gui/container/generic_54.png")
|| screen.contains("Button.builder")
|| screen.contains('Button.builder(Component.translatable("gui.done")')
|| !networking.contains("PROTOCOL_VERSION = 7")
|| networking.contains("storage_terminal_snapshot")
|| networking.contains("storage_terminal_action")
@@ -7077,7 +7081,7 @@ tasks.register("verifyAlpha222Release") {
it.path == "mods/jei-26.2-fabric-30.29.0.199.jar"
}
if (rootProject.pack_version != "26.2.0-alpha.228"
|| rootProject.redstoner_version != "0.0.0-alpha.15"
|| rootProject.redstoner_version != "0.0.0-alpha.16"
|| rootProject.sanctuary_version != "0.0.0-alpha.132"
|| migration.source?.pack_version != "26.2.0-alpha.221"
|| migration.source?.modules != [
@@ -7191,6 +7195,40 @@ tasks.register("verifyStorageTerminalSearchFocus") {
}
}
tasks.register("verifyStorageTerminalSorting") {
group = "verification"
description = "Checks the three server-authoritative storage-terminal sorting modes and client control."
inputs.file(file(
"redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSort.java"))
inputs.file(file(
"redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalMenu.java"))
inputs.file(file(
"redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/client/gui/StorageTerminalScreen.java"))
inputs.file(file(
"redstoner/src/test/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSmoke.java"))
doLast {
def sort = file(
"redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSort.java").text
def menu = file(
"redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalMenu.java").text
def screen = file(
"redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/client/gui/StorageTerminalScreen.java").text
def smoke = file(
"redstoner/src/test/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSmoke.java").text
if (!sort.contains('ALPHABETICAL("alphabetical")')
|| !sort.contains('COUNT_ASCENDING("count_ascending")')
|| !sort.contains('COUNT_DESCENDING("count_descending")')
|| !menu.contains("SORT_BUTTON_BASE")
|| !menu.contains("sortMode.order(")
|| !screen.contains("button -> requestSort()")
|| !screen.contains("sortMode = sortMode.next()")
|| !smoke.contains("verifySorting()")
|| !smoke.contains("periodic quantity refresh")) {
throw new GradleException("The storage-terminal sorting regression contract is incomplete")
}
}
}
tasks.register("verifyAlpha223Release") {
group = "verification"
description = "Checks the Sanctuary 26.2.0-alpha.223 inventory-sort and quest-tracker display release."
@@ -7503,7 +7541,7 @@ tasks.register("verifyAlpha225Release") {
def workspaceModules = moduleIds.collectEntries { id ->
[(id): project(":${id}").version.toString()]
}
def releasedModules = workspaceModules
def releasedModules = workspaceModules + [redstoner: "0.0.0-alpha.15"]
def requiredJars = releasedModules.collect { id, version ->
"file = \"mods/${id}-${version}.jar\""
}
@@ -7663,7 +7701,7 @@ tasks.register("verifyAlpha226Release") {
def workspaceModules = moduleIds.collectEntries { id ->
[(id): project(":${id}").version.toString()]
}
def releasedModules = workspaceModules
def releasedModules = workspaceModules + [redstoner: "0.0.0-alpha.15"]
def requiredJars = releasedModules.collect { id, version ->
"file = \"mods/${id}-${version}.jar\""
}
@@ -7754,7 +7792,7 @@ tasks.register("verifyAlpha227Release") {
def workspaceModules = moduleIds.collectEntries { id ->
[(id): project(":${id}").version.toString()]
}
def releasedModules = workspaceModules
def releasedModules = workspaceModules + [redstoner: "0.0.0-alpha.15"]
def requiredJars = releasedModules.collect { id, version ->
"file = \"mods/${id}-${version}.jar\""
}
@@ -7852,11 +7890,12 @@ tasks.register("verifyAlpha228Release") {
def workspaceModules = moduleIds.collectEntries { id ->
[(id): project(":${id}").version.toString()]
}
def requiredJars = workspaceModules.collect { id, version ->
def releasedModules = workspaceModules + [redstoner: "0.0.0-alpha.15"]
def requiredJars = releasedModules.collect { id, version ->
"file = \"mods/${id}-${version}.jar\""
}
if (rootProject.pack_version != "26.2.0-alpha.228"
|| rootProject.redstoner_version != "0.0.0-alpha.15"
|| rootProject.redstoner_version != "0.0.0-alpha.16"
|| rootProject.sanctuary_version != "0.0.0-alpha.132"
|| migration.status != "released"
|| migration.source?.pack_version != "26.2.0-alpha.227"
@@ -7928,6 +7967,7 @@ tasks.named("verifyWorkspace") {
dependsOn tasks.named("verifyAlpha221Release")
dependsOn tasks.named("verifyAlpha222Release")
dependsOn tasks.named("verifyStorageTerminalSearchFocus")
dependsOn tasks.named("verifyStorageTerminalSorting")
dependsOn tasks.named("verifyAlpha223Release")
dependsOn tasks.named("verifyAlpha224Release")
dependsOn tasks.named("verifyAlpha225Release")
+1 -1
View File
@@ -14,7 +14,7 @@ mod_version=0.0.0-alpha.0
module_lifecycle=standby
ambiance_version=0.0.0-alpha.22
ambiance_lifecycle=active
redstoner_version=0.0.0-alpha.15
redstoner_version=0.0.0-alpha.16
redstoner_lifecycle=active
anotherworld_version=0.0.0-alpha.57
anotherworld_lifecycle=active
@@ -2,12 +2,15 @@ package fr.koka99cab.sanctuary26.redstoner.client.gui;
import fr.koka99cab.sanctuary26.redstoner.storage.StorageTerminalMenu;
import fr.koka99cab.sanctuary26.redstoner.storage.StorageTerminalRules;
import fr.koka99cab.sanctuary26.redstoner.storage.StorageTerminalSort;
import fr.koka99cab.sanctuary26.redstoner.storage.StorageTerminalStatus;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.input.KeyEvent;
import net.minecraft.client.input.MouseButtonEvent;
@@ -28,8 +31,10 @@ public final class StorageTerminalScreen extends AbstractContainerScreen<Storage
private static final int SCROLL_WIDTH = 4;
private int requestedPage;
private EditBox searchBox;
private Button sortButton;
private String searchValue = "";
private String sentSearchValue = "";
private StorageTerminalSort sortMode = StorageTerminalSort.ALPHABETICAL;
public StorageTerminalScreen(StorageTerminalMenu menu, Inventory inventory, Component title) {
super(menu, inventory, title, 176, 222);
@@ -40,12 +45,16 @@ public final class StorageTerminalScreen extends AbstractContainerScreen<Storage
protected void init() {
super.init();
titleLabelY = 6;
searchBox = addRenderableWidget(new EditBox(font, leftPos + 8, topPos + 2, 126, 14,
searchBox = addRenderableWidget(new EditBox(font, leftPos + 8, topPos + 2, 96, 14,
Component.translatable("screen.redstoner.storage_terminal.search")));
searchBox.setMaxLength(StorageTerminalRules.MAX_SEARCH_LENGTH);
searchBox.setHint(Component.translatable("screen.redstoner.storage_terminal.search"));
searchBox.setValue(searchValue);
searchBox.setResponder(this::searchChanged);
sortButton = addRenderableWidget(Button.builder(sortButtonLabel(), button -> requestSort())
.bounds(leftPos + 108, topPos + 2, 34, 14)
.tooltip(Tooltip.create(sortTooltip()))
.build());
}
@Override
@@ -151,6 +160,28 @@ public final class StorageTerminalScreen extends AbstractContainerScreen<Storage
}
}
private void requestSort() {
sortMode = sortMode.next();
requestedPage = 0;
sortButton.setMessage(sortButtonLabel());
sortButton.setTooltip(Tooltip.create(sortTooltip()));
sendMenuButton(StorageTerminalMenu.SORT_BUTTON_BASE + sortMode.ordinal());
}
private Component sortButtonLabel() {
return Component.literal(switch (sortMode) {
case ALPHABETICAL -> "A→Z";
case COUNT_ASCENDING -> "1↑";
case COUNT_DESCENDING -> "1↓";
});
}
private Component sortTooltip() {
return Component.translatable("screen.redstoner.storage_terminal.sort",
Component.translatable("screen.redstoner.storage_terminal.sort."
+ sortMode.translationSuffix()));
}
private void searchChanged(String value) {
String previous = sentSearchValue;
searchValue = value;
@@ -27,6 +27,7 @@ public final class StorageTerminalMenu extends AbstractContainerMenu {
public static final int PAGE_BUTTON_BASE = 10;
public static final int SEARCH_CLEAR_BUTTON = 100;
public static final int SEARCH_BACKSPACE_BUTTON = 101;
public static final int SORT_BUTTON_BASE = 200;
public static final int SEARCH_CODEPOINT_BUTTON_BASE = 1_000;
private static final int PLAYER_INVENTORY_Y = 140;
private static final int REFRESH_INTERVAL = 20;
@@ -49,6 +50,7 @@ public final class StorageTerminalMenu extends AbstractContainerMenu {
private final StorageTerminalLayout layout = new StorageTerminalLayout();
private List<StorageTerminalInventory.Entry> entries = List.of();
private String searchQuery = "";
private StorageTerminalSort sortMode = StorageTerminalSort.ALPHABETICAL;
private int refreshCooldown = REFRESH_INTERVAL;
public StorageTerminalMenu(int containerId, Inventory inventory) {
@@ -131,6 +133,9 @@ public final class StorageTerminalMenu extends AbstractContainerMenu {
if (searchQuery.isEmpty()) return true;
searchQuery = searchQuery.substring(0,
searchQuery.offsetByCodePoints(searchQuery.length(), -1));
} else if (buttonId >= SORT_BUTTON_BASE
&& buttonId < SORT_BUTTON_BASE + StorageTerminalSort.values().length) {
sortMode = StorageTerminalSort.values()[buttonId - SORT_BUTTON_BASE];
} else {
int codePoint = buttonId - SEARCH_CODEPOINT_BUTTON_BASE;
if (!isSearchCodePoint(codePoint)) return false;
@@ -211,7 +216,7 @@ public final class StorageTerminalMenu extends AbstractContainerMenu {
if (serverPlayer == null || terminalPos == null) return;
discovery = StorageTerminalNetwork.discover(serverPlayer, terminalPos);
List<StorageTerminalInventory.Entry> liveEntries =
StorageTerminalNetwork.filteredEntries(discovery, searchQuery);
sortMode.order(StorageTerminalNetwork.filteredEntries(discovery, searchQuery));
layout.update(liveEntries, resetOrder);
int page = discovery.status() == StorageTerminalStatus.READY
@@ -0,0 +1,59 @@
package fr.koka99cab.sanctuary26.redstoner.storage;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.Identifier;
import net.minecraft.world.item.ItemStack;
/** Explicit server-side ordering modes for terminal entries. */
public enum StorageTerminalSort {
ALPHABETICAL("alphabetical"),
COUNT_ASCENDING("count_ascending"),
COUNT_DESCENDING("count_descending");
private final String translationSuffix;
StorageTerminalSort(String translationSuffix) {
this.translationSuffix = translationSuffix;
}
public String translationSuffix() {
return translationSuffix;
}
public StorageTerminalSort next() {
StorageTerminalSort[] values = values();
return values[(ordinal() + 1) % values.length];
}
public List<StorageTerminalInventory.Entry> order(
List<StorageTerminalInventory.Entry> entries) {
List<StorageTerminalInventory.Entry> ordered = new ArrayList<>(entries);
ordered.sort(comparator());
return List.copyOf(ordered);
}
private Comparator<StorageTerminalInventory.Entry> comparator() {
Comparator<StorageTerminalInventory.Entry> alphabetical = Comparator
.comparing((StorageTerminalInventory.Entry entry) -> alphabeticalName(entry.stack()))
.thenComparing(entry -> itemId(entry.stack()));
return switch (this) {
case ALPHABETICAL -> alphabetical;
case COUNT_ASCENDING -> Comparator
.comparingInt(StorageTerminalInventory.Entry::total).thenComparing(alphabetical);
case COUNT_DESCENDING -> Comparator
.comparingInt(StorageTerminalInventory.Entry::total).reversed().thenComparing(alphabetical);
};
}
private static String alphabeticalName(ItemStack stack) {
return StorageTerminalSearch.normalize(stack.getHoverName().getString());
}
private static String itemId(ItemStack stack) {
Identifier id = BuiltInRegistries.ITEM.getKey(stack.getItem());
return id == null ? "" : id.toString();
}
}
@@ -55,6 +55,10 @@
"screen.redstoner.storage_terminal.search": "Search…",
"screen.redstoner.storage_terminal.no_results": "No items found.",
"screen.redstoner.storage_terminal.summary": "%s/%s · %s blocks · %s types",
"screen.redstoner.storage_terminal.sort": "Sort: %s",
"screen.redstoner.storage_terminal.sort.alphabetical": "alphabetical",
"screen.redstoner.storage_terminal.sort.count_ascending": "quantity, ascending",
"screen.redstoner.storage_terminal.sort.count_descending": "quantity, descending",
"screen.redstoner.storage_terminal.error.invalid_terminal": "The terminal is no longer accessible.",
"screen.redstoner.storage_terminal.error.disconnected": "No connected chest or barrel.",
"screen.redstoner.storage_terminal.error.too_large": "Network too large (maximum 128 storage blocks).",
@@ -55,6 +55,10 @@
"screen.redstoner.storage_terminal.search": "Rechercher…",
"screen.redstoner.storage_terminal.no_results": "Aucun objet trouvé.",
"screen.redstoner.storage_terminal.summary": "%s/%s · %s blocs · %s types",
"screen.redstoner.storage_terminal.sort": "Tri : %s",
"screen.redstoner.storage_terminal.sort.alphabetical": "alphabétique",
"screen.redstoner.storage_terminal.sort.count_ascending": "quantité croissante",
"screen.redstoner.storage_terminal.sort.count_descending": "quantité décroissante",
"screen.redstoner.storage_terminal.error.invalid_terminal": "Le terminal n'est plus accessible.",
"screen.redstoner.storage_terminal.error.disconnected": "Aucun coffre ou baril connecté.",
"screen.redstoner.storage_terminal.error.too_large": "Réseau trop grand (128 stockages maximum).",
@@ -55,6 +55,10 @@
"screen.redstoner.storage_terminal.search": "Поиск…",
"screen.redstoner.storage_terminal.no_results": "Предметы не найдены.",
"screen.redstoner.storage_terminal.summary": "%s/%s · блоков: %s · типов: %s",
"screen.redstoner.storage_terminal.sort": "Сортировка: %s",
"screen.redstoner.storage_terminal.sort.alphabetical": "по алфавиту",
"screen.redstoner.storage_terminal.sort.count_ascending": "по возрастанию количества",
"screen.redstoner.storage_terminal.sort.count_descending": "по убыванию количества",
"screen.redstoner.storage_terminal.error.invalid_terminal": "Терминал больше недоступен.",
"screen.redstoner.storage_terminal.error.disconnected": "Нет подключённых сундуков или бочек.",
"screen.redstoner.storage_terminal.error.too_large": "Сеть слишком велика (не более 128 хранилищ).",
@@ -27,6 +27,7 @@ public final class StorageTerminalSmoke {
verifyPagination();
verifyAggregationAndTransfers();
verifySearch();
verifySorting();
verifyStableLayout();
}
@@ -149,6 +150,47 @@ public final class StorageTerminalSmoke {
.set(DataComponents.MAX_STACK_SIZE, 64).build());
}
private static void verifySorting() {
ItemStack eclair = new ItemStack(stackable(Items.STONE));
eclair.set(DataComponents.CUSTOM_NAME, Component.literal("Éclair"));
ItemStack banana = new ItemStack(stackable(Items.DIRT));
banana.set(DataComponents.CUSTOM_NAME, Component.literal("Banane"));
ItemStack zebra = new ItemStack(stackable(Items.DIAMOND));
zebra.set(DataComponents.CUSTOM_NAME, Component.literal("Zèbre"));
List<StorageTerminalInventory.Entry> entries = List.of(
new StorageTerminalInventory.Entry(eclair, 64),
new StorageTerminalInventory.Entry(banana, 8),
new StorageTerminalInventory.Entry(zebra, 2));
require(names(StorageTerminalSort.ALPHABETICAL.order(entries))
.equals(List.of("Banane", "Éclair", "Zèbre")),
"Alphabetical terminal sorting ignored names or accent folding");
require(names(StorageTerminalSort.COUNT_ASCENDING.order(entries))
.equals(List.of("Zèbre", "Banane", "Éclair")),
"Ascending terminal quantity sorting is incorrect");
require(names(StorageTerminalSort.COUNT_DESCENDING.order(entries))
.equals(List.of("Éclair", "Banane", "Zèbre")),
"Descending terminal quantity sorting is incorrect");
require(StorageTerminalSort.ALPHABETICAL.next() == StorageTerminalSort.COUNT_ASCENDING
&& StorageTerminalSort.COUNT_ASCENDING.next() == StorageTerminalSort.COUNT_DESCENDING
&& StorageTerminalSort.COUNT_DESCENDING.next() == StorageTerminalSort.ALPHABETICAL,
"Terminal sorting modes do not cycle deterministically");
StorageTerminalLayout stable = new StorageTerminalLayout();
stable.update(StorageTerminalSort.COUNT_DESCENDING.order(entries), true);
List<StorageTerminalInventory.Entry> changedCounts = List.of(
new StorageTerminalInventory.Entry(eclair, 1),
new StorageTerminalInventory.Entry(banana, 80),
new StorageTerminalInventory.Entry(zebra, 2));
stable.update(StorageTerminalSort.COUNT_DESCENDING.order(changedCounts), false);
require(names(stable.page(0).subList(0, 3)).equals(List.of("Éclair", "Banane", "Zèbre")),
"A periodic quantity refresh moved terminal entries under the cursor");
}
private static List<String> names(List<StorageTerminalInventory.Entry> entries) {
return entries.stream().map(entry -> entry.stack().getHoverName().getString()).toList();
}
private static void verifyStableLayout() {
StorageTerminalInventory.Entry stone = new StorageTerminalInventory.Entry(
new ItemStack(stackable(Items.STONE)), 64);