feat: ajouter une cape craftable décorée par bannière #126
@@ -6,6 +6,15 @@ 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.
|
||||
|
||||
## `26.2.0-alpha.226`
|
||||
|
||||
Module modifié : **Sanctuary `0.0.0-alpha.130`**. Les neuf autres modules conservent leur version issue de l'alpha.225.
|
||||
|
||||
- ajoute `sanctuary:white_cape`, une cape blanche craftable avec six laines blanches disposées comme une porte, en deux colonnes de trois ;
|
||||
- permet d'associer simplement la cape et n'importe quelle bannière dans l'établi afin d'en conserver la couleur de fond et jusqu'à six motifs ;
|
||||
- affiche les motifs dans l'inventaire et sur la cape équipée, avec la physique native des capes, y compris en multijoueur ;
|
||||
- conserve les sauvegardes Sanctuary en data v13 ; le payload d'apparence transporte désormais la couleur et les motifs bornés, donc le protocole Sanctuary passe de 23 à 24 et client comme serveur doivent être mis à jour, sans nouveau monde ni réinitialisation.
|
||||
|
||||
## `26.2.0-alpha.225`
|
||||
|
||||
Modules modifiés : **Another World `0.0.0-alpha.56`**, **It's Alive `0.0.0-alpha.38`**, **I Like To Move It `0.0.0-alpha.18`**, **Only Fun `0.0.0-alpha.27`** et **Sanctuary `0.0.0-alpha.129`**. Les cinq autres modules conservent leur version issue de l'alpha.224.
|
||||
|
||||
@@ -22,7 +22,7 @@ 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.225`**. Cette release distribue Another World **`0.0.0-alpha.56`**, It's Alive **`0.0.0-alpha.38`**, I Like To Move It **`0.0.0-alpha.18`**, Only Fun **`0.0.0-alpha.27`** et Sanctuary **`0.0.0-alpha.129`** ; les cinq autres modules conservent leur version issue de l'alpha.224.
|
||||
Version courante du pack distribué : **`26.2.0-alpha.226`**. Cette release distribue Sanctuary **`0.0.0-alpha.130`** ; les neuf autres modules conservent leur version issue de l'alpha.225.
|
||||
|
||||
L’alpha.225 corrige quatre tickets : posséder un coffre-fort devient obligatoire pour en braquer un autre, les familiers temporaires ne donnent plus de butin, d’expérience, de lootbox ni de progression de quête, les seize variantes Canaplia survivent à tout leur cycle de culture, et le Magic Carpet retrouve la texture historique du Totem de Notch à la place de la laine violette. Les IDs, sauvegardes, formats de données et protocoles restent compatibles.
|
||||
|
||||
@@ -116,6 +116,8 @@ L’alpha.173 centralise l’audio du Miner : une seule boucle de frappe sur le
|
||||
|
||||
L’alpha.171 rassemble les **46 capes** dans un onglet créatif dédié « Capes ». Elles ne sont plus mélangées à l’onglet vanilla Tools & Utilities et ne sont donc affichées qu’une seule fois.
|
||||
|
||||
L’alpha.226 ajoute une 47e cape, `sanctuary:white_cape`, distincte de la collection Only Fun. Elle se fabrique avec six laines blanches en deux colonnes de trois, puis accepte une bannière dans une recette simple pour en conserver la couleur et jusqu’à six motifs ; les motifs s’affichent dans l’inventaire comme une fois la cape équipée.
|
||||
|
||||
L’alpha.170 ajoute les pictogrammes fournis aux emplacements vides de la cape et du familier. Ils disparaissent dès qu’une cape ou un œuf est équipé, sans modifier les objets, la persistance ni le protocole.
|
||||
|
||||
L’alpha.169 agrandit la collection de 13 à **46 capes équipables**. Les 33 nouvelles textures 64×32 fournies sont conservées telles quelles, avec une icône d’inventaire, un modèle et des traductions FR/EN/RU pour chaque cape. Elles utilisent le slot, la persistance et le rendu multijoueur déjà existants, restent sans recette et rejoignent toutes la lootbox de capes Only Fun. Les caméras Canon restent accessibles dans la lootbox normale et peuvent désormais aussi apparaître directement, très rarement, dans les coffres d’exploration et du donjon de cobblestone.
|
||||
|
||||
+306
-189
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -28,7 +28,7 @@ iliketomoveit_version=0.0.0-alpha.18
|
||||
iliketomoveit_lifecycle=active
|
||||
onlyfun_version=0.0.0-alpha.27
|
||||
onlyfun_lifecycle=active
|
||||
sanctuary_version=0.0.0-alpha.129
|
||||
sanctuary_version=0.0.0-alpha.130
|
||||
sanctuary_lifecycle=active
|
||||
pack_version=26.2.0-alpha.225
|
||||
pack_version=26.2.0-alpha.226
|
||||
maven_group=fr.koka99cab.sanctuary26
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"status": "released",
|
||||
"source": {
|
||||
"pack_version": "26.2.0-alpha.225",
|
||||
"modules": {
|
||||
"sanctuary": "0.0.0-alpha.129"
|
||||
}
|
||||
},
|
||||
"target": {
|
||||
"pack_version": "26.2.0-alpha.226",
|
||||
"modules": {
|
||||
"sanctuary": "0.0.0-alpha.130"
|
||||
}
|
||||
},
|
||||
"white_cape": {
|
||||
"owner": "sanctuary",
|
||||
"item_id": "sanctuary:white_cape",
|
||||
"crafting_material": "minecraft:white_wool",
|
||||
"crafting_material_count": 6,
|
||||
"crafting_pattern": ["WW", "WW", "WW"],
|
||||
"banner_decoration_recipe": "cape_plus_banner",
|
||||
"accepted_banners": "#minecraft:banners",
|
||||
"base_color_preserved": true,
|
||||
"maximum_pattern_layers": 6,
|
||||
"inventory_pattern_rendering": true,
|
||||
"equipped_pattern_rendering": true,
|
||||
"vanilla_cape_physics": true,
|
||||
"included_in_onlyfun_lootbox": false
|
||||
},
|
||||
"compatibility": {
|
||||
"existing_saves_supported": true,
|
||||
"new_world_required": false,
|
||||
"save_reset_required": false,
|
||||
"registry_ids_added": ["sanctuary:white_cape"],
|
||||
"persistent_data_formats_changed": false,
|
||||
"sanctuary_data_version": 13,
|
||||
"sanctuary_source_network_protocol": 23,
|
||||
"sanctuary_target_network_protocol": 24,
|
||||
"sanctuary_network_protocol_changed": true,
|
||||
"client_server_update_required": true
|
||||
},
|
||||
"distribution": {
|
||||
"pack_release_updated": true,
|
||||
"packwiz_index_refreshed": true,
|
||||
"prism_modrinth_index_updated": true,
|
||||
"shared_client_server_mrpack": true
|
||||
}
|
||||
}
|
||||
+11
-11
@@ -14,15 +14,15 @@ hash = "41f4f4c88c71d0a23446d5014f7407d55ad477441847099cad24492b032335a0"
|
||||
|
||||
[[files]]
|
||||
file = "mods/ambiance-0.0.0-alpha.22.jar"
|
||||
hash = "d6c44f09ddc455ae0cec02a1568e07036f11a3b2d105659e9aa14289357ddb47"
|
||||
hash = "0e862db1c5efb5c0745b82f991d5480f4305822c3d3f53ed38e2d2d47efabee1"
|
||||
|
||||
[[files]]
|
||||
file = "mods/anotherworld-0.0.0-alpha.56.jar"
|
||||
hash = "ede131b654d26110af8c7e64238c1b5f99634c3e1307346953ad6ab622ccfedc"
|
||||
hash = "cec8ed493f3c86e4c4481081be110382407f7a5c631b7cf27dd80f80c6f2e92a"
|
||||
|
||||
[[files]]
|
||||
file = "mods/events-0.0.0-alpha.0.jar"
|
||||
hash = "9ccc30733117ab9984df3d69b98ab82d731578a6c35116595c96f2362ea22b76"
|
||||
hash = "16bf9fc194988086f51ba6987546b6aa5bc634fe8fcb118b88540ed16991049c"
|
||||
|
||||
[[files]]
|
||||
file = "mods/fabric-api.pw.toml"
|
||||
@@ -31,7 +31,7 @@ metafile = true
|
||||
|
||||
[[files]]
|
||||
file = "mods/iliketomoveit-0.0.0-alpha.18.jar"
|
||||
hash = "f0ccb5a3f411cb01bcf328a139a1f90e8357587c549778f98354d910d69b2616"
|
||||
hash = "94044341de08c9b89e0c1e6af6857da64b472fadbc94051ab76fc3cf1a0f0006"
|
||||
|
||||
[[files]]
|
||||
file = "mods/iris.pw.toml"
|
||||
@@ -40,7 +40,7 @@ metafile = true
|
||||
|
||||
[[files]]
|
||||
file = "mods/itsalive-0.0.0-alpha.38.jar"
|
||||
hash = "6dab7b2dca11cc1cd9789ed66cc8403b849cb73227d965d979eeebcf5c201309"
|
||||
hash = "a42885703439646687ac281b1c124d1cddb3ca0595a41d930733b94bd8f1aa2f"
|
||||
|
||||
[[files]]
|
||||
file = "mods/jei.pw.toml"
|
||||
@@ -49,23 +49,23 @@ metafile = true
|
||||
|
||||
[[files]]
|
||||
file = "mods/masterkey-0.0.0-alpha.9.jar"
|
||||
hash = "ab0241386a0958505622487318c2e2e269e57e793ef873ed71dac8e0f420338b"
|
||||
hash = "979c14e0a527be63688e97af8349be07b027d3149107c3e9e728a2b1e1ec65ff"
|
||||
|
||||
[[files]]
|
||||
file = "mods/onlyfun-0.0.0-alpha.27.jar"
|
||||
hash = "15dbc6a9c3d4fef699c1f62f989c1d47e366cc781747c76640f5de668ea00510"
|
||||
hash = "023ba80a8f3326a6b534b343753d5630d3be0700d25613ea21057cbfe44fdc73"
|
||||
|
||||
[[files]]
|
||||
file = "mods/ouch-0.0.0-alpha.11.jar"
|
||||
hash = "5bc1e244309a8835ef1d7869225201179cbee12853fb2f2390bfd47f2fcc3c4a"
|
||||
hash = "2c15c9d0adeb315b2c3a6796f27c9dc62dd33365f35ea584552d97500b833eb8"
|
||||
|
||||
[[files]]
|
||||
file = "mods/redstoner-0.0.0-alpha.13.jar"
|
||||
hash = "d913aac62b6e7152c5751ea0ddc820a98a0b74c3864f4c0bdbfc3b44eeaa3417"
|
||||
hash = "753835558c1037e39ca669737190013bd59a3ab16f7d5156c52e940c54ff9c60"
|
||||
|
||||
[[files]]
|
||||
file = "mods/sanctuary-0.0.0-alpha.129.jar"
|
||||
hash = "0b67c37680e5004b7f08ac9de32b501fc8f2025fe5a7649858948f14a5d8f5f5"
|
||||
file = "mods/sanctuary-0.0.0-alpha.130.jar"
|
||||
hash = "8906be26de369903a0d5152d80a95a1c47b3468e4afca17ab4683b8300ad69a9"
|
||||
|
||||
[[files]]
|
||||
file = "mods/simple-voice-chat.pw.toml"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
name = "Sanctuary 26.2"
|
||||
author = "KOKA99CAB"
|
||||
version = "26.2.0-alpha.225"
|
||||
version = "26.2.0-alpha.226"
|
||||
pack-format = "packwiz:1.1.0"
|
||||
|
||||
[index]
|
||||
file = "index.toml"
|
||||
hash-format = "sha256"
|
||||
hash = "19d66dcd7dbbc8e1a9471dcb259a9c9aad727acbc8428f114a48c05c2fd1d8de"
|
||||
hash = "d3b929a596081adc3960866a2f939d4155d1b67ecb22bfe6050931a7d1d0f0a2"
|
||||
|
||||
[versions]
|
||||
fabric = "0.19.3"
|
||||
|
||||
@@ -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.225",
|
||||
"versionId": "26.2.0-alpha.226",
|
||||
"files": [
|
||||
{
|
||||
"path": "mods/fabric-api-0.158.0+26.2.jar",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"pack_version": "26.2.0-alpha.225",
|
||||
"pack_version": "26.2.0-alpha.226",
|
||||
"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.225.mrpack",
|
||||
"primary_file": "sanctuary-26.2.0-alpha.226.mrpack",
|
||||
"shared_client_server_pack": true,
|
||||
"update_owner": "packwiz-installer",
|
||||
"check_on_launch": true,
|
||||
|
||||
+88
-17
@@ -116,6 +116,14 @@ tasks.register("capeJoinSyncSmoke", JavaExec) {
|
||||
mainClass = "fr.koka99cab.sanctuary26.sanctuary.gameplay.CapeJoinSyncQueueSmoke"
|
||||
}
|
||||
|
||||
tasks.register("capeSyncPayloadSmoke", JavaExec) {
|
||||
group = "verification"
|
||||
description = "Checks banner cape colors and the six-pattern network bound."
|
||||
dependsOn tasks.named("testClasses")
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
mainClass = "fr.koka99cab.sanctuary26.sanctuary.network.CapeSyncPayloadSmoke"
|
||||
}
|
||||
|
||||
tasks.register("inventorySortSmoke", JavaExec) {
|
||||
group = "verification"
|
||||
description = "Checks that storage stacks merge and sort while the hotbar row stays out of the sort."
|
||||
@@ -183,7 +191,7 @@ tasks.register("verifyBlackMarket") {
|
||||
def questData = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/QuestBoardData.java").text
|
||||
def quests = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/QuestBoardService.java").text
|
||||
def inventoryView = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/api/inventory/SanctuaryPlayerInventoryView.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| !data.contains('SAVE_KEY = "black_market"') || !data.contains("DATA_VERSION = 2")
|
||||
|| !data.contains("ItemStack.CODEC.fieldOf(\"item\")") || !data.contains("replaceListing")
|
||||
|| !service.contains("LISTING_FEE_RUBIES = 1") || !service.contains("COMMISSION_PERCENT = 5")
|
||||
@@ -197,7 +205,7 @@ tasks.register("verifyBlackMarket") {
|
||||
|| !minecraftMixin.contains('method = "pickBlockOrEntity"')
|
||||
|| !middleClickPayload.contains('SanctuaryMod.id("c2s/black_market_interact")')
|
||||
|| !middleClickPayload.contains("No client position, listing ID, item or price is trusted")
|
||||
|| !network.contains("PROTOCOL_VERSION = 23")
|
||||
|| !network.contains("PROTOCOL_VERSION = 24")
|
||||
|| !network.contains("BlackMarketService.middleClick(context.player())")
|
||||
|| !service.contains("loadedDeliveryBox") || !service.contains("PARCEL_RETENTION_MILLIS")
|
||||
|| !policy.contains("DataComponents.CONTAINER") || !policy.contains("DataComponents.BUNDLE_CONTENTS")
|
||||
@@ -1193,12 +1201,12 @@ tasks.register("verifySanctuary") {
|
||||
|
||||
def manifest = new JsonSlurper().parse(file("src/main/resources/fabric.mod.json"))
|
||||
if (manifest.custom?.sanctuary26?.data_version != 13
|
||||
|| manifest.custom?.sanctuary26?.network_protocol != 23
|
||||
|| manifest.custom?.sanctuary26?.network_protocol != 24
|
||||
|| manifest.depends?.jei != ">=30.24.0.176"
|
||||
|| manifest.depends?."fabric-api" != ">=0.155.0+26.2"
|
||||
|| !manifest.entrypoints?.client?.contains("fr.koka99cab.sanctuary26.sanctuary.client.SanctuaryClient")
|
||||
|| !manifest.mixins?.contains("sanctuary.mixins.json")) {
|
||||
throw new GradleException("Sanctuary must expose data v13, protocol 22, JEI and the client entrypoint")
|
||||
throw new GradleException("Sanctuary must expose data v13, protocol 24, JEI and the client entrypoint")
|
||||
}
|
||||
def capeMigration = new JsonSlurper().parse(rootProject.file(
|
||||
"pack/migrations/26.2.0-alpha.155-cape-equipment-to-alpha.156.json"))
|
||||
@@ -2899,7 +2907,7 @@ tasks.register("verifyShopCurrencyRowsRelease") {
|
||||
"src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/BlackMarketService.java").text
|
||||
def shop = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopService.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.117"
|
||||
|| migration.shop_currency_inventory?.all_unlocked_rows_counted != true
|
||||
|| migration.shop_currency_inventory?.overflow_rows_counted != true
|
||||
@@ -3065,7 +3073,7 @@ tasks.register("verifyProgressivePauseMenu") {
|
||||
"pack/migrations/26.2.0-alpha.190-progressive-pause-menu-to-alpha.191.json"))
|
||||
def pauseMenu = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/client/PauseScreenMixin.java").text
|
||||
def costs = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/progression/SanctuaryProgressionCosts.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.target?.pack_version != "26.2.0-alpha.191"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.109"
|
||||
|| migration.pause_menu?.progression_full_width != true
|
||||
@@ -3103,7 +3111,7 @@ tasks.register("verifyShopPricingLayout") {
|
||||
"pack/migrations/26.2.0-alpha.192-shop-pricing-layout-to-alpha.193.json"))
|
||||
def screen = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/client/SanctuaryShopScreen.java").text
|
||||
def progression = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopProgression.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.110"
|
||||
|| migration.shop_layout?.locked_offer_buttons != 6
|
||||
|| migration.shop_layout?.button_slots != [4, 5, 6, 7, 8, 9]
|
||||
@@ -3134,7 +3142,7 @@ tasks.register("verifyShopCsvBossGates") {
|
||||
"pack/migrations/26.2.0-alpha.194-shop-csv-boss-gates-to-alpha.195.json"))
|
||||
def provider = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopCatalogProvider.java").text
|
||||
def catalog = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopOfferCatalog.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.target?.pack_version != "26.2.0-alpha.195"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.111"
|
||||
|| migration.compatibility?.sanctuary_data_version != 11
|
||||
@@ -3165,7 +3173,7 @@ tasks.register("verifyDeliveryBoxOrientation") {
|
||||
def blockstate = new JsonSlurper().parse(
|
||||
file("src/main/resources/assets/sanctuary/blockstates/delivery_box.json"))
|
||||
def expectedVariants = ["facing=north", "facing=east", "facing=south", "facing=west"].toSet()
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.112"
|
||||
|| migration.mailbox?.id != "sanctuary:delivery_box"
|
||||
|| migration.mailbox?.horizontal_facing != true
|
||||
@@ -3193,7 +3201,7 @@ tasks.register("verifyInventoryShiftClick") {
|
||||
def mixin = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/AbstractContainerMenuMixin.java").text
|
||||
def screen = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/client/AbstractContainerScreenMixin.java").text
|
||||
def gui = file("INVENTORY_GUI.md").text
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| !transfer.contains("shouldAppendOverflow")
|
||||
|| !transfer.contains("isVanillaStorageRange")
|
||||
|| !transfer.contains("hotbarStart")
|
||||
@@ -3239,7 +3247,7 @@ tasks.register("verifyQuestRequests") {
|
||||
new JsonSlurper().parse(file("src/main/resources/data/sanctuary/loot_table/blocks/${path}.json"))
|
||||
.pools[0].entries[0].name
|
||||
}
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.113"
|
||||
|| migration.quest_requests?.save_key != "quest_requests"
|
||||
|| migration.quest_requests?.data_version != 1
|
||||
@@ -3251,7 +3259,7 @@ tasks.register("verifyQuestRequests") {
|
||||
|| !data.contains('SAVE_KEY = "quest_requests"') || !data.contains("DATA_VERSION = 3")
|
||||
|| !service.contains("PlayerBlockBreakEvents.BEFORE")
|
||||
|| !service.contains("player.blockPosition().distSqr(payload.pos()) > 64.0D")
|
||||
|| !network.contains("PROTOCOL_VERSION = 23")
|
||||
|| !network.contains("PROTOCOL_VERSION = 24")
|
||||
|| !network.contains("QuestRequestSubmitPayload.ID")
|
||||
|| !network.contains("QuestRequestAcceptPayload.ID")
|
||||
|| !registry.contains('SanctuaryMod.id("quest_request_sign")')
|
||||
@@ -3297,7 +3305,7 @@ tasks.register("verifyQuestRequestContracts") {
|
||||
def locales = ["fr_fr", "en_us", "ru_ru"].collectEntries { locale ->
|
||||
[(locale): new JsonSlurper().parse(file("src/main/resources/assets/sanctuary/lang/${locale}.json"))]
|
||||
}
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.source?.pack_version != "26.2.0-alpha.198"
|
||||
|| migration.source?.modules?.sanctuary != "0.0.0-alpha.114"
|
||||
|| migration.target?.pack_version != "26.2.0-alpha.199"
|
||||
@@ -3317,7 +3325,7 @@ tasks.register("verifyQuestRequestContracts") {
|
||||
|| !service.contains('REWARD_LOAN = "loan"')
|
||||
|| !service.contains('PLAYER.equals(targetKind)')
|
||||
|| !screen.contains("rewardQuantity") || !screen.contains("killTargetIndex")
|
||||
|| !network.contains("PROTOCOL_VERSION = 23")
|
||||
|| !network.contains("PROTOCOL_VERSION = 24")
|
||||
|| standingModel.textures?.all != "sanctuary:block/pink_quest_request_sign"
|
||||
|| itemModel.textures?.layer0 != "sanctuary:item/pink_quest_request_sign"
|
||||
|| blockTexture == null || blockTexture.width != 32 || blockTexture.height != 32
|
||||
@@ -3366,7 +3374,7 @@ tasks.register("verifyQuestRequestEscrow") {
|
||||
def locales = ["fr_fr", "en_us", "ru_ru"].collectEntries { locale ->
|
||||
[(locale): new JsonSlurper().parse(file("src/main/resources/assets/sanctuary/lang/${locale}.json"))]
|
||||
}
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.source?.pack_version != "26.2.0-alpha.200"
|
||||
|| migration.source?.modules?.sanctuary != "0.0.0-alpha.115"
|
||||
|| migration.target?.pack_version != "26.2.0-alpha.201"
|
||||
@@ -3387,7 +3395,7 @@ tasks.register("verifyQuestRequestEscrow") {
|
||||
|| !service.contains('"quest_request_reward"')
|
||||
|| !service.contains('"quest_request_delivery"')
|
||||
|| service.contains("sanctuary_quest_accept")
|
||||
|| !network.contains("PROTOCOL_VERSION = 23")
|
||||
|| !network.contains("PROTOCOL_VERSION = 24")
|
||||
|| !network.contains("QuestRequestAcceptPayload.ID")
|
||||
|| !network.contains("QuestRequestConfirmPayload.ID")
|
||||
|| !acceptPayload.contains("readUtf(36)")
|
||||
@@ -3411,6 +3419,68 @@ tasks.named("verifySanctuary") {
|
||||
dependsOn tasks.named("verifyInventoryShiftClick")
|
||||
}
|
||||
|
||||
tasks.register("verifyCraftableBannerCape") {
|
||||
group = "verification"
|
||||
description = "Checks the white wool recipe, banner decoration and equipped pattern rendering."
|
||||
inputs.file(rootProject.file(
|
||||
"pack/migrations/26.2.0-alpha.225-craftable-banner-cape-to-alpha.226.json"))
|
||||
inputs.file(file("src/main/resources/data/sanctuary/recipe/white_cape.json"))
|
||||
inputs.file(file("src/main/resources/data/sanctuary/recipe/white_cape_banner.json"))
|
||||
inputs.file(file("src/main/resources/assets/sanctuary/items/white_cape.json"))
|
||||
inputs.files(fileTree("src/main/resources/assets/sanctuary/lang") { include "*.json" })
|
||||
inputs.files(
|
||||
file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/gameplay/SanctuaryCapes.java"),
|
||||
file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/network/CapeSyncPayload.java"),
|
||||
file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/network/SanctuaryNetworking.java"),
|
||||
file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/client/SanctuaryBannerCapeLayer.java"))
|
||||
doLast {
|
||||
def migration = new JsonSlurper().parse(rootProject.file(
|
||||
"pack/migrations/26.2.0-alpha.225-craftable-banner-cape-to-alpha.226.json"))
|
||||
def baseRecipe = new JsonSlurper().parse(file(
|
||||
"src/main/resources/data/sanctuary/recipe/white_cape.json"))
|
||||
def bannerRecipe = new JsonSlurper().parse(file(
|
||||
"src/main/resources/data/sanctuary/recipe/white_cape_banner.json"))
|
||||
def itemDefinition = new JsonSlurper().parse(file(
|
||||
"src/main/resources/assets/sanctuary/items/white_cape.json"))
|
||||
def capes = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/sanctuary/gameplay/SanctuaryCapes.java").text
|
||||
def payload = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/sanctuary/network/CapeSyncPayload.java").text
|
||||
def networking = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/sanctuary/network/SanctuaryNetworking.java").text
|
||||
def renderer = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/sanctuary/client/SanctuaryBannerCapeLayer.java").text
|
||||
def locales = ["fr_fr", "en_us", "ru_ru"].collect {
|
||||
new JsonSlurper().parse(file("src/main/resources/assets/sanctuary/lang/${it}.json"))
|
||||
}
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.130"
|
||||
|| migration.white_cape?.crafting_material_count != 6
|
||||
|| migration.white_cape?.maximum_pattern_layers != 6
|
||||
|| baseRecipe.pattern != ["WW", "WW", "WW"]
|
||||
|| baseRecipe.key?.W != "minecraft:white_wool"
|
||||
|| baseRecipe.result?.id != "sanctuary:white_cape"
|
||||
|| bannerRecipe.type != "minecraft:crafting_special_shielddecoration"
|
||||
|| bannerRecipe.banner != "#minecraft:banners"
|
||||
|| bannerRecipe.target != "sanctuary:white_cape"
|
||||
|| itemDefinition.model?.type != "minecraft:special"
|
||||
|| itemDefinition.model?.model?.type != "minecraft:banner"
|
||||
|| locales.any { !it.containsKey("item.sanctuary.white_cape") }
|
||||
|| !capes.contains('WHITE_CAPE_PATH = "white_cape"')
|
||||
|| !payload.contains("MAX_PATTERN_LAYERS = 6")
|
||||
|| !payload.contains("DataComponents.BASE_COLOR")
|
||||
|| !payload.contains("DataComponents.BANNER_PATTERNS")
|
||||
|| !networking.contains("PROTOCOL_VERSION = 24")
|
||||
|| !renderer.contains("BannerRenderer.submitPatterns")) {
|
||||
throw new GradleException("Sanctuary alpha.130 craftable banner cape is incomplete")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("verifySanctuary") {
|
||||
dependsOn tasks.named("verifyCraftableBannerCape")
|
||||
}
|
||||
|
||||
tasks.register("verifyAlphaIndev") {
|
||||
group = "verification"
|
||||
description = "Checks the alpha.122 finite Indev world, migration and attribution contract."
|
||||
@@ -3438,7 +3508,7 @@ tasks.register("verifyAlphaIndev") {
|
||||
def locales = ["fr_fr", "en_us", "ru_ru"].collect {
|
||||
new JsonSlurper().parse(file("src/main/resources/assets/sanctuary/lang/${it}.json"))
|
||||
}
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
if (project.version.toString() != "0.0.0-alpha.130"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.122"
|
||||
|| migration.alpha_world?.dimension != "sanctuary:alpha"
|
||||
|| migration.alpha_world?.legacy_dimension != "portals:alpha"
|
||||
@@ -3475,6 +3545,7 @@ tasks.named("check") {
|
||||
dependsOn tasks.named("questIdSuggestionsSmoke")
|
||||
dependsOn tasks.named("alphaIndevGeneratorSmoke")
|
||||
dependsOn tasks.named("capeJoinSyncSmoke")
|
||||
dependsOn tasks.named("capeSyncPayloadSmoke")
|
||||
dependsOn tasks.named("inventorySortSmoke")
|
||||
dependsOn tasks.named("entityPolicySmoke")
|
||||
dependsOn tasks.named("verifyAlphaIndev")
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.client;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.SanctuaryMod;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityRenderLayerRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.ModelLayerRegistry;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
import net.minecraft.client.model.geom.builders.CubeDeformation;
|
||||
import net.minecraft.client.model.geom.builders.CubeListBuilder;
|
||||
import net.minecraft.client.model.geom.builders.LayerDefinition;
|
||||
import net.minecraft.client.model.geom.builders.MeshDefinition;
|
||||
import net.minecraft.client.model.geom.builders.PartDefinition;
|
||||
import net.minecraft.client.model.player.PlayerCapeModel;
|
||||
import net.minecraft.client.model.player.PlayerModel;
|
||||
import net.minecraft.client.renderer.SubmitNodeCollector;
|
||||
import net.minecraft.client.renderer.blockentity.BannerRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.entity.RenderLayerParent;
|
||||
import net.minecraft.client.renderer.entity.layers.RenderLayer;
|
||||
import net.minecraft.client.renderer.entity.player.AvatarRenderer;
|
||||
import net.minecraft.client.renderer.entity.state.AvatarRenderState;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.client.resources.model.EquipmentAssetManager;
|
||||
import net.minecraft.client.resources.model.EquipmentClientInfo;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.world.entity.EntityTypes;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.equipment.Equippable;
|
||||
|
||||
/** Renders a banner-decorated cape with vanilla cape physics and banner pattern sprites. */
|
||||
public final class SanctuaryBannerCapeLayer extends RenderLayer<AvatarRenderState, PlayerModel> {
|
||||
private static final ModelLayerLocation MODEL_LAYER =
|
||||
new ModelLayerLocation(SanctuaryMod.id("banner_cape"), "main");
|
||||
private final PlayerCapeModel model;
|
||||
private final EquipmentAssetManager equipmentAssets;
|
||||
private final EntityRendererProvider.Context context;
|
||||
|
||||
private SanctuaryBannerCapeLayer(RenderLayerParent<AvatarRenderState, PlayerModel> parent,
|
||||
EntityRendererProvider.Context context) {
|
||||
super(parent);
|
||||
this.context = context;
|
||||
this.model = new PlayerCapeModel(context.bakeLayer(MODEL_LAYER));
|
||||
this.equipmentAssets = context.getEquipmentAssets();
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
ModelLayerRegistry.registerModelLayer(MODEL_LAYER, SanctuaryBannerCapeLayer::createLayer);
|
||||
LivingEntityRenderLayerRegistrationCallback.EVENT.register((entityType, renderer, helper, context) -> {
|
||||
if (entityType == EntityTypes.PLAYER && renderer instanceof AvatarRenderer<?> avatarRenderer) {
|
||||
register(helper, avatarRenderer, context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static void register(LivingEntityRenderLayerRegistrationCallback.RegistrationHelper helper,
|
||||
AvatarRenderer<?> renderer, EntityRendererProvider.Context context) {
|
||||
RenderLayerParent<AvatarRenderState, PlayerModel> parent =
|
||||
(RenderLayerParent<AvatarRenderState, PlayerModel>) (RenderLayerParent) renderer;
|
||||
helper.register(new SanctuaryBannerCapeLayer(parent, context));
|
||||
}
|
||||
|
||||
private static LayerDefinition createLayer() {
|
||||
MeshDefinition mesh = PlayerModel.createMesh(CubeDeformation.NONE, false);
|
||||
PartDefinition root = mesh.getRoot().clearRecursively();
|
||||
PartDefinition body = root.getChild("body");
|
||||
body.addOrReplaceChild("cape", CubeListBuilder.create().texOffs(0, 0)
|
||||
.addBox(-5.0F, 0.0F, -1.0F, 10.0F, 16.0F, 1.0F,
|
||||
CubeDeformation.NONE, 2.0F, 2.5F),
|
||||
PartPose.offsetAndRotation(0.0F, 0.0F, 2.0F, 0.0F, (float) Math.PI, 0.0F));
|
||||
return LayerDefinition.create(mesh, 64, 64);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submit(PoseStack poseStack, SubmitNodeCollector collector, int packedLight,
|
||||
AvatarRenderState state, float yRot, float xRot) {
|
||||
SanctuaryClientCapes.Appearance appearance =
|
||||
((SanctuaryCapeRenderState) state).sanctuary$capeAppearance();
|
||||
if (state.isInvisible || appearance == null || !appearance.bannerCape()
|
||||
|| hasLayer(state.chestEquipment, EquipmentClientInfo.LayerType.WINGS)) return;
|
||||
|
||||
poseStack.pushPose();
|
||||
if (hasLayer(state.chestEquipment, EquipmentClientInfo.LayerType.HUMANOID)) {
|
||||
poseStack.translate(0.0F, -0.053125F, 0.06875F);
|
||||
}
|
||||
BannerRenderer.submitPatterns(context.getSprites(), poseStack, collector, packedLight,
|
||||
OverlayTexture.NO_OVERLAY, model, state, true, appearance.baseColor(),
|
||||
appearance.patterns(), null);
|
||||
poseStack.popPose();
|
||||
}
|
||||
|
||||
private boolean hasLayer(ItemStack stack, EquipmentClientInfo.LayerType layerType) {
|
||||
Equippable equippable = stack.get(DataComponents.EQUIPPABLE);
|
||||
return equippable != null && equippable.assetId().isPresent()
|
||||
&& !equipmentAssets.get(equippable.assetId().get()).getLayers(layerType).isEmpty();
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.client;
|
||||
|
||||
/** Mixin-backed bridge carrying equipped cape data into the extracted render state. */
|
||||
public interface SanctuaryCapeRenderState {
|
||||
SanctuaryClientCapes.Appearance sanctuary$capeAppearance();
|
||||
|
||||
void sanctuary$setCapeAppearance(SanctuaryClientCapes.Appearance appearance);
|
||||
}
|
||||
@@ -119,6 +119,7 @@ public final class SanctuaryClient implements ClientModInitializer {
|
||||
SanctuaryBulkActionsClient.initialize();
|
||||
SanctuaryHealthPreview.register();
|
||||
CelestialSpyglassSelection.register();
|
||||
SanctuaryBannerCapeLayer.initialize();
|
||||
EntityRendererRegistry.register(SanctuaryRegistries.SEAT, NoopRenderer::new);
|
||||
ClientTickEvents.END_CLIENT_TICK.register(SanctuaryClient::tick);
|
||||
ClientPlayNetworking.registerGlobalReceiver(ProgressionSnapshotPayload.ID, (payload, context) ->
|
||||
|
||||
+14
-6
@@ -6,25 +6,33 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
|
||||
/** Client mirror of server-authoritative equipped cape choices. */
|
||||
public final class SanctuaryClientCapes {
|
||||
private static final Map<UUID, Identifier> TEXTURES = new HashMap<>();
|
||||
private static final Map<UUID, Appearance> APPEARANCES = new HashMap<>();
|
||||
|
||||
private SanctuaryClientCapes() {
|
||||
}
|
||||
|
||||
public static void update(CapeSyncPayload payload) {
|
||||
Identifier texture = SanctuaryCapes.texture(payload.capeItemId());
|
||||
if (texture == null) TEXTURES.remove(payload.playerId());
|
||||
else TEXTURES.put(payload.playerId(), texture);
|
||||
boolean bannerCape = SanctuaryCapes.isBannerCape(payload.capeItemId());
|
||||
if (texture == null && !bannerCape) APPEARANCES.remove(payload.playerId());
|
||||
else APPEARANCES.put(payload.playerId(), new Appearance(
|
||||
texture, bannerCape, payload.baseColor(), payload.patterns()));
|
||||
}
|
||||
|
||||
public static Identifier texture(UUID playerId) {
|
||||
return TEXTURES.get(playerId);
|
||||
public static Appearance appearance(UUID playerId) {
|
||||
return APPEARANCES.get(playerId);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
TEXTURES.clear();
|
||||
APPEARANCES.clear();
|
||||
}
|
||||
|
||||
public record Appearance(Identifier texture, boolean bannerCape, DyeColor baseColor,
|
||||
BannerPatternLayers patterns) {
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@ public final class SanctuaryCapeService {
|
||||
|
||||
private static void send(ServerPlayer receiver, ServerPlayer owner) {
|
||||
if (!ServerPlayNetworking.canSend(receiver, CapeSyncPayload.ID)) return;
|
||||
String capeId = SanctuaryCapes.itemId(SanctuaryInventoryCapacity.equippedCape(owner));
|
||||
ServerPlayNetworking.send(receiver, new CapeSyncPayload(owner.getUUID(), capeId));
|
||||
ServerPlayNetworking.send(receiver, CapeSyncPayload.fromStack(
|
||||
owner.getUUID(), SanctuaryInventoryCapacity.equippedCape(owner)));
|
||||
}
|
||||
}
|
||||
|
||||
+11
-4
@@ -14,8 +14,10 @@ import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/** Registration and immutable visual definitions for Sanctuary's collectible capes. */
|
||||
/** Registration and immutable visual definitions for Sanctuary's equippable capes. */
|
||||
public final class SanctuaryCapes {
|
||||
public static final String WHITE_CAPE_PATH = "white_cape";
|
||||
public static final String WHITE_CAPE_ID = "sanctuary:" + WHITE_CAPE_PATH;
|
||||
public static final List<String> PATHS = List.of(
|
||||
"transgender_cape", "mojang_cape", "founders_cape", "nes_cape", "pride_cape",
|
||||
"pink_cape", "feesh_gang_cape", "gameboy_cape", "blue_cape", "chicken_cape",
|
||||
@@ -28,7 +30,7 @@ public final class SanctuaryCapes {
|
||||
"vanilla_cape", "java_edition_cape", "migration_cape", "creeper_anniversary_cape",
|
||||
"java_twitch_cape", "minecon_enderman_cape", "iron_golem_minecon_cape", "minecon_2013_cape",
|
||||
"crafter_twitchcon_cape", "minecon_pickaxe_cape", "minecon_2011_cape", "mojang_classic_cape",
|
||||
"blue_scroll_cape"
|
||||
"blue_scroll_cape", WHITE_CAPE_PATH
|
||||
);
|
||||
private static final Map<Identifier, Definition> DEFINITIONS = registerDefinitions();
|
||||
private static final ResourceKey<CreativeModeTab> CREATIVE_TAB_KEY = ResourceKey.create(
|
||||
@@ -74,6 +76,10 @@ public final class SanctuaryCapes {
|
||||
return definition == null ? null : definition.texture();
|
||||
}
|
||||
|
||||
public static boolean isBannerCape(String itemId) {
|
||||
return WHITE_CAPE_ID.equals(itemId);
|
||||
}
|
||||
|
||||
public static List<Definition> definitions() {
|
||||
return List.copyOf(DEFINITIONS.values());
|
||||
}
|
||||
@@ -85,8 +91,9 @@ public final class SanctuaryCapes {
|
||||
ResourceKey<Item> key = ResourceKey.create(Registries.ITEM, id);
|
||||
Item item = Registry.register(BuiltInRegistries.ITEM, key,
|
||||
new SanctuaryCapeItem(new Item.Properties().stacksTo(1).setId(key)));
|
||||
definitions.put(id, new Definition(id, item,
|
||||
SanctuaryMod.id("entity/cape/" + path)));
|
||||
Identifier texture = WHITE_CAPE_PATH.equals(path)
|
||||
? null : SanctuaryMod.id("entity/cape/" + path);
|
||||
definitions.put(id, new Definition(id, item, texture));
|
||||
}
|
||||
return Map.copyOf(definitions);
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.mixin.client;
|
||||
|
||||
import fr.koka99cab.sanctuary26.sanctuary.client.SanctuaryCapeRenderState;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.client.SanctuaryClientCapes;
|
||||
import net.minecraft.client.renderer.entity.state.AvatarRenderState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
/** Stores the server-authoritative dynamic cape appearance on each extracted player state. */
|
||||
@Mixin(AvatarRenderState.class)
|
||||
abstract class AvatarRenderStateMixin implements SanctuaryCapeRenderState {
|
||||
@Unique private SanctuaryClientCapes.Appearance sanctuary$capeAppearance;
|
||||
|
||||
@Override
|
||||
public SanctuaryClientCapes.Appearance sanctuary$capeAppearance() {
|
||||
return sanctuary$capeAppearance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sanctuary$setCapeAppearance(SanctuaryClientCapes.Appearance appearance) {
|
||||
sanctuary$capeAppearance = appearance;
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -1,6 +1,7 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.mixin.client;
|
||||
|
||||
import fr.koka99cab.sanctuary26.sanctuary.client.SanctuaryClientCapes;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.client.SanctuaryCapeRenderState;
|
||||
import net.minecraft.client.renderer.entity.player.AvatarRenderer;
|
||||
import net.minecraft.client.renderer.entity.state.AvatarRenderState;
|
||||
import net.minecraft.core.ClientAsset;
|
||||
@@ -21,7 +22,13 @@ abstract class AvatarRendererMixin {
|
||||
)
|
||||
private void sanctuary$applyEquippedCape(Avatar avatar, AvatarRenderState state,
|
||||
float partialTick, CallbackInfo ci) {
|
||||
Identifier texture = SanctuaryClientCapes.texture(avatar.getUUID());
|
||||
SanctuaryClientCapes.Appearance appearance = SanctuaryClientCapes.appearance(avatar.getUUID());
|
||||
((SanctuaryCapeRenderState) state).sanctuary$setCapeAppearance(appearance);
|
||||
if (appearance == null || appearance.bannerCape()) {
|
||||
if (appearance != null) state.showCape = false;
|
||||
return;
|
||||
}
|
||||
Identifier texture = appearance.texture();
|
||||
PlayerSkin skin = state.skin;
|
||||
if (texture == null || skin == null) return;
|
||||
state.skin = PlayerSkin.insecure(skin.body(), new ClientAsset.ResourceTexture(texture),
|
||||
|
||||
+43
-2
@@ -1,13 +1,22 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.network;
|
||||
|
||||
import fr.koka99cab.sanctuary26.sanctuary.SanctuaryMod;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryCapes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
|
||||
/** Announces one player's currently equipped Sanctuary cape to clients. */
|
||||
public record CapeSyncPayload(UUID playerId, String capeItemId) implements CustomPacketPayload {
|
||||
public record CapeSyncPayload(UUID playerId, String capeItemId, DyeColor baseColor,
|
||||
BannerPatternLayers patterns) implements CustomPacketPayload {
|
||||
public static final int MAX_PATTERN_LAYERS = 6;
|
||||
public static final Type<CapeSyncPayload> ID = new Type<>(SanctuaryMod.id("s2c/cape_sync"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, CapeSyncPayload> CODEC =
|
||||
CustomPacketPayload.codec(CapeSyncPayload::write, CapeSyncPayload::new);
|
||||
@@ -15,15 +24,47 @@ public record CapeSyncPayload(UUID playerId, String capeItemId) implements Custo
|
||||
public CapeSyncPayload {
|
||||
if (playerId == null) throw new IllegalArgumentException("Cape player UUID cannot be null");
|
||||
capeItemId = capeItemId == null ? "" : capeItemId;
|
||||
baseColor = baseColor == null ? DyeColor.WHITE : baseColor;
|
||||
patterns = bounded(patterns);
|
||||
}
|
||||
|
||||
private CapeSyncPayload(RegistryFriendlyByteBuf buffer) {
|
||||
this(buffer.readUUID(), buffer.readUtf(128));
|
||||
this(buffer.readUUID(), buffer.readUtf(128), DyeColor.STREAM_CODEC.decode(buffer), readPatterns(buffer));
|
||||
}
|
||||
|
||||
private void write(RegistryFriendlyByteBuf buffer) {
|
||||
buffer.writeUUID(playerId);
|
||||
buffer.writeUtf(capeItemId, 128);
|
||||
DyeColor.STREAM_CODEC.encode(buffer, baseColor);
|
||||
buffer.writeVarInt(patterns.layers().size());
|
||||
patterns.layers().forEach(layer -> BannerPatternLayers.Layer.STREAM_CODEC.encode(buffer, layer));
|
||||
}
|
||||
|
||||
public static CapeSyncPayload fromStack(UUID playerId, ItemStack stack) {
|
||||
String capeItemId = SanctuaryCapes.itemId(stack);
|
||||
DyeColor baseColor = stack.get(DataComponents.BASE_COLOR);
|
||||
BannerPatternLayers patterns = stack.get(DataComponents.BANNER_PATTERNS);
|
||||
return new CapeSyncPayload(playerId, capeItemId, baseColor, patterns);
|
||||
}
|
||||
|
||||
private static BannerPatternLayers readPatterns(RegistryFriendlyByteBuf buffer) {
|
||||
int size = buffer.readVarInt();
|
||||
if (size < 0 || size > MAX_PATTERN_LAYERS) {
|
||||
throw new IllegalArgumentException("Cape banner pattern count must be between 0 and "
|
||||
+ MAX_PATTERN_LAYERS + ": " + size);
|
||||
}
|
||||
List<BannerPatternLayers.Layer> layers = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
layers.add(BannerPatternLayers.Layer.STREAM_CODEC.decode(buffer));
|
||||
}
|
||||
return layers.isEmpty() ? BannerPatternLayers.EMPTY : new BannerPatternLayers(List.copyOf(layers));
|
||||
}
|
||||
|
||||
private static BannerPatternLayers bounded(BannerPatternLayers patterns) {
|
||||
if (patterns == null || patterns.layers().isEmpty()) return BannerPatternLayers.EMPTY;
|
||||
List<BannerPatternLayers.Layer> layers = patterns.layers();
|
||||
if (layers.size() <= MAX_PATTERN_LAYERS) return patterns;
|
||||
return new BannerPatternLayers(List.copyOf(layers.subList(0, MAX_PATTERN_LAYERS)));
|
||||
}
|
||||
|
||||
@Override public Type<? extends CustomPacketPayload> type() { return ID; }
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import net.minecraft.server.level.ServerPlayer;
|
||||
* Networking contract for Sanctuary. Payload IDs must use the module namespace.
|
||||
*/
|
||||
public final class SanctuaryNetworking {
|
||||
public static final int PROTOCOL_VERSION = 23;
|
||||
public static final int PROTOCOL_VERSION = 24;
|
||||
private static boolean initialized;
|
||||
|
||||
private SanctuaryNetworking() {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:special",
|
||||
"base": "minecraft:item/template_banner",
|
||||
"model": {
|
||||
"type": "minecraft:banner",
|
||||
"color": "white"
|
||||
},
|
||||
"transformation": {
|
||||
"left_rotation": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"right_rotation": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"scale": [
|
||||
0.6666667,
|
||||
-0.6666667,
|
||||
-0.6666667
|
||||
],
|
||||
"translation": [
|
||||
0.5,
|
||||
0.0,
|
||||
0.5
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -514,6 +514,7 @@
|
||||
"item.sanctuary.minecon_2011_cape": "Minecon 2011 Cape",
|
||||
"item.sanctuary.mojang_classic_cape": "Mojang Classic Cape",
|
||||
"item.sanctuary.blue_scroll_cape": "Blue Scroll Cape",
|
||||
"item.sanctuary.white_cape": "White Cape",
|
||||
"dimension.sanctuary.alpha": "Alpha / Indev Island",
|
||||
"screen.sanctuary.atlas.landmark.boss": "Defeated boss",
|
||||
"message.sanctuary.notch_defeated": "Notch Titan has been defeated. The victory is now recorded in this world."
|
||||
|
||||
@@ -514,6 +514,7 @@
|
||||
"item.sanctuary.minecon_2011_cape": "Cape Minecon 2011",
|
||||
"item.sanctuary.mojang_classic_cape": "Cape Mojang classique",
|
||||
"item.sanctuary.blue_scroll_cape": "Cape parchemin bleu",
|
||||
"item.sanctuary.white_cape": "Cape blanche",
|
||||
"dimension.sanctuary.alpha": "Île Alpha / Indev",
|
||||
"screen.sanctuary.atlas.landmark.boss": "Boss vaincu",
|
||||
"message.sanctuary.notch_defeated": "Notch Titan a été vaincu. La victoire est désormais inscrite dans ce monde."
|
||||
|
||||
@@ -470,6 +470,7 @@
|
||||
"item.sanctuary.minecon_2011_cape": "Плащ Minecon 2011",
|
||||
"item.sanctuary.mojang_classic_cape": "Классический плащ Mojang",
|
||||
"item.sanctuary.blue_scroll_cape": "Плащ синего свитка",
|
||||
"item.sanctuary.white_cape": "Белый плащ",
|
||||
"dimension.sanctuary.alpha": "Остров Альфа / Indev",
|
||||
"screen.sanctuary.atlas.landmark.boss": "Побеждённый босс",
|
||||
"message.sanctuary.notch_defeated": "Нотч-Титан побеждён. Победа навсегда записана в этом мире."
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "equipment",
|
||||
"key": {
|
||||
"W": "minecraft:white_wool"
|
||||
},
|
||||
"pattern": [
|
||||
"WW",
|
||||
"WW",
|
||||
"WW"
|
||||
],
|
||||
"result": {
|
||||
"count": 1,
|
||||
"id": "sanctuary:white_cape"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "minecraft:crafting_special_shielddecoration",
|
||||
"banner": "#minecraft:banners",
|
||||
"result": {
|
||||
"id": "sanctuary:white_cape"
|
||||
},
|
||||
"target": "sanctuary:white_cape"
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
"system_type": "gameplay-system",
|
||||
"namespace": "sanctuary",
|
||||
"data_version": 13,
|
||||
"network_protocol": 23,
|
||||
"network_protocol": 24,
|
||||
"lifecycle": "${lifecycle}",
|
||||
"pack_version": "${pack_version}",
|
||||
"descriptions": {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
],
|
||||
"client": [
|
||||
"client.AbstractButtonMixin",
|
||||
"client.AvatarRenderStateMixin",
|
||||
"client.AvatarRendererMixin",
|
||||
"client.AbstractContainerScreenAccessor",
|
||||
"client.AbstractContainerScreenMixin",
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.network;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.level.block.entity.BannerPattern;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
|
||||
/** Executable coverage for the bounded banner appearance carried by cape synchronization. */
|
||||
public final class CapeSyncPayloadSmoke {
|
||||
private CapeSyncPayloadSmoke() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<BannerPatternLayers.Layer> layers = new ArrayList<>();
|
||||
Holder<BannerPattern> pattern = Holder.direct(new BannerPattern(
|
||||
Identifier.withDefaultNamespace("base"), "block.minecraft.banner.base.white"));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
layers.add(new BannerPatternLayers.Layer(pattern, DyeColor.BLUE));
|
||||
}
|
||||
|
||||
CapeSyncPayload payload = new CapeSyncPayload(UUID.fromString(
|
||||
"12345678-1234-5678-9abc-def012345678"), "sanctuary:white_cape", DyeColor.RED,
|
||||
new BannerPatternLayers(List.copyOf(layers)));
|
||||
require(payload.baseColor() == DyeColor.RED, "Banner base color was not preserved");
|
||||
require(payload.patterns().layers().size() == CapeSyncPayload.MAX_PATTERN_LAYERS,
|
||||
"Cape synchronization did not cap banner patterns at six");
|
||||
|
||||
CapeSyncPayload blank = new CapeSyncPayload(UUID.randomUUID(), "", null, null);
|
||||
require(blank.baseColor() == DyeColor.WHITE, "Blank cape payload did not default to white");
|
||||
require(blank.patterns().layers().isEmpty(), "Blank cape payload carried patterns");
|
||||
}
|
||||
|
||||
private static void require(boolean condition, String message) {
|
||||
if (!condition) throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user