release: prepare Sanctuary 26.2.0-alpha.232
Verification 26.2 / check (pull_request) Canceled after 0s

This commit is contained in:
2026-09-03 02:22:46 +02:00
parent 7e1ed85307
commit ddaf71c7cd
17 changed files with 620 additions and 261 deletions
+9 -1
View File
@@ -8,7 +8,15 @@ Ce journal suit les **versions du modpack**. Lorsqu'un seul module change, sa ve
## Non publié
Modules modifiés : **Sanctuary `0.0.0-alpha.138`** et **Red-Stoner `0.0.0-alpha.19`**.
Aucun changement non publié.
## `26.2.0-alpha.232`
Modules modifiés : **Another World `0.0.0-alpha.58`**, **Sanctuary `0.0.0-alpha.138`** et **Red-Stoner `0.0.0-alpha.19`**.
- ferme le ticket #146 en enregistrant chaque cliché pris avec un appareil Canon dans le dossier `screenshots` de linstance, à côté des captures F2 ;
- exporte le carré cadré à sa résolution native avec le filtre de la pellicule, sans le réduire aux 128 × 128 pixels de la carte en jeu ;
- écrit le PNG hors du thread de rendu et réserve un nom horodaté unique afin d’éviter les freezes comme les écrasements lors de plusieurs clichés rapprochés ;
- remplace le volume de la cape à motifs par les deux faces opposées de l’îlot UV exact du bouclier, redimensionnées aux proportions 10 × 16 de la cape, afin de corriger la projection et le culling sans afficher le manche ni l’épaisseur du bouclier ;
- laisse la touche dinventaire fermer le terminal tant que la recherche nest pas focalisée, mais la traite comme du texte dans le champ de recherche afin que la lettre `E` ne ferme plus l’écran ;
+2 -2
View File
@@ -22,9 +22,9 @@ Licence : **GNU GPL 3.0 or Later** (`GPL-3.0-or-later`)
Les descriptions FR/EN/RU sont déclarées dans chaque `fabric.mod.json` et dans les fichiers de langue. Les images fournies servent d'icônes Fabric de leur module.
Version courante du pack distribué : **`26.2.0-alpha.231`**. Cette release distribue Sanctuary **`0.0.0-alpha.137`** et Red-Stoner **`0.0.0-alpha.18`** ; les huit autres modules conservent leur version issue de l'alpha.230.
Version courante du pack distribué : **`26.2.0-alpha.232`**. Cette release distribue Another World **`0.0.0-alpha.58`**, Sanctuary **`0.0.0-alpha.138`** et Red-Stoner **`0.0.0-alpha.19`** ; les sept autres modules conservent leur version issue de l'alpha.231.
Les prochains modules Sanctuary **`0.0.0-alpha.138`** et Red-Stoner **`0.0.0-alpha.19`** corrigent la cape blanche décorée avec deux faces planes utilisant lUV exact du bouclier, empêchent la touche `E` de fermer le terminal pendant une recherche et étendent le panneau de trophées à huit lignes. Chaque statistique y possède une page de noms puis une page de valeurs ; un œuf dapparition ajoute directement le classement des créatures correspondantes tuées. Les sauvegardes restent en données Sanctuary v15 et Red-Stoner v4 ; le snapshot du panneau passant à sept entrées, le protocole Red-Stoner passe à 8.
Lalpha.232 enregistre automatiquement chaque cliché Canon dans le dossier `screenshots` de linstance, à côté des captures F2. Le PNG conserve le carré réellement cadré, sa résolution et le filtre de la pellicule ; l’écriture asynchrone et les noms uniques évitent les freezes et les écrasements. Cette release corrige aussi la cape blanche décorée, la touche `E` du terminal et étend le panneau de trophées à huit lignes. Chaque statistique y possède une page de noms puis une page de valeurs ; un œuf dapparition ajoute directement le classement des créatures correspondantes tuées. Les sauvegardes restent en données Another World v5, Sanctuary v15 et Red-Stoner v4 ; le snapshot du panneau passant à sept entrées, le protocole Red-Stoner passe à 8.
Lalpha.231 rend les sorties et le butin de la Backroom réellement trouvables sur ses cinq niveaux. Chaque niveau garantit désormais un coffre et un lit de sortie par cellule de 3 × 3 chunks dans les nouveaux chunks, sans ajouter de livres au butin. Le panneau de trophées devient une plaque verticale de chêne avec quatre lignes lisibles : titre et numéro de page, puis les trois places du podium. Un clic droit avance dune page et `Maj` + clic droit revient à la précédente. JEI passe à la bêta Fabric `30.29.0.201`, qui corrige un crash lié à latlas de textures. Les sauvegardes et les chunks déjà générés restent compatibles ; Sanctuary conserve les données v15/protocole 24 et Red-Stoner les données v4/protocole 7.
+12 -3
View File
@@ -15,6 +15,14 @@ tasks.register("vaultRulesSmoke", JavaExec) {
mainClass = "fr.koka99cab.sanctuary26.anotherworld.vault.VaultRulesSmoke"
}
tasks.register("canonScreenshotFilesSmoke", JavaExec) {
group = "verification"
description = "Checks Canon photo exports use the vanilla screenshots directory and collision-safe names."
dependsOn tasks.named("testClasses")
classpath = sourceSets.test.runtimeClasspath
mainClass = "fr.koka99cab.canon.client.CanonScreenshotFilesSmoke"
}
tasks.named("test") {
failOnNoDiscoveredTests = false
}
@@ -1956,7 +1964,7 @@ tasks.register("verifyAnotherWorldCatalog") {
"src/main/resources/assets/redstoner/textures/item/weather_gauge.png",
"src/main/resources/data/redstoner/recipe/weather_gauge.json"
]
if (project.version.toString() != "0.0.0-alpha.57"
if (project.version.toString() != "0.0.0-alpha.58"
|| sulfurMigration.target?.modules?.anotherworld != "0.0.0-alpha.44"
|| sulfurMigration.sulfur_caves?.sanctuary?.dripstone_cell_period != 2
|| !javaText.contains('Identifier.parse("sanctuary:sulfur_caves")')
@@ -3261,6 +3269,7 @@ tasks.register("verifyAnotherWorldCatalog") {
tasks.named("check") {
dependsOn tasks.named("verifyAnotherWorldCatalog")
dependsOn tasks.named("vaultRulesSmoke")
dependsOn tasks.named("canonScreenshotFilesSmoke")
}
tasks.register("verifyClimateBiomeMixer") {
@@ -3273,7 +3282,7 @@ tasks.register("verifyClimateBiomeMixer") {
"pack/migrations/26.2.0-alpha.188-beta-climate-provinces-to-alpha.189.json"))
def mixer = file(
"src/main/java/fr/koka99cab/sanctuary26/anotherworld/mixin/MultiNoiseBiomeSourceParameterListPresetMixin.java").text
if (project.version.toString() != "0.0.0-alpha.57"
if (project.version.toString() != "0.0.0-alpha.58"
|| migration.source?.pack_version != "26.2.0-alpha.188"
|| migration.target?.pack_version != "26.2.0-alpha.189"
|| migration.target?.modules?.anotherworld != "0.0.0-alpha.48"
@@ -3315,7 +3324,7 @@ tasks.register("verifyBackroomMaterialIntegration") {
"sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/api/backroom/BackroomMaterialApi.java").text
def rootBuild = rootProject.file("build.gradle").text
def manifest = new JsonSlurper().parse(file("src/main/resources/fabric.mod.json"))
if (project.version.toString() != "0.0.0-alpha.57"
if (project.version.toString() != "0.0.0-alpha.58"
|| !initializer.contains("BackroomMaterialApi.registerAnomalyMaterial(AnotherWorldBlocks.TURQUOISE_MOSS)")
|| !blocks.contains('"turquoise_moss"')
|| !api.contains("public static void registerAnomalyMaterial")
@@ -0,0 +1,38 @@
package fr.koka99cab.canon.client;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
final class CanonScreenshotFiles {
private static final String SCREENSHOT_DIRECTORY = "screenshots";
private static final int MAX_SUFFIX = 10_000;
private CanonScreenshotFiles() {
}
static Path reserve(Path gameDirectory, String timestamp) throws IOException {
Path screenshots = gameDirectory.resolve(SCREENSHOT_DIRECTORY);
Files.createDirectories(screenshots);
for (int suffix = 1; suffix <= MAX_SUFFIX; suffix++) {
Path candidate = screenshots.resolve(fileName(timestamp, suffix));
try {
return Files.createFile(candidate);
} catch (FileAlreadyExistsException ignored) {
// Keep the vanilla timestamp readable while avoiding same-second collisions.
}
}
throw new IOException("Could not reserve a unique Canon screenshot filename");
}
static String fileName(String timestamp, int suffix) {
if (timestamp == null || timestamp.isBlank()) {
throw new IllegalArgumentException("timestamp must not be blank");
}
if (suffix < 1) {
throw new IllegalArgumentException("suffix must be positive");
}
return timestamp + "-canon" + (suffix == 1 ? "" : "_" + suffix) + ".png";
}
}
@@ -8,12 +8,15 @@ import fr.koka99cab.canon.network.CapturePhotoPayload;
import fr.koka99cab.canon.photo.PhotoMapPalette;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.platform.Window;
import java.nio.file.Files;
import java.nio.file.Path;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Screenshot;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.ARGB;
import net.minecraft.util.Util;
import net.minecraft.world.item.ItemStack;
public final class ClientPhotoCapture {
@@ -65,6 +68,7 @@ public final class ClientPhotoCapture {
ClientPlayNetworking.send(new CapturePhotoPayload(colors));
client.player.getCooldowns().addCooldown(capture.cameraStack(), CanonConstants.CAMERA_COOLDOWN_TICKS);
client.player.playSound(SoundEvents.SPYGLASS_USE, 0.7F, 1.35F);
exportScreenshot(client, screenshot, capture.style(), offsetX, offsetY, cropWidth, cropHeight);
} catch (Exception exception) {
CanonMod.LOGGER.warn("Failed to capture photo", exception);
if (client.player != null) {
@@ -74,6 +78,72 @@ public final class ClientPhotoCapture {
});
}
private static void exportScreenshot(
Minecraft client,
NativeImage screenshot,
PhotoStyle style,
int offsetX,
int offsetY,
int cropWidth,
int cropHeight
) {
NativeImage exported = null;
Path target = null;
try {
exported = new NativeImage(cropWidth, cropHeight, false);
screenshot.copyRect(exported, offsetX, offsetY, 0, 0, cropWidth, cropHeight, false, false);
target = CanonScreenshotFiles.reserve(client.gameDirectory.toPath(), Util.getFilenameFormattedDateTime());
} catch (Exception exception) {
if (exported != null) exported.close();
if (target != null) deleteReservedFile(target);
notifyScreenshotFailure(client, exception);
return;
}
NativeImage image = exported;
Path output = target;
Util.ioPool().execute(() -> {
try (image) {
applyStyle(image, style);
image.writeToFile(output);
client.execute(() -> {
if (client.player != null) {
client.player.sendSystemMessage(Component.translatable(
"message.canon.camera.screenshot_saved", output.getFileName().toString()));
}
});
} catch (Exception exception) {
deleteReservedFile(output);
notifyScreenshotFailure(client, exception);
}
});
}
private static void applyStyle(NativeImage image, PhotoStyle style) {
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
image.setPixel(x, y, style.applyFilter(image.getPixel(x, y)));
}
}
}
private static void notifyScreenshotFailure(Minecraft client, Exception exception) {
CanonMod.LOGGER.warn("Failed to save Canon photo in the screenshots directory", exception);
client.execute(() -> {
if (client.player != null) {
client.player.sendSystemMessage(Component.translatable("message.canon.camera.screenshot_failed"));
}
});
}
private static void deleteReservedFile(Path target) {
try {
Files.deleteIfExists(target);
} catch (Exception cleanupException) {
CanonMod.LOGGER.debug("Could not remove incomplete Canon screenshot {}", target, cleanupException);
}
}
private static int clamp(int value, int min, int max) {
return Math.max(min, Math.min(max, value));
}
@@ -24,6 +24,8 @@
"message.canon.camera.full": "The camera is already full.",
"message.canon.camera.captured": "Photo captured.",
"message.canon.camera.capture_failed": "Could not capture the current frame.",
"message.canon.camera.screenshot_saved": "Photo saved to screenshots: %s",
"message.canon.camera.screenshot_failed": "The in-game photo was captured, but its PNG could not be saved to screenshots.",
"style.canon.white": "Neutral",
"style.canon.white.effect": "Neutral stock with soft paper whites.",
"style.canon.light_gray": "Matte",
@@ -24,6 +24,8 @@
"message.canon.camera.full": "L'appareil photo est deja plein.",
"message.canon.camera.captured": "Photo prise.",
"message.canon.camera.capture_failed": "Impossible de capturer l'image actuelle.",
"message.canon.camera.screenshot_saved": "Photo sauvegardee dans screenshots : %s",
"message.canon.camera.screenshot_failed": "La photo en jeu est prise, mais son PNG n'a pas pu etre sauvegarde dans screenshots.",
"style.canon.white": "Neutre",
"style.canon.white.effect": "Rendu neutre avec des blancs doux.",
"style.canon.light_gray": "Mat",
@@ -24,6 +24,8 @@
"message.canon.camera.full": "Фотоаппарат уже полностью заряжен.",
"message.canon.camera.captured": "Фотография сделана.",
"message.canon.camera.capture_failed": "Не удалось снять текущий кадр.",
"message.canon.camera.screenshot_saved": "Фотография сохранена в screenshots: %s",
"message.canon.camera.screenshot_failed": "Снимок в игре сделан, но PNG не удалось сохранить в screenshots.",
"style.canon.white": "Нейтральный",
"style.canon.white.effect": "Нейтральная плёнка с мягкими белыми тонами.",
"style.canon.light_gray": "Матовый",
@@ -0,0 +1,33 @@
package fr.koka99cab.canon.client;
import java.nio.file.Files;
import java.nio.file.Path;
public final class CanonScreenshotFilesSmoke {
private CanonScreenshotFilesSmoke() {
}
public static void main(String[] args) throws Exception {
String timestamp = "2026-09-03_01.23.45";
require(CanonScreenshotFiles.fileName(timestamp, 1).equals("2026-09-03_01.23.45-canon.png"),
"first screenshot filename");
require(CanonScreenshotFiles.fileName(timestamp, 2).equals("2026-09-03_01.23.45-canon_2.png"),
"same-second screenshot filename");
Path gameDirectory = Files.createTempDirectory("canon-screenshot-smoke-");
Path first = CanonScreenshotFiles.reserve(gameDirectory, timestamp);
Path second = CanonScreenshotFiles.reserve(gameDirectory, timestamp);
require(first.getParent().equals(gameDirectory.resolve("screenshots")),
"screenshots directory");
require(first.getFileName().toString().equals("2026-09-03_01.23.45-canon.png"),
"first reserved screenshot");
require(second.getFileName().toString().equals("2026-09-03_01.23.45-canon_2.png"),
"collision-safe screenshot");
require(Files.isRegularFile(first) && Files.isRegularFile(second),
"reserved screenshot files");
}
private static void require(boolean condition, String contract) {
if (!condition) throw new IllegalStateException("Invalid Canon screenshot contract: " + contract);
}
}
+370 -234
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -16,7 +16,7 @@ ambiance_version=0.0.0-alpha.22
ambiance_lifecycle=active
redstoner_version=0.0.0-alpha.19
redstoner_lifecycle=active
anotherworld_version=0.0.0-alpha.57
anotherworld_version=0.0.0-alpha.58
anotherworld_lifecycle=active
itsalive_version=0.0.0-alpha.38
itsalive_lifecycle=active
@@ -30,5 +30,5 @@ onlyfun_version=0.0.0-alpha.27
onlyfun_lifecycle=active
sanctuary_version=0.0.0-alpha.138
sanctuary_lifecycle=active
pack_version=26.2.0-alpha.231
pack_version=26.2.0-alpha.232
maven_group=fr.koka99cab.sanctuary26
@@ -0,0 +1,58 @@
{
"schema_version": 1,
"status": "released",
"source": {
"pack_version": "26.2.0-alpha.231",
"modules": {
"anotherworld": "0.0.0-alpha.57",
"sanctuary": "0.0.0-alpha.137",
"redstoner": "0.0.0-alpha.18"
}
},
"target": {
"pack_version": "26.2.0-alpha.232",
"modules": {
"anotherworld": "0.0.0-alpha.58",
"sanctuary": "0.0.0-alpha.138",
"redstoner": "0.0.0-alpha.19"
}
},
"fixed_issues": [146],
"canon_photo_screenshots": {
"directory": "screenshots",
"viewfinder_crop_only": true,
"film_filter_preserved": true,
"full_crop_resolution": true,
"asynchronous_png_write": true,
"collision_safe_names": true
},
"alpha231_follow_up": {
"migration": "alpha231-follow-up-cape-terminal-trophy.json",
"banner_cape_fixed": true,
"terminal_inventory_key_fixed": true,
"trophy_panel_visible_lines": 8,
"trophy_panel_entries_per_view": 7,
"spawn_egg_kill_ranking": true
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"persistent_data_formats_changed": false,
"network_protocols_changed": true,
"anotherworld_data_version": 5,
"anotherworld_network_protocol": 2,
"sanctuary_data_version": 15,
"sanctuary_network_protocol": 24,
"redstoner_data_version": 4,
"redstoner_source_network_protocol": 7,
"redstoner_target_network_protocol": 8,
"client_server_update_required": true
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true
}
}
@@ -1,6 +1,7 @@
{
"schema_version": 1,
"status": "unreleased",
"status": "released",
"released_in": "26.2.0-alpha.232",
"modules": {
"sanctuary": {
"source_module_version": "0.0.0-alpha.137",
+13 -13
View File
@@ -14,15 +14,15 @@ hash = "41f4f4c88c71d0a23446d5014f7407d55ad477441847099cad24492b032335a0"
[[files]]
file = "mods/ambiance-0.0.0-alpha.22.jar"
hash = "e06efa96be6b8b528b1ff1e4cc611c369bc5cf628f48821b3848c046872116ea"
hash = "0e25a85502da59ff111485d47223a6e653db4466cf59f84e7abf8c6b1405aa6a"
[[files]]
file = "mods/anotherworld-0.0.0-alpha.57.jar"
hash = "7a0fd7805cec95229588ecaa442c58dde5d03caf8a64d342290c3bcf12aa4bce"
file = "mods/anotherworld-0.0.0-alpha.58.jar"
hash = "c28aaeedc1fb6c8fb8613ba0f12af9a1327edc2ef0eeb20cb73f685c52a29034"
[[files]]
file = "mods/events-0.0.0-alpha.0.jar"
hash = "6431a33158d74c4ad0205dcc1389c1c2cdbe4f38ee7277a9197cbb160586fbc5"
hash = "4dd9eeccc4f9d2ab2804339320a1a2a24174706cc8a4ac2853b16765b8b2a050"
[[files]]
file = "mods/fabric-api.pw.toml"
@@ -31,7 +31,7 @@ metafile = true
[[files]]
file = "mods/iliketomoveit-0.0.0-alpha.18.jar"
hash = "bc8c79a37bff199d4bcd9d3ae15ae066e5c892284f3e54b08b851389f03b7a7a"
hash = "a445606ebd5f1a35efdc205de87e4b6b83720d31b06787ae67cc21bc19df9e26"
[[files]]
file = "mods/iris.pw.toml"
@@ -40,7 +40,7 @@ metafile = true
[[files]]
file = "mods/itsalive-0.0.0-alpha.38.jar"
hash = "7938c7ccca31b1aa68e741ab0e0bdfd5cba965fc82dfcadeee6bdf34fc7d4878"
hash = "d4605837e99bcbf86bbb46a1a71931a39b8e3087cdb0bee648b61996360b942e"
[[files]]
file = "mods/jei.pw.toml"
@@ -49,23 +49,23 @@ metafile = true
[[files]]
file = "mods/masterkey-0.0.0-alpha.9.jar"
hash = "778a9c841fb3bdcba1284f9a779c15f6ed5a19156e4e99f342a6c4e5680422f5"
hash = "d36b860a0bae9a4e52d2093146a1aeca8e7882b72462bd2cb8575fff262ebf23"
[[files]]
file = "mods/onlyfun-0.0.0-alpha.27.jar"
hash = "1a4869438543d43f841ee414dd72ab6d8b03c603b10d0b087ef76d070e09186d"
hash = "a77aafb200faaf1162f6f5c0f047bfb5672702ca9d32d0e16e176f3f2455e24c"
[[files]]
file = "mods/ouch-0.0.0-alpha.11.jar"
hash = "5003f4be2d60672aedece2061c11d5e79478d45c271d19dd6639150dcff88ee9"
hash = "d234613542669e2eddf6e393d3451b2a2786f18ae5a01a61940611199f5e16ef"
[[files]]
file = "mods/redstoner-0.0.0-alpha.18.jar"
hash = "f89076b3d1fa06b17813d350e6ecd53674d19a6c7b5b9f5fc61ed65d6bcb2830"
file = "mods/redstoner-0.0.0-alpha.19.jar"
hash = "0891d96adb71c3843028dfe704ab33cac8470712277cef8f9527131f9c02e471"
[[files]]
file = "mods/sanctuary-0.0.0-alpha.137.jar"
hash = "0c217a1632413e0af12e39f5096fd057d2dceaf6f437ca7044571c3635414d44"
file = "mods/sanctuary-0.0.0-alpha.138.jar"
hash = "233c84a9ba7ffb172618e541ab3635f3b675a858bacf80c4712feb329069c99c"
[[files]]
file = "mods/simple-voice-chat.pw.toml"
+2 -2
View File
@@ -1,12 +1,12 @@
name = "Sanctuary 26.2"
author = "KOKA99CAB"
version = "26.2.0-alpha.231"
version = "26.2.0-alpha.232"
pack-format = "packwiz:1.1.0"
[index]
file = "index.toml"
hash-format = "sha256"
hash = "8187dc94434a311a501171c456051c0d7de4e43a7f68956ef5b3a25ab188a473"
hash = "0a35e9686a1c272ed05694b8509273334bf7770951349465c0280428b2c06338"
[versions]
fabric = "0.19.3"
+1 -1
View File
@@ -3,7 +3,7 @@
"game": "minecraft",
"name": "Sanctuary 26.2",
"summary": "Sanctuary 26.2 — progression, exploration, worldgen and multiplayer systems by KOKA99CAB.",
"versionId": "26.2.0-alpha.231",
"versionId": "26.2.0-alpha.232",
"files": [
{
"path": "mods/fabric-api-0.159.0+26.2.jar",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"schema_version": 1,
"pack_version": "26.2.0-alpha.231",
"pack_version": "26.2.0-alpha.232",
"release_channel": "alpha",
"minecraft_version": "26.2",
"fabric_loader_version": "0.19.3",
@@ -9,7 +9,7 @@
"project_id": "FgOqyAXd",
"project_slug": "sanctuary-koka99",
"project_url": "https://modrinth.com/modpack/sanctuary-koka99",
"primary_file": "sanctuary-26.2.0-alpha.231.mrpack",
"primary_file": "sanctuary-26.2.0-alpha.232.mrpack",
"shared_client_server_pack": true,
"update_owner": "packwiz-installer",
"check_on_launch": true,