From a72bd7d590ec9e0ebd67b32619c08b2864ac8ac0 Mon Sep 17 00:00:00 2001 From: craisinlord Date: Fri, 18 Sep 2026 00:57:10 -0500 Subject: [PATCH] infinite dimension fallback dims seperated computer to different mod --- .../caches/9.4.1/fileContent/fileContent.lock | Bin 17 -> 0 bytes .../caches/9.4.1/fileHashes/fileHashes.bin | Bin 85215 -> 0 bytes .../caches/9.4.1/fileHashes/fileHashes.lock | Bin 17 -> 0 bytes .../9.4.1/fileHashes/resourceHashesCache.bin | Bin 22475 -> 0 bytes .gradle-codex/caches/9.4.1/gc.properties | 0 .../9.4.1/transforms/.internal/gc.properties | 0 .../gradle-9.4.1-bin.zip.lck | 0 .../gradle-9.4.1-bin.zip.part | 0 .../antarchy/config/AntarchySettings.java | 21 +- .../antarchy/content/AntarchyObjects.java | 32 +- .../antarchy/content/AntarchySoundEvents.java | 9 + .../content/antmail/AntmailAddress.java | 76 - .../content/antmail/AntmailAttachment.java | 102 - .../antmail/AntmailAttachmentFiles.java | 93 - .../content/antmail/AntmailDebug.java | 19 - .../antmail/AntmailDeliveryResult.java | 28 - .../content/antmail/AntmailDraft.java | 43 - .../content/antmail/AntmailEventData.java | 102 - .../content/antmail/AntmailMailbox.java | 126 - .../content/antmail/AntmailMailboxStore.java | 54 - .../content/antmail/AntmailMessage.java | 93 - .../content/antmail/AntmailServerData.java | 339 -- .../content/antmail/AntmailValidation.java | 67 - .../antarchy/content/antmail/AntmailWire.java | 72 - .../antarchy/content/block/AntTrapBlock.java | 52 + .../antarchy/content/block/ComputerBlock.java | 147 - .../content/block/UndertrialSpawnerBlock.java | 21 + .../block/entity/AntNestBlockEntity.java | 70 +- .../block/entity/ComputerBlockEntity.java | 458 --- .../block/entity/UnderVaultBlockEntity.java | 6 +- .../entity/UndertrialSpawnerBlockEntity.java | 13 +- .../content/client/AntarchyClientHooks.java | 21 - .../client/AntarchyGameClientState.java | 32 + .../content/client/AntmailClientState.java | 81 - .../content/client/BlockleClientState.java | 8 +- .../client/ComputerAccessClientState.java | 39 - .../client/ComputerFileSystemClientState.java | 94 - .../ComputerStructureLocatorClientState.java | 43 - .../client/game/AntarchyComputerGames.java | 79 + .../content/client/game/BlockleProgram.java | 10 +- .../content/client/model/ComputerModel.java | 27 - .../client/particle/BugSprayParticle.java | 52 + .../client/renderer/ComputerRenderer.java | 17 - .../RoyalAssailantAxeAfterimageLayer.java | 86 - .../renderer/RoyalIndicatorRenderer.java | 19 +- .../content/client/screen/ComputerScreen.java | 3029 ----------------- .../computer/AntarchyGameSavedData.java | 75 + .../computer/ComputerDesktopState.java | 197 -- .../content/computer/ComputerFileSystem.java | 220 -- .../computer/paint/AntPaintCanvas.java | 114 - .../content/computer/paint/AntPaintFile.java | 26 - .../computer/paint/AntPaintFileCodec.java | 86 - .../computer/paint/AntPaintFilename.java | 38 - .../computer/paint/AntPaintHistory.java | 47 - .../computer/paint/AntPaintRequests.java | 35 - .../computer/paint/AntPaintResults.java | 40 - .../content/computer/paint/AntPaintTool.java | 7 - .../computer/terminal/TerminalCommand.java | 11 - .../terminal/TerminalCommandParser.java | 79 - .../terminal/TerminalCommandService.java | 207 -- .../computer/terminal/TerminalFileSystem.java | 39 - .../computer/terminal/TerminalResult.java | 33 - .../content/creative/CreativeTabContents.java | 8 +- .../content/effect/InvertedMobEffect.java | 9 + .../content/entity/HoverboardEntity.java | 18 + .../content/entity/ant/TermiteEntity.java | 58 +- .../content/entity/royal/KingEntity.java | 40 +- .../content/entity/royal/QueenEntity.java | 61 +- .../content/entity/royal/RoyalBossEntity.java | 8 +- .../royal/RoyalElementalProjectileEntity.java | 39 +- .../entity/royal/RoyalMountEntity.java | 69 +- .../royal/decree/StandYourGroundDecree.java | 7 +- .../trades/ComputerScientistTradeManager.java | 6 +- .../trades/PestControlTradeManager.java | 76 + .../content/guide/ComputerGuideData.java | 290 -- .../antarchy/content/item/BigBerthaItem.java | 7 +- .../antarchy/content/item/BugSprayItem.java | 60 + .../antarchy/content/item/ComputerItem.java | 10 - .../antarchy/content/item/FloppyDiskItem.java | 53 - .../item/RoyalGuardianSwordAbilities.java | 22 +- .../content/item/RoyalGuardianSwordItem.java | 7 +- .../network/AntarchyGameNetworkHandler.java | 121 + .../network/AntarchyGameNetworking.java | 20 + .../content/network/AntarchyGamePayload.java | 32 + .../network/AntarchyGameResultPayload.java | 24 + .../content/network/AntmailDeletePayload.java | 15 - .../content/network/AntmailDraftPayload.java | 17 - .../network/AntmailMessageRequestPayload.java | 23 - .../content/network/AntmailNetworking.java | 83 - .../content/network/AntmailPayloadTypes.java | 24 - .../content/network/AntmailReadPayload.java | 15 - .../content/network/AntmailResultPayload.java | 33 - .../content/network/AntmailRetryPayload.java | 15 - .../content/network/AntmailSendPayload.java | 15 - .../content/network/AntmailServerHandler.java | 266 -- .../content/network/AntmailSetupPayload.java | 15 - .../network/AntmailStateRequestPayload.java | 23 - .../network/ComputerAccessHandler.java | 455 --- .../network/ComputerAccessPayload.java | 50 - .../network/ComputerAccessResultPayload.java | 36 - .../content/network/ComputerNetworking.java | 110 - .../RoyalGuardianSwordModeCyclePayload.java | 19 + .../content/time/TimeDilationFieldEntity.java | 13 +- .../worldgen/VillagePoolAdditions.java | 5 +- .../elythia/KingsTreeGridFeature.java | 5 +- .../mixins/PlayerAdvancementsMixin.java | 22 - .../antarchy/mixins/PoiTypesMixin.java | 7 +- ...emporalContractionLivingRendererMixin.java | 7 +- .../AlexsCavesCandyCaneHookMixin.java | 2 + .../AlexsCavesDepthChargeMixin.java | 2 + .../alexscaves/AlexsCavesTephraMixin.java | 2 + .../alexscaves/AlexsCavesWaterBoltMixin.java | 2 + .../ArsNouveauProjectileGravityMixin.java | 2 + ...IronsSpellbooksProjectileGravityMixin.java | 2 + .../scguns/ScgunsArrowGravityMixin.java | 2 + .../ScgunsProjectileEntityGravityMixin.java | 2 + .../time/EntityMoveTimeDilationMixin.java | 4 + .../resources/antarchy-common.mixins.json | 1 - .../animations/computer.animation.json | 43 - .../assets/antarchy/blockstates/ant_trap.json | 1 + .../assets/antarchy/blockstates/computer.json | 9 - .../assets/antarchy/geo/computer.geo.json | 189 - .../resources/assets/antarchy/lang/de_de.json | 5 +- .../resources/assets/antarchy/lang/en_us.json | 40 +- .../antarchy/models/block/ant_trap.json | 77 + .../antarchy/models/block/computer.json | 6 - .../antarchy/models/block/verdant_nyxium.json | 2 +- .../assets/antarchy/models/item/ant_trap.json | 1 + .../antarchy/models/item/bug_spray.json | 1 + .../models/item/bug_spray_spraying.json | 1 + .../assets/antarchy/models/item/computer.json | 232 -- .../antarchy/models/item/floppy_disk.json | 20 - .../item/floppy_disk/cavaryn_floppy_disk.json | 6 - .../item/floppy_disk/elythia_floppy_disk.json | 6 - .../floppy_disk/thoraxis_floppy_disk.json | 6 - .../assets/antarchy/particles/bug_spray.json | 1 + .../resources/assets/antarchy/sounds.json | 21 + .../antarchy/sounds/hoverboard/dismount1.ogg | Bin 0 -> 31715 bytes .../antarchy/sounds/hoverboard/dismount2.ogg | Bin 0 -> 32526 bytes .../antarchy/sounds/hoverboard/dismount3.ogg | Bin 0 -> 30728 bytes .../antarchy/sounds/hoverboard/idle1.ogg | Bin 0 -> 40925 bytes .../antarchy/sounds/hoverboard/mount1.ogg | Bin 0 -> 28126 bytes .../antarchy/sounds/hoverboard/mount2.ogg | Bin 0 -> 29869 bytes .../antarchy/sounds/hoverboard/mount3.ogg | Bin 0 -> 28283 bytes .../antarchy/sounds/hoverboard/mount4.ogg | Bin 0 -> 28931 bytes .../antarchy/textures/block/ant_trap.png | Bin 0 -> 1011 bytes .../block/computer/computer.animation.json | 158 - .../textures/block/computer/computer.png | Bin 10764 -> 0 bytes .../block/computer/computer.png.mcmeta | 5 - .../villager/profession/pest_control.png | Bin 0 -> 613 bytes .../profession/pest_control.png.mcmeta | 5 + .../textures/gui/antpaint/ATTRIBUTION.txt | 7 - .../gui/antpaint/LICENSE-pixelarticons.txt | 21 - .../antarchy/textures/gui/antpaint/eraser.png | Bin 233 -> 0 bytes .../antarchy/textures/gui/antpaint/eraser.svg | 1 - .../antarchy/textures/gui/antpaint/pencil.png | Bin 252 -> 0 bytes .../antarchy/textures/gui/antpaint/pencil.svg | 1 - .../antarchy/textures/gui/antpaint/redo.png | Bin 218 -> 0 bytes .../antarchy/textures/gui/antpaint/redo.svg | 4 - .../antarchy/textures/gui/antpaint/trash.png | Bin 209 -> 0 bytes .../antarchy/textures/gui/antpaint/trash.svg | 3 - .../antarchy/textures/gui/antpaint/undo.png | Bin 218 -> 0 bytes .../antarchy/textures/gui/antpaint/undo.svg | 3 - .../antarchy/textures/item/bug_spray.png | Bin 0 -> 335 bytes .../textures/item/bug_spray_spraying.png | Bin 0 -> 348 bytes .../antarchy/textures/item/computer.png | Bin 2156 -> 0 bytes .../bug_spray/bug_spray_particle1.png | Bin 0 -> 683 bytes .../bug_spray/bug_spray_particle2.png | Bin 0 -> 623 bytes .../bug_spray/bug_spray_particle3.png | Bin 0 -> 586 bytes .../bug_spray/bug_spray_particle4.png | Bin 0 -> 588 bytes .../advancement/down_with_the_king.json | 34 + .../advancement/down_with_the_queen.json | 34 + .../antarchy/computer/category/cavaryn.json | 4 + .../antarchy/computer/category/elythia.json | 4 + .../antarchy/computer/category/general.json | 4 + .../antarchy/computer/category/thoraxis.json | 4 + .../antarchy/computer/disk/antman_game.json | 1 + .../antarchy/computer/disk/basilisk_game.json | 2 +- .../antarchy/computer/disk/blockle_game.json | 1 + .../antarchy/computer/disk/bloodglass.json | 5 +- .../data/antarchy/computer/disk/cavaryn.json | 5 +- .../antarchy/computer/disk/chronosphere.json | 2 +- .../data/antarchy/computer/disk/elythia.json | 5 +- .../computer/disk/flying_squirrel.json | 2 +- .../disk/high_fructose_corn_syrup.json | 2 +- .../data/antarchy/computer/disk/king.json | 4 +- .../data/antarchy/computer/disk/kraken.json | 2 +- .../data/antarchy/computer/disk/queen.json | 4 +- .../computer/disk/temporal_fields.json | 5 +- .../data/antarchy/computer/disk/thoraxis.json | 5 +- .../computer/disk/toreterror_arena.json | 2 +- .../data/antarchy/computer/disk/vortex.json | 5 +- .../antarchy/computer/entry/introduction.json | 2 +- .../data/antarchy/computer/entry/prince.json | 9 + .../antarchy/computer/entry/princess.json | 9 + .../computer_scientist_trades/trades.json | 10 +- .../chests/basilisk_lair/treasure.json | 4 +- .../loot_table/chests/computer_prototype.json | 20 +- .../chests/underside_ruins/generic.json | 8 +- .../antarchy/pest_control_trades/trades.json | 1 + .../data/antarchy/recipe/ant_trap.json | 1 + .../data/antarchy/recipe/computer.json | 2 +- .../village/computer_scientist_basement.nbt | Bin 1451 -> 1430 bytes .../antarchy/tags/entity_type/arthropods.json | 1 + .../has_structure/trayaurus_lab_biomes.json | 2 +- .../placed_feature/dimensional_tears.json | 8 +- .../placed_feature/fallen_ouranwood_tree.json | 18 +- .../placed_feature/fungal_mushroom.json | 18 +- .../placed_feature/giant_lily_pad_patch.json | 18 +- .../giant_lily_pad_patch_common.json | 18 +- .../giant_lily_pad_patch_meadow.json | 18 +- .../giant_lily_pad_patch_peach_forest.json | 18 +- .../lucid_blood_crystal_spires.json | 2 - .../lucid_large_blood_crystal_spires.json | 2 - .../lucid_medium_blood_crystal_spires.json | 2 - .../worldgen/placed_feature/ore_magma.json | 12 +- .../placed_feature/ouranwood_trees.json | 18 +- .../ouranwood_trees_sparse.json | 18 +- .../thoraxis_floor_rib_columns.json | 4 +- .../placed_feature/thoraxis_floor_spikes.json | 4 +- .../placed_feature/thoraxis_ichor_lake.json | 8 +- .../placed_feature/typhonite_veined_ore.json | 8 +- .../acquirable_job_site.json | 7 +- .../biome/has_structure/bastion_remnant.json | 4 + .../biome/has_structure/desert_pyramid.json | 4 + .../biome/has_structure/end_city.json | 4 + .../worldgen/noise/nether/temperature.json | 7 - .../worldgen/noise/nether/vegetation.json | 7 - .../antarchy/computer/fixtures/README.md | 5 - .../built_in_introduction_protection.json | 10 - .../computer/fixtures/duplicate_disk.json | 8 - .../computer/fixtures/ejection_and_drop.json | 7 - .../computer/fixtures/newly_placed.json | 8 - fabric-1.21.1/build.gradle | 2 + .../antarchy/fabric/AntarchyFabric.java | 42 +- .../fabric/AntarchyFabricContent.java | 10 +- .../antarchy/fabric/AntarchyFabricEvents.java | 11 +- .../fabric/client/AntarchyFabricClient.java | 14 +- .../client/AntarchyFabricClientBootstrap.java | 12 +- .../fabric/client/BigBerthaClientHandler.java | 11 +- .../AntarchyFabricClientNetworking.java | 9 +- .../network/AntarchyFabricNetworking.java | 50 +- .../fabric/registry/AntarchyFabricBlocks.java | 20 +- .../fabric/registry/AntarchyFabricItems.java | 8 +- .../fabric/registry/AntarchyFabricMisc.java | 18 +- .../fabric/registry/AntarchyFabricSounds.java | 11 + .../src/main/resources/fabric.mod.json | 1 + neoforge-1.21.1/build.gradle | 3 + .../antarchy/neoforge/AntarchyMobsConfig.java | 4 +- .../antarchy/neoforge/AntarchyNeoforge.java | 14 +- .../neoforge/AntarchyNeoforgeEvents.java | 2 + .../neoforge/AntarchyToolsConfig.java | 8 +- .../client/AntarchyNeoForgeClient.java | 23 +- .../client/BigBerthaClientHandler.java | 11 +- .../network/AntarchyGravityNetworking.java | 17 +- .../registry/AntarchyNeoForgeEvents.java | 13 +- .../registry/AntarchyNeoforgeBlocks.java | 20 +- .../registry/AntarchyNeoforgeItems.java | 8 +- .../registry/AntarchyNeoforgeMisc.java | 17 +- .../AntarchyNeoforgePayloadHandlers.java | 63 +- .../registry/AntarchyNeoforgeSounds.java | 3 + .../resources/META-INF/neoforge.mods.toml | 9 +- .../biome_modifier/octopus_bomb_spawns.json | 17 - 263 files changed, 1781 insertions(+), 9288 deletions(-) delete mode 100644 .gradle-codex/caches/9.4.1/fileContent/fileContent.lock delete mode 100644 .gradle-codex/caches/9.4.1/fileHashes/fileHashes.bin delete mode 100644 .gradle-codex/caches/9.4.1/fileHashes/fileHashes.lock delete mode 100644 .gradle-codex/caches/9.4.1/fileHashes/resourceHashesCache.bin delete mode 100644 .gradle-codex/caches/9.4.1/gc.properties delete mode 100644 .gradle-codex/caches/9.4.1/transforms/.internal/gc.properties delete mode 100644 .gradle-codex/wrapper/dists/gradle-9.4.1-bin/arn2x92ynaizyzdaamcbpbhtj/gradle-9.4.1-bin.zip.lck delete mode 100644 .gradle-codex/wrapper/dists/gradle-9.4.1-bin/arn2x92ynaizyzdaamcbpbhtj/gradle-9.4.1-bin.zip.part delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAddress.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAttachment.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAttachmentFiles.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDebug.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDeliveryResult.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDraft.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailEventData.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMailbox.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMailboxStore.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMessage.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailServerData.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailValidation.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailWire.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/AntTrapBlock.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/ComputerBlock.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/ComputerBlockEntity.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntarchyClientHooks.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntarchyGameClientState.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntmailClientState.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerAccessClientState.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerFileSystemClientState.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerStructureLocatorClientState.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/AntarchyComputerGames.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/ComputerModel.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/particle/BugSprayParticle.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/ComputerRenderer.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalAssailantAxeAfterimageLayer.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/screen/ComputerScreen.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/AntarchyGameSavedData.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerDesktopState.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerFileSystem.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintCanvas.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFile.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFileCodec.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFilename.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintHistory.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintRequests.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintResults.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintTool.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommand.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommandParser.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommandService.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalFileSystem.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalResult.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/trades/PestControlTradeManager.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/guide/ComputerGuideData.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BugSprayItem.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/ComputerItem.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/FloppyDiskItem.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameNetworkHandler.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameNetworking.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGamePayload.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameResultPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailDeletePayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailDraftPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailMessageRequestPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailNetworking.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailPayloadTypes.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailReadPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailResultPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailRetryPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailSendPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailServerHandler.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailSetupPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailStateRequestPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessHandler.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessResultPayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerNetworking.java create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/RoyalGuardianSwordModeCyclePayload.java delete mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PlayerAdvancementsMixin.java delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/animations/computer.animation.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/blockstates/ant_trap.json delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/blockstates/computer.json delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/geo/computer.geo.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/block/ant_trap.json delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/block/computer.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/item/ant_trap.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/item/bug_spray.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/item/bug_spray_spraying.json delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/item/computer.json delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk.json delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/cavaryn_floppy_disk.json delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/elythia_floppy_disk.json delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/thoraxis_floppy_disk.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/particles/bug_spray.json create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/dismount1.ogg create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/dismount2.ogg create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/dismount3.ogg create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/idle1.ogg create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/mount1.ogg create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/mount2.ogg create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/mount3.ogg create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/mount4.ogg create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/block/ant_trap.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.animation.json delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.png.mcmeta create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/villager/profession/pest_control.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/villager/profession/pest_control.png.mcmeta delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/ATTRIBUTION.txt delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/LICENSE-pixelarticons.txt delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/eraser.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/eraser.svg delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/pencil.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/pencil.svg delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/redo.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/redo.svg delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/trash.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/trash.svg delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/undo.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/undo.svg create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/item/bug_spray.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/item/bug_spray_spraying.png delete mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/item/computer.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/particle/bug_spray/bug_spray_particle1.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/particle/bug_spray/bug_spray_particle2.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/particle/bug_spray/bug_spray_particle3.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/particle/bug_spray/bug_spray_particle4.png create mode 100644 common-1.21.1/src/main/resources/data/antarchy/advancement/down_with_the_king.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/advancement/down_with_the_queen.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/computer/category/cavaryn.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/computer/category/elythia.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/computer/category/general.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/computer/category/thoraxis.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/computer/entry/prince.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/computer/entry/princess.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/pest_control_trades/trades.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/recipe/ant_trap.json create mode 100644 common-1.21.1/src/main/resources/data/antarchy/tags/entity_type/arthropods.json create mode 100644 common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/bastion_remnant.json create mode 100644 common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/desert_pyramid.json create mode 100644 common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/end_city.json delete mode 100644 common-1.21.1/src/main/resources/data/minecraft/worldgen/noise/nether/temperature.json delete mode 100644 common-1.21.1/src/main/resources/data/minecraft/worldgen/noise/nether/vegetation.json delete mode 100644 common-1.21.1/src/test/resources/antarchy/computer/fixtures/README.md delete mode 100644 common-1.21.1/src/test/resources/antarchy/computer/fixtures/built_in_introduction_protection.json delete mode 100644 common-1.21.1/src/test/resources/antarchy/computer/fixtures/duplicate_disk.json delete mode 100644 common-1.21.1/src/test/resources/antarchy/computer/fixtures/ejection_and_drop.json delete mode 100644 common-1.21.1/src/test/resources/antarchy/computer/fixtures/newly_placed.json delete mode 100644 neoforge-1.21.1/src/main/resources/data/antarchy/neoforge/biome_modifier/octopus_bomb_spawns.json diff --git a/.gradle-codex/caches/9.4.1/fileContent/fileContent.lock b/.gradle-codex/caches/9.4.1/fileContent/fileContent.lock deleted file mode 100644 index 5179b48089af9c487a5adecc808ef5e5dfa6c6ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 TcmZQx!0dD6{T{wF1}FdkFZTo1 diff --git a/.gradle-codex/caches/9.4.1/fileHashes/fileHashes.bin b/.gradle-codex/caches/9.4.1/fileHashes/fileHashes.bin deleted file mode 100644 index 21dae325630937c7e1dddb027b06cda9b436140c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85215 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 diff --git a/.gradle-codex/caches/9.4.1/fileHashes/fileHashes.lock b/.gradle-codex/caches/9.4.1/fileHashes/fileHashes.lock deleted file mode 100644 index 7feee32230d49859b75221ae56d920cd9bd946c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 VcmZQxBymhrTZ`*00~j!$1^^}z1IGXW diff --git a/.gradle-codex/caches/9.4.1/fileHashes/resourceHashesCache.bin b/.gradle-codex/caches/9.4.1/fileHashes/resourceHashesCache.bin deleted file mode 100644 index 22063717572e5cd35ee86883e2338339e2dd8fec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22475 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 GLIMMER_VARIANT_COMPONENT = (Supplier>) UNBOUND; @SuppressWarnings("unchecked") - public static Supplier> FLOPPY_DISK_COMPONENT = - (Supplier>) UNBOUND; - @SuppressWarnings("unchecked") - public static Supplier FLOPPY_DISK = (Supplier) UNBOUND; - @SuppressWarnings("unchecked") - public static Supplier COMPUTER = (Supplier) UNBOUND; - @SuppressWarnings("unchecked") - public static Supplier> COMPUTER_BLOCK_ENTITY = - (Supplier>) UNBOUND; - @SuppressWarnings("unchecked") public static Supplier> DORRIE_INVENTORY_MENU = (Supplier>) UNBOUND; @SuppressWarnings("unchecked") public static Supplier> ROYAL_JUDGMENT_MENU = (Supplier>) UNBOUND; @@ -403,6 +393,8 @@ public final class AntarchyObjects { @SuppressWarnings("unchecked") public static Supplier STINKY_GAS = (Supplier) UNBOUND; @SuppressWarnings("unchecked") + public static Supplier BUG_SPRAY_PARTICLE = (Supplier) UNBOUND; + @SuppressWarnings("unchecked") public static Supplier NIGHTMARE_FIRE_FLAME = (Supplier) UNBOUND; @SuppressWarnings("unchecked") public static Supplier STINKY_FLY = (Supplier) UNBOUND; @@ -650,6 +642,10 @@ public final class AntarchyObjects { OCTOPUS_BOMB = supplier; } + public static void setBugSprayParticle(Supplier supplier) { + BUG_SPRAY_PARTICLE = supplier; + } + public static void setTentacle(Supplier> supplier) { TENTACLE = supplier; } @@ -912,22 +908,6 @@ public final class AntarchyObjects { GLIMMER_VARIANT_COMPONENT = supplier; } - public static void setFloppyDiskComponent(Supplier> supplier) { - FLOPPY_DISK_COMPONENT = supplier; - } - - public static void setFloppyDisk(Supplier supplier) { - FLOPPY_DISK = supplier; - } - - public static void setComputer(Supplier supplier) { - COMPUTER = supplier; - } - - public static void setComputerBlockEntity(Supplier> supplier) { - COMPUTER_BLOCK_ENTITY = supplier; - } - public static void setDorrieInventoryMenu(Supplier> supplier) { DORRIE_INVENTORY_MENU = supplier; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchySoundEvents.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchySoundEvents.java index 2a90521c..3dae94cd 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchySoundEvents.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchySoundEvents.java @@ -221,6 +221,9 @@ public final class AntarchySoundEvents { public static Supplier KING_DECREE = (Supplier) UNBOUND; public static Supplier KING_SUCCESS = (Supplier) UNBOUND; public static Supplier KING_JUDGEMENT = (Supplier) UNBOUND; + public static Supplier HOVERBOARD_MOUNT = (Supplier) UNBOUND; + public static Supplier HOVERBOARD_DISMOUNT = (Supplier) UNBOUND; + public static Supplier HOVERBOARD_IDLE = (Supplier) UNBOUND; private AntarchySoundEvents() { } @@ -656,4 +659,10 @@ public final class AntarchySoundEvents { DIMENSIONAL_TEAR_CREATURE_LEAVES = dimensionalTearCreatureLeaves; DIMENSIONAL_TEAR_CLOSE = dimensionalTearClose; } + + public static void bindHoverboardSounds(Supplier mount, Supplier dismount, Supplier idle) { + HOVERBOARD_MOUNT = mount; + HOVERBOARD_DISMOUNT = dismount; + HOVERBOARD_IDLE = idle; + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAddress.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAddress.java deleted file mode 100644 index 26d6a820..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAddress.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import java.util.Locale; -import java.util.Objects; -import java.util.regex.Pattern; - -public record AntmailAddress(String username) { - public static final String DOMAIN = "antmail.com"; - private static final Pattern USERNAME = Pattern.compile("[a-z0-9_]{3,16}"); - - public AntmailAddress { - username = normalizeUsername(username); - } - - public static AntmailAddress parse(String value) { - if (value == null) { - throw new IllegalArgumentException("Address is null"); - } - String normalized = value.trim().toLowerCase(Locale.ROOT); - String suffix = "@" + DOMAIN; - if (!normalized.endsWith(suffix)) { - throw new IllegalArgumentException("Invalid Antmail domain"); - } - return new AntmailAddress(normalized.substring(0, normalized.length() - suffix.length())); - } - - public static AntmailAddress ofUsername(String username) { - return new AntmailAddress(username); - } - - public String fullAddress() { - return username + "@" + DOMAIN; - } - - public static String normalizeUsername(String value) { - if (value == null) { - throw new IllegalArgumentException("Username is null"); - } - String normalized = value.trim().toLowerCase(Locale.ROOT); - if (!USERNAME.matcher(normalized).matches()) { - throw new IllegalArgumentException("Username must match [a-z0-9_]{3,16}"); - } - return normalized; - } - - public static boolean isValidUsername(String value) { - if (value == null) { - return false; - } - return USERNAME.matcher(value.trim().toLowerCase(Locale.ROOT)).matches(); - } - - public static boolean isValidAddress(String value) { - try { - parse(value); - return true; - } catch (IllegalArgumentException exception) { - return false; - } - } - - @Override - public String toString() { - return fullAddress(); - } - - @Override - public boolean equals(Object object) { - return object instanceof AntmailAddress other && username.equals(other.username); - } - - @Override - public int hashCode() { - return Objects.hash(username); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAttachment.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAttachment.java deleted file mode 100644 index e6efc7a9..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAttachment.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import net.minecraft.nbt.CompoundTag; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Objects; - -public sealed interface AntmailAttachment permits AntmailAttachment.TextFile, AntmailAttachment.PaintImage { - String TYPE_TAG = "Type"; - String NAME_TAG = "Name"; - - String fileName(); - - int byteSize(); - - CompoundTag toTag(); - - static AntmailAttachment fromTag(CompoundTag tag) { - return switch (tag.getString(TYPE_TAG)) { - case "text_file" -> TextFile.fromTag(tag); - case "paint_image" -> PaintImage.fromTag(tag); - default -> throw new IllegalArgumentException("Unknown Antmail attachment type"); - }; - } - - record TextFile(String fileName, String contents) implements AntmailAttachment { - public TextFile { - fileName = validateFileName(fileName); - contents = Objects.requireNonNull(contents, "contents"); - if (contents.length() > AntmailValidation.MAX_TEXT_ATTACHMENT_CHARACTERS) { - throw new IllegalArgumentException("Text attachment is too large"); - } - } - - @Override - public int byteSize() { - return contents.getBytes(StandardCharsets.UTF_8).length; - } - - @Override - public CompoundTag toTag() { - CompoundTag tag = new CompoundTag(); - tag.putString(TYPE_TAG, "text_file"); - tag.putString(NAME_TAG, fileName); - tag.putString("Contents", contents); - return tag; - } - - static TextFile fromTag(CompoundTag tag) { - return new TextFile(tag.getString(NAME_TAG), tag.getString("Contents")); - } - } - - record PaintImage(String fileName, int width, int height, byte[] pixels) implements AntmailAttachment { - public PaintImage { - fileName = validateFileName(fileName); - if (width != AntmailValidation.MAX_PAINT_WIDTH || height != AntmailValidation.MAX_PAINT_HEIGHT) { - throw new IllegalArgumentException("Invalid Paint image dimensions"); - } - if (pixels == null || pixels.length != width * height) { - throw new IllegalArgumentException("Paint pixel data does not match dimensions"); - } - for (byte pixel : pixels) { - if (pixel != 0 && pixel != 1) throw new IllegalArgumentException("Paint pixel data must be monochrome"); - } - pixels = Arrays.copyOf(pixels, pixels.length); - } - - @Override - public byte[] pixels() { - return Arrays.copyOf(pixels, pixels.length); - } - - @Override - public int byteSize() { - return pixels.length; - } - - @Override - public CompoundTag toTag() { - CompoundTag tag = new CompoundTag(); - tag.putString(TYPE_TAG, "paint_image"); - tag.putString(NAME_TAG, fileName); - tag.putInt("Width", width); - tag.putInt("Height", height); - tag.putByteArray("Pixels", pixels); - return tag; - } - - static PaintImage fromTag(CompoundTag tag) { - return new PaintImage(tag.getString(NAME_TAG), tag.getInt("Width"), tag.getInt("Height"), tag.getByteArray("Pixels")); - } - } - - private static String validateFileName(String value) { - if (value == null || value.isBlank() || value.length() > AntmailValidation.MAX_FILE_NAME_LENGTH || value.contains("/") || value.contains("\\")) { - throw new IllegalArgumentException("Invalid attachment filename"); - } - return value; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAttachmentFiles.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAttachmentFiles.java deleted file mode 100644 index 05e2a268..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailAttachmentFiles.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import com.craisinlord.antarchy.content.computer.ComputerFileSystem; -import com.craisinlord.antarchy.content.computer.paint.AntPaintCanvas; -import com.craisinlord.antarchy.content.computer.paint.AntPaintFile; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -public final class AntmailAttachmentFiles { - private AntmailAttachmentFiles() { - } - - public static Optional fromComputerFile(ComputerFileSystem.ComputerFile file) { - if (file == null || file.type() != ComputerFileSystem.ComputerFile.Type.TEXT) return Optional.empty(); - try { - return Optional.of(new AntmailAttachment.TextFile(filename(file.path()), file.contents())); - } catch (IllegalArgumentException exception) { - return Optional.empty(); - } - } - - public static AntmailAttachment.TextFile fromTextFile(String path, String contents) { - return new AntmailAttachment.TextFile(filename(path), contents); - } - - public static AntmailAttachment.PaintImage fromPaintFile(AntPaintFile file) { - if (file == null) throw new IllegalArgumentException("Paint file cannot be null"); - AntPaintCanvas canvas = file.canvas(); - if (canvas == null || canvas.copyPixels().length != AntPaintCanvas.PIXEL_COUNT) { - throw new IllegalArgumentException("Invalid AntPaint canvas"); - } - boolean[] source = canvas.copyPixels(); - byte[] pixels = new byte[source.length]; - for (int index = 0; index < source.length; index++) pixels[index] = source[index] ? (byte) 1 : (byte) 0; - return new AntmailAttachment.PaintImage(file.filename(), AntPaintCanvas.WIDTH, AntPaintCanvas.HEIGHT, pixels); - } - - public static AntPaintFile toPaintFile(AntmailAttachment.PaintImage image, String fileId, long createdTime, long modifiedTime) { - if (image == null) throw new IllegalArgumentException("Paint attachment cannot be null"); - if (image.width() != AntPaintCanvas.WIDTH || image.height() != AntPaintCanvas.HEIGHT) { - throw new IllegalArgumentException("Unsupported AntPaint canvas size"); - } - byte[] source = image.pixels(); - boolean[] pixels = new boolean[source.length]; - for (int index = 0; index < source.length; index++) { - if (source[index] != 0 && source[index] != 1) throw new IllegalArgumentException("Invalid AntPaint pixel"); - pixels[index] = source[index] == 1; - } - AntPaintCanvas canvas = new AntPaintCanvas(); - for (int index = 0; index < pixels.length; index++) { - if (pixels[index]) canvas.set(index % AntPaintCanvas.WIDTH, index / AntPaintCanvas.WIDTH, true); - } - return new AntPaintFile(fileId, image.fileName(), canvas, createdTime, modifiedTime); - } - - public static ComputerFileSystem.ComputerFile toComputerTextFile(String path, AntmailAttachment.TextFile file) { - if (file == null) throw new IllegalArgumentException("Text attachment cannot be null"); - String normalized = ComputerFileSystem.normalize(path); - if (normalized.isEmpty() || normalized.equals(ComputerFileSystem.ROOT)) throw new IllegalArgumentException("Invalid text file path"); - return ComputerFileSystem.ComputerFile.text(normalized, file.contents()); - } - - public static List selectableTextFiles(Collection files) { - if (files == null) return List.of(); - List result = new ArrayList<>(); - for (ComputerFileSystem.ComputerFile file : files) { - if (fromComputerFile(file).isPresent()) result.add(file); - } - return List.copyOf(result); - } - - public static List selectablePaintFiles(Collection files) { - if (files == null) return List.of(); - List result = new ArrayList<>(); - for (AntPaintFile file : files) { - try { - result.add(fromPaintFile(file)); - } catch (IllegalArgumentException ignored) { - } - } - return List.copyOf(result); - } - - private static String filename(String path) { - String normalized = ComputerFileSystem.normalize(path); - if (normalized.isEmpty() || normalized.equals(ComputerFileSystem.ROOT)) throw new IllegalArgumentException("Invalid file path"); - int separator = normalized.lastIndexOf('/'); - return normalized.substring(separator + 1); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDebug.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDebug.java deleted file mode 100644 index cceeac19..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDebug.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import com.craisinlord.antarchy.Antarchy; - -/** Opt-in diagnostics for AntMail networking and mailbox state transitions. */ -public final class AntmailDebug { - private static final String PROPERTY = "antarchy.antmail.debug"; - - private AntmailDebug() { - } - - public static void log(String message) { - if (Boolean.getBoolean(PROPERTY)) Antarchy.LOGGER.info("[AntMail debug] {}", message); - } - - public static void error(String message, Throwable throwable) { - Antarchy.LOGGER.error("[AntMail] {}", message, throwable); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDeliveryResult.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDeliveryResult.java deleted file mode 100644 index 7cba81c4..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDeliveryResult.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import java.util.UUID; - -public record AntmailDeliveryResult(Status status, UUID messageId, AntmailAddress sender, AntmailAddress recipient, String detail) { - public enum Status { - DELIVERED, - QUEUED, - INVALID_ADDRESS, - ADDRESS_NOT_FOUND, - MAILBOX_FULL, - MESSAGE_INVALID, - DUPLICATE, - FAILED - } - - public static AntmailDeliveryResult delivered(AntmailMessage message) { - return new AntmailDeliveryResult(Status.DELIVERED, message.id(), message.sender(), message.recipient(), ""); - } - - public static AntmailDeliveryResult queued(AntmailMessage message) { - return new AntmailDeliveryResult(Status.QUEUED, message.id(), message.sender(), message.recipient(), ""); - } - - public static AntmailDeliveryResult failed(Status status, AntmailMessage message, String detail) { - return new AntmailDeliveryResult(status, message == null ? null : message.id(), message == null ? null : message.sender(), message == null ? null : message.recipient(), detail); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDraft.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDraft.java deleted file mode 100644 index 4fe498ab..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailDraft.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -public record AntmailDraft(UUID id, AntmailAddress sender, AntmailAddress recipient, String subject, String body, List attachments) { - public AntmailDraft { - id = id == null ? UUID.randomUUID() : id; - subject = subject == null ? "" : subject; - body = body == null ? "" : body; - attachments = attachments == null ? List.of() : List.copyOf(attachments); - if (subject.length() > AntmailValidation.MAX_SUBJECT_LENGTH || body.length() > AntmailValidation.MAX_BODY_LENGTH || attachments.size() > AntmailValidation.MAX_ATTACHMENTS) { - throw new IllegalArgumentException("Draft exceeds Antmail limits"); - } - } - - public CompoundTag toTag() { - CompoundTag tag = new CompoundTag(); - tag.putString("Id", id.toString()); - if (sender != null) tag.putString("From", sender.fullAddress()); - if (recipient != null) tag.putString("To", recipient.fullAddress()); - tag.putString("Subject", subject); - tag.putString("Body", body); - ListTag attachmentTags = new ListTag(); - for (AntmailAttachment attachment : attachments) attachmentTags.add(attachment.toTag()); - tag.put("Attachments", attachmentTags); - return tag; - } - - public static AntmailDraft fromTag(CompoundTag tag) { - List attachments = new ArrayList<>(); - ListTag attachmentTags = tag.getList("Attachments", 10); - for (int index = 0; index < attachmentTags.size(); index++) attachments.add(AntmailAttachment.fromTag(attachmentTags.getCompound(index))); - UUID id = UUID.fromString(tag.getString("Id")); - AntmailAddress sender = tag.contains("From") ? AntmailAddress.parse(tag.getString("From")) : null; - AntmailAddress recipient = tag.contains("To") ? AntmailAddress.parse(tag.getString("To")) : null; - return new AntmailDraft(id, sender, recipient, tag.getString("Subject"), tag.getString("Body"), attachments); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailEventData.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailEventData.java deleted file mode 100644 index 688605c9..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailEventData.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.server.packs.resources.SimplePreparableReloadListener; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.advancements.AdvancementHolder; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public final class AntmailEventData extends SimplePreparableReloadListener> { - private static final AntmailEventData INSTANCE = new AntmailEventData(); - private static volatile Map definitions = Map.of(); - private AntmailEventData() { - } - - public static AntmailEventData instance() { - return INSTANCE; - } - - public static void onTick(MinecraftServer server) { - long dayTime = server.overworld().getDayTime(); - long day = dayTime / 24000L; - if (dayTime % 24000L >= 20L) return; - AntmailServerData data = AntmailServerData.access(server); - if (!data.beginRandomMailDay(day)) return; - for (AntmailAddress address : data.registeredAddresses()) { - for (Map.Entry entry : definitions.entrySet()) { - Definition definition = entry.getValue(); - if (definition.trigger().type().equals("random") && server.overworld().random.nextDouble() < definition.trigger().chance()) deliver(server, address, entry.getKey(), definition, false); - } - } - } - - public static void onAdvancement(ServerPlayer player, AdvancementHolder advancement) { - AntmailServerData data = AntmailServerData.access(player.server); - AntmailAddress address = data.lastUsedAddress(player); - if (address == null) return; - for (Map.Entry entry : definitions.entrySet()) { - Trigger trigger = entry.getValue().trigger(); - if (trigger.type().equals("advancement") && trigger.value().equals(advancement.id().toString())) deliver(player.server, address, entry.getKey(), entry.getValue(), true); - } - } - - private static void deliver(MinecraftServer server, AntmailAddress recipient, ResourceLocation id, Definition definition, boolean permanentClaim) { - AntmailServerData data = AntmailServerData.access(server); - try { - AntmailAddress sender = AntmailAddress.parse(definition.sender()); - AntmailMessage message = AntmailMessage.create(sender, recipient, definition.subject(), definition.body(), server.overworld().getGameTime(), List.of()); - AntmailDeliveryResult result = data.deliver(server, message); - if (permanentClaim && result.status() != AntmailDeliveryResult.Status.MAILBOX_FULL - && result.status() != AntmailDeliveryResult.Status.FAILED - && result.status() != AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND) { - data.claimTrigger(recipient, id); - } - } catch (IllegalArgumentException ignored) { - } - } - - @Override - protected Map prepare(ResourceManager manager, ProfilerFiller profiler) { - Map loaded = new HashMap<>(); - for (Map.Entry resource : manager.listResources("antmail/message", path -> path.getPath().endsWith(".json")).entrySet()) { - try { - JsonElement element = JsonParser.parseReader(resource.getValue().openAsReader()); - if (!element.isJsonObject()) continue; - var object = element.getAsJsonObject(); - var trigger = object.getAsJsonObject("trigger"); - String type = trigger.get("type").getAsString(); - String value = trigger.has("value") ? trigger.get("value").getAsString() : String.valueOf(trigger.get("day").getAsInt()); - ResourceLocation id = ResourceLocation.fromNamespaceAndPath(resource.getKey().getNamespace(), resource.getKey().getPath().substring("antmail/message/".length(), resource.getKey().getPath().length() - 5)); - loaded.put(id, new Definition(object.get("sender").getAsString(), object.get("subject").getAsString(), object.get("body").getAsString(), new Trigger(type, value))); - } catch (Exception ignored) { - } - } - return Map.copyOf(loaded); - } - - @Override - protected void apply(Map loaded, ResourceManager manager, ProfilerFiller profiler) { - definitions = loaded; - } - - public record Definition(String sender, String subject, String body, Trigger trigger) { - } - - public record Trigger(String type, String value) { - double chance() { - try { - return Math.max(0.0D, Math.min(1.0D, Double.parseDouble(value))); - } catch (NumberFormatException ignored) { - return 0.0D; - } - } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMailbox.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMailbox.java deleted file mode 100644 index af60e400..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMailbox.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -public final class AntmailMailbox { - public static final int PAGE_SIZE = 20; - private final AntmailAddress address; - private final List inbox = new ArrayList<>(); - private final List sent = new ArrayList<>(); - private final List drafts = new ArrayList<>(); - private int reportedUnreadCount = -1; - - public AntmailMailbox(AntmailAddress address) { - this.address = address; - } - - public AntmailAddress address() { return address; } - public List inbox() { return List.copyOf(inbox); } - public List sent() { return List.copyOf(sent); } - public List drafts() { return List.copyOf(drafts); } - public int unreadCount() { return reportedUnreadCount >= 0 ? reportedUnreadCount : (int) inbox.stream().filter(message -> !message.read()).count(); } - - public boolean addIncoming(AntmailMessage message) { - if (!address.equals(message.recipient()) || inbox.size() >= AntmailValidation.MAX_MAILBOX_MESSAGES || contains(inbox, message.id())) return false; - inbox.add(message); - return true; - } - - public boolean addSent(AntmailMessage message) { - if (!address.equals(message.sender()) || sent.size() >= AntmailValidation.MAX_MAILBOX_MESSAGES || contains(sent, message.id())) return false; - sent.add(message); - return true; - } - - public boolean saveDraft(AntmailDraft draft) { - if (!address.equals(draft.sender()) || drafts.size() >= AntmailValidation.MAX_DRAFTS && findDraft(draft.id()) == null) return false; - drafts.removeIf(existing -> existing.id().equals(draft.id())); - drafts.add(draft); - return true; - } - - public boolean removeDraft(UUID id) { return drafts.removeIf(draft -> draft.id().equals(id)); } - public boolean removeInbox(UUID id) { return inbox.removeIf(message -> message.id().equals(id)); } - public boolean removeSent(UUID id) { return sent.removeIf(message -> message.id().equals(id)); } - public AntmailMessage findSent(UUID id) { return sent.stream().filter(message -> message.id().equals(id)).findFirst().orElse(null); } - public AntmailMessage findInbox(UUID id) { return inbox.stream().filter(message -> message.id().equals(id)).findFirst().orElse(null); } - - public boolean markRead(UUID id) { - for (AntmailMessage message : inbox) if (message.id().equals(id)) { message.markRead(); return true; } - return false; - } - - public AntmailDraft findDraft(UUID id) { return drafts.stream().filter(draft -> draft.id().equals(id)).findFirst().orElse(null); } - - public CompoundTag toTag() { - return toTag(true); - } - - public CompoundTag toTag(boolean includeMessageAttachments) { - CompoundTag tag = new CompoundTag(); - tag.putString("Address", address.fullAddress()); - tag.put("Inbox", messagesToTag(inbox, includeMessageAttachments)); - tag.put("Sent", messagesToTag(sent, includeMessageAttachments)); - ListTag draftTags = new ListTag(); - for (AntmailDraft draft : drafts) draftTags.add(draft.toTag()); - tag.put("Drafts", draftTags); - return tag; - } - - public CompoundTag toPageTag(int folder, int page) { - int safePage = Math.max(0, page); - int start = safePage * PAGE_SIZE; - CompoundTag tag = new CompoundTag(); - tag.putString("Address", address.fullAddress()); - tag.putInt("Folder", folder); - tag.putInt("Page", safePage); - tag.putInt("PageSize", PAGE_SIZE); - tag.putInt("TotalInbox", inbox.size()); - tag.putInt("TotalSent", sent.size()); - tag.putInt("UnreadCount", (int) inbox.stream().filter(message -> !message.read()).count()); - tag.put("Inbox", folder == 0 ? messagesToTag(page(inbox, start), false) : new ListTag()); - tag.put("Sent", folder == 1 ? messagesToTag(page(sent, start), false) : new ListTag()); - ListTag draftTags = new ListTag(); - for (AntmailDraft draft : drafts) draftTags.add(draft.toTag()); - tag.put("Drafts", draftTags); - return tag; - } - - private static List page(List values, int start) { - if (start >= values.size()) return List.of(); - return values.subList(start, Math.min(start + PAGE_SIZE, values.size())); - } - - public static AntmailMailbox fromTag(CompoundTag tag) { - AntmailMailbox mailbox = new AntmailMailbox(AntmailAddress.parse(tag.getString("Address"))); - if (tag.contains("UnreadCount")) mailbox.reportedUnreadCount = Math.max(0, tag.getInt("UnreadCount")); - readMessages(tag.getList("Inbox", 10), mailbox.inbox); - readMessages(tag.getList("Sent", 10), mailbox.sent); - ListTag draftTags = tag.getList("Drafts", 10); - for (int index = 0; index < draftTags.size() && mailbox.drafts.size() < AntmailValidation.MAX_DRAFTS; index++) { - try { mailbox.drafts.add(AntmailDraft.fromTag(draftTags.getCompound(index))); } catch (RuntimeException ignored) { } - } - return mailbox; - } - - private static ListTag messagesToTag(List messages, boolean includeMessageAttachments) { - ListTag tags = new ListTag(); - for (AntmailMessage message : messages) tags.add(message.toTag(includeMessageAttachments)); - return tags; - } - - private static void readMessages(ListTag tags, List target) { - for (int index = 0; index < tags.size() && target.size() < AntmailValidation.MAX_MAILBOX_MESSAGES; index++) { - try { target.add(AntmailMessage.fromTag(tags.getCompound(index))); } catch (RuntimeException ignored) { } - } - } - - private static boolean contains(List messages, UUID id) { - return messages.stream().anyMatch(message -> message.id().equals(id)); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMailboxStore.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMailboxStore.java deleted file mode 100644 index 5fe98546..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMailboxStore.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -public final class AntmailMailboxStore { - private final Map mailboxes = new LinkedHashMap<>(); - - public AntmailMailbox getOrCreate(AntmailAddress address) { - return mailboxes.computeIfAbsent(address, AntmailMailbox::new); - } - - public AntmailMailbox get(AntmailAddress address) { - return mailboxes.get(address); - } - - public boolean contains(AntmailAddress address) { - return mailboxes.containsKey(address); - } - - public void put(AntmailMailbox mailbox) { - mailboxes.put(mailbox.address(), mailbox); - } - - public Collection mailboxes() { - return List.copyOf(mailboxes.values()); - } - - public CompoundTag toTag() { - CompoundTag tag = new CompoundTag(); - ListTag mailboxTags = new ListTag(); - for (AntmailMailbox mailbox : mailboxes.values()) mailboxTags.add(mailbox.toTag()); - tag.put("Mailboxes", mailboxTags); - return tag; - } - - public static AntmailMailboxStore fromTag(CompoundTag tag) { - AntmailMailboxStore store = new AntmailMailboxStore(); - ListTag mailboxTags = tag.getList("Mailboxes", 10); - for (int index = 0; index < mailboxTags.size(); index++) { - try { - AntmailMailbox mailbox = AntmailMailbox.fromTag(mailboxTags.getCompound(index)); - store.mailboxes.put(mailbox.address(), mailbox); - } catch (RuntimeException ignored) { - } - } - return store; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMessage.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMessage.java deleted file mode 100644 index 74e9751b..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailMessage.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -public final class AntmailMessage { - private static final String ID_TAG = "Id"; - private static final String FROM_TAG = "From"; - private static final String TO_TAG = "To"; - private static final String SUBJECT_TAG = "Subject"; - private static final String BODY_TAG = "Body"; - private static final String CREATED_TAG = "Created"; - private static final String READ_TAG = "Read"; - private static final String ATTACHMENTS_TAG = "Attachments"; - private static final String DELIVERY_STATUS_TAG = "DeliveryStatus"; - - private final UUID id; - private final AntmailAddress sender; - private final AntmailAddress recipient; - private final String subject; - private final String body; - private final long createdAt; - private final List attachments; - private boolean read; - private String deliveryStatus = "DELIVERED"; - - public AntmailMessage(UUID id, AntmailAddress sender, AntmailAddress recipient, String subject, String body, long createdAt, List attachments, boolean read) { - AntmailValidation.Result result = AntmailValidation.validateMessage(sender, recipient, subject, body, attachments); - if (!result.valid()) { - throw new IllegalArgumentException(result.reason()); - } - this.id = id == null ? UUID.randomUUID() : id; - this.sender = sender; - this.recipient = recipient; - this.subject = subject; - this.body = body; - this.createdAt = createdAt; - this.attachments = List.copyOf(attachments); - this.read = read; - } - - public static AntmailMessage create(AntmailAddress sender, AntmailAddress recipient, String subject, String body, long createdAt, List attachments) { - return new AntmailMessage(UUID.randomUUID(), sender, recipient, subject, body, createdAt, attachments, false); - } - - public UUID id() { return id; } - public AntmailAddress sender() { return sender; } - public AntmailAddress recipient() { return recipient; } - public String subject() { return subject; } - public String body() { return body; } - public long createdAt() { return createdAt; } - public List attachments() { return attachments; } - public boolean read() { return read; } - public String deliveryStatus() { return deliveryStatus; } - public void setDeliveryStatus(String status) { deliveryStatus = status == null || status.isBlank() ? "DELIVERED" : status; } - public void markRead() { read = true; } - public void markUnread() { read = false; } - - public CompoundTag toTag() { - return toTag(true); - } - - public CompoundTag toTag(boolean includeAttachments) { - CompoundTag tag = new CompoundTag(); - tag.putString(ID_TAG, id.toString()); - tag.putString(FROM_TAG, sender.fullAddress()); - tag.putString(TO_TAG, recipient.fullAddress()); - tag.putString(SUBJECT_TAG, subject); - tag.putString(BODY_TAG, body); - tag.putLong(CREATED_TAG, createdAt); - tag.putBoolean(READ_TAG, read); - tag.putString(DELIVERY_STATUS_TAG, deliveryStatus); - if (includeAttachments) { - ListTag attachmentTags = new ListTag(); - for (AntmailAttachment attachment : attachments) attachmentTags.add(attachment.toTag()); - tag.put(ATTACHMENTS_TAG, attachmentTags); - } - return tag; - } - - public static AntmailMessage fromTag(CompoundTag tag) { - List attachments = new ArrayList<>(); - ListTag attachmentTags = tag.getList(ATTACHMENTS_TAG, 10); - for (int index = 0; index < attachmentTags.size(); index++) attachments.add(AntmailAttachment.fromTag(attachmentTags.getCompound(index))); - AntmailMessage message = new AntmailMessage(UUID.fromString(tag.getString(ID_TAG)), AntmailAddress.parse(tag.getString(FROM_TAG)), AntmailAddress.parse(tag.getString(TO_TAG)), tag.getString(SUBJECT_TAG), tag.getString(BODY_TAG), tag.getLong(CREATED_TAG), attachments, tag.getBoolean(READ_TAG)); - message.setDeliveryStatus(tag.getString(DELIVERY_STATUS_TAG)); - return message; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailServerData.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailServerData.java deleted file mode 100644 index 60686fe9..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailServerData.java +++ /dev/null @@ -1,339 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import net.minecraft.core.HolderLookup; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; -import net.minecraft.world.level.saveddata.SavedData; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -public final class AntmailServerData extends SavedData { - private static final String ID = "antarchy_antmail"; - private static final int MAX_PENDING_MESSAGES = AntmailValidation.MAX_MAILBOX_MESSAGES; - private final Map endpoints = new LinkedHashMap<>(); - private final AntmailMailboxStore mailboxes = new AntmailMailboxStore(); - private final List pending = new ArrayList<>(); - private final Map> triggered = new LinkedHashMap<>(); - private final Map mailboxVersions = new LinkedHashMap<>(); - private final Map lastUsedAddresses = new LinkedHashMap<>(); - private long randomMailDay = Long.MIN_VALUE; - private int retryTicks; - - public static AntmailServerData create() { - return new AntmailServerData(); - } - - public static AntmailServerData load(CompoundTag tag, HolderLookup.Provider registries) { - AntmailServerData data = new AntmailServerData(); - ListTag endpointTags = tag.getList("Endpoints", 10); - for (int i = 0; i < endpointTags.size(); i++) { - try { - CompoundTag endpoint = endpointTags.getCompound(i); - AntmailAddress address = AntmailAddress.parse(endpoint.getString("Address")); - data.endpoints.put(address, new Endpoint(endpoint.getString("Dimension"), endpoint.getLong("Position"))); - } catch (RuntimeException ignored) { - } - } - AntmailServerData loaded = data; - AntmailMailboxStore restored = AntmailMailboxStore.fromTag(tag.getCompound("Mailboxes")); - for (AntmailMailbox mailbox : restored.mailboxes()) { - loaded.mailboxes.put(mailbox); - } - ListTag pendingTags = tag.getList("Pending", 10); - for (int i = 0; i < pendingTags.size(); i++) { - try { - CompoundTag pendingTag = pendingTags.getCompound(i); - loaded.pending.add(new PendingMessage(AntmailAddress.parse(pendingTag.getString("Recipient")), AntmailMessage.fromTag(pendingTag.getCompound("Message")))); - } catch (RuntimeException ignored) { - } - } - ListTag triggeredTags = tag.getList("Triggered", 10); - for (int i = 0; i < triggeredTags.size(); i++) { - CompoundTag triggeredTag = triggeredTags.getCompound(i); - try { - AntmailAddress address = AntmailAddress.parse(triggeredTag.getString("Address")); - java.util.Set ids = new java.util.LinkedHashSet<>(); - ListTag idsTag = triggeredTag.getList("Ids", 8); - for (int j = 0; j < idsTag.size(); j++) ids.add(ResourceLocation.parse(idsTag.getString(j))); - data.triggered.put(address, ids); - } catch (RuntimeException ignored) { - } - } - ListTag lastUsedTags = tag.getList("LastUsedAddresses", 10); - for (int i = 0; i < lastUsedTags.size(); i++) { - try { - CompoundTag lastUsedTag = lastUsedTags.getCompound(i); - data.lastUsedAddresses.put(UUID.fromString(lastUsedTag.getString("Player")), AntmailAddress.parse(lastUsedTag.getString("Address"))); - } catch (RuntimeException ignored) { - } - } - data.randomMailDay = tag.contains("RandomMailDay") ? tag.getLong("RandomMailDay") : Long.MIN_VALUE; - ListTag versionTags = tag.getList("MailboxVersions", 10); - for (int i = 0; i < versionTags.size(); i++) { - try { - CompoundTag versionTag = versionTags.getCompound(i); - data.mailboxVersions.put(AntmailAddress.parse(versionTag.getString("Address")), Math.max(1L, versionTag.getLong("Version"))); - } catch (RuntimeException ignored) { - } - } - return loaded; - } - - @Override - public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) { - ListTag endpointTags = new ListTag(); - for (Map.Entry entry : endpoints.entrySet()) { - CompoundTag endpoint = new CompoundTag(); - endpoint.putString("Address", entry.getKey().fullAddress()); - endpoint.putString("Dimension", entry.getValue().dimension()); - endpoint.putLong("Position", entry.getValue().position()); - endpointTags.add(endpoint); - } - tag.put("Endpoints", endpointTags); - tag.put("Mailboxes", mailboxes.toTag()); - ListTag pendingTags = new ListTag(); - for (PendingMessage message : pending) { - CompoundTag pendingTag = new CompoundTag(); - pendingTag.putString("Recipient", message.recipient().fullAddress()); - pendingTag.put("Message", message.message().toTag()); - pendingTags.add(pendingTag); - } - tag.put("Pending", pendingTags); - ListTag triggeredTags = new ListTag(); - for (Map.Entry> entry : triggered.entrySet()) { - CompoundTag triggeredTag = new CompoundTag(); - triggeredTag.putString("Address", entry.getKey().fullAddress()); - ListTag idsTag = new ListTag(); - for (ResourceLocation id : entry.getValue()) idsTag.add(net.minecraft.nbt.StringTag.valueOf(id.toString())); - triggeredTag.put("Ids", idsTag); - triggeredTags.add(triggeredTag); - } - tag.put("Triggered", triggeredTags); - ListTag lastUsedTags = new ListTag(); - for (Map.Entry entry : lastUsedAddresses.entrySet()) { - CompoundTag lastUsedTag = new CompoundTag(); - lastUsedTag.putString("Player", entry.getKey().toString()); - lastUsedTag.putString("Address", entry.getValue().fullAddress()); - lastUsedTags.add(lastUsedTag); - } - tag.put("LastUsedAddresses", lastUsedTags); - tag.putLong("RandomMailDay", randomMailDay); - ListTag versionTags = new ListTag(); - for (Map.Entry entry : mailboxVersions.entrySet()) { - CompoundTag versionTag = new CompoundTag(); - versionTag.putString("Address", entry.getKey().fullAddress()); - versionTag.putLong("Version", entry.getValue()); - versionTags.add(versionTag); - } - tag.put("MailboxVersions", versionTags); - return tag; - } - - private static AntmailServerData get(MinecraftServer server) { - return server.overworld().getDataStorage().computeIfAbsent( - new SavedData.Factory<>(AntmailServerData::create, AntmailServerData::load, null), ID); - } - - public static AntmailServerData access(MinecraftServer server) { - return get(server); - } - - public synchronized Registration register(MinecraftServer server, ServerLevel level, BlockPos position, String username) { - final AntmailAddress address; - try { - address = AntmailAddress.ofUsername(username); - } catch (IllegalArgumentException exception) { - return Registration.invalid("invalid_username"); - } - AntmailAddress previous = addressAt(level.dimension().location(), position); - Endpoint existing = endpoints.get(address); - if (existing != null && (!existing.dimension().equals(level.dimension().location().toString()) || existing.position() != position.asLong())) { - return Registration.invalid("username_taken"); - } - if (previous != null && !previous.equals(address)) { - endpoints.remove(previous); - } - endpoints.put(address, new Endpoint(level.dimension().location().toString(), position.asLong())); - get(server).drain(server, address); - setDirty(); - return Registration.accepted(address); - } - - public synchronized AntmailAddress addressAt(ResourceLocation dimension, BlockPos position) { - for (Map.Entry entry : endpoints.entrySet()) { - if (entry.getValue().dimension().equals(dimension.toString()) && entry.getValue().position() == position.asLong()) return entry.getKey(); - } - return null; - } - - public synchronized boolean unregister(ResourceLocation dimension, BlockPos position) { - AntmailAddress address = addressAt(dimension, position); - if (address == null) return false; - endpoints.remove(address); - setDirty(); - return true; - } - - public synchronized Endpoint endpoint(AntmailAddress address) { - return endpoints.get(address); - } - - public synchronized List registeredAddresses() { - return List.copyOf(endpoints.keySet()); - } - - public synchronized AntmailAddress addressFor(ServerPlayer player) { - AntmailAddress address = lastUsedAddresses.get(player.getUUID()); - return address != null && endpoints.containsKey(address) ? address : null; - } - - public synchronized AntmailAddress lastUsedAddress(ServerPlayer player) { - AntmailAddress address = lastUsedAddresses.get(player.getUUID()); - return address != null && endpoints.containsKey(address) ? address : null; - } - - public synchronized void setLastUsedAddress(ServerPlayer player, AntmailAddress address) { - if (address == null) return; - lastUsedAddresses.put(player.getUUID(), address); - setDirty(); - } - - public synchronized boolean claimTrigger(AntmailAddress address, ResourceLocation triggerId) { - java.util.Set ids = triggered.computeIfAbsent(address, ignored -> new java.util.LinkedHashSet<>()); - if (!ids.add(triggerId)) return false; - setDirty(); - return true; - } - - public synchronized boolean beginRandomMailDay(long day) { - if (randomMailDay == day) return false; - randomMailDay = day; - setDirty(); - return true; - } - - public synchronized AntmailMailbox mailbox(AntmailAddress address) { - return mailboxes.get(address); - } - - public synchronized AntmailMailbox mailboxOrCreate(AntmailAddress address) { - AntmailMailbox mailbox = mailboxes.getOrCreate(address); - setDirty(); - return mailbox; - } - - public synchronized long mailboxVersion(AntmailAddress address) { - return mailboxVersions.getOrDefault(address, 1L); - } - - public synchronized void touchMailbox(AntmailAddress address) { - if (address == null) return; - mailboxVersions.put(address, mailboxVersion(address) + 1L); - setDirty(); - } - - public synchronized void drainAddress(MinecraftServer server, ServerLevel level, BlockPos position) { - AntmailAddress address = addressAt(level.dimension().location(), position); - if (address != null) drain(server, address); - } - - public synchronized void drainAvailable(MinecraftServer server) { - if (pending.isEmpty() || ++retryTicks < 20) return; - retryTicks = 0; - List available = new ArrayList<>(); - for (Map.Entry entry : endpoints.entrySet()) { - Endpoint endpoint = entry.getValue(); - ServerLevel level = server.getLevel(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, ResourceLocation.parse(endpoint.dimension()))); - if (level != null && level.hasChunkAt(BlockPos.of(endpoint.position()))) available.add(entry.getKey()); - } - for (AntmailAddress address : available) drain(server, address); - } - - public synchronized AntmailDeliveryResult deliver(MinecraftServer server, AntmailMessage message) { - if (message == null) return AntmailDeliveryResult.failed(AntmailDeliveryResult.Status.MESSAGE_INVALID, null, "missing_message"); - if (endpoint(message.recipient()) == null) return failedAndRecorded(message, AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND, "address_not_found"); - AntmailMailbox destination = mailboxes.getOrCreate(message.recipient()); - if (destination.inbox().size() >= AntmailValidation.MAX_MAILBOX_MESSAGES) return failedAndRecorded(message, AntmailDeliveryResult.Status.MAILBOX_FULL, "mailbox_full"); - Endpoint target = endpoint(message.recipient()); - ServerLevel level = server.getLevel(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, ResourceLocation.parse(target.dimension()))); - if (level == null || !level.hasChunkAt(BlockPos.of(target.position()))) { - if (pending.stream().anyMatch(item -> item.message().id().equals(message.id()))) { - return AntmailDeliveryResult.failed(AntmailDeliveryResult.Status.DUPLICATE, message, "already_pending"); - } - if (pending.size() >= MAX_PENDING_MESSAGES) { - return failedAndRecorded(message, AntmailDeliveryResult.Status.FAILED, "pending_queue_full"); - } - message.setDeliveryStatus("QUEUED"); - pending.add(new PendingMessage(message.recipient(), message)); - mailboxes.getOrCreate(message.sender()).addSent(message); - touchMailbox(message.recipient()); - touchMailbox(message.sender()); - setDirty(); - return AntmailDeliveryResult.queued(message); - } - message.setDeliveryStatus("DELIVERED"); - destination.addIncoming(message); - mailboxes.getOrCreate(message.sender()).addSent(message); - touchMailbox(message.recipient()); - touchMailbox(message.sender()); - setDirty(); - return AntmailDeliveryResult.delivered(message); - } - - private AntmailDeliveryResult failedAndRecorded(AntmailMessage message, AntmailDeliveryResult.Status status, String detail) { - message.setDeliveryStatus("FAILED // " + detail.toUpperCase()); - mailboxes.getOrCreate(message.sender()).addSent(message); - touchMailbox(message.sender()); - setDirty(); - return AntmailDeliveryResult.failed(status, message, detail); - } - - public synchronized void drain(MinecraftServer server, AntmailAddress address) { - AntmailMailbox mailbox = mailboxes.getOrCreate(address); - final boolean[] delivered = {false}; - Set deliveredSenders = new LinkedHashSet<>(); - pending.removeIf(pendingMessage -> { - if (!pendingMessage.recipient().equals(address) || mailbox.inbox().size() >= AntmailValidation.MAX_MAILBOX_MESSAGES) return false; - if (!mailbox.addIncoming(pendingMessage.message())) return false; - pendingMessage.message().setDeliveryStatus("DELIVERED"); - delivered[0] = true; - deliveredSenders.add(pendingMessage.message().sender()); - return true; - }); - if (delivered[0]) { - touchMailbox(address); - for (AntmailAddress sender : deliveredSenders) touchMailbox(sender); - setDirty(); - } - } - - public synchronized AntmailDeliveryResult retry(MinecraftServer server, AntmailAddress sender, UUID messageId) { - AntmailMessage message = mailboxes.getOrCreate(sender).findSent(messageId); - if (message == null) return null; - pending.removeIf(item -> item.message().id().equals(messageId)); - return deliver(server, message); - } - - public record Endpoint(String dimension, long position) { - } - - public record Registration(boolean valid, AntmailAddress address, String reason) { - static Registration accepted(AntmailAddress address) { return new Registration(true, address, ""); } - static Registration invalid(String reason) { return new Registration(false, null, reason); } - } - - private record PendingMessage(AntmailAddress recipient, AntmailMessage message) { - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailValidation.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailValidation.java deleted file mode 100644 index a9676e10..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailValidation.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import java.nio.charset.StandardCharsets; -import java.util.List; - -public final class AntmailValidation { - public static final int MAX_SUBJECT_LENGTH = 64; - public static final int MAX_BODY_LENGTH = 4096; - public static final int MAX_FILE_NAME_LENGTH = 32; - public static final int MAX_ATTACHMENTS = 8; - public static final int MAX_TEXT_ATTACHMENT_CHARACTERS = 16384; - public static final int MAX_PAINT_WIDTH = 32; - public static final int MAX_PAINT_HEIGHT = 24; - public static final int MAX_MESSAGE_BYTES = 65536; - public static final int MAX_ATTACHMENT_WIRE_CHARS = 65536; - public static final int MAX_MAILBOX_MESSAGES = 100; - public static final int MAX_DRAFTS = 25; - - private AntmailValidation() { - } - - public static Result validateMessage(AntmailAddress sender, AntmailAddress recipient, String subject, String body, List attachments) { - if (sender == null || recipient == null) { - return Result.invalid("missing address"); - } - if (subject == null || subject.length() > MAX_SUBJECT_LENGTH) { - return Result.invalid("subject too long"); - } - if (body == null || body.length() > MAX_BODY_LENGTH) { - return Result.invalid("body too long"); - } - Result attachmentResult = validateAttachments(attachments); - if (!attachmentResult.valid()) return attachmentResult; - int bytes = subject.getBytes(StandardCharsets.UTF_8).length + body.getBytes(StandardCharsets.UTF_8).length; - for (AntmailAttachment attachment : attachments) bytes += attachment.byteSize(); - if (bytes > MAX_MESSAGE_BYTES) { - return Result.invalid("message too large"); - } - return Result.accepted(); - } - - public static Result validateAttachments(List attachments) { - if (attachments == null || attachments.size() > MAX_ATTACHMENTS) return Result.invalid("too many attachments"); - int textCharacters = 0; - for (AntmailAttachment attachment : attachments) { - if (attachment == null) return Result.invalid("null attachment"); - if (attachment instanceof AntmailAttachment.TextFile textFile) { - if (textFile.contents().length() > MAX_TEXT_ATTACHMENT_CHARACTERS) return Result.invalid("text attachment too large"); - textCharacters += textFile.contents().length(); - } else if (attachment instanceof AntmailAttachment.PaintImage paintImage) { - if (paintImage.width() != MAX_PAINT_WIDTH || paintImage.height() != MAX_PAINT_HEIGHT) return Result.invalid("unsupported paint size"); - } - } - if (textCharacters > MAX_TEXT_ATTACHMENT_CHARACTERS) return Result.invalid("text attachments too large"); - return Result.accepted(); - } - - public record Result(boolean valid, String reason) { - public static Result accepted() { - return new Result(true, ""); - } - - public static Result invalid(String reason) { - return new Result(false, reason); - } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailWire.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailWire.java deleted file mode 100644 index 07f6b92b..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/antmail/AntmailWire.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.craisinlord.antarchy.content.antmail; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtAccounter; -import net.minecraft.nbt.NbtIo; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Base64; -import java.util.List; - -public final class AntmailWire { - private static final int MAX_COMPRESSED_BYTES = 1024 * 1024; - private static final long MAX_DECOMPRESSED_BYTES = 8L * 1024L * 1024L; - private AntmailWire() { - } - - public static String encodeAttachments(List attachments) { - AntmailValidation.Result validation = AntmailValidation.validateAttachments(attachments); - if (!validation.valid()) throw new IllegalArgumentException(validation.reason()); - CompoundTag root = new CompoundTag(); - ListTag list = new ListTag(); - for (AntmailAttachment attachment : attachments) list.add(attachment.toTag()); - root.put("Attachments", list); - String encoded = encodeTag(root); - if (encoded.length() > AntmailValidation.MAX_ATTACHMENT_WIRE_CHARS) throw new IllegalArgumentException("Attachment payload is too large"); - return encoded; - } - - public static String encodeTag(CompoundTag tag) { - if (tag == null) throw new IllegalArgumentException("Antmail tag cannot be null"); - try { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - NbtIo.writeCompressed(tag, output); - byte[] compressed = output.toByteArray(); - if (compressed.length > MAX_COMPRESSED_BYTES) throw new IllegalArgumentException("Antmail data is too large"); - return Base64.getEncoder().encodeToString(compressed); - } catch (IOException exception) { - throw new IllegalArgumentException("Could not encode Antmail data", exception); - } - } - - public static CompoundTag decodeTag(String encoded) { - try { - if (encoded == null || encoded.isBlank()) throw new IllegalArgumentException("Antmail data is empty"); - byte[] compressed = Base64.getDecoder().decode(encoded); - if (compressed.length > MAX_COMPRESSED_BYTES) throw new IllegalArgumentException("Antmail data is too large"); - return NbtIo.readCompressed(new ByteArrayInputStream(compressed), NbtAccounter.create(MAX_DECOMPRESSED_BYTES)); - } catch (Exception exception) { - throw new IllegalArgumentException("Invalid Antmail data", exception); - } - } - - public static List decodeAttachments(String encoded) { - if (encoded == null || encoded.isBlank()) return List.of(); - if (encoded.length() > AntmailValidation.MAX_ATTACHMENT_WIRE_CHARS) throw new IllegalArgumentException("Attachment payload is too large"); - try { - CompoundTag root = decodeTag(encoded); - ListTag list = root.getList("Attachments", 10); - List result = new ArrayList<>(); - for (int i = 0; i < list.size(); i++) result.add(AntmailAttachment.fromTag(list.getCompound(i))); - AntmailValidation.Result validation = AntmailValidation.validateAttachments(result); - if (!validation.valid()) throw new IllegalArgumentException(validation.reason()); - return List.copyOf(result); - } catch (Exception exception) { - throw new IllegalArgumentException("Invalid attachment data", exception); - } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/AntTrapBlock.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/AntTrapBlock.java new file mode 100644 index 00000000..7534f2b4 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/AntTrapBlock.java @@ -0,0 +1,52 @@ +package com.craisinlord.antarchy.content.block; + +import com.craisinlord.antarchy.content.block.entity.AntNestBlockEntity; +import com.craisinlord.antarchy.content.entity.ant.BaseAntEntity; +import java.util.List; +import java.util.function.Supplier; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraft.world.level.BlockGetter; + +public final class AntTrapBlock extends AntNestBlock { + private static final VoxelShape SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 5.0D, 13.0D); + + public AntTrapBlock(Supplier>> antTypesSupplier, + Supplier> blockEntityTypeSupplier, + BlockBehaviour.Properties properties) { + super(antTypesSupplier, blockEntityTypeSupplier, 10, false, false, properties); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return SHAPE; + } + + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!state.is(newState.getBlock()) && level instanceof ServerLevel serverLevel + && level.getBlockEntity(pos) instanceof AntNestBlockEntity trap) { + trap.releaseAll(serverLevel); + } + super.onRemove(state, level, pos, newState, movedByPiston); + } + + @Override + public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, ItemStack stack) { + if (blockEntity instanceof AntNestBlockEntity trap && level instanceof ServerLevel serverLevel) { + trap.releaseAll(serverLevel); + } + super.playerDestroy(level, player, pos, state, blockEntity, stack); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/ComputerBlock.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/ComputerBlock.java deleted file mode 100644 index c69141b9..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/ComputerBlock.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.craisinlord.antarchy.content.block; - -import com.craisinlord.antarchy.content.AntarchyObjects; -import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; -import com.craisinlord.antarchy.content.antmail.AntmailServerData; -import com.craisinlord.antarchy.content.client.AntarchyClientHooks; -import com.mojang.serialization.MapCodec; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.ItemInteractionResult; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.BaseEntityBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Mirror; -import net.minecraft.world.level.block.RenderShape; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityTicker; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.block.state.properties.DirectionProperty; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.VoxelShape; - -public final class ComputerBlock extends BaseEntityBlock { - public static final MapCodec CODEC = Block.simpleCodec(ComputerBlock::new); - public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); - public static final DirectionProperty FACING = net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING; - private static final VoxelShape SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D); - - public ComputerBlock(BlockBehaviour.Properties properties) { - super(properties.lightLevel(state -> state.getValue(ACTIVE) ? 7 : 0)); - registerDefaultState(stateDefinition.any().setValue(ACTIVE, false).setValue(FACING, Direction.NORTH)); - } - - @Override - public MapCodec codec() { - return CODEC; - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(ACTIVE, FACING); - } - - @Override - public BlockState getStateForPlacement(BlockPlaceContext context) { - return defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); - } - - @Override - protected BlockState rotate(BlockState state, Rotation rotation) { - return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); - } - - @Override - protected BlockState mirror(BlockState state, Mirror mirror) { - return state.rotate(mirror.getRotation(state.getValue(FACING))); - } - - @Override - protected RenderShape getRenderShape(BlockState state) { - return RenderShape.ENTITYBLOCK_ANIMATED; - } - - @Override - protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { - return SHAPE; - } - - @Override - public boolean isSignalSource(BlockState state) { - return true; - } - - @Override - protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { - return isAuthenticated(level, pos) ? 15 : 0; - } - - @Override - protected int getDirectSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { - return getSignal(state, level, pos, direction); - } - - private static boolean isAuthenticated(BlockGetter level, BlockPos pos) { - return level.getBlockEntity(pos) instanceof ComputerBlockEntity computer && computer.isAuthenticated(); - } - - @Override - protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { - if (!state.is(newState.getBlock()) && level.getBlockEntity(pos) instanceof ComputerBlockEntity computer) { - computer.dropDisks(level, pos); - if (level instanceof net.minecraft.server.level.ServerLevel serverLevel) { - AntmailServerData.access(serverLevel.getServer()).unregister(serverLevel.dimension().location(), pos); - } - } - super.onRemove(state, level, pos, newState, movedByPiston); - } - - @Override - public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { - return new ComputerBlockEntity(pos, state, AntarchyObjects.COMPUTER_BLOCK_ENTITY); - } - - @Override - public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { - return createTickerHelper(blockEntityType, AntarchyObjects.COMPUTER_BLOCK_ENTITY.get(), ComputerBlockEntity::tick); - } - - @Override - protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, - InteractionHand hand, BlockHitResult hitResult) { - if (level.getBlockEntity(pos) instanceof ComputerBlockEntity computer) { - if (level.isClientSide) { - if (stack.is(AntarchyObjects.FLOPPY_DISK.get())) { - return ItemInteractionResult.SUCCESS; - } - } else if (computer.insert(stack, player)) { - return ItemInteractionResult.CONSUME; - } - } - return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; - } - - @Override - protected net.minecraft.world.InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, - BlockHitResult hitResult) { - if (level.getBlockEntity(pos) instanceof ComputerBlockEntity computer) { - computer.activate(); - if (level.isClientSide) { - AntarchyClientHooks.openComputer(pos); - } - return net.minecraft.world.InteractionResult.SUCCESS; - } - return net.minecraft.world.InteractionResult.PASS; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/UndertrialSpawnerBlock.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/UndertrialSpawnerBlock.java index e7dd0661..19ac93be 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/UndertrialSpawnerBlock.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/UndertrialSpawnerBlock.java @@ -6,6 +6,11 @@ import com.mojang.serialization.MapCodec; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.SpawnEggItem; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; @@ -62,6 +67,22 @@ public final class UndertrialSpawnerBlock extends BaseEntityBlock { return new UndertrialSpawnerBlockEntity(pos, state, blockEntityType); } + @Override + protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, + Player player, net.minecraft.world.InteractionHand hand, + BlockHitResult hitResult) { + if (!(stack.getItem() instanceof SpawnEggItem)) { + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + if (level.getBlockEntity(pos) instanceof UndertrialSpawnerBlockEntity spawner) { + if (!level.isClientSide) { + spawner.setSpawnEntity(((SpawnEggItem) stack.getItem()).getType(stack)); + } + return ItemInteractionResult.sidedSuccess(level.isClientSide); + } + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + @Override public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { if (level.isClientSide) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/AntNestBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/AntNestBlockEntity.java index 76c68ee4..59a98938 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/AntNestBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/AntNestBlockEntity.java @@ -40,6 +40,28 @@ public class AntNestBlockEntity extends BlockEntity { } public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, AntNestBlockEntity nest) { + if (state.getBlock() instanceof com.craisinlord.antarchy.content.block.AntTrapBlock) { + if (level.getGameTime() % 20L == 0L && nest.storedAnts.size() < nest.maxOccupants()) { + net.minecraft.world.phys.AABB searchArea = new net.minecraft.world.phys.AABB(pos).inflate(15.0D); + for (BaseAntEntity ant : level.getEntitiesOfClass(BaseAntEntity.class, searchArea, BaseAntEntity::isAlive)) { + if (ant.distanceToSqr(net.minecraft.world.phys.Vec3.atCenterOf(pos)) > 225.0D) { + continue; + } + if (!nest.canAccept(ant.getType())) { + continue; + } + if (ant.distanceToSqr(net.minecraft.world.phys.Vec3.atCenterOf(pos)) <= 2.25D) { + nest.tryStoreAnt(ant); + if (nest.storedAnts.size() >= nest.maxOccupants()) { + break; + } + } else { + ant.getNavigation().moveTo(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 1.05D); + } + } + } + return; + } boolean playerNearby = level.hasNearbyAlivePlayer( pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, PLAYER_ACTIVATION_RANGE); if (!playerNearby) { @@ -115,12 +137,17 @@ public class AntNestBlockEntity extends BlockEntity { } public void releaseAll(ServerLevel level) { - while (!this.storedAnts.isEmpty()) { - this.releaseOne(level); + int remaining = this.storedAnts.size(); + while (remaining-- > 0 && !this.storedAnts.isEmpty()) { + this.releaseOne(level, true); } } private void releaseOne(ServerLevel level) { + this.releaseOne(level, false); + } + + private void releaseOne(ServerLevel level, boolean forceRelease) { if (this.storedAnts.isEmpty()) { return; } @@ -132,20 +159,39 @@ public class AntNestBlockEntity extends BlockEntity { return; } - BlockPos spawnPos = this.getBlockPos().above(); - if (!level.getBlockState(spawnPos).canBeReplaced()) { + BlockPos origin = this.getBlockPos(); + java.util.List candidates = new java.util.ArrayList<>(); + for (int y = 1; y <= 3; y++) { + candidates.add(origin.above(y)); + for (int radius = 1; radius <= 2; radius++) { + candidates.add(origin.offset(radius, y, 0)); + candidates.add(origin.offset(-radius, y, 0)); + candidates.add(origin.offset(0, y, radius)); + candidates.add(origin.offset(0, y, -radius)); + } + } + BlockPos spawnPos = null; + for (BlockPos candidate : candidates) { + antEntity.moveTo(candidate.getX() + SPAWN_OFFSET_XZ, candidate.getY() + SPAWN_OFFSET_Y, candidate.getZ() + SPAWN_OFFSET_XZ, + level.random.nextFloat() * 360.0F, 0.0F); + if (level.noCollision(antEntity)) { + spawnPos = candidate; + break; + } + } + if (spawnPos == null && !forceRelease) { return; } - - antEntity.moveTo(spawnPos.getX() + SPAWN_OFFSET_XZ, spawnPos.getY() + SPAWN_OFFSET_Y, spawnPos.getZ() + SPAWN_OFFSET_XZ, level.random.nextFloat() * 360.0F, 0.0F); - if (!level.noCollision(antEntity)) { - return; + if (spawnPos == null) { + antEntity.moveTo(origin.getX() + SPAWN_OFFSET_XZ, origin.getY() + SPAWN_OFFSET_Y, origin.getZ() + SPAWN_OFFSET_XZ, + level.random.nextFloat() * 360.0F, 0.0F); } - - this.storedAnts.remove(0); - antEntity.setNestPos(this.getBlockPos()); + antEntity.setNestPos(origin); antEntity.onExitNest(); - level.addFreshEntity(antEntity); + if (!level.addFreshEntity(antEntity) && !forceRelease) { + return; + } + this.storedAnts.remove(0); this.setChanged(); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/ComputerBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/ComputerBlockEntity.java deleted file mode 100644 index ed8b291d..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/ComputerBlockEntity.java +++ /dev/null @@ -1,458 +0,0 @@ -package com.craisinlord.antarchy.content.block.entity; - -import com.craisinlord.antarchy.content.AntarchyObjects; -import com.craisinlord.antarchy.content.block.ComputerBlock; -import com.craisinlord.antarchy.content.computer.ComputerFileSystem; -import com.craisinlord.antarchy.content.computer.ComputerDesktopState; -import com.craisinlord.antarchy.content.guide.ComputerGuideData; -import net.minecraft.core.BlockPos; -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.Containers; -import net.minecraft.world.entity.player.Player; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; -import software.bernie.geckolib.animatable.GeoBlockEntity; -import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; -import software.bernie.geckolib.animation.AnimatableManager; -import software.bernie.geckolib.animation.AnimationController; -import software.bernie.geckolib.animation.RawAnimation; -import software.bernie.geckolib.animation.PlayState; -import software.bernie.geckolib.util.GeckoLibUtil; - -public final class ComputerBlockEntity extends BlockEntity implements GeoBlockEntity { - private static final ResourceLocation INTRODUCTION_DISK = ResourceLocation.fromNamespaceAndPath("antarchy", "introduction"); - private static final String DISKS_TAG = "Disks"; - private static final String ACTIVE_UNTIL_TAG = "ActiveUntil"; - private static final String PASSWORD_TAG = "Password"; - private static final String AUTHENTICATED_TAG = "Authenticated"; - private static final String AUTHENTICATED_UNTIL_TAG = "AuthenticatedUntil"; - private static final String DESKTOP_TAG = "Desktop"; - private static final String BASILISK_SCORE_TAG = "BasiliskScore"; - private static final String BASILISK_HIGH_SCORE_TAG = "BasiliskHighScore"; - private static final String ANTMAN_SCORE_TAG = "AntmanScore"; - private static final String ANTMAN_HIGH_SCORE_TAG = "AntmanHighScore"; - private static final String BLOCKLE_DAY_TAG = "BlockleDay"; - private static final String BLOCKLE_GUESSES_TAG = "BlockleGuesses"; - private static final long AUTHENTICATION_TIMEOUT = 6000L; - private final Set physicalDiskIds = new LinkedHashSet<>(); - private final ComputerFileSystem fileSystem = new ComputerFileSystem(); - private final ComputerDesktopState desktopState = new ComputerDesktopState(); - private final AnimatableInstanceCache animatableCache = GeckoLibUtil.createInstanceCache(this); - private static final RawAnimation ON = RawAnimation.begin().thenLoop("on_state"); - private static final RawAnimation OFF = RawAnimation.begin().thenLoop("off_state"); - private static final RawAnimation TURN_ON = RawAnimation.begin().thenPlay("turn_on"); - private static final RawAnimation TURN_OFF = RawAnimation.begin().thenPlay("turn_off"); - private long activeUntil; - private String password = ""; - private boolean authenticated; - private long authenticatedUntil; - private java.util.UUID activeUser; - private boolean lastActive; - private int basiliskScore; - private int basiliskHighScore; - private int antmanScore; - private int antmanHighScore; - private long blockleDay = -1L; - private final List blockleGuesses = new ArrayList<>(); - - public ComputerBlockEntity(BlockPos pos, BlockState state, Supplier> type) { - super(type.get(), pos, state); - } - - public static void tick(Level level, BlockPos pos, BlockState state, ComputerBlockEntity computer) { - boolean active = computer.isActive(); - if (!level.isClientSide && state.getValue(ComputerBlock.ACTIVE) != active) { - level.setBlock(pos, state.setValue(ComputerBlock.ACTIVE, active), 3); - } - if (active != computer.lastActive) { - computer.lastActive = active; - computer.triggerAnim("transitions", active ? "turn_on" : "turn_off"); - } - if (!level.isClientSide && computer.activeUntil > 0L && level.getGameTime() >= computer.activeUntil && !level.hasNeighborSignal(pos)) { - computer.activeUntil = 0L; - computer.setChanged(); - level.sendBlockUpdated(pos, state, state, 3); - } - if (!level.isClientSide && computer.authenticated && (level.getGameTime() >= computer.authenticatedUntil || computer.activeUser == null || - (level instanceof net.minecraft.server.level.ServerLevel serverLevel && serverLevel.getServer().getPlayerList().getPlayer(computer.activeUser) == null))) { - computer.clearAuthentication(); - level.sendBlockUpdated(pos, state, state, 3); - } - } - - public boolean insert(ItemStack stack, Player player) { - if (!stack.is(AntarchyObjects.FLOPPY_DISK.get())) { - return false; - } - ResourceLocation diskId = stack.get(AntarchyObjects.FLOPPY_DISK_COMPONENT.get()); - if (diskId == null || this.physicalDiskIds.contains(diskId)) { - stack.shrink(1); - activate(); - return true; - } - this.physicalDiskIds.add(diskId); - ComputerGuideData.Disk disk = ComputerGuideData.disk(diskId); - if (disk != null && !disk.wallpaper().isBlank()) { - try { - desktopState.unlockWallpaper(ResourceLocation.parse(disk.wallpaper())); - } catch (RuntimeException ignored) { - } - } - stack.shrink(1); - activate(); - setChanged(); - if (level != null) { - level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3); - } - return true; - } - - public void activate() { - if (level != null && !level.isClientSide) { - activeUntil = Math.max(activeUntil, level.getGameTime() + 40L); - if (!getBlockState().getValue(ComputerBlock.ACTIVE)) { - level.setBlock(worldPosition, getBlockState().setValue(ComputerBlock.ACTIVE, true), 3); - } - setChanged(); - level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3); - } - } - - public boolean isActive() { - return level != null && (level.hasNeighborSignal(worldPosition) || isAuthenticated()); - } - - @Override - public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { - controllers.add(new AnimationController<>(this, "state", state -> state.setAndContinue(isActive() ? ON : OFF))); - controllers.add(new AnimationController<>(this, "transitions", state -> PlayState.STOP) - .triggerableAnim("turn_on", TURN_ON) - .triggerableAnim("turn_off", TURN_OFF)); - } - - @Override - public AnimatableInstanceCache getAnimatableInstanceCache() { - return animatableCache; - } - - public List diskIds() { - List ids = new ArrayList<>(physicalDiskIds.size() + 1); - ids.add(INTRODUCTION_DISK); - ids.addAll(physicalDiskIds); - return List.copyOf(ids); - } - - public boolean ejectOne(Player player) { - if (physicalDiskIds.isEmpty() || !(player instanceof ServerPlayer serverPlayer)) { - return false; - } - return ejectOne(serverPlayer, physicalDiskIds.iterator().next()); - } - - public boolean ejectOne(ServerPlayer player, ResourceLocation diskId) { - if (level == null || level.isClientSide || diskId == null || !isAuthenticated() || !playerCanReach(player) || !physicalDiskIds.remove(diskId)) { - return false; - } - ItemStack disk = diskStack(diskId); - if (!player.addItem(disk)) { - player.drop(disk, false); - } - setChanged(); - level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3); - return true; - } - - public void dropDisks(Level level, BlockPos pos) { - if (level.isClientSide) { - return; - } - for (ResourceLocation diskId : physicalDiskIds) { - Containers.dropItemStack(level, pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, diskStack(diskId)); - } - physicalDiskIds.clear(); - } - - public boolean hasPassword() { - return !password.isEmpty(); - } - - public boolean setPassword(ServerPlayer player, String newPassword) { - if (newPassword == null || newPassword.isBlank() || !isAuthenticated()) { - return false; - } - password = newPassword; - setChanged(); - return true; - } - - public boolean initializePassword(ServerPlayer player, String newPassword) { - if (hasPassword() || newPassword == null || newPassword.isBlank() || !claimUser(player)) { - return false; - } - password = newPassword; - authenticated = true; - authenticatedUntil = level.getGameTime() + AUTHENTICATION_TIMEOUT; - setChanged(); - return true; - } - - public boolean initializePassword(String newPassword) { - return false; - } - - public boolean authenticate(ServerPlayer player, String attemptedPassword) { - if (!hasPassword() || !password.equals(attemptedPassword) || !claimUser(player)) { - return false; - } - authenticated = true; - authenticatedUntil = level == null ? 0L : level.getGameTime() + AUTHENTICATION_TIMEOUT; - setChanged(); - return true; - } - - public boolean authenticate(String attemptedPassword) { - return false; - } - - public boolean isAuthenticated() { - if (!authenticated) { - return false; - } - if (level != null && level.getGameTime() >= authenticatedUntil) { - authenticated = false; - authenticatedUntil = 0L; - setChanged(); - return false; - } - return true; - } - - public boolean isAuthenticatedBy(ServerPlayer player) { - return isAuthenticated() && activeUser != null && activeUser.equals(player.getUUID()); - } - - public boolean claimUser(ServerPlayer player) { - if (level == null || level.isClientSide || !playerCanReach(player)) { - return false; - } - if (activeUser != null && !activeUser.equals(player.getUUID()) && isAuthenticated()) { - return false; - } - activeUser = player.getUUID(); - return true; - } - - public boolean playerCanReach(ServerPlayer player) { - return player.level() == level && player.distanceToSqr(worldPosition.getX() + 0.5D, worldPosition.getY() + 0.5D, worldPosition.getZ() + 0.5D) <= 64.0D; - } - - public ComputerFileSystem fileSystem() { - return fileSystem; - } - - public ComputerDesktopState desktopState() { - return desktopState; - } - - public int basiliskScore() { - return basiliskScore; - } - - public int basiliskHighScore() { - return basiliskHighScore; - } - - public void setBasiliskScore(int score) { - basiliskScore = Math.max(0, score); - basiliskHighScore = Math.max(basiliskHighScore, basiliskScore); - setChanged(); - } - - public int antmanScore() { return antmanScore; } - public int antmanHighScore() { return antmanHighScore; } - public void setAntmanScore(int score) { - antmanScore = Math.max(0, score); - antmanHighScore = Math.max(antmanHighScore, antmanScore); - setChanged(); - } - - public long blockleDay() { - return blockleDay; - } - - public List blockleGuesses() { - return List.copyOf(blockleGuesses); - } - - public void resetBlockle(long day) { - if (blockleDay == day) return; - blockleDay = day; - blockleGuesses.clear(); - setChanged(); - } - - public boolean addBlockleGuess(String guess) { - if (blockleGuesses.size() >= 6) return false; - blockleGuesses.add(guess); - setChanged(); - return true; - } - - public boolean unlockWallpaper(ResourceLocation id) { - return serverDesktopMutation(() -> desktopState.unlockWallpaper(id)); - } - - public boolean installProgram(ResourceLocation id) { - return serverDesktopMutation(() -> desktopState.installProgram(id)); - } - - public boolean uninstallProgram(ResourceLocation id) { - return serverDesktopMutation(() -> desktopState.uninstallProgram(id)); - } - - public boolean selectWallpaper(ResourceLocation id) { - return serverDesktopMutation(() -> desktopState.selectWallpaper(id)); - } - - public boolean setIconPosition(ResourceLocation id, int x, int y) { - return serverDesktopMutation(() -> desktopState.setIconPosition(id, x, y)); - } - - public boolean removeIconPosition(ResourceLocation id) { - return serverDesktopMutation(() -> desktopState.removeIconPosition(id)); - } - - private boolean serverDesktopMutation(java.util.function.BooleanSupplier mutation) { - if (level == null || level.isClientSide) { - return false; - } - boolean changed = mutation.getAsBoolean(); - if (changed) { - setChanged(); - level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3); - } - return changed; - } - - public boolean canUseFileSystem(ServerPlayer player) { - return level != null && !level.isClientSide && isAuthenticated() && playerCanReach(player) && (activeUser == null || activeUser.equals(player.getUUID())); - } - - public void logout() { - clearAuthentication(); - } - - public void releaseUser(ServerPlayer player) { - if (activeUser != null && activeUser.equals(player.getUUID())) { - activeUser = null; - setChanged(); - } - } - - public void clearAuthentication() { - authenticated = false; - authenticatedUntil = 0L; - activeUser = null; - setChanged(); - } - - public long authenticationTimeout() { - return AUTHENTICATION_TIMEOUT; - } - - public boolean hasActiveUser() { - return activeUser != null && isAuthenticated(); - } - - private static ItemStack diskStack(ResourceLocation diskId) { - ItemStack stack = new ItemStack(AntarchyObjects.FLOPPY_DISK.get()); - stack.set(AntarchyObjects.FLOPPY_DISK_COMPONENT.get(), diskId); - return stack; - } - - @Override - protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { - super.loadAdditional(tag, registries); - physicalDiskIds.clear(); - ListTag disks = tag.getList(DISKS_TAG, 8); - for (int index = 0; index < disks.size(); index++) { - try { - ResourceLocation diskId = ResourceLocation.parse(disks.getString(index)); - if (!INTRODUCTION_DISK.equals(diskId)) { - physicalDiskIds.add(diskId); - } - } catch (Exception ignored) { - } - } - activeUntil = tag.getLong(ACTIVE_UNTIL_TAG); - password = tag.getString(PASSWORD_TAG); - fileSystem.load(tag, registries); - desktopState.load(tag.getCompound(DESKTOP_TAG)); - basiliskScore = tag.getInt(BASILISK_SCORE_TAG); - basiliskHighScore = tag.getInt(BASILISK_HIGH_SCORE_TAG); - antmanScore = tag.getInt(ANTMAN_SCORE_TAG); - antmanHighScore = tag.getInt(ANTMAN_HIGH_SCORE_TAG); - blockleDay = tag.contains(BLOCKLE_DAY_TAG) ? tag.getLong(BLOCKLE_DAY_TAG) : -1L; - blockleGuesses.clear(); - ListTag blockle = tag.getList(BLOCKLE_GUESSES_TAG, 8); - for (int index = 0; index < blockle.size(); index++) { - String guess = blockle.getString(index); - if (guess.length() == 5 && !blockleGuesses.contains(guess) && blockleGuesses.size() < 6) blockleGuesses.add(guess); - } - authenticated = false; - authenticatedUntil = 0L; - activeUser = null; - } - - @Override - protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { - super.saveAdditional(tag, registries); - ListTag disks = new ListTag(); - for (ResourceLocation diskId : physicalDiskIds) { - disks.add(StringTag.valueOf(diskId.toString())); - } - tag.put(DISKS_TAG, disks); - tag.putLong(ACTIVE_UNTIL_TAG, activeUntil); - tag.putString(PASSWORD_TAG, password); - fileSystem.save(tag, registries); - tag.put(DESKTOP_TAG, desktopState.save()); - tag.putInt(BASILISK_SCORE_TAG, basiliskScore); - tag.putInt(BASILISK_HIGH_SCORE_TAG, basiliskHighScore); - tag.putInt(ANTMAN_SCORE_TAG, antmanScore); - tag.putInt(ANTMAN_HIGH_SCORE_TAG, antmanHighScore); - tag.putLong(BLOCKLE_DAY_TAG, blockleDay); - ListTag blockle = new ListTag(); - for (String guess : blockleGuesses) blockle.add(StringTag.valueOf(guess)); - tag.put(BLOCKLE_GUESSES_TAG, blockle); - } - - @Override - public CompoundTag getUpdateTag(HolderLookup.Provider registries) { - CompoundTag tag = new CompoundTag(); - tag.putBoolean("HasPassword", hasPassword()); - tag.putBoolean(AUTHENTICATED_TAG, isAuthenticated()); - ListTag disks = new ListTag(); - for (ResourceLocation diskId : physicalDiskIds) disks.add(StringTag.valueOf(diskId.toString())); - tag.put(DISKS_TAG, disks); - return tag; - } - - @Override - public net.minecraft.network.protocol.Packet getUpdatePacket() { - return net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket.create(this); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UnderVaultBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UnderVaultBlockEntity.java index 841b0871..83cf27eb 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UnderVaultBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UnderVaultBlockEntity.java @@ -30,6 +30,7 @@ import java.util.function.Supplier; public final class UnderVaultBlockEntity extends BlockEntity { private final VaultBlockEntity vaultData; + private boolean playerWasNearby; public UnderVaultBlockEntity(BlockPos pos, BlockState state, Supplier> type) { super(type.get(), pos, state); @@ -49,11 +50,12 @@ public final class UnderVaultBlockEntity extends BlockEntity { // The vanilla vault detector can miss this ceiling-mounted wrapper because its // block state has a separate render-facing property. Keep the state synchronized // with the same four-block activation range before running vanilla's state machine. - if (state.getValue(UnderVaultBlock.STATE) == VaultState.INACTIVE - && !level.getEntitiesOfClass(Player.class, new AABB(pos).inflate(4.0D), player -> !player.isSpectator()).isEmpty()) { + boolean playerNearby = !level.getEntitiesOfClass(Player.class, new AABB(pos).inflate(4.0D), player -> !player.isSpectator()).isEmpty(); + if (state.getValue(UnderVaultBlock.STATE) == VaultState.INACTIVE && playerNearby && !blockEntity.playerWasNearby) { state = state.setValue(UnderVaultBlock.STATE, VaultState.ACTIVE); level.setBlockAndUpdate(pos, state); } + blockEntity.playerWasNearby = playerNearby; VaultBlockEntity.Server.tick(level, pos, state, blockEntity.vaultData.getConfig(), blockEntity.vaultData.getServerData(), blockEntity.vaultData.getSharedData()); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UndertrialSpawnerBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UndertrialSpawnerBlockEntity.java index 3f893500..db0965e7 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UndertrialSpawnerBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UndertrialSpawnerBlockEntity.java @@ -20,6 +20,7 @@ import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.Mob; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawner; import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerConfig; @@ -182,6 +183,16 @@ public final class UndertrialSpawnerBlockEntity extends BlockEntity implements T return trialSpawner; } + public void setSpawnEntity(EntityType entityType) { + if (this.level == null) { + return; + } + // Match vanilla Trial Spawner interaction: override the next mob's entity id while + // preserving weighted potentials, ominous settings, and the trial's reward configuration. + this.trialSpawner.getData().setEntityId(this.trialSpawner, this.level.random, entityType); + this.markUpdated(); + } + public double getClientSpin(float partialTick) { return this.clientOldSpin + (this.clientSpin - this.clientOldSpin) * partialTick; } @@ -199,7 +210,7 @@ public final class UndertrialSpawnerBlockEntity extends BlockEntity implements T private TrialSpawner createRewardlessSpawner(TrialSpawner source) { TrialSpawner rewardless = new TrialSpawner(withoutLootTables(source.getNormalConfig()), withoutLootTables(source.getOminousConfig()), - source.getData(), source.getRequiredPlayerRange(), targetCooldownLength(source), this, + source.getData(), targetCooldownLength(source), source.getRequiredPlayerRange(), this, source.getPlayerDetector(), source.getEntitySelector()); rewardless.overridePeacefulAndMobSpawnRule(); return rewardless; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntarchyClientHooks.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntarchyClientHooks.java deleted file mode 100644 index 1b822b80..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntarchyClientHooks.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.craisinlord.antarchy.content.client; - -import net.minecraft.core.BlockPos; - -import java.util.function.Consumer; - -public final class AntarchyClientHooks { - private static Consumer computerOpener = pos -> { - }; - - private AntarchyClientHooks() { - } - - public static void setComputerOpener(Consumer opener) { - computerOpener = opener; - } - - public static void openComputer(BlockPos pos) { - computerOpener.accept(pos); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntarchyGameClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntarchyGameClientState.java new file mode 100644 index 00000000..1b991feb --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntarchyGameClientState.java @@ -0,0 +1,32 @@ +package com.craisinlord.antarchy.content.client; + +import com.craisinlord.antarchy.content.network.AntarchyGamePayload; +import com.craisinlord.antarchy.content.network.AntarchyGameResultPayload; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import net.minecraft.core.BlockPos; + +/** Client state for Antarchy-owned games hosted by AntOS. */ +public final class AntarchyGameClientState { + private static final Map GAME_STATES = new ConcurrentHashMap<>(); + + private AntarchyGameClientState() { } + + public static void update(AntarchyGameResultPayload result) { + String[] envelope = result.data().split("\u0000", 3); + if (envelope.length < 3) return; + if (envelope[0].equals(Integer.toString(AntarchyGamePayload.BASILISK_STATE)) + || envelope[0].equals(Integer.toString(AntarchyGamePayload.ANTMAN_STATE))) { + GAME_STATES.put(key(result.pos(), envelope[0]), + result.success() ? envelope[2] : ""); + } else if (envelope[0].equals(Integer.toString(AntarchyGamePayload.BLOCKLE_STATE)) + || envelope[0].equals(Integer.toString(AntarchyGamePayload.BLOCKLE_GUESS))) { + if (!result.success()) return; + BlockleClientState.update(result); + } + } + + public static String getGameState(BlockPos pos, int action) { return GAME_STATES.get(key(pos, Integer.toString(action))); } + public static void clearGameState(BlockPos pos, int action) { GAME_STATES.remove(key(pos, Integer.toString(action))); } + private static String key(BlockPos pos, String action) { return pos.asLong() + "|" + action; } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntmailClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntmailClientState.java deleted file mode 100644 index 6057fc03..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/AntmailClientState.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.craisinlord.antarchy.content.client; - -import com.craisinlord.antarchy.content.network.AntmailResultPayload; -import com.craisinlord.antarchy.content.antmail.AntmailDebug; -import com.craisinlord.antarchy.content.antmail.AntmailMessage; -import net.minecraft.core.BlockPos; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public final class AntmailClientState { - private static final int MAX_CACHED_DETAILS_PER_MAILBOX = 8; - private static final Map RESULTS = new ConcurrentHashMap<>(); - private static final Map MAILBOXES = new ConcurrentHashMap<>(); - private static final Map VERSIONS = new ConcurrentHashMap<>(); - private static final Map> DETAILS = new ConcurrentHashMap<>(); - - private AntmailClientState() { - } - - public static void update(AntmailResultPayload payload) { - AntmailDebug.log("S2C result pos=" + payload.pos() + " status=" + payload.status() + " detail=" + payload.detail() - + " addressPresent=" + !payload.address().isBlank() + " dataChars=" + payload.data().length() - + " version=" + payload.version()); - RESULTS.put(payload.pos(), payload); - if ("unchanged".equals(payload.detail())) { - if (MAILBOXES.containsKey(payload.pos())) { - VERSIONS.put(payload.pos(), payload.version()); - } else { - // An unchanged response cannot initialize a client that has no cached snapshot. - // Keep the next poll unconditional instead of getting stuck on "unchanged". - AntmailDebug.log("received unchanged without cached mailbox at " + payload.pos() + "; forcing full snapshot on next poll"); - VERSIONS.remove(payload.pos()); - } - return; - } else if ("message_detail".equals(payload.detail()) && !payload.data().isBlank()) { - try { - AntmailMessage message = AntmailMessage.fromTag(com.craisinlord.antarchy.content.antmail.AntmailWire.decodeTag(payload.data())); - Map details = DETAILS.computeIfAbsent(payload.pos(), ignored -> new ConcurrentHashMap<>()); - details.put(message.id(), message); - while (details.size() > MAX_CACHED_DETAILS_PER_MAILBOX) { - java.util.UUID oldest = details.keySet().iterator().next(); - details.remove(oldest); - } - } catch (RuntimeException ignored) { - } - } else if (!payload.data().isBlank()) { - MAILBOXES.put(payload.pos(), payload); - VERSIONS.put(payload.pos(), payload.version()); - } else { - // A failed or unconfigured response is authoritative; never show a - // mailbox from a previous address/computer at this position. - MAILBOXES.remove(payload.pos()); - VERSIONS.remove(payload.pos()); - } - } - - public static void clear(BlockPos pos) { - RESULTS.remove(pos); - MAILBOXES.remove(pos); - VERSIONS.remove(pos); - DETAILS.remove(pos); - } - - public static AntmailResultPayload get(BlockPos pos) { - return RESULTS.get(pos); - } - - public static AntmailResultPayload getMailbox(BlockPos pos) { - return MAILBOXES.get(pos); - } - - public static long getVersion(BlockPos pos) { - return VERSIONS.getOrDefault(pos, 0L); - } - - public static AntmailMessage getMessage(BlockPos pos, java.util.UUID id) { - Map details = DETAILS.get(pos); - return details == null ? null : details.get(id); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/BlockleClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/BlockleClientState.java index a11c902a..34c663cb 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/BlockleClientState.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/BlockleClientState.java @@ -1,22 +1,22 @@ package com.craisinlord.antarchy.content.client; -import com.craisinlord.antarchy.content.network.ComputerAccessResultPayload; +import com.craisinlord.antarchy.content.network.AntarchyGameResultPayload; import net.minecraft.core.BlockPos; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public final class BlockleClientState { - private static final Map RESULTS = new ConcurrentHashMap<>(); + private static final Map RESULTS = new ConcurrentHashMap<>(); private BlockleClientState() { } - public static void update(ComputerAccessResultPayload result) { + public static void update(AntarchyGameResultPayload result) { RESULTS.put(result.pos(), result); } - public static ComputerAccessResultPayload get(BlockPos pos) { + public static AntarchyGameResultPayload get(BlockPos pos) { return RESULTS.get(pos); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerAccessClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerAccessClientState.java deleted file mode 100644 index 18c550ad..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerAccessClientState.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.craisinlord.antarchy.content.client; - -import com.craisinlord.antarchy.content.network.ComputerAccessResultPayload; -import net.minecraft.core.BlockPos; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public final class ComputerAccessClientState { - private static final Map RESULTS = new ConcurrentHashMap<>(); - - private ComputerAccessClientState() { - } - - public static void update(ComputerAccessResultPayload result) { - ComputerStructureLocatorClientState.update(result); - if (result.data().startsWith(com.craisinlord.antarchy.content.network.ComputerAccessPayload.ARCHIVE_STATE + "\0")) { - String[] envelope = result.data().split("\u0000", 3); - if (envelope.length == 3) { - com.craisinlord.antarchy.content.guide.ComputerGuideData.applyNetworkSnapshot(envelope[2]); - } - } - if (result.data().startsWith("18\u0000") || result.data().startsWith("19\u0000")) { - BlockleClientState.update(result); - } - // File, terminal, desktop, and wallpaper responses carry their own operation - // status. They must not replace the last authoritative login state. - if (result.data().isBlank()) RESULTS.put(result.pos(), result); - ComputerFileSystemClientState.update(result); - } - - public static void clear(BlockPos pos) { - RESULTS.remove(pos); - } - - public static ComputerAccessResultPayload get(BlockPos pos) { - return RESULTS.get(pos); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerFileSystemClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerFileSystemClientState.java deleted file mode 100644 index 9442bb73..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerFileSystemClientState.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.craisinlord.antarchy.content.client; - -import com.craisinlord.antarchy.content.network.ComputerAccessPayload; -import com.craisinlord.antarchy.content.network.ComputerAccessResultPayload; -import net.minecraft.core.BlockPos; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public final class ComputerFileSystemClientState { - private static final Map STATES = new ConcurrentHashMap<>(); - - private ComputerFileSystemClientState() { - } - - public static void update(ComputerAccessResultPayload payload) { - if (payload.data().isEmpty()) return; - String[] envelope = payload.data().split("\u0000", 3); - if (envelope.length < 3) return; - int action; - try { - action = Integer.parseInt(envelope[0]); - } catch (NumberFormatException ignored) { - return; - } - // Terminal, desktop, and game responses use the same payload envelope, - // but their status must not leak into the file explorer's shared state. - if (action != ComputerAccessPayload.FILE_LIST - && action != ComputerAccessPayload.FILE_OPEN - && action != ComputerAccessPayload.FILE_CREATE - && action != ComputerAccessPayload.FILE_SAVE - && action != ComputerAccessPayload.FILE_DELETE - && action != ComputerAccessPayload.FILE_MOVE) { - return; - } - State state = STATES.computeIfAbsent(payload.pos(), ignored -> new State()); - state.success = payload.result() == ComputerAccessResultPayload.SUCCESS; - state.error = envelope[1]; - if (state.success) state.error = ""; - if (action == ComputerAccessPayload.FILE_CREATE || action == ComputerAccessPayload.FILE_SAVE - || action == ComputerAccessPayload.FILE_DELETE || action == ComputerAccessPayload.FILE_MOVE) { - state.lastMutationAction = action; - state.lastMutationPath = envelope[2]; - state.lastMutationSuccess = state.success; - state.lastMutationError = envelope[1]; - } - if (action == ComputerAccessPayload.FILE_LIST) { - state.files = new ArrayList<>(); - if (state.success && !envelope[2].isEmpty()) state.files.addAll(List.of(envelope[2].split("\\n"))); - } else if (action == ComputerAccessPayload.FILE_OPEN) { - if (state.success) { - String[] file = envelope[2].split("\u0000", 2); - if (file.length == 2) { - state.openedPath = file[0]; - state.openedContents = file[1]; - } - } else { - state.openedPath = ""; - state.openedContents = ""; - } - } else if ((action == ComputerAccessPayload.FILE_DELETE || action == ComputerAccessPayload.FILE_MOVE) && state.success) { - state.openedPath = ""; - state.openedContents = ""; - } - } - - public static State get(BlockPos pos) { - return STATES.computeIfAbsent(pos, ignored -> new State()); - } - - public static final class State { - private List files = List.of(); - private String openedPath = ""; - private String openedContents = ""; - private String error = ""; - private boolean success; - private int lastMutationAction = -1; - private String lastMutationPath = ""; - private boolean lastMutationSuccess; - private String lastMutationError = ""; - - public List files() { return List.copyOf(files); } - public String openedPath() { return openedPath; } - public String openedContents() { return openedContents; } - public String error() { return error; } - public boolean success() { return success; } - public int lastMutationAction() { return lastMutationAction; } - public String lastMutationPath() { return lastMutationPath; } - public boolean lastMutationSuccess() { return lastMutationSuccess; } - public String lastMutationError() { return lastMutationError; } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerStructureLocatorClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerStructureLocatorClientState.java deleted file mode 100644 index 03697861..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerStructureLocatorClientState.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.craisinlord.antarchy.content.client; - -import com.craisinlord.antarchy.content.network.ComputerAccessPayload; -import com.craisinlord.antarchy.content.network.ComputerAccessResultPayload; -import net.minecraft.core.BlockPos; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public final class ComputerStructureLocatorClientState { - private static final Map RESULTS = new ConcurrentHashMap<>(); - - private ComputerStructureLocatorClientState() { - } - - public static void searching(BlockPos pos, String dimensionId) { - String dimension = dimensionId == null || dimensionId.isBlank() ? "TARGET DIMENSION" : dimensionId.toUpperCase(java.util.Locale.ROOT); - RESULTS.put(pos, "SEARCHING IN " + dimension + "..."); - } - - public static void update(ComputerAccessResultPayload payload) { - String[] envelope = payload.data().split("\u0000", 3); - if (envelope.length != 3) return; - try { - if (Integer.parseInt(envelope[0]) != ComputerAccessPayload.LOCATE_STRUCTURE) return; - } catch (NumberFormatException ignored) { - return; - } - - String message = switch (envelope[1]) { - case "not_found" -> "NO STRUCTURE FOUND WITHIN RANGE"; - case "dimension_unavailable" -> "TARGET DIMENSION UNAVAILABLE"; - default -> "LOCATOR UNAVAILABLE"; - }; - RESULTS.put(payload.pos(), payload.result() == ComputerAccessResultPayload.SUCCESS - ? "STRUCTURE START // " + envelope[2] - : message); - } - - public static String get(BlockPos pos) { - return RESULTS.getOrDefault(pos, "COORDINATES // SEARCH WHEN SELECTED"); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/AntarchyComputerGames.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/AntarchyComputerGames.java new file mode 100644 index 00000000..4860aa62 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/AntarchyComputerGames.java @@ -0,0 +1,79 @@ +package com.craisinlord.antarchy.content.client.game; + +import com.craisinlord.antos.api.client.game.ComputerGame; +import com.craisinlord.antos.api.client.game.ComputerGameRegistry; +import com.craisinlord.antarchy.content.network.AntarchyGameNetworking; +import com.craisinlord.antarchy.content.network.AntarchyGamePayload; +import com.craisinlord.antarchy.content.client.AntarchyGameClientState; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import java.util.function.IntPredicate; +import java.util.function.Predicate; + +/** Registers Antarchy's native games with the AntOS Games app. */ +public final class AntarchyComputerGames { + private AntarchyComputerGames() {} + + public static void register() { + ComputerGameRegistry.register(game("basilisk", "BASILISK", pos -> { + BasiliskProgram program = new BasiliskProgram(true, 0, 0, score -> AntarchyGameNetworking.saveBasiliskScore(pos, score)); + var loaded = new java.util.concurrent.atomic.AtomicBoolean(); + AntarchyGameNetworking.requestBasiliskState(pos); + return new GameSession(() -> { if (!loaded.get()) loaded.set(loadScores(pos, AntarchyGamePayload.BASILISK_STATE, program::setStoredScores)); program.tick(); }, program::render, key -> loaded.get() && program.keyPressed(key), ignored -> false); + })); + ComputerGameRegistry.register(game("antman", "ANTMAN.EXE", pos -> { + AntmanProgram program = new AntmanProgram(true, 0, 0, score -> AntarchyGameNetworking.saveAntmanScore(pos, score)); + var loaded = new java.util.concurrent.atomic.AtomicBoolean(); + AntarchyGameNetworking.requestAntmanState(pos); + return new GameSession(() -> { if (!loaded.get()) loaded.set(loadScores(pos, AntarchyGamePayload.ANTMAN_STATE, program::setStoredScores)); program.tick(); }, program::render, key -> loaded.get() && program.keyPressed(key), ignored -> false); + })); + ComputerGameRegistry.register(game("blockle", "BLOCKLE", pos -> { + BlockleProgram program = new BlockleProgram(pos); + program.setInstalled(true); + return new GameSession(program::tick, program::render, program::keyPressed, program::charTyped); + })); + } + + private static boolean loadScores(BlockPos pos, int action, java.util.function.BiConsumer setter) { + String response = AntarchyGameClientState.getGameState(pos, action); + if (response == null) return false; + if (response.isBlank()) { AntarchyGameClientState.clearGameState(pos, action); return true; } + String[] scores = response.split("\0", 2); + if (scores.length != 2) return false; + try { setter.accept(Integer.parseInt(scores[0]), Integer.parseInt(scores[1])); } + catch (NumberFormatException ignored) { return false; } + AntarchyGameClientState.clearGameState(pos, action); + return true; + } + + private static ComputerGame game(String path, String title, ProgramFactory factory) { + ResourceLocation id = ResourceLocation.fromNamespaceAndPath("antarchy", path); + ResourceLocation disk = ResourceLocation.fromNamespaceAndPath("antarchy", path + "_game"); + return new ComputerGame() { + @Override public ResourceLocation id() { return id; } + @Override public ResourceLocation diskId() { return disk; } + @Override public String title() { return title; } + @Override public Session create(BlockPos position) { return factory.create(position); } + }; + } + + @FunctionalInterface + private interface ProgramFactory { ComputerGame.Session create(BlockPos position); } + + @FunctionalInterface private interface GameRenderer { void render(GuiGraphics graphics, Font font, int x, int y, int width, int height); } + private static final class GameSession implements ComputerGame.Session { + private final Runnable ticker; + private final GameRenderer renderer; + private final IntPredicate keyHandler; + private final Predicate charHandler; + private GameSession(Runnable ticker, GameRenderer renderer, IntPredicate keyHandler, Predicate charHandler) { + this.ticker = ticker; this.renderer = renderer; this.keyHandler = keyHandler; this.charHandler = charHandler; + } + @Override public void tick() { ticker.run(); } + @Override public void render(GuiGraphics graphics, Font font, int x, int y, int width, int height) { renderer.render(graphics, font, x, y, width, height); } + @Override public boolean keyPressed(int keyCode) { return keyHandler.test(keyCode); } + @Override public boolean charTyped(char codePoint) { return charHandler.test(codePoint); } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/BlockleProgram.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/BlockleProgram.java index c2413431..61642d14 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/BlockleProgram.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/BlockleProgram.java @@ -1,8 +1,8 @@ package com.craisinlord.antarchy.content.client.game; import com.craisinlord.antarchy.content.client.BlockleClientState; -import com.craisinlord.antarchy.content.network.ComputerAccessResultPayload; -import com.craisinlord.antarchy.content.network.ComputerNetworking; +import com.craisinlord.antarchy.content.network.AntarchyGameResultPayload; +import com.craisinlord.antarchy.content.network.AntarchyGameNetworking; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.core.BlockPos; @@ -40,11 +40,11 @@ public final class BlockleProgram { } public void requestState() { - if (installed) ComputerNetworking.requestBlockleState(position); + if (installed) AntarchyGameNetworking.requestBlockleState(position); } public void tick() { - ComputerAccessResultPayload result = BlockleClientState.get(position); + AntarchyGameResultPayload result = BlockleClientState.get(position); if (result == null) return; if (result.data().equals(lastResponse)) return; lastResponse = result.data(); @@ -72,7 +72,7 @@ public final class BlockleProgram { if (keyCode == 257 || keyCode == 335) { if (!received) return true; if (input.length() == 5 && phase.equals("PLAYING") && !pending) { - ComputerNetworking.submitBlockleGuess(position, input); + AntarchyGameNetworking.submitBlockleGuess(position, input); pending = true; status = "SUBMITTING"; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/ComputerModel.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/ComputerModel.java deleted file mode 100644 index 87bdb90b..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/model/ComputerModel.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.craisinlord.antarchy.content.client.model; - -import com.craisinlord.antarchy.Antarchy; -import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; -import net.minecraft.resources.ResourceLocation; -import software.bernie.geckolib.model.GeoModel; - -public final class ComputerModel extends GeoModel { - private static final ResourceLocation MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/computer.geo.json"); - private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/block/computer/computer.png"); - private static final ResourceLocation ANIMATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/computer.animation.json"); - - @Override - public ResourceLocation getModelResource(ComputerBlockEntity animatable) { - return MODEL; - } - - @Override - public ResourceLocation getTextureResource(ComputerBlockEntity animatable) { - return TEXTURE; - } - - @Override - public ResourceLocation getAnimationResource(ComputerBlockEntity animatable) { - return ANIMATION; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/particle/BugSprayParticle.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/particle/BugSprayParticle.java new file mode 100644 index 00000000..917e4267 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/particle/BugSprayParticle.java @@ -0,0 +1,52 @@ +package com.craisinlord.antarchy.content.client.particle; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.client.particle.ParticleRenderType; +import net.minecraft.client.particle.SpriteSet; +import net.minecraft.client.particle.TextureSheetParticle; +import net.minecraft.core.particles.SimpleParticleType; + +public final class BugSprayParticle extends TextureSheetParticle { + private final SpriteSet sprites; + + private BugSprayParticle(ClientLevel level, double x, double y, double z, double xd, double yd, double zd, SpriteSet sprites) { + super(level, x, y, z, xd, yd, zd); + this.sprites = sprites; + this.setSprite(sprites.get(level.random)); + this.lifetime = 14 + this.random.nextInt(8); + this.quadSize = 0.16F + this.random.nextFloat() * 0.14F; + this.friction = 0.88F; + this.gravity = -0.01F; + this.alpha = 0.9F; + } + + @Override + public void tick() { + super.tick(); + if (!this.removed) { + this.setSprite(this.sprites.get(Math.min(this.age, this.lifetime - 1), this.lifetime)); + this.alpha = 0.9F * (1.0F - (float) this.age / this.lifetime); + } + } + + @Override + public ParticleRenderType getRenderType() { + return ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT; + } + + public static final class Provider implements ParticleProvider { + private final SpriteSet sprites; + + public Provider(SpriteSet sprites) { + this.sprites = sprites; + } + + @Override + public Particle createParticle(SimpleParticleType type, ClientLevel level, double x, double y, double z, + double xd, double yd, double zd) { + return new BugSprayParticle(level, x, y, z, xd, yd, zd, this.sprites); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/ComputerRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/ComputerRenderer.java deleted file mode 100644 index 24dd5208..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/ComputerRenderer.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.craisinlord.antarchy.content.client.renderer; - -import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; -import com.craisinlord.antarchy.content.client.model.ComputerModel; -import net.minecraft.core.Direction; -import software.bernie.geckolib.renderer.GeoBlockRenderer; - -public final class ComputerRenderer extends GeoBlockRenderer { - public ComputerRenderer(net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider.Context context) { - super(new ComputerModel()); - } - - @Override - protected Direction getFacing(ComputerBlockEntity computer) { - return computer.getBlockState().getValue(com.craisinlord.antarchy.content.block.ComputerBlock.FACING); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalAssailantAxeAfterimageLayer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalAssailantAxeAfterimageLayer.java deleted file mode 100644 index 47fb9cdb..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalAssailantAxeAfterimageLayer.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.craisinlord.antarchy.content.client.renderer; - -import com.craisinlord.antarchy.content.item.RoyalAssailantBattleAxeItem; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.math.Axis; -import net.minecraft.client.Minecraft; -import net.minecraft.client.model.PlayerModel; -import net.minecraft.client.player.AbstractClientPlayer; -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.entity.LivingEntityRenderer; -import net.minecraft.client.renderer.entity.RenderLayerParent; -import net.minecraft.client.renderer.entity.layers.RenderLayer; -import net.minecraft.world.entity.HumanoidArm; -import net.minecraft.world.item.ItemDisplayContext; -import net.minecraft.world.item.ItemStack; - -public final class RoyalAssailantAxeAfterimageLayer extends RenderLayer> { - private static final int[] TINTS = { - 0x66FFD966, - 0x4DFFC94D, - 0x33FFB52E, - 0x1FFF8C14 - }; - - public RoyalAssailantAxeAfterimageLayer(RenderLayerParent> renderer) { - super(renderer); - } - - @Override - public void render(PoseStack poseStack, MultiBufferSource buffer, int packedLight, AbstractClientPlayer player, - float limbSwing, float limbSwingAmount, float partialTick, float ageInTicks, float netHeadYaw, - float headPitch) { - if (player.isSpectator() || player.isInvisible() || player.getAttackAnim(partialTick) <= 0.01F) { - return; - } - - ItemStack stack = player.getMainHandItem(); - HumanoidArm arm = player.getMainArm(); - if (!(stack.getItem() instanceof RoyalAssailantBattleAxeItem)) { - stack = player.getOffhandItem(); - arm = player.getMainArm() == HumanoidArm.RIGHT ? HumanoidArm.LEFT : HumanoidArm.RIGHT; - } - if (!(stack.getItem() instanceof RoyalAssailantBattleAxeItem)) { - return; - } - - VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(player))); - float swing = player.getAttackAnim(partialTick); - for (int i = TINTS.length - 1; i >= 0; i--) { - poseStack.pushPose(); - float offset = (i + 1) * 0.08F; - poseStack.translate(0.0D, 0.0D, offset); - poseStack.mulPose(Axis.YP.rotation((i + 1) * 0.12F * (swing < 0.5F ? -1.0F : 1.0F))); - this.getParentModel().renderToBuffer( - poseStack, - vertexConsumer, - LightTexture.FULL_BRIGHT, - LivingEntityRenderer.getOverlayCoords(player, 0.0F), - TINTS[i] - ); - poseStack.popPose(); - } - - for (int i = TINTS.length - 1; i >= 0; i--) { - poseStack.pushPose(); - this.getParentModel().translateToHand(arm, poseStack); - poseStack.mulPose(Axis.YP.rotation((i + 1) * 0.12F * (swing < 0.5F ? -1.0F : 1.0F))); - Minecraft.getInstance().getItemRenderer().renderStatic( - player, - stack, - arm == HumanoidArm.RIGHT ? ItemDisplayContext.THIRD_PERSON_RIGHT_HAND : ItemDisplayContext.THIRD_PERSON_LEFT_HAND, - arm == HumanoidArm.LEFT, - poseStack, - buffer, - player.level(), - packedLight, - LivingEntityRenderer.getOverlayCoords(player, 0.0F), - player.getId() - ); - poseStack.popPose(); - } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalIndicatorRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalIndicatorRenderer.java index d8c45925..1d60e898 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalIndicatorRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalIndicatorRenderer.java @@ -49,11 +49,20 @@ public final class RoyalIndicatorRenderer { private static void crownPoints(VertexConsumer out, PoseStack.Pose pose, float radius, float rotation, float r, float g, float b, float a) { - for (int i = 0; i < 3; i++) { - double angle = rotation + Math.PI * 2.0D * i / 3.0D; - float x = (float) Math.cos(angle) * radius; - float z = (float) Math.sin(angle) * radius; - line(out, pose, x, 0.0F, z, x * 0.68F, 0.19F, z * 0.68F, r, g, b, a); + final int spikes = 5; + float innerRadius = radius * 0.72F; + for (int i = 0; i < spikes; i++) { + double outerAngle = rotation + Math.PI * 2.0D * i / spikes; + double innerAngle = rotation + Math.PI * 2.0D * (i + 0.5D) / spikes; + float outerX = (float) Math.cos(outerAngle) * radius; + float outerZ = (float) Math.sin(outerAngle) * radius; + float innerX = (float) Math.cos(innerAngle) * innerRadius; + float innerZ = (float) Math.sin(innerAngle) * innerRadius; + float nextOuterX = (float) Math.cos(rotation + Math.PI * 2.0D * (i + 1) / spikes) * radius; + float nextOuterZ = (float) Math.sin(rotation + Math.PI * 2.0D * (i + 1) / spikes) * radius; + line(out, pose, outerX, 0.0F, outerZ, innerX, 0.34F, innerZ, r, g, b, a); + line(out, pose, innerX, 0.34F, innerZ, nextOuterX, 0.0F, nextOuterZ, r, g, b, a); + line(out, pose, outerX, 0.0F, outerZ, nextOuterX, 0.0F, nextOuterZ, r, g, b, a); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/screen/ComputerScreen.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/screen/ComputerScreen.java deleted file mode 100644 index 60f3c40a..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/screen/ComputerScreen.java +++ /dev/null @@ -1,3029 +0,0 @@ -package com.craisinlord.antarchy.content.client.screen; - -import com.craisinlord.antarchy.Antarchy; -import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; -import com.craisinlord.antarchy.content.client.ComputerAccessClientState; -import com.craisinlord.antarchy.content.client.BlockleClientState; -import com.craisinlord.antarchy.content.client.ComputerFileSystemClientState; -import com.craisinlord.antarchy.content.client.AntmailClientState; -import com.craisinlord.antarchy.content.antmail.AntmailAddress; -import com.craisinlord.antarchy.content.antmail.AntmailMailbox; -import com.craisinlord.antarchy.content.antmail.AntmailMessage; -import com.craisinlord.antarchy.content.antmail.AntmailWire; -import com.craisinlord.antarchy.content.antmail.AntmailAttachment; -import com.craisinlord.antarchy.content.antmail.AntmailAttachmentFiles; -import com.craisinlord.antarchy.content.antmail.AntmailDraft; -import com.craisinlord.antarchy.content.guide.ComputerGuideData; -import com.craisinlord.antarchy.content.network.ComputerAccessResultPayload; -import com.craisinlord.antarchy.content.network.AntmailResultPayload; -import com.craisinlord.antarchy.content.network.ComputerNetworking; -import com.craisinlord.antarchy.content.network.AntmailNetworking; -import com.craisinlord.antarchy.content.computer.paint.AntPaintCanvas; -import com.craisinlord.antarchy.content.computer.paint.AntPaintHistory; -import com.craisinlord.antarchy.content.computer.paint.AntPaintTool; -import com.craisinlord.antarchy.content.computer.paint.AntPaintFile; -import com.craisinlord.antarchy.content.computer.paint.AntPaintFileCodec; -import com.craisinlord.antarchy.content.computer.ComputerFileSystem; -import com.craisinlord.antarchy.content.computer.ComputerDesktopState; -import com.craisinlord.antarchy.content.client.game.BasiliskProgram; -import com.craisinlord.antarchy.content.client.game.AntmanProgram; -import com.craisinlord.antarchy.content.client.game.BlockleProgram; -import com.craisinlord.antarchy.content.computer.terminal.TerminalCommandParser; -import com.craisinlord.antarchy.content.computer.terminal.TerminalResult; -import com.craisinlord.antarchy.config.AntarchySettings; -import com.craisinlord.antarchy.content.entity.nightmare.NightmareEntity; -import org.lwjgl.glfw.GLFW; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.resources.ResourceKey; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.core.component.DataComponents; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.client.gui.screens.inventory.InventoryScreen; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.alchemy.Potion; -import net.minecraft.world.item.alchemy.PotionContents; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeHolder; -import net.minecraft.world.item.crafting.ShapedRecipe; -import net.minecraft.world.item.enchantment.Enchantment; -import net.minecraft.world.item.enchantment.ItemEnchantments; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Base64; -import java.util.HashSet; -import java.util.HashMap; -import java.util.Set; -import java.util.UUID; - -public final class ComputerScreen extends Screen { - private static final int WIDTH = 440; - private static final int HEIGHT = 286; - private static final int GREEN = 0xFF65FF65; - private static final int PALE_GREEN = 0xFFB8FFB8; - private static final int DARK_GREEN = 0xFF102010; - private static final int BLACK = 0xFF030603; - private static final int TITLE_BAR_HEIGHT = 20; - private static final int WINDOW_CONTROL_WIDTH = 18; - private static final int WINDOW_CONTROL_COUNT = 3; - private static final int TRASH_ICON_INDEX = 8; - private static final String[] BOOT_MESSAGES = {"ANTS MARCHING...", "COMPUTER COMPUTING...", "HCFS BREWING...", "WAKING THE QUEEN..."}; - private static final long SESSION_TIMEOUT = 300_000L; - private static final Map SESSIONS = new LinkedHashMap<>(); - private static final String[] ICONS = {"ARCHIVE", "FILES", "SETTINGS", "TERMINAL", "TEXT", "PAINT", "ANTMAIL", "GAMES", "TRASH"}; - private static final Map TITLES = Map.of("ARCHIVE", "ANTARCHIVE", "FILES", "FILE EXPLORER", "SETTINGS", "SYSTEM SETTINGS", "TERMINAL", "ANTINTOSH TERMINAL", "TEXT", "ANTTEXT EDITOR", "PAINT", "ANTPAINT", "ANTMAIL", "ANTMAIL", "GAMES", "INSTALLED GAMES", "TRASH", "RECYCLE BIN"); - private final net.minecraft.core.BlockPos position; - private final String sessionKey; - private final Session session; - private boolean loggedIn; - private String password = ""; - private String confirmation = ""; - private boolean confirmingPassword; - private String loginMessage = ""; - private int loginMessageTicks; - private ComputerAccessResultPayload accessResult; - private int observedResult = -1; - private Window dragging; - private boolean paintStrokeActive; - private int paintStrokeButton = -1; - private int lastPaintPixelX = -1; - private int lastPaintPixelY = -1; - private String draggedDisk; - private ResourceLocation selectedDisk; - private int dragX; - private int dragY; - private Window activeWindow; - private final BasiliskProgram basilisk; - private final AntmanProgram antman; - private final BlockleProgram blockle; - private final Set archiveRenderLog = new HashSet<>(); - private final Map archiveEntityPreviews = new HashMap<>(); - private final Set unavailableArchiveEntities = new HashSet<>(); - - public ComputerScreen(net.minecraft.core.BlockPos position) { - super(Component.translatable("screen.antarchy.computer")); - this.position = position; - this.basilisk = new BasiliskProgram(false, 0, 0, score -> ComputerNetworking.saveBasiliskScore(this.position, score)); - this.antman = new AntmanProgram(false, 0, 0, score -> ComputerNetworking.saveAntmanScore(this.position, score)); - this.blockle = new BlockleProgram(this.position); - String dimension = Minecraft.getInstance().level == null ? "" : Minecraft.getInstance().level.dimension().location().toString(); - this.sessionKey = dimension + ":" + position.asLong(); - this.session = SESSIONS.computeIfAbsent(sessionKey, ignored -> new Session()); - this.session.windows.removeIf(window -> window.type.equals("GAMES") && window.gameOpen); - if (this.session.bootTicks < 0) this.session.bootTicks = 80; - this.session.antmanStateRequested = false; - this.session.blockleStateRequested = false; - this.session.antmanResponse = ""; - ComputerAccessClientState.clear(position); - BlockleClientState.clear(position); - ComputerNetworking.open(position); - this.accessResult = ComputerAccessClientState.get(position); - this.loggedIn = accessResult != null && accessResult.authenticated() && session.lastUse + SESSION_TIMEOUT > System.currentTimeMillis(); - if (loggedIn) { - requestAntmailState(); - session.antmailRefreshTicks = 40; - } - if (!loggedIn) { - session.authenticated = false; - session.windows.clear(); - } - } - - @Override - public void tick() { - if (session.bootTicks > 0) session.bootTicks--; - if (loginMessageTicks > 0) loginMessageTicks--; - ComputerAccessResultPayload currentResult = ComputerAccessClientState.get(position); - if (currentResult != null) { - accessResult = currentResult; - if (currentResult.result() != observedResult) { - observedResult = currentResult.result(); - if (currentResult.result() == ComputerAccessResultPayload.SUCCESS) { - loginMessage = "ACCESS GRANTED // WELCOME"; - loginMessageTicks = 40; - } else if (currentResult.result() == ComputerAccessResultPayload.INVALID_PASSWORD) { - loginMessage = "ACCESS DENIED // PASSWORD REJECTED"; - loginMessageTicks = 100; - } else if (currentResult.result() == ComputerAccessResultPayload.BUSY) { - loginMessage = "TERMINAL BUSY // USER PRESENT"; - loginMessageTicks = 100; - } - } - } - AntmailResultPayload mailResult = AntmailClientState.get(position); - syncAntmailPaintAttachment(); - if (loggedIn && --session.antmailRefreshTicks <= 0) { - requestAntmailState(); - session.antmailRefreshTicks = 40; - } - if (loggedIn && AntmailClientState.getMailbox(position) == null) session.antmailStateWaitTicks++; - else session.antmailStateWaitTicks = 0; - if (session.antmailRegistrationPending && mailResult != null) { - String expectedAddress = session.antmailUsername + "@antmail.com"; - if (mailResult.address().equalsIgnoreCase(expectedAddress)) { - session.antmailStatus = "ADDRESS REGISTERED"; - session.antmailRegistrationPending = false; - requestAntmailState(); - } else if (mailResult.detail().startsWith("registration_failed:")) { - String reason = mailResult.detail().substring("registration_failed:".length()).trim(); - session.antmailStatus = "ADDRESS REGISTRATION FAILED // " + (reason.isBlank() ? "SERVER REJECTED" : reason.toUpperCase()); - session.antmailRegistrationPending = false; - } - } - if (!session.antmailRetryMessageId.isBlank() && mailResult != null - && mailResult != session.antmailRetryBaseline - && session.antmailRetryMessageId.equals(mailResult.messageId()) - && !"message_detail".equals(mailResult.detail())) { - session.antmailStatus = antmailStatus(mailResult); - session.antmailRetryMessageId = ""; - session.antmailRetryBaseline = null; - session.antmailRetryTicks = 0; - requestAntmailState(true); - } else if (!session.antmailRetryMessageId.isBlank() && --session.antmailRetryTicks <= 0) { - session.antmailStatus = "RETRY TIMED OUT // TRY AGAIN"; - session.antmailRetryMessageId = ""; - session.antmailRetryBaseline = null; - requestAntmailState(true); - } - if (!loggedIn && accessResult != null && accessResult.authenticated()) { - loggedIn = true; - session.authenticated = true; - session.lastUse = System.currentTimeMillis(); - session.antmailRefreshTicks = 40; - requestAntmailState(); - } - if (loggedIn && (session.lastUse + SESSION_TIMEOUT <= System.currentTimeMillis() || accessResult == null || !accessResult.authenticated())) { - loggedIn = false; - session.authenticated = false; - session.windows.clear(); - } - if (loggedIn && !session.desktopRequested) { - ComputerNetworking.requestDesktopState(position); - session.desktopRequested = true; - } - if (loggedIn && !session.basiliskStateRequested) { - ComputerNetworking.requestBasiliskState(position); - session.basiliskStateRequested = true; - } - if (loggedIn && !session.antmanStateRequested) { - ComputerNetworking.requestAntmanState(position); - session.antmanStateRequested = true; - } - if (loggedIn && !session.blockleStateRequested) { - ComputerNetworking.requestBlockleState(position); - session.blockleStateRequested = true; - } - if (currentResult != null && currentResult.data().startsWith("11\u0000")) { - String[] desktop = currentResult.data().split("\u0000", 3); - if (desktop.length == 3 && !desktop[2].isBlank()) { - try { - var state = new ComputerDesktopState(); - state.load(AntmailWire.decodeTag(desktop[2])); - session.wallpaperId = state.selectedWallpaper() == null ? ComputerDesktopState.DEFAULT_WALLPAPER.toString() : state.selectedWallpaper().toString(); - session.wallpapers = state.unlockedWallpaperIds().stream().map(ResourceLocation::toString).toList(); - } catch (RuntimeException ignored) { - } - } - } - if (currentResult != null && currentResult.data().startsWith("15\u0000") && !currentResult.data().equals(session.terminalResponse)) { - session.terminalResponse = currentResult.data(); - try { - var terminal = AntmailWire.decodeTag(currentResult.data().substring(3)); - session.terminalDirectory = terminal.getString("Directory"); - if (terminal.getBoolean("Clear")) session.terminalOutput.clear(); - var lines = terminal.getList("Lines", 8); - for (int index = 0; index < lines.size(); index++) session.terminalOutput.add(lines.getString(index)); - String openPath = terminal.getString("Open"); - if (!openPath.isBlank()) { - ComputerNetworking.openFile(position, openPath); - open(openPath.endsWith(".antpaint") ? "PAINT" : "TEXT"); - } - while (session.terminalOutput.size() > 40) session.terminalOutput.remove(0); - } catch (RuntimeException ignored) { - session.terminalOutput.add("ERROR: CORRUPTED SERVER RESPONSE"); - } - } - boolean unlockAllGames = AntarchySettings.unlockAllArchives(); - basilisk.setInstalled(unlockAllGames || physicalDisks().stream().anyMatch(id -> id.getPath().equals("basilisk_game"))); - antman.setInstalled(unlockAllGames || physicalDisks().stream().anyMatch(id -> id.getPath().equals("antman_game"))); - boolean blockleWasInstalled = blockle.installed(); - blockle.setInstalled(unlockAllGames || physicalDisks().stream().anyMatch(id -> id.getPath().equals("blockle_game"))); - if (loggedIn && blockle.installed() && !blockleWasInstalled) { - ComputerNetworking.requestBlockleState(position); - session.blockleStateRequested = true; - } - blockle.tick(); - if (currentResult != null && currentResult.data().startsWith("16\u0000") && !currentResult.data().equals(session.basiliskResponse)) { - session.basiliskResponse = currentResult.data(); - String[] scores = currentResult.data().split("\u0000", 3); - if (scores.length == 3) { - String[] values = scores[2].split("\u0000", 2); - if (values.length == 2) try { basilisk.setStoredScores(Integer.parseInt(values[0]), Integer.parseInt(values[1])); } catch (NumberFormatException ignored) { } - } - } - if (currentResult != null && currentResult.data().startsWith("17\u0000") && !currentResult.data().equals(session.antmanResponse)) { - session.antmanResponse = currentResult.data(); - String[] scores = currentResult.data().split("\u0000", 3); - if (scores.length == 3) { - String[] values = scores[2].split("\u0000", 2); - if (values.length == 2) try { antman.setStoredScores(Integer.parseInt(values[0]), Integer.parseInt(values[1])); } catch (NumberFormatException ignored) { } - } - } - if (activeWindow != null && activeWindow.type.equals("GAMES") && activeWindow.gameOpen) { - if (activeWindow.gameId.equals("antman")) antman.tick(); - else if (activeWindow.gameId.equals("basilisk")) basilisk.tick(); - } - } - - @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) { - this.renderBackground(graphics, mouseX, mouseY, partialTick); - float scale = uiScale(); - graphics.pose().pushPose(); - graphics.pose().translate(width / 2.0F, height / 2.0F, 0.0F); - graphics.pose().scale(scale, scale, 1.0F); - int left = -WIDTH / 2; - int top = -HEIGHT / 2; - int localMouseX = (int) ((mouseX - width / 2.0F) / scale); - int localMouseY = (int) ((mouseY - height / 2.0F) / scale); - graphics.fill(left - 5, top - 5, left + WIDTH + 5, top + HEIGHT + 5, GREEN); - graphics.fill(left, top, left + WIDTH, top + HEIGHT, BLACK); - graphics.fill(left + 5, top + 5, left + WIDTH - 5, top + HEIGHT - 5, DARK_GREEN); - if (session.bootTicks > 0) renderBoot(graphics, left, top); else if (loggedIn) renderDesktop(graphics, left, top, localMouseX, localMouseY); else renderLogin(graphics, left, top); - graphics.pose().popPose(); - } - - private float uiScale() { - return Math.min(1.0F, Math.min((width - 24.0F) / WIDTH, (height - 24.0F) / HEIGHT)); - } - - private void renderBoot(GuiGraphics g, int l, int t) { - int progress = Math.min(100, (80 - session.bootTicks) * 100 / 80); - int message = Math.min(BOOT_MESSAGES.length - 1, progress * BOOT_MESSAGES.length / 101); - g.drawString(font, Component.literal("ANTINTOSH OS // v" + Antarchy.MOD_VERSION), l + 28, t + 78, GREEN, false); - g.drawString(font, Component.literal("INITIALIZING SYSTEM..."), l + 28, t + 105, PALE_GREEN, false); - g.fill(l + 28, t + 132, l + WIDTH - 28, t + 148, BLACK); - box(g, l + 28, t + 132, l + WIDTH - 28, t + 148, GREEN); - g.fill(l + 32, t + 136, l + 32 + (WIDTH - 64) * progress / 100, t + 144, GREEN); - g.drawString(font, Component.literal(progress + "%"), l + 28, t + 164, GREEN, false); - g.drawString(font, Component.literal(BOOT_MESSAGES[message]), l + 28, t + 190, PALE_GREEN, false); - } - - private void renderLogin(GuiGraphics g, int l, int t) { - g.drawString(font, Component.literal("ANTINTOSH OS // v" + Antarchy.MOD_VERSION), l + 28, t + 25, GREEN, false); - g.drawString(font, Component.literal("BOOT SEQUENCE COMPLETE"), l + 28, t + 43, PALE_GREEN, false); - g.fill(l + 28, t + 59, l + WIDTH - 28, t + 61, GREEN); - ComputerBlockEntity computer = computer(); - boolean setup = (accessResult != null && !accessResult.hasPassword()) || (computer != null && !computer.hasPassword()); - int formX = setup ? l + 110 : l + 242; - int formRight = setup ? l + 330 : l + WIDTH - 28; - g.drawString(font, Component.literal(setup ? "INITIALIZE COMPUTER" : "LOGIN REQUIRED"), formX, t + 105, GREEN, false); - g.drawString(font, Component.literal(setup ? (confirmingPassword ? "CONFIRM PASSWORD" : "CREATE PASSWORD") : "PASSWORD"), formX, t + 132, PALE_GREEN, false); - g.fill(formX, t + 150, formRight, t + 173, BLACK); - box(g, formX, t + 150, formRight, t + 173, GREEN); - g.drawString(font, Component.literal("*".repeat((confirmingPassword ? confirmation : password).length())), formX + 6, t + 157, GREEN, false); - g.drawString(font, Component.literal(setup ? (confirmingPassword ? "[ ENTER ] CONFIRM" : "[ ENTER ] CONTINUE") : "[ ENTER ] LOGIN"), formX, t + 187, GREEN, false); - g.drawString(font, Component.literal("[ ESC ] POWER DOWN"), formX, t + 205, PALE_GREEN, false); - if (loginMessageTicks > 0) wrapLimited(g, loginMessage, formX, t + 232, setup ? 220 : 165, t + HEIGHT - 8, PALE_GREEN); - } - - private void renderDesktop(GuiGraphics g, int l, int t, int mouseX, int mouseY) { - session.lastUse = System.currentTimeMillis(); - session.mouseX = mouseX; - session.mouseY = mouseY; - session.archiveMouseX = mouseX; - session.archiveMouseY = mouseY; - renderWallpaper(g, l, t); - g.fill(l + 9, t + 9, l + WIDTH - 9, t + 30, BLACK); - g.drawString(font, Component.literal("ANTINTOSH OS // v" + Antarchy.MOD_VERSION), l + 18, t + 15, GREEN, false); - long dayTime = Minecraft.getInstance().level == null ? 0L : Minecraft.getInstance().level.getDayTime(); - long dayTicks = Math.floorMod(dayTime, 24000L); - int hours = (int) ((dayTicks / 1000L + 6L) % 24L); - int minutes = (int) (dayTicks % 1000L * 60L / 1000L); - g.drawString(font, Component.literal(String.format("%02d:%02d", hours, minutes)), l + WIDTH - 48, t + 15, PALE_GREEN, false); - g.fill(l + 9, t + 31, l + WIDTH - 9, t + 33, GREEN); - for (int i = 0; i < ICONS.length; i++) { - int x = l + 24 + i % 4 * 94; - int y = t + 48 + i / 4 * 76; - boolean hover = inside(x - 6, y - 6, 76, 57, mouseX, mouseY); - if (hover) g.fill(x - 7, y - 7, x + 69, y + 51, 0xFF173817); - icon(g, ICONS[i], x + 25, y + 2); - if (ICONS[i].equals("ANTMAIL") && antmailHasUnreadMessages()) drawNotificationBadge(g, x + 35, y + 6); - g.drawString(font, Component.literal(ICONS[i]), x, y + 35, GREEN, false); - } - List disks = physicalDisks().stream().filter(id -> !id.getPath().equals("blockle_game")).toList(); - for (int i = 0; i < disks.size() && i < 3; i++) { - int x = l + 24 + (i + 1) * 94; - int y = t + 200 + i / 4 * 54; - icon(g, "DISK", x + 25, y + 2); - String name = disks.get(i).getPath(); - g.drawString(font, Component.literal(name.length() > 12 ? name.substring(0, 12) : name), x, y + 35, GREEN, false); - } - // Commit the desktop before compositing the independent window layers. - g.flush(); - if (activeWindow != null && !activeWindow.minimized && session.windows.remove(activeWindow)) { - session.windows.add(activeWindow); - } - int layer = 1; - for (Window window : session.windows) { - if (!window.minimized) renderWindow(g, window, l, t, mouseX, mouseY, layer++); - } - } - - private boolean antmailHasUnreadMessages() { - AntmailResultPayload result = AntmailClientState.getMailbox(position); - AntmailMailbox mailbox = mailbox(result); - return mailbox != null && mailbox.unreadCount() > 0; - } - - private void drawNotificationBadge(GuiGraphics g, int x, int y) { - String[] circle = { - "....#####....", - "..##+++++##..", - ".#+++++++++#.", - "#+++++++++++#", - "#+++++++++++#", - "#+++++++++++#", - "#+++++++++++#", - "#+++++++++++#", - "#+++++++++++#", - "#+++++++++++#", - ".#+++++++++#.", - "..##+++++##..", - "....#####...." - }; - int badgeFill = GREEN; - for (int row = 0; row < circle.length; row++) { - for (int column = 0; column < circle[row].length(); column++) { - char pixel = circle[row].charAt(column); - if (pixel == '#') g.fill(x - 6 + column, y - 6 + row, x - 5 + column, y - 5 + row, BLACK); - else if (pixel == '+') g.fill(x - 6 + column, y - 6 + row, x - 5 + column, y - 5 + row, badgeFill); - } - } - // Two-pixel stem and a centered two-pixel dot keep the exclamation bold and symmetrical. - g.fill(x - 1, y - 4, x + 1, y + 2, BLACK); - g.fill(x - 1, y + 3, x + 1, y + 5, BLACK); - } - - private void renderWallpaper(GuiGraphics g, int l, int t) { - int left = l + 9; - int top = t + 34; - int right = l + WIDTH - 9; - int bottom = t + HEIGHT - 8; - String wallpaperId = ComputerDesktopState.DEFAULT_WALLPAPER.toString(); - int hash = wallpaperId.hashCode(); - int green = 24 + Math.floorMod(hash, 40); - int blue = 12 + Math.floorMod(hash >>> 8, 24); - int base = 0xFF000000 | (green / 2 << 16) | (green << 8) | blue; - g.fill(left, top, right, bottom, base); - if (wallpaperId.endsWith("grid_ant")) { - for (int py = top + 2; py < bottom; py += 18) { - for (int px = left + 2; px < right; px += 36) { - g.fill(px, py, px + 2, py + 2, 0xFF173817); - } - } - return; - } - int accent = 0xFF000000 | (Math.min(120, green + 34) << 8) | Math.min(80, blue + 34); - int spacing = 18 + Math.floorMod(hash, 13); - int offset = Math.floorMod(hash >>> 16, spacing); - for (int y = top - spacing; y < bottom + spacing; y += spacing) { - int x = left - spacing + offset; - while (x < right) { - int size = 3 + Math.floorMod(x + y + hash, 7); - g.fill(x, y, Math.min(right, x + size), Math.min(bottom, y + 2), accent); - x += spacing * 2; - } - offset = spacing - offset; - } - } - - private String wallpaperName() { - int separator = session.wallpaperId.indexOf(':'); - String name = separator >= 0 ? session.wallpaperId.substring(separator + 1) : session.wallpaperId; - return name.toUpperCase(); - } - - private void icon(GuiGraphics g, String type, int x, int y) { - if (type.equals("ARCHIVE") || type.equals("DISK")) { - // Chunky drive/archive with an inset label window and lower status slot. - g.fill(x - 11, y - 2, x + 11, y + 20, BLACK); - g.fill(x - 8, y + 1, x + 8, y + 16, GREEN); - g.fill(x - 5, y + 3, x + 5, y + 8, BLACK); - g.fill(x - 4, y + 4, x + 4, y + 7, PALE_GREEN); - g.fill(x - 5, y + 11, x + 5, y + 13, BLACK); - g.fill(x + 5, y + 14, x + 7, y + 16, BLACK); - return; - } - if (type.equals("FILES")) { - // Folder silhouette with a clearly outlined tab and a second document peeking out. - g.fill(x - 8, y - 2, x + 9, y + 14, BLACK); - g.fill(x - 5, y + 1, x + 6, y + 11, PALE_GREEN); - g.fill(x - 10, y + 4, x + 11, y + 20, BLACK); - g.fill(x - 7, y + 7, x + 8, y + 17, GREEN); - g.fill(x - 8, y + 4, x - 2, y + 7, GREEN); - g.fill(x - 4, y + 10, x + 5, y + 12, BLACK); - g.fill(x - 4, y + 14, x + 3, y + 16, BLACK); - return; - } - if (type.equals("SETTINGS")) { - // One continuous cog silhouette, with broad square teeth and an open center. - g.fill(x - 4, y - 2, x + 4, y + 22, BLACK); - g.fill(x - 12, y + 6, x + 12, y + 14, BLACK); - g.fill(x - 10, y + 1, x - 5, y + 5, BLACK); - g.fill(x + 5, y + 1, x + 10, y + 5, BLACK); - g.fill(x - 10, y + 15, x - 5, y + 19, BLACK); - g.fill(x + 5, y + 15, x + 10, y + 19, BLACK); - g.fill(x - 2, y, x + 2, y + 20, GREEN); - g.fill(x - 10, y + 8, x + 10, y + 12, GREEN); - g.fill(x - 7, y + 3, x + 7, y + 17, GREEN); - g.fill(x - 3, y + 7, x + 3, y + 13, BLACK); - g.fill(x - 1, y + 9, x + 1, y + 11, PALE_GREEN); - return; - } - if (type.equals("TERMINAL")) { - g.fill(x - 12, y - 1, x + 12, y + 19, BLACK); - g.fill(x - 9, y + 2, x + 9, y + 14, GREEN); - g.fill(x - 6, y + 5, x - 3, y + 8, BLACK); - g.fill(x - 3, y + 8, x + 1, y + 11, BLACK); - g.fill(x - 6, y + 11, x - 3, y + 14, BLACK); - g.fill(x + 3, y + 11, x + 7, y + 13, BLACK); - g.fill(x - 5, y + 16, x + 5, y + 18, BLACK); - return; - } - if (type.equals("TEXT")) { - g.fill(x - 9, y - 2, x + 10, y + 20, BLACK); - g.fill(x - 6, y + 1, x + 7, y + 17, GREEN); - g.fill(x + 4, y + 1, x + 7, y + 4, BLACK); - g.fill(x - 3, y + 5, x + 4, y + 7, BLACK); - g.fill(x - 3, y + 9, x + 4, y + 11, BLACK); - g.fill(x - 3, y + 13, x + 2, y + 15, BLACK); - return; - } - if (type.equals("PAINT")) { - // Squared palette with a true thumb notch and a distinct, outlined brush. - g.fill(x - 11, y + 2, x + 8, y + 20, BLACK); - g.fill(x - 8, y + 5, x + 5, y + 17, GREEN); - g.fill(x - 11, y + 5, x - 8, y + 7, GREEN); - g.fill(x - 6, y + 8, x - 3, y + 11, BLACK); - g.fill(x, y + 7, x + 3, y + 10, BLACK); - g.fill(x - 1, y + 13, x + 2, y + 16, BLACK); - g.fill(x + 6, y - 3, x + 11, y + 1, BLACK); - g.fill(x + 4, y, x + 9, y + 6, BLACK); - g.fill(x + 2, y + 5, x + 7, y + 11, BLACK); - g.fill(x, y + 10, x + 5, y + 16, BLACK); - g.fill(x + 7, y - 1, x + 9, y + 2, PALE_GREEN); - g.fill(x + 5, y + 3, x + 7, y + 7, PALE_GREEN); - g.fill(x + 3, y + 8, x + 5, y + 11, PALE_GREEN); - return; - } - if (type.equals("ANTMAIL")) { - g.fill(x - 12, y + 1, x + 12, y + 18, BLACK); - g.fill(x - 9, y + 4, x + 9, y + 15, GREEN); - // Envelope flap and two symmetric folded corners remain legible at icon scale. - g.fill(x - 9, y + 4, x + 9, y + 6, BLACK); - g.fill(x - 7, y + 6, x - 4, y + 8, BLACK); - g.fill(x + 4, y + 6, x + 7, y + 8, BLACK); - g.fill(x - 4, y + 8, x + 4, y + 10, BLACK); - g.fill(x - 9, y + 11, x - 6, y + 13, BLACK); - g.fill(x + 6, y + 11, x + 9, y + 13, BLACK); - g.fill(x - 6, y + 13, x - 3, y + 15, BLACK); - g.fill(x + 3, y + 13, x + 6, y + 15, BLACK); - return; - } - if (type.equals("GAMES")) { - // Pixel controller with tapered grips, a bold D-pad and paired buttons. - g.fill(x - 7, y + 2, x + 7, y + 5, BLACK); - g.fill(x - 11, y + 5, x + 11, y + 15, BLACK); - g.fill(x - 9, y + 15, x - 5, y + 19, BLACK); - g.fill(x + 5, y + 15, x + 9, y + 19, BLACK); - g.fill(x - 8, y + 5, x + 8, y + 13, GREEN); - g.fill(x - 7, y + 8, x - 1, y + 10, BLACK); - g.fill(x - 5, y + 6, x - 3, y + 12, BLACK); - g.fill(x + 3, y + 7, x + 5, y + 9, BLACK); - g.fill(x + 6, y + 10, x + 8, y + 12, BLACK); - return; - } - if (type.equals("TRASH")) { - g.fill(x - 8, y + 3, x + 8, y + 20, BLACK); - g.fill(x - 11, y + 1, x + 11, y + 4, BLACK); - g.fill(x - 5, y - 1, x + 5, y + 1, BLACK); - g.fill(x - 6, y + 4, x + 6, y + 18, GREEN); - g.fill(x - 3, y + 7, x - 1, y + 16, BLACK); - g.fill(x + 2, y + 7, x + 4, y + 16, BLACK); - return; - } - g.fill(x - 10, y, x + 10, y + 18, BLACK); - g.fill(x - 8, y + 2, x + 8, y + 16, GREEN); - } - - private void renderWindow(GuiGraphics g, Window window, int l, int t, int mouseX, int mouseY, int layer) { - g.flush(); - g.pose().pushPose(); - g.pose().translate(0.0F, 0.0F, layer * 10.0F); - try { - renderWindowLayer(g, window, l, t, mouseX, mouseY); - } finally { - g.flush(); - g.pose().popPose(); - } - } - - private void renderWindowLayer(GuiGraphics g, Window window, int l, int t, int mouseX, int mouseY) { - if (!window.maximized) { - window.x = Math.max(12, Math.min(WIDTH - window.width - 12, window.x)); - window.y = Math.max(34, Math.min(HEIGHT - window.height - 12, window.y)); - } - int x = l + windowLocalX(window); - int y = t + windowLocalY(window); - int w = windowWidth(window); - int h = windowHeight(window); - window.renderWidth = w; - window.renderHeight = h; - g.fill(x - 2, y - 2, x + w + 2, y + h + 2, GREEN); - g.fill(x, y, x + w, y + h, BLACK); - g.fill(x, y, x + w, y + TITLE_BAR_HEIGHT, 0xFF173817); - int controlsLeft = x + w - WINDOW_CONTROL_WIDTH * WINDOW_CONTROL_COUNT; - g.drawString(font, Component.literal(trimToWidth(window.title, Math.max(1, controlsLeft - x - 12))), x + 7, y + 6, GREEN, false); - g.fill(controlsLeft, y, x + w, y + TITLE_BAR_HEIGHT, 0xFF173817); - g.fill(controlsLeft, y, controlsLeft + 1, y + TITLE_BAR_HEIGHT, GREEN); - g.fill(controlsLeft + WINDOW_CONTROL_WIDTH, y, controlsLeft + WINDOW_CONTROL_WIDTH + 1, y + TITLE_BAR_HEIGHT, GREEN); - g.fill(controlsLeft + WINDOW_CONTROL_WIDTH * 2, y, controlsLeft + WINDOW_CONTROL_WIDTH * 2 + 1, y + TITLE_BAR_HEIGHT, GREEN); - g.drawCenteredString(font, Component.literal("_"), controlsLeft + WINDOW_CONTROL_WIDTH / 2, y + 5, PALE_GREEN); - g.drawCenteredString(font, Component.literal(window.maximized ? "❐" : "□"), controlsLeft + WINDOW_CONTROL_WIDTH + WINDOW_CONTROL_WIDTH / 2, y + 5, PALE_GREEN); - g.drawCenteredString(font, Component.literal("X"), controlsLeft + WINDOW_CONTROL_WIDTH * 2 + WINDOW_CONTROL_WIDTH / 2, y + 6, GREEN); - int cx = x + 8; - int cy = y + 28; - enableComputerScissor(g, x + 1, y + TITLE_BAR_HEIGHT, x + w - 1, y + h - 1); - try { - if (window.type.equals("ARCHIVE")) renderArchive(g, cx, cy, w - 16, h - 34, window.maximized); - else if (window.type.equals("FILES")) renderFileExplorer(g, cx, cy, w - 16, h - 34); - else if (window.type.equals("SETTINGS")) renderSettings(g, cx, cy, h - 34); - else if (window.type.equals("TERMINAL")) renderTerminal(g, cx, cy, h - 34); - else if (window.type.equals("TEXT")) renderTextEditor(g, cx, cy, w - 16, h - 34); - else if (window.type.equals("PAINT")) renderPaint(g, cx, cy, w - 16, h - 34); - else if (window.type.equals("ANTMAIL")) renderAntmail(g, cx, cy, h - 34); - else if (window.type.equals("GAMES")) renderGames(g, window, cx, cy, mouseX, mouseY); - else renderTrash(g, cx, cy); - } finally { - g.disableScissor(); - } - } - - private void enableComputerScissor(GuiGraphics g, int left, int top, int right, int bottom) { - float scale = uiScale(); - int screenLeft = (int) Math.floor(width / 2.0F + left * scale); - int screenTop = (int) Math.floor(height / 2.0F + top * scale); - int screenRight = (int) Math.ceil(width / 2.0F + right * scale); - int screenBottom = (int) Math.ceil(height / 2.0F + bottom * scale); - g.enableScissor(screenLeft, screenTop, screenRight, screenBottom); - } - - private static int windowLocalX(Window window) { return window.maximized ? 14 : window.x; } - private static int windowLocalY(Window window) { return window.maximized ? 34 : window.y; } - private static int windowWidth(Window window) { return window.maximized ? WIDTH - 28 : window.width; } - private static int windowHeight(Window window) { return window.maximized ? HEIGHT - 48 : window.height; } - - private static void toggleMaximized(Window window) { - if (window.maximized) { - window.maximized = false; - window.x = window.restoreX; - window.y = window.restoreY; - } else { - window.restoreX = window.x; - window.restoreY = window.y; - window.maximized = true; - } - } - - private void renderArchive(GuiGraphics g, int x, int y, int w, int h, boolean maximized) { - List entries = List.of(); - if (Minecraft.getInstance().level != null && Minecraft.getInstance().level.getBlockEntity(position) instanceof ComputerBlockEntity computer) entries = ComputerGuideData.entriesFor(computer.diskIds()); - - ComputerGuideData.Entry selected = null; - if (session.archiveEntryId != null) { - selected = entries.stream().filter(entry -> entry.id().equals(session.archiveEntryId)).findFirst().orElse(null); - if (selected == null) session.archiveEntryId = null; - } - - if (selected != null) { - g.drawString(font, Component.literal("< BACK // RECOVERED FILES"), x, y, GREEN, false); - g.fill(x, y + 16, x + w, y + 18, GREEN); - g.drawString(font, Component.literal(trimToWidth(Component.translatable(selected.titleKey()).getString(), w)), x, y + 27, GREEN, false); - int contentBottom = y + h - 20; - enableComputerScissor(g, x, y + 36, x + w, contentBottom); - int line = y + 43 - session.archiveDetailScroll * 11; - line = wrap(g, selected.type().toUpperCase() + " // " + selected.category().toUpperCase(), x, line, w, PALE_GREEN) + 5; - if (!selected.entityId().isBlank()) line = renderArchivePreview(g, "", selected.entityId(), "", x, line, w, selected.rotation(), selected.renderScale()) + 2; - for (String descriptionPart : selected.descriptionKeys()) { - line = renderArchiveDescriptionPart(g, descriptionPart, x, line, w); - } - if (!selected.itemId().isBlank()) line = renderArchivePreview(g, selected.itemId(), "", "", x, line, w) + 2; - if (!selected.enchantmentId().isBlank()) line = renderArchivePreview(g, "", "", selected.enchantmentId(), x, line, w) + 2; - if (!selected.recipeId().isBlank()) line = renderArchiveRecipe(g, selected.recipeId(), x, line, w) + 2; - if (!selected.structureId().isBlank()) { - line = wrap(g, "STRUCTURE // " + selected.structureId(), x, line, w, PALE_GREEN) + 2; - line = wrap(g, com.craisinlord.antarchy.content.client.ComputerStructureLocatorClientState.get(position), x, line, w, GREEN) + 2; - } - if (!selected.dimensionId().isBlank()) line = wrap(g, "DIMENSION // " + selected.dimensionId(), x, line, w, PALE_GREEN); - g.disableScissor(); - session.archiveDetailMaxScroll = Math.max(0, (line + session.archiveDetailScroll * 11 - contentBottom + 10) / 11); - session.archiveDetailScroll = Math.min(session.archiveDetailScroll, session.archiveDetailMaxScroll); - if (session.archiveDetailMaxScroll > 0) g.drawString(font, Component.literal("SCROLL " + session.archiveDetailScroll + "/" + session.archiveDetailMaxScroll), x + w - 82, y + h - 12, PALE_GREEN, false); - g.drawString(font, Component.literal("[ ESC ] BACK"), x, y + h - 12, PALE_GREEN, false); - return; - } - - List filteredEntries = filterArchiveEntries(entries); - g.drawString(font, Component.literal("RECOVERED FILES // " + filteredEntries.size() + "/" + entries.size()), x, y, GREEN, false); - g.fill(x, y + 14, x + w - 8, y + 35, BLACK); - box(g, x, y + 14, x + w - 8, y + 35, session.archiveSearchFocused ? GREEN : PALE_GREEN); - String searchText = session.archiveSearch.isBlank() && !session.archiveSearchFocused ? "SEARCH TITLE" : session.archiveSearch; - g.drawString(font, Component.literal(trimToWidth(searchText + (session.archiveSearchFocused ? "_" : ""), w - 16)), x + 6, y + 20, - session.archiveSearch.isBlank() ? PALE_GREEN : GREEN, false); - if (entries.isEmpty()) { - g.drawString(font, Component.literal("NO ARCHIVE DATA"), x, y + 28, PALE_GREEN, false); - return; - } - if (filteredEntries.isEmpty()) { - g.drawString(font, Component.literal("NO MATCHING ENTRIES"), x, y + 52, PALE_GREEN, false); - return; - } - int columns = maximized ? 3 : 1; - int cellHeight = maximized ? 72 : 38; - int thumbnailSize = maximized ? 44 : 28; - int listTop = y + 40; - int visibleRows = Math.max(1, (h - 58) / cellHeight); - int totalRows = (filteredEntries.size() + columns - 1) / columns; - int startRow = Math.max(0, Math.min(session.archiveScroll, Math.max(0, totalRows - visibleRows))); - for (int rowIndex = startRow; rowIndex < totalRows && rowIndex < startRow + visibleRows; rowIndex++) { - int rowTop = listTop + (rowIndex - startRow) * cellHeight; - for (int column = 0; column < columns; column++) { - int index = rowIndex * columns + column; - if (index >= filteredEntries.size()) break; - ComputerGuideData.Entry entry = filteredEntries.get(index); - int cellWidth = (w - 8) / columns; - int cellX = x + column * cellWidth; - int thumbX = maximized ? cellX + (cellWidth - thumbnailSize) / 2 : cellX + 3; - int thumbY = maximized ? rowTop + 2 : rowTop + 4; - int textX = maximized ? cellX + 3 : cellX + thumbnailSize + 10; - int textY = maximized ? rowTop + thumbnailSize + 5 : rowTop + 7; - boolean hover = inside(cellX, rowTop, cellWidth - 2, cellHeight - 2, session.archiveMouseX, session.archiveMouseY); - if (hover) g.fill(cellX, rowTop, cellX + cellWidth - 2, rowTop + cellHeight - 2, 0xFF173817); - g.fill(thumbX, thumbY, thumbX + thumbnailSize, thumbY + thumbnailSize, BLACK); - renderThumbnail(g, entry, thumbX + thumbnailSize / 2, thumbY + thumbnailSize / 2, hover, thumbnailSize); - int textWidth = maximized ? cellWidth - 8 : cellWidth - thumbnailSize - 12; - g.drawString(font, Component.literal(trimToWidth(Component.translatable(entry.titleKey()).getString(), textWidth)), textX, textY, GREEN, false); - g.drawString(font, Component.literal(trimToWidth(entry.category().toUpperCase(), textWidth)), textX, textY + 12, PALE_GREEN, false); - } - } - int shownStart = startRow * columns; - int shownEnd = Math.min(filteredEntries.size(), (startRow + visibleRows) * columns); - if (totalRows > visibleRows) g.drawString(font, Component.literal("SCROLL " + (shownStart + 1) + "-" + shownEnd + " / " + filteredEntries.size()), x, y + h - 12, PALE_GREEN, false); - else g.drawString(font, Component.literal("[ CLICK ENTRY TO OPEN ]"), x, y + h - 12, PALE_GREEN, false); - } - - private List filterArchiveEntries(List entries) { - String query = session.archiveSearch.trim().toLowerCase(Locale.ROOT); - if (query.isEmpty()) return entries; - return entries.stream().filter(entry -> Component.translatable(entry.titleKey()).getString().toLowerCase(Locale.ROOT).contains(query)).toList(); - } - - private void renderThumbnail(GuiGraphics g, ComputerGuideData.Entry entry, int centerX, int centerY, boolean hover, int size) { - int background = hover ? 0xFF173817 : 0xFF102010; - g.fill(centerX - size / 2, centerY - size / 2, centerX + size / 2, centerY + size / 2, background); - if (!entry.recipeId().isBlank() && renderArchiveRecipe(g, entry.recipeId(), centerX - size / 2, centerY - size / 2 + size / 5, size, Math.max(6, size / 4))) return; - String itemId = entry.coverItemId().isBlank() ? entry.itemId() : entry.coverItemId(); - String entityId = entry.coverEntityId().isBlank() ? entry.entityId() : entry.coverEntityId(); - if (!renderArchiveAsset(g, itemId, entityId, entry.enchantmentId(), entry.coverPotionId(), centerX, centerY, size, entry.rotation(), entry.renderScale())) { - String fallback = !itemId.isBlank() ? itemId : !entry.enchantmentId().isBlank() ? entry.enchantmentId() : entityId; - g.drawString(font, Component.literal(trimToWidth(fallback.isBlank() ? "NO RENDER" : fallback, size - 4)), centerX - size / 2 + 2, centerY - 4, hover ? GREEN : PALE_GREEN, false); - } - } - - private int renderArchiveDescriptionPart(GuiGraphics g, String part, int x, int y, int w) { - if (part.startsWith("@item:")) return renderArchivePreview(g, part.substring(6), "", "", x, y, w) + 2; - if (part.startsWith("@entity:")) return renderArchivePreview(g, "", part.substring(8), "", x, y, w) + 2; - if (part.startsWith("@enchantment:")) return renderArchivePreview(g, "", "", part.substring(13), x, y, w) + 2; - if (part.startsWith("@recipe:")) return renderArchiveRecipe(g, part.substring(8), x, y, w) + 2; - return wrap(g, Component.translatable(part), x, y, w, GREEN) + 3; - } - - private int renderArchiveRecipe(GuiGraphics g, String recipeId, int x, int y, int w) { - int slot = 16; - int recipeWidth = slot * 3 + 24; - if (!renderArchiveRecipe(g, recipeId, x, y, Math.min(w, recipeWidth), slot)) { - return wrap(g, "RECIPE // " + recipeId, x, y, w, PALE_GREEN); - } - return y + slot * 3; - } - - private boolean renderArchiveRecipe(GuiGraphics g, String recipeId, int x, int y, int width, int slot) { - if (Minecraft.getInstance().level == null) return false; - try { - var recipeManager = Minecraft.getInstance().level.getRecipeManager(); - ResourceLocation requestedId = ResourceLocation.parse(recipeId); - RecipeHolder holder = recipeManager.byKey(requestedId).orElse(null); - if (holder == null) { - var requestedItem = BuiltInRegistries.ITEM.getOptional(requestedId).orElse(null); - if (requestedItem != null && requestedItem != Items.AIR) { - holder = recipeManager.getRecipes().stream() - .filter(candidate -> candidate.value().getResultItem(Minecraft.getInstance().level.registryAccess()).is(requestedItem)) - .findFirst().orElse(null); - } - } - if (holder == null) return false; - var recipe = holder.value(); - List ingredients = recipe.getIngredients(); - int actualSlot = Math.min(slot, Math.max(4, (width - 8) / 4)); - int gridWidth = actualSlot * 3; - int outputX = x + gridWidth + 8; - int outputY = y + actualSlot; - int shapedWidth = recipe instanceof ShapedRecipe shaped ? shaped.getWidth() : 3; - int shapedHeight = recipe instanceof ShapedRecipe shaped ? shaped.getHeight() : Math.min(3, (ingredients.size() + 2) / 3); - for (int index = 0; index < 9; index++) { - int column = index % 3; - int row = index / 3; - int cellX = x + column * actualSlot; - int cellY = y + row * actualSlot; - g.fill(cellX, cellY, cellX + actualSlot - 1, cellY + actualSlot - 1, 0xFF173817); - int ingredientIndex = recipe instanceof ShapedRecipe - ? (column < shapedWidth && row < shapedHeight ? row * shapedWidth + column : -1) - : index; - if (ingredientIndex >= 0 && ingredientIndex < ingredients.size()) { - ItemStack[] choices = ingredients.get(ingredientIndex).getItems(); - if (choices.length > 0) renderArchiveItem(g, choices[0], cellX, cellY, actualSlot); - } - } - g.fill(x + gridWidth + 2, y + actualSlot + actualSlot / 2 - 1, x + gridWidth + 6, y + actualSlot + actualSlot / 2 + 1, PALE_GREEN); - g.fill(x + gridWidth + 5, y + actualSlot + actualSlot / 2 - 3, x + gridWidth + 7, y + actualSlot + actualSlot / 2 + 3, PALE_GREEN); - ItemStack result = recipe.getResultItem(Minecraft.getInstance().level.registryAccess()); - if (!result.isEmpty()) { - g.fill(outputX, outputY, outputX + actualSlot - 1, outputY + actualSlot - 1, 0xFF173817); - renderArchiveItem(g, result, outputX, outputY, actualSlot); - } - return true; - } catch (RuntimeException exception) { - logArchivePreviewOnce("recipe.error." + recipeId, "Archive recipe preview failed id={} error={}", recipeId, exception.toString()); - return false; - } - } - - private int renderArchivePreview(GuiGraphics g, String itemId, String entityId, String enchantmentId, int x, int y, int w) { - return renderArchivePreview(g, itemId, entityId, enchantmentId, x, y, w, 0.0F, 1.0F); - } - - private int renderArchivePreview(GuiGraphics g, String itemId, String entityId, String enchantmentId, int x, int y, int w, float rotation) { - return renderArchivePreview(g, itemId, entityId, enchantmentId, x, y, w, rotation, 1.0F); - } - - private int renderArchivePreview(GuiGraphics g, String itemId, String entityId, String enchantmentId, int x, int y, int w, float rotation, float renderScale) { - int previewSize = entityId.isBlank() ? 48 : 96; - int boxWidth = Math.min(previewSize, w); - g.fill(x, y, x + boxWidth, y + previewSize, 0xFF102010); - int centerX = x + boxWidth / 2; - int centerY = y + previewSize / 2; - if (!renderArchiveAsset(g, itemId, entityId, enchantmentId, "", centerX, centerY, boxWidth, rotation, renderScale)) { - String fallback = !itemId.isBlank() ? itemId : !enchantmentId.isBlank() ? enchantmentId : entityId; - g.drawString(font, Component.literal(trimToWidth(fallback, Math.max(1, w - boxWidth - 6))), x + boxWidth + 6, y + previewSize / 2 - 4, PALE_GREEN, false); - } else if (!itemId.isBlank() || !entityId.isBlank() || !enchantmentId.isBlank()) { - String label = !itemId.isBlank() ? "ITEM // " + itemId - : !enchantmentId.isBlank() ? "ENCHANTMENT // " + enchantmentId : "MOB // " + archiveMobName(entityId); - g.drawString(font, Component.literal(trimToWidth(label, Math.max(1, w - boxWidth - 6))), x + boxWidth + 6, y + previewSize / 2 - 4, PALE_GREEN, false); - } - return y + previewSize; - } - - private String archiveMobName(String entityId) { - int separator = entityId.indexOf(':'); - return (separator >= 0 ? entityId.substring(separator + 1) : entityId).replace('_', ' ').toUpperCase(Locale.ROOT); - } - - private boolean renderArchiveAsset(GuiGraphics g, String itemId, String entityId, String enchantmentId, String potionId, int centerX, int centerY, int size, float rotation, float renderScale) { - if (!itemId.isBlank()) { - try { - ResourceLocation resourceId = ResourceLocation.parse(itemId); - var item = BuiltInRegistries.ITEM.getOptional(resourceId).orElse(null); - logArchivePreviewOnce("item.lookup." + itemId, "Archive item preview lookup id={} found={} registryName={}", itemId, item != null, item == null ? "" : BuiltInRegistries.ITEM.getKey(item)); - if (item != null && !item.equals(net.minecraft.world.item.Items.AIR)) { - ItemStack stack = new ItemStack(item); - int iconSize = Math.min(32, Math.max(16, size - 4)); - renderArchiveItem(g, stack, centerX - iconSize / 2, centerY - iconSize / 2, iconSize); - logArchivePreviewOnce("item.render." + itemId, "Archive item preview rendered id={} descriptionId={} center=({}, {})", itemId, stack.getDescriptionId(), centerX, centerY); - return true; - } - logArchivePreviewOnce("item.unrenderable." + itemId, "Archive item preview unavailable id={} (missing or AIR)", itemId); - } catch (RuntimeException exception) { - logArchivePreviewOnce("item.error." + itemId, "Archive item preview failed id={} error={}", itemId, exception.toString()); - } - } - if (!enchantmentId.isBlank()) { - ItemStack stack = createArchiveEnchantedBook(enchantmentId); - if (!stack.isEmpty()) { - int iconSize = Math.min(32, Math.max(16, size - 4)); - renderArchiveItem(g, stack, centerX - iconSize / 2, centerY - iconSize / 2, iconSize); - logArchivePreviewOnce("enchantment.render." + enchantmentId, "Archive enchantment preview rendered id={}", enchantmentId); - return true; - } - logArchivePreviewOnce("enchantment.unrenderable." + enchantmentId, "Archive enchantment preview unavailable id={}", enchantmentId); - } - if (!potionId.isBlank() && Minecraft.getInstance().level != null) { - try { - ResourceLocation potionLocation = ResourceLocation.parse(potionId); - ResourceKey potionKey = ResourceKey.create(Registries.POTION, potionLocation); - var potion = Minecraft.getInstance().level.registryAccess().lookupOrThrow(Registries.POTION).get(potionKey).orElse(null); - if (potion != null) { - int iconSize = Math.min(32, Math.max(16, size - 4)); - renderArchiveItem(g, PotionContents.createItemStack(Items.POTION, potion), centerX - iconSize / 2, centerY - iconSize / 2, iconSize); - return true; - } - } catch (RuntimeException exception) { - logArchivePreviewOnce("potion.error." + potionId, "Archive potion preview failed id={} error={}", potionId, exception.toString()); - } - } - if (!entityId.isBlank() && Minecraft.getInstance().level == null) { - logArchivePreviewOnce("entity.no_level." + entityId, "Archive mob preview skipped id={} because the client level is null", entityId); - } - if (!entityId.isBlank() && Minecraft.getInstance().level != null) { - try { - ResourceLocation resourceId = ResourceLocation.parse(entityId); - var entityType = BuiltInRegistries.ENTITY_TYPE.getOptional(resourceId).orElse(null); - logArchivePreviewOnce("entity.lookup." + entityId, "Archive mob preview lookup id={} found={} registryName={}", entityId, entityType != null, entityType == null ? "" : BuiltInRegistries.ENTITY_TYPE.getKey(entityType)); - LivingEntity living = archiveEntityPreviews.get(entityId); - if (living == null && entityType != null && !unavailableArchiveEntities.contains(entityId)) { - var entity = entityType.create(Minecraft.getInstance().level); - if (entity instanceof LivingEntity created && !created.isRemoved()) { - living = created; - archiveEntityPreviews.put(entityId, created); - } else { - unavailableArchiveEntities.add(entityId); - logArchivePreviewOnce("entity.unrenderable." + entityId, "Archive mob preview unavailable id={} createdClass={} isLiving={}", entityId, entity == null ? "" : entity.getClass().getName(), entity instanceof LivingEntity); - } - } - if (living != null && !living.isRemoved()) { - if (living instanceof NightmareEntity nightmare) nightmare.setArchivePreviewFlying(); - living.setPos(0.0D, 0.0D, 0.0D); - float yaw = 180.0F + rotation; - living.setYRot(yaw); - living.yRotO = yaw; - living.yBodyRot = yaw; - living.yBodyRotO = yaw; - living.yHeadRot = yaw; - living.yHeadRotO = yaw; - living.tickCount = (int) (Minecraft.getInstance().level.getGameTime() & Integer.MAX_VALUE); - float largestDimension = Math.max(0.45F, Math.max(living.getBbWidth(), living.getBbHeight())); - // Leave room for wide silhouettes and configured yaw rotations (notably the flying squirrel). - int entityRenderScale = Math.max(4, Math.min(size, Math.round(size * 0.52F * renderScale / largestDimension))); - float entityScale = Math.max(0.01F, living.getScale()); - Vector3f translation = new Vector3f(0.0F, living.getBbHeight() / 2.0F + 0.0625F * entityScale, 0.0F); - Quaternionf pose = new Quaternionf().rotateZ((float) Math.PI); - Quaternionf camera = new Quaternionf(); - g.flush(); - enableComputerScissor(g, centerX - size / 2, centerY - size / 2, centerX + size / 2, centerY + size / 2); - g.setColor(0.0F, 1.0F, 0.0F, 1.0F); - try { - InventoryScreen.renderEntityInInventory(g, centerX, centerY, entityRenderScale / entityScale, - translation, pose, camera, living); - g.flush(); - } finally { - g.setColor(1.0F, 1.0F, 1.0F, 1.0F); - g.disableScissor(); - } - logArchivePreviewOnce("entity.render." + entityId, "Archive mob preview rendered id={} class={} size={} center=({}, {})", entityId, living.getClass().getName(), size, centerX, centerY); - return true; - } - } catch (RuntimeException exception) { - logArchivePreviewOnce("entity.error." + entityId, "Archive mob preview failed id={} error={}", entityId, exception.toString()); - } - } - return false; - } - - private void renderArchiveItem(GuiGraphics g, ItemStack stack, int x, int y) { - renderArchiveItem(g, stack, x, y, 16); - } - - private void renderArchiveItem(GuiGraphics g, ItemStack stack, int x, int y, int size) { - g.flush(); - // Keep only the green channel, matching the archive mob treatment and preventing source hues from bleeding through. - g.setColor(0.0F, 1.0F, 0.0F, 1.0F); - g.pose().pushPose(); - try { - g.pose().translate(x, y, 0.0F); - float scale = size / 16.0F; - g.pose().scale(scale, scale, 1.0F); - g.renderItem(stack, 0, 0); - g.flush(); - } finally { - g.pose().popPose(); - g.setColor(1.0F, 1.0F, 1.0F, 1.0F); - } - } - - private ItemStack createArchiveEnchantedBook(String enchantmentId) { - if (Minecraft.getInstance().level == null) return ItemStack.EMPTY; - try { - ResourceLocation id = ResourceLocation.parse(enchantmentId); - ResourceKey key = ResourceKey.create(Registries.ENCHANTMENT, id); - var holder = Minecraft.getInstance().level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).get(key).orElse(null); - if (holder == null) return ItemStack.EMPTY; - ItemEnchantments.Mutable enchantments = new ItemEnchantments.Mutable(ItemEnchantments.EMPTY); - enchantments.set(holder, 1); - ItemStack stack = new ItemStack(Items.ENCHANTED_BOOK); - stack.set(DataComponents.STORED_ENCHANTMENTS, enchantments.toImmutable().withTooltip(true)); - return stack; - } catch (RuntimeException exception) { - logArchivePreviewOnce("enchantment.error." + enchantmentId, "Archive enchantment preview failed id={} error={}", enchantmentId, exception.toString()); - return ItemStack.EMPTY; - } - } - - private void logArchivePreviewOnce(String key, String message, Object... arguments) { - if (archiveRenderLog.add(key)) Antarchy.LOGGER.info(message, arguments); - } - - private void renderFileExplorer(GuiGraphics g, int x, int y, int w, int h) { - var files = ComputerFileSystemClientState.get(position).files(); - g.drawString(font, Component.literal("FILE EXPLORER // " + trimToWidth(session.fileExplorerDirectory, w - 4)), x, y, GREEN, false); - if (!session.fileExplorerDirectory.equals("/")) g.drawString(font, Component.literal("[ UP ]"), x + w - 40, y, GREEN, false); - g.drawString(font, Component.literal("TYPE PATH SIZE"), x, y + 18, PALE_GREEN, false); - int line = y + 34; - int fileIndex = 0; - for (String file : files) { - String[] fields = file.split("\\t", 3); - if (fields.length < 3 || !isDirectChild(fields[1], session.fileExplorerDirectory)) continue; - if (fileIndex++ < session.fileExplorerScroll || line > y + h - 32) continue; - boolean selected = fields[1].equals(session.fileExplorerSelected); - if (selected) g.fill(x - 3, line - 2, x + w - 3, line + 11, 0xFF173817); - g.drawString(font, Component.literal(fileIcon(fields[0]) + " " + trimToWidth(fields[0], 30)), x, line, PALE_GREEN, false); - g.drawString(font, Component.literal(trimToWidth(fields[1], 112)), x + 42, line, GREEN, false); - g.drawString(font, Component.literal(trimToWidth(fields[2], 28)), x + w - 28, line, PALE_GREEN, false); - line += 14; - } - if (line == y + 34) g.drawString(font, Component.literal("NO FILES // OPEN TERMINAL TO CREATE ONE"), x, y + 38, PALE_GREEN, false); - else { - var state = ComputerFileSystemClientState.get(position); - String footer = session.fileExplorerCreatingDirectory ? "NEW FOLDER: " + session.fileExplorerRename + "_" - : session.fileExplorerMoving ? "MOVE TO PATH: " + session.fileExplorerRename + "_" - : session.fileExplorerRenaming ? "RENAME: " + session.fileExplorerRename + "_" - : session.fileExplorerDeleteConfirm ? "DELETE SELECTED FILE? [ ENTER / ESC ]" : "[ CTRL+N NEW ] [ CTRL+M MOVE ] [ DEL ] [ CTRL+R RENAME ]"; - if (state.lastMutationAction() == com.craisinlord.antarchy.content.network.ComputerAccessPayload.FILE_DELETE - || state.lastMutationAction() == com.craisinlord.antarchy.content.network.ComputerAccessPayload.FILE_MOVE) { - footer += state.lastMutationSuccess() ? " // OK" : " // ERR: " + state.lastMutationError(); - } - g.drawString(font, Component.literal(trimToWidth(footer, w - 4)), x, y + h - 16, PALE_GREEN, false); - } - } - - private String fileIcon(String type) { - return type.equals("DIRECTORY") ? "[D]" : type.equals("TEXT") ? "[T]" : "[I]"; - } - - private boolean isDirectChild(String path, String directory) { - if (path.equals(directory)) return false; - String prefix = directory.equals("/") ? "/" : directory + "/"; - if (!path.startsWith(prefix)) return false; - return path.indexOf('/', prefix.length()) < 0; - } - - private String parentDirectory(String path) { - if (path.equals("/")) return "/"; - int slash = path.lastIndexOf('/'); - return slash <= 0 ? "/" : path.substring(0, slash); - } - - private String trimToWidth(String value, int width) { - if (font.width(value) <= width) return value; - String ellipsis = "..."; - int end = value.length(); - while (end > 0 && font.width(value.substring(0, end) + ellipsis) > width) end--; - return value.substring(0, end) + ellipsis; - } - - private void renderSettings(GuiGraphics g, int x, int y, int h) { - g.drawString(font, Component.literal("SYSTEM STATUS"), x, y, GREEN, false); - g.drawString(font, Component.literal("CPU " + (System.currentTimeMillis() / 100 % 87 + 12) + "%"), x, y + 18, PALE_GREEN, false); - g.drawString(font, Component.literal("MEM " + (System.currentTimeMillis() / 250 % 42 + 31) + "%"), x, y + 32, PALE_GREEN, false); - g.drawString(font, Component.literal("WALLPAPER DEFAULT // LOCKED"), x, y + 46, PALE_GREEN, false); - g.drawString(font, Component.literal("PASSWORD CHANGE IN FULL BUILD"), x, y + 60, GREEN, false); - g.drawString(font, Component.literal("PHYSICAL DISKS"), x, y + 91, GREEN, false); - List disks = physicalDisks(); - if (disks.isEmpty()) { - g.drawString(font, Component.literal("NONE INSTALLED"), x, y + 108, PALE_GREEN, false); - } else { - for (int i = 0; i < disks.size() && i < 3; i++) { - ResourceLocation disk = disks.get(i); - boolean active = disk.equals(selectedDisk); - if (active) g.fill(x - 3, y + 105 + i * 17, x + 205, y + 120 + i * 17, 0xFF173817); - g.drawString(font, Component.literal(trimToWidth(disk.toString(), 202)), x, y + 108 + i * 17, active ? GREEN : PALE_GREEN, false); - } - g.drawString(font, Component.literal("[ EJECT SELECTED ]"), x, y + 162, selectedDisk == null ? 0xFF4A754A : GREEN, false); - } - g.drawString(font, Component.literal("LOG OUT"), x, y + 74, GREEN, false); - } - - private void drawWallpaperPreview(GuiGraphics g, int x, int y, int w, int h, String id) { - int hash = id.hashCode(); - int green = 24 + Math.floorMod(hash, 40); - int blue = 12 + Math.floorMod(hash >>> 8, 24); - int base = 0xFF000000 | (green / 2 << 16) | (green << 8) | blue; - g.fill(x - 1, y - 1, x + w + 1, y + h + 1, GREEN); - g.fill(x, y, x + w, y + h, base); - int accent = 0xFF000000 | (Math.min(120, green + 34) << 8) | Math.min(80, blue + 34); - for (int py = y + 4; py < y + h; py += 9) { - for (int px = x + 4; px < x + w; px += 12) { - if (Math.floorMod(px + py + hash, 3) == 0) g.fill(px, py, Math.min(x + w, px + 3), Math.min(y + h, py + 2), accent); - } - } - } - - private void renderTerminal(GuiGraphics g, int x, int y, int h) { - g.drawString(font, Component.literal("ANTINTOSH TERMINAL [READY]"), x, y, GREEN, false); - int line = y + 17; - for (String output : session.terminalOutput) { - line = wrapLimited(g, output, x, line, 208, y + h - 48, PALE_GREEN) + 1; - if (line >= y + h - 48) break; - } - g.drawString(font, Component.literal(trimToWidth(session.terminalDirectory + "> " + session.terminalInput + "_", 208)), x, y + h - 26, GREEN, false); - } - - private void renderPaint(GuiGraphics g, int x, int y, int w, int h) { - var fileState = ComputerFileSystemClientState.get(position); - if (!session.paintDirty && fileState.openedPath().endsWith(".antpaint") && !fileState.openedContents().isEmpty()) { - try { - AntPaintFile file = AntPaintFileCodec.decode(Base64.getDecoder().decode(fileState.openedContents())); - session.paintName = file.filename(); - session.antmailPaintPath = fileState.openedPath(); - session.paintCanvas = file.canvas().copy(); - } catch (IllegalArgumentException ignored) { - } - } - g.drawString(font, Component.literal(trimToWidth("ANTPAINT // " + session.paintName + (session.paintDirty ? " *" : ""), w)), x, y, GREEN, false); - renderPaintToolbar(g, x, y); - // w and h are already the window's content dimensions; keep rendering in - // lockstep with paintAt(), which receives these same adjusted dimensions. - int canvasHeight = Math.max(1, h - 32); - int size = Math.max(1, Math.min(w / AntPaintCanvas.WIDTH, canvasHeight / AntPaintCanvas.HEIGHT)); - int cx = x + (w - size * AntPaintCanvas.WIDTH) / 2; - int cy = y + 30 + Math.max(0, (canvasHeight - size * AntPaintCanvas.HEIGHT) / 2); - g.fill(cx - 1, cy - 1, cx + size * AntPaintCanvas.WIDTH + 1, cy + size * AntPaintCanvas.HEIGHT + 1, BLACK); - g.fill(cx, cy, cx + size * AntPaintCanvas.WIDTH, cy + size * AntPaintCanvas.HEIGHT, BLACK); - for (int py = 0; py < AntPaintCanvas.HEIGHT; py++) for (int px = 0; px < AntPaintCanvas.WIDTH; px++) if (session.paintCanvas.get(px, py)) g.fill(cx + px * size, cy + py * size, cx + (px + 1) * size, cy + (py + 1) * size, GREEN); - int hoverX = (session.mouseX - cx) / size; - int hoverY = (session.mouseY - cy) / size; - if (session.mouseX >= cx && session.mouseY >= cy && hoverX >= 0 && hoverX < AntPaintCanvas.WIDTH && hoverY >= 0 && hoverY < AntPaintCanvas.HEIGHT) { - int pixelX = cx + hoverX * size; - int pixelY = cy + hoverY * size; - g.fill(pixelX, pixelY, pixelX + size, pixelY + 1, PALE_GREEN); - g.fill(pixelX, pixelY + size - 1, pixelX + size, pixelY + size, PALE_GREEN); - g.fill(pixelX, pixelY, pixelX + 1, pixelY + size, PALE_GREEN); - g.fill(pixelX + size - 1, pixelY, pixelX + size, pixelY + size, PALE_GREEN); - } - } - - private void renderPaintToolbar(GuiGraphics g, int x, int y) { - for (int index = 0; index < 7; index++) { - int buttonX = x + index * 28; - if (index < 3 && ((index == 0 && session.paintTool == AntPaintTool.PENCIL) - || (index == 1 && session.paintTool == AntPaintTool.ERASER) - || (index == 2 && session.paintTool == AntPaintTool.FILL))) { - box(g, buttonX, y + 11, buttonX + 22, y + 28, GREEN); - } - paintToolbarIcon(g, index, buttonX + 11, y + 19); - } - } - - private void paintToolbarIcon(GuiGraphics g, int index, int x, int y) { - String externalIcon = switch (index) { - case 0 -> "pencil"; - case 1 -> "eraser"; - case 3 -> "trash"; - case 4 -> "undo"; - case 5 -> "redo"; - default -> null; - }; - if (externalIcon != null) { - ResourceLocation texture = ResourceLocation.fromNamespaceAndPath( - "antarchy", "textures/gui/antpaint/" + externalIcon + ".png"); - g.blit(texture, x - 9, y - 9, 0, 0, 18, 18, 24, 24); - return; - } - int color = PALE_GREEN; - String[][] sprites = { - // Pencil: graphite point at lower-left, long barrel, and capped eraser at upper-right. - { - ".............##.", - "............####", - "...........##++#", - "..........##+++##", - ".........##+++##.", - "........##+++##..", - ".......##+++##...", - "......##+++##....", - ".....##+++##.....", - "....##+++##......", - "...##+++##.......", - "..##+++##........", - ".##++##..........", - ".####............", - "..##.............", - "...t............." - }, - // Eraser: angled block with a contrasting paper edge and colored rubber. - { - "................", - "...........####.", - ".........###++##", - ".......###+++##.", - ".....###+++##...", - "...###+++##.....", - ".###+++##.......", - "##+++###........", - "#++###...........", - "####.............", - "................", - "................", - "................", - "................", - "................", - "................" - }, - // Bucket: pale arched handle, wide rim, tapered body, and visible paint. - { - "....######......", - "...##....##.....", - "..##......##....", - "..#........#....", - ".#############..", - ".#+++++++++++##.", - ".#++gggggg+++##.", - ".#+++++++++++##.", - "..#+++++++++##..", - "..##++++++++##..", - "...##++++++##...", - "...##++++++##...", - "....########....", - "................", - "................", - "................" - }, - // Clear: trash bin with lid and vertical slats. - { - ".....######.....", - "...##########...", - "....##.##.##....", - "..############..", - "..##++++++++##..", - "..##++##++##++##.", - "..##++##++##++##.", - "..##++##++##++##.", - "...##++++++++##..", - "...############..", - "....##......##...", - "................", - "................", - "................", - "................", - "................" - }, - // Undo: left-facing hooked arrow. - { - "......####......", - "................", - "................", - "....##..........", - "...####.........", - "..##++##........", - ".##++++##.......", - "##++++++++++##..", - ".##++++##.......", - "..##++##........", - "...####.........", - "....##..........", - "................", - "................", - "................", - "................" - }, - // Redo: right-facing hooked arrow, mirrored at render time. - { - "......####......", - "................", - "................", - "....##..........", - "...####.........", - "..##++##........", - ".##++++##.......", - "##++++++++++##..", - ".##++++##.......", - "..##++##........", - "...####.........", - "....##..........", - "................", - "................", - "................", - "................" - }, - // Save icon intentionally retained. - { - ".#############.", - "#+++++++++++++#", - "#++#####++++++#", - "#++#...#++++++#", - "#++#...#++++++#", - "#++#####++++++#", - "#+++++++++++++#", - "#++#########++#", - "#++#+++++++#++#", - "#++#++gg+++#++#", - "#++#+++++++#++#", - "#++#########++#", - "#+++++++++++++#", - ".#############.", - "..............." - } - }; - String[] sprite = sprites[Math.max(0, Math.min(index, sprites.length - 1))]; - if (index == 5) { - sprite = sprite.clone(); - for (int row = 0; row < sprite.length; row++) { - sprite[row] = new StringBuilder(sprite[row].substring(0, Math.min(16, sprite[row].length()))).reverse().toString(); - } - } - for (int row = 0; row < 16; row++) { - String line = row < sprite.length ? sprite[row] : ""; - if (line.length() > 16) line = line.substring(0, 16); - for (int column = 0; column < 16; column++) { - char pixel = column < line.length() ? line.charAt(column) : '.'; - if (pixel != '#' && pixel != '+' && pixel != 'g' && pixel != 't') continue; - for (int[] offset : new int[][]{{0, -1}, {0, 1}, {-1, 0}, {1, 0}}) { - int dx = offset[0]; - int dy = offset[1]; - int nx = column + dx; - int ny = row + dy; - if (nx < 0 || nx >= 16 || ny < 0 || ny >= 16) continue; - String neighborLine = ny < sprite.length ? sprite[ny] : ""; - char neighbor = nx < neighborLine.length() ? neighborLine.charAt(nx) : '.'; - if (neighbor == '.' || neighbor == ' ') g.fill(x - 8 + nx, y - 8 + ny, x - 7 + nx, y - 7 + ny, 0xFF78A878); - } - } - } - for (int row = 0; row < 16; row++) { - String line = row < sprite.length ? sprite[row] : ""; - for (int column = 0; column < 16; column++) { - char pixel = column < line.length() ? line.charAt(column) : '.'; - if (pixel == '#') g.fill(x - 8 + column, y - 8 + row, x - 7 + column, y - 7 + row, BLACK); - else if (pixel == '+') g.fill(x - 8 + column, y - 8 + row, x - 7 + column, y - 7 + row, color); - else if (pixel == 'g') g.fill(x - 8 + column, y - 8 + row, x - 7 + column, y - 7 + row, GREEN); - else if (pixel == 't') g.fill(x - 8 + column, y - 8 + row, x - 7 + column, y - 7 + row, 0xFF657465); - } - } - } - - private void paintToolbarIconLegacy(GuiGraphics g, int index, int x, int y) { - int color = PALE_GREEN; - if (index == 0) { - // Pencil: a full diagonal silhouette with graphite, wood, painted barrel, ferrule, and eraser. - g.fill(x - 9, y + 4, x - 5, y + 8, BLACK); - g.fill(x - 7, y + 2, x - 3, y + 7, BLACK); - g.fill(x - 5, y, x + 3, y + 6, BLACK); - g.fill(x - 2, y - 3, x + 6, y + 3, BLACK); - g.fill(x + 4, y - 6, x + 8, y - 1, BLACK); - g.fill(x - 8, y + 5, x - 6, y + 7, color); - g.fill(x - 6, y + 3, x - 4, y + 5, color); - g.fill(x - 4, y + 1, x - 2, y + 3, color); - g.fill(x - 2, y - 1, x + 1, y + 2, color); - g.fill(x + 1, y - 3, x + 4, y, color); - g.fill(x + 4, y - 5, x + 6, y - 2, color); - g.fill(x - 4, y + 3, x - 2, y + 5, GREEN); - g.fill(x - 2, y + 1, x + 1, y + 3, GREEN); - g.fill(x + 1, y - 1, x + 4, y + 1, GREEN); - g.fill(x + 3, y - 4, x + 6, y - 2, BLACK); - g.fill(x + 5, y - 6, x + 8, y - 4, BLACK); - g.fill(x + 5, y - 4, x + 7, y - 2, color); - } else if (index == 1) { - // Eraser: large rotated block with a paper edge, center band, shadow, and shine. - g.fill(x - 9, y - 3, x + 5, y + 7, BLACK); - g.fill(x - 6, y - 6, x + 8, y + 4, BLACK); - g.fill(x - 7, y - 2, x - 2, y + 3, color); - g.fill(x - 4, y - 4, x + 2, y + 1, color); - g.fill(x - 1, y - 5, x + 5, y, color); - g.fill(x - 9, y + 4, x + 3, y + 8, BLACK); - g.fill(x - 7, y + 4, x - 1, y + 6, color); - g.fill(x - 6, y - 1, x - 3, y + 2, PALE_GREEN); - g.fill(x - 3, y - 3, x, y, PALE_GREEN); - g.fill(x + 1, y - 4, x + 4, y - 1, PALE_GREEN); - g.fill(x + 3, y, x + 6, y + 3, BLACK); - g.fill(x + 5, y - 3, x + 8, y + 1, BLACK); - g.fill(x + 5, y - 2, x + 6, y, color); - } else if (index == 2) { - // Fill bucket: arched handle, thick rim, tapered metal body, and visible paint inside. - g.fill(x - 6, y - 7, x + 6, y - 4, BLACK); - g.fill(x - 8, y - 5, x - 5, y + 1, BLACK); - g.fill(x + 5, y - 5, x + 8, y + 1, BLACK); - g.fill(x - 6, y - 5, x + 6, y - 3, color); - g.fill(x - 7, y - 2, x + 7, y + 2, BLACK); - g.fill(x - 5, y - 1, x + 5, y + 1, color); - g.fill(x - 5, y + 1, x + 5, y + 7, BLACK); - g.fill(x - 4, y + 3, x + 4, y + 6, color); - g.fill(x - 3, y + 2, x + 4, y + 4, PALE_GREEN); - g.fill(x - 4, y + 6, x + 4, y + 8, BLACK); - g.fill(x - 2, y + 5, x + 3, y + 6, GREEN); - g.fill(x - 5, y - 6, x - 3, y - 4, GREEN); - g.fill(x + 3, y - 6, x + 5, y - 4, GREEN); - } else if (index == 3) { - // Clear canvas: trash can with lid, slats, and a strong diagonal clear mark. - g.fill(x - 6, y - 7, x + 6, y - 4, BLACK); - g.fill(x - 3, y - 9, x + 3, y - 6, BLACK); - g.fill(x - 5, y - 3, x + 5, y + 8, BLACK); - g.fill(x - 3, y - 1, x + 3, y + 6, color); - g.fill(x - 4, y - 2, x + 4, y, PALE_GREEN); - g.fill(x - 3, y + 1, x - 1, y + 6, GREEN); - g.fill(x + 1, y + 1, x + 3, y + 6, GREEN); - g.fill(x - 8, y - 8, x + 8, y - 5, BLACK); - g.fill(x - 7, y - 4, x + 7, y - 2, BLACK); - g.fill(x - 8, y - 8, x - 5, y + 8, BLACK); - g.fill(x + 5, y - 8, x + 8, y + 8, BLACK); - g.fill(x - 9, y - 7, x - 7, y + 8, color); - g.fill(x + 7, y - 7, x + 9, y + 8, color); - g.fill(x - 8, y - 1, x + 8, y + 1, BLACK); - g.fill(x - 8, y + 5, x + 8, y + 7, BLACK); - g.fill(x - 8, y - 8, x + 8, y - 6, color); - g.fill(x - 7, y - 7, x + 7, y - 5, BLACK); - } else if (index == 4) { - // Undo: broad pixel arrow, stepped hook, and hollow center. - g.fill(x - 9, y - 4, x - 5, y + 2, color); - g.fill(x - 7, y - 6, x - 3, y - 2, color); - g.fill(x - 7, y, x - 3, y + 4, color); - g.fill(x - 5, y + 3, x + 3, y + 7, color); - g.fill(x + 1, y + 1, x + 6, y + 5, color); - g.fill(x + 4, y - 3, x + 8, y + 3, color); - g.fill(x + 2, y - 6, x + 6, y - 2, color); - g.fill(x - 5, y - 1, x + 3, y + 2, BLACK); - g.fill(x - 3, y + 2, x + 3, y + 4, BLACK); - g.fill(x - 8, y - 5, x - 5, y - 3, PALE_GREEN); - g.fill(x + 5, y - 2, x + 7, y, PALE_GREEN); - } else if (index == 5) { - // Redo: mirrored broad pixel arrow, stepped hook, and hollow center. - g.fill(x + 5, y - 4, x + 9, y + 2, color); - g.fill(x + 3, y - 6, x + 7, y - 2, color); - g.fill(x + 3, y, x + 7, y + 4, color); - g.fill(x - 3, y + 3, x + 5, y + 7, color); - g.fill(x - 6, y + 1, x - 1, y + 5, color); - g.fill(x - 8, y - 3, x - 4, y + 3, color); - g.fill(x - 6, y - 6, x - 2, y - 2, color); - g.fill(x - 3, y - 1, x + 5, y + 2, BLACK); - g.fill(x - 3, y + 2, x + 3, y + 4, BLACK); - g.fill(x + 5, y - 5, x + 8, y - 3, PALE_GREEN); - g.fill(x - 7, y - 2, x - 5, y, PALE_GREEN); - } else { - // Save: floppy disk with corner notch, shutter, label, hub, and highlight. - g.fill(x - 8, y - 8, x + 8, y + 8, BLACK); - g.fill(x - 5, y - 6, x + 5, y + 6, color); - g.fill(x - 5, y - 6, x + 2, y - 2, GREEN); - g.fill(x + 2, y - 6, x + 5, y - 1, BLACK); - g.fill(x + 4, y - 6, x + 5, y - 3, PALE_GREEN); - g.fill(x - 5, y, x + 5, y + 6, BLACK); - g.fill(x - 3, y + 1, x + 3, y + 5, color); - g.fill(x - 2, y + 2, x + 2, y + 4, PALE_GREEN); - g.fill(x - 7, y - 7, x - 5, y + 5, PALE_GREEN); - g.fill(x - 7, y + 6, x + 7, y + 8, BLACK); - g.fill(x - 4, y + 6, x + 4, y + 7, GREEN); - } - } - - private void renderTextEditor(GuiGraphics g, int x, int y, int w, int h) { - var fileState = ComputerFileSystemClientState.get(position); - if (session.textDirty - && fileState.lastMutationSuccess() - && (fileState.lastMutationAction() == com.craisinlord.antarchy.content.network.ComputerAccessPayload.FILE_CREATE - || fileState.lastMutationAction() == com.craisinlord.antarchy.content.network.ComputerAccessPayload.FILE_SAVE) - && fileState.lastMutationPath().equals(session.textPath)) { - session.textDirty = false; - } - if (!session.textSaveAsPendingPath.isBlank() - && fileState.lastMutationAction() == com.craisinlord.antarchy.content.network.ComputerAccessPayload.FILE_CREATE - && fileState.lastMutationPath().equals(session.textSaveAsPendingPath)) { - if (fileState.lastMutationSuccess()) { - session.textPath = session.textSaveAsPendingPath; - session.textDirty = false; - session.textJustSavedAs = true; - } - session.textSaveAsPendingPath = ""; - } - if (!session.textMovePendingPath.isBlank() - && fileState.lastMutationAction() == com.craisinlord.antarchy.content.network.ComputerAccessPayload.FILE_MOVE - && fileState.lastMutationPath().equals(session.textMovePendingPath)) { - if (fileState.lastMutationSuccess()) { - session.textPath = session.textMovePendingPath; - session.textJustSavedAs = true; - } - session.textMovePendingPath = ""; - } - if (!fileState.openedPath().isEmpty() && !session.textDirty && !session.textJustSavedAs) { - session.textPath = fileState.openedPath(); - session.textContent = fileState.openedContents(); - session.textCursor = session.textContent.length(); - } - session.textJustSavedAs = false; - String editorTitle = session.textRenaming ? "RENAME // " + session.textRename + "_" : "ANTTEXT // " + session.textPath + (session.textDirty ? " *" : ""); - g.drawString(font, Component.literal(trimToWidth(editorTitle, Math.max(1, w - 104))), x, y, GREEN, false); - g.drawString(font, Component.literal(trimToWidth("NEW OPEN WRITE SAVE AS", w)), x, y + 18, PALE_GREEN, false); - if (fileState.lastMutationAction() == com.craisinlord.antarchy.content.network.ComputerAccessPayload.FILE_CREATE - || fileState.lastMutationAction() == com.craisinlord.antarchy.content.network.ComputerAccessPayload.FILE_SAVE - || fileState.lastMutationAction() == com.craisinlord.antarchy.content.network.ComputerAccessPayload.FILE_MOVE) { - String status = fileState.lastMutationSuccess() ? "OPERATION OK" : "OPERATION ERROR // " + fileState.lastMutationError(); - String displayStatus = trimToWidth(status, Math.min(100, w)); - g.drawString(font, Component.literal(displayStatus), x + w - font.width(displayStatus), y, fileState.lastMutationSuccess() ? PALE_GREEN : GREEN, false); - } - g.fill(x + 4, y + 36, x + w - 4, y + h - 4, BLACK); - if (session.textListing) { - g.drawString(font, Component.literal("SELECT FILE"), x + 10, y + 46, GREEN, false); - int line = y + 62; - int visibleFiles = 0; - int textIndex = 0; - for (String file : fileState.files()) { - String[] fields = file.split("\\t", 3); - if (fields.length < 3 || !fields[0].equals("TEXT")) continue; - if (textIndex++ < session.textPickerScroll || line > y + h - 50) continue; - g.drawString(font, Component.literal(fields[1]), x + 10, line, PALE_GREEN, false); - line += 14; - visibleFiles++; - } - if (visibleFiles == 0) g.drawString(font, Component.literal("NO TEXT FILES"), x + 10, y + 62, PALE_GREEN, false); - } else { - String content = session.textContent.isEmpty() ? "TYPE HERE..." : session.textContent; - if (session.textFocused) content = content.substring(0, Math.min(session.textCursor, content.length())) + "|" - + content.substring(Math.min(session.textCursor, content.length())); - List lines = font.split(Component.literal(content), w - 28); - int maxLines = Math.max(1, (h - 88) / 11); - session.textScroll = Math.max(0, Math.min(session.textScroll, Math.max(0, lines.size() - maxLines))); - for (int i = session.textScroll; i < lines.size() && i < session.textScroll + maxLines; i++) { - g.drawString(font, lines.get(i), x + 10, y + 46 + (i - session.textScroll) * 11, GREEN, false); - } - } - if (session.textDeleteConfirm) { - g.drawString(font, Component.literal("DELETE CURRENT FILE? [ ENTER / ESC ]"), x, y + h - 28, PALE_GREEN, false); - } else { - g.drawString(font, Component.literal(trimToWidth("[CTRL+S] SAVE [CTRL+SHIFT+S] SAVE AS", w)), x, y + h - 39, PALE_GREEN, false); - g.drawString(font, Component.literal(trimToWidth("[CTRL+R] RENAME [CTRL+DEL] DELETE [ESC] CLOSE", w)), x, y + h - 26, PALE_GREEN, false); - } - } - - private void renderAntmail(GuiGraphics g, int x, int y, int h) { - var result = AntmailClientState.getMailbox(position); - AntmailResultPayload lastResponse = AntmailClientState.get(position); - boolean unconfigured = (result != null && result.address().isBlank()) - || (result == null && lastResponse != null && ("unconfigured".equals(lastResponse.detail()) - || "address_not_found".equals(lastResponse.detail()) || lastResponse.detail().startsWith("registration_failed:"))); - g.drawString(font, Component.literal("ANTMAIL // COMPUTER ADDRESS"), x, y, GREEN, false); - session.antmailAttachmentStartLine = -1; - session.antmailVisibleAttachmentCount = 0; - if (result == null && !unconfigured) { - g.drawString(font, Component.literal("CONTACTING ANTMAIL NETWORK"), x, y + 20, GREEN, false); - String waitStatus = lastResponse != null && !lastResponse.detail().isBlank() - ? "SERVER // " + lastResponse.detail().toUpperCase() - : session.antmailStateWaitTicks >= 100 ? "NO RESPONSE // CHECK CLIENT + SERVER LOGS" : "PLEASE WAIT // RETRYING STATE SYNC"; - g.drawString(font, Component.literal(trimToWidth(waitStatus, 208)), x, y + 40, PALE_GREEN, false); - box(g, x, y + 56, x + 150, y + 79, GREEN); - g.drawString(font, Component.literal("[ RETRY STATE LINK ]"), x + 6, y + 63, GREEN, false); - } else if (unconfigured) { - String registrationStatus = lastResponse != null && lastResponse.detail().startsWith("registration_failed:") - ? "REGISTRATION FAILED // " + lastResponse.detail().substring("registration_failed:".length()).toUpperCase() - : "NO ADDRESS CONFIGURED"; - g.drawString(font, Component.literal(trimToWidth(registrationStatus, 208)), x, y + 20, PALE_GREEN, false); - g.drawString(font, Component.literal("USERNAME // 3-16 CHARACTERS"), x, y + 40, GREEN, false); - g.fill(x, y + 56, x + 206, y + 79, BLACK); - box(g, x, y + 56, x + 206, y + 79, GREEN); - g.drawString(font, Component.literal(trimToWidth(session.antmailUsername + "@antmail.com", 194)), x + 6, y + 63, GREEN, false); - g.drawString(font, Component.literal("[ ENTER ] REGISTER ADDRESS"), x, y + 88, GREEN, false); - } else { - g.drawString(font, Component.literal(trimToWidth(result.address(), 208)), x, y + 20, PALE_GREEN, false); - if (session.antmailMode.equals("inbox")) box(g, x, y + 30, x + 54, y + 53, PALE_GREEN); - if (session.antmailMode.equals("sent")) box(g, x + 54, y + 30, x + 100, y + 53, PALE_GREEN); - if (session.antmailMode.equals("drafts")) box(g, x + 100, y + 30, x + 154, y + 53, PALE_GREEN); - g.drawString(font, Component.literal("INBOX"), x + 6, y + 38, session.antmailMode.equals("inbox") ? PALE_GREEN : GREEN, false); - g.drawString(font, Component.literal("SENT"), x + 60, y + 38, session.antmailMode.equals("sent") ? PALE_GREEN : GREEN, false); - g.drawString(font, Component.literal("DRAFTS"), x + 106, y + 38, session.antmailMode.equals("drafts") ? PALE_GREEN : GREEN, false); - box(g, x + 158, y + 30, x + 214, y + 53, session.antmailMode.equals("compose") ? PALE_GREEN : GREEN); - g.drawString(font, Component.literal("COMPOSE"), x + 163, y + 38, session.antmailMode.equals("compose") ? PALE_GREEN : GREEN, false); - if (session.antmailMode.equals("compose")) { - g.drawString(font, Component.literal(trimToWidth(session.antmailRecipient, 180)), x + 28, y + 52, PALE_GREEN, false); - g.drawString(font, Component.literal(trimToWidth(session.antmailSubject, 156)), x + 52, y + 66, PALE_GREEN, false); - g.drawString(font, Component.literal("TO"), x, y + 52, GREEN, false); - g.drawString(font, Component.literal("SUBJECT"), x, y + 66, GREEN, false); - g.drawString(font, Component.literal("BODY"), x, y + 80, GREEN, false); - wrapLimited(g, session.antmailBody.isEmpty() ? "TYPE MESSAGE..." : session.antmailBody, x, y + 94, 208, y + 124, PALE_GREEN); - g.drawString(font, Component.literal(trimToWidth("[ " + (session.antmailAttachText ? "X" : " ") + " ] TEXT FILE [ " + (session.antmailAttachPaint ? "X" : " ") + " ] PAINTING", 208)), x, y + 128, PALE_GREEN, false); - if (session.antmailPickingAttachment) { - g.fill(x + 4, y + 42, x + 210, y + h - 40, BLACK); - box(g, x + 4, y + 42, x + 210, y + h - 40, GREEN); - g.drawString(font, Component.literal("SELECT FILE TO ATTACH"), x + 10, y + 48, GREEN, false); - int line = y + 64; - for (String file : ComputerFileSystemClientState.get(position).files()) { - String[] fields = file.split("\\t", 3); - if (fields.length < 2 || (!fields[0].equals("TEXT") && !fields[0].equals("IMAGE")) || line > y + h - 52) continue; - g.drawString(font, Component.literal(trimToWidth(fields[1], 190)), x + 10, line, PALE_GREEN, false); - line += 14; - } - } - g.drawString(font, Component.literal("[ ENTER ] SEND"), x, y + h - 28, GREEN, false); - } else if (session.antmailMode.equals("message")) { - AntmailMessage message = selectedAntmailMessage(result); - if (message != null) { - boolean showRetry = session.antmailSent && !message.deliveryStatus().equals("DELIVERED"); - int attachmentBottom = y + h - (showRetry ? 54 : 36); - g.drawString(font, Component.literal("< BACK // " + (session.antmailSent ? "SENT" : "INBOX")), x, y + 40, GREEN, false); - g.drawString(font, Component.literal(trimToWidth(session.antmailSent ? message.recipient().fullAddress() : message.sender().fullAddress(), 208)), x, y + 62, PALE_GREEN, false); - g.drawString(font, Component.literal(trimToWidth(message.subject(), 208)), x, y + 78, GREEN, false); - int messageLine = wrapLimited(g, message.body(), x, y + 98, 208, attachmentBottom, PALE_GREEN) + 4; - if (!message.attachments().isEmpty() && messageLine + 9 <= attachmentBottom) { - session.antmailAttachmentStartLine = messageLine; - for (AntmailAttachment attachment : message.attachments()) { - if (messageLine + 9 > attachmentBottom) break; - g.drawString(font, Component.literal(trimToWidth("[ SAVE ATTACHMENT ] " + attachment.fileName(), 208)), x, messageLine, GREEN, false); - session.antmailVisibleAttachmentCount++; - messageLine += 14; - } - } - if (showRetry) { - String retryLabel = session.antmailRetryMessageId.equals(message.id().toString()) ? "[ RETRYING... ]" : "[ RETRY DELIVERY ]"; - g.drawString(font, Component.literal(retryLabel), x, y + h - 46, GREEN, false); - } - g.drawString(font, Component.literal("[ DELETE MESSAGE ]"), x, y + h - 28, GREEN, false); - } - } else if (session.antmailMode.equals("drafts")) { - List drafts = mailbox(result) == null ? List.of() : mailbox(result).drafts(); - g.drawString(font, Component.literal("DRAFTS // " + drafts.size()), x, y + 62, GREEN, false); - int line = y + 80; - for (AntmailDraft draft : drafts) { - if (line > y + h - 38) break; - g.drawString(font, Component.literal(trimToWidth("* " + (draft.subject().isBlank() ? "UNTITLED DRAFT" : draft.subject()), 30)), x, line, PALE_GREEN, false); - line += 14; - } - if (drafts.isEmpty()) g.drawString(font, Component.literal("NO SAVED DRAFTS"), x, y + 80, PALE_GREEN, false); - } else { - List messages = mailboxMessages(result, session.antmailSent); - int total = antmailTotal(result, session.antmailSent); - g.drawString(font, Component.literal((session.antmailSent ? "SENT // " : "INBOX // ") + total), x, y + 62, GREEN, false); - int line = y + 80; - for (AntmailMessage message : messages) { - if (line > y + h - 38) break; - String status = session.antmailSent ? " // " + message.deliveryStatus() : ""; - g.drawString(font, Component.literal((message.read() ? " " : "* ") + trimToWidth(message.subject() + status, 28)), x, line, message.read() ? PALE_GREEN : GREEN, false); - line += 14; - } - g.drawString(font, Component.literal("[ < ] PAGE " + (session.antmailPage + 1) + " [ > ]"), x, y + h - 42, GREEN, false); - } - } - AntmailResultPayload operation = AntmailClientState.get(position); - if (session.antmailSendPending && operation != null && operation != session.antmailSendBaseline && !operation.messageId().isBlank()) { - session.antmailSendPending = false; - session.antmailSendBaseline = null; - if (operation.status() == 0 || operation.status() == 1) { - if (session.antmailDraftId != null) AntmailNetworking.deleteDraft(position, session.antmailDraftId); - session.antmailDraftId = null; - session.antmailDraftLoaded = false; - session.antmailStatus = antmailStatus(operation); - session.antmailMode = "inbox"; - session.antmailFocused = false; - session.antmailRecipient = ""; - session.antmailSubject = ""; - session.antmailBody = ""; - session.antmailAttachText = false; - session.antmailAttachPaint = false; - session.antmailPaintAttachment = null; - session.antmailPaintLoadPendingPath = ""; - session.antmailTextPath = ""; - session.antmailPaintPath = ""; - session.antmailPage = 0; - requestAntmailState(true); - } else { - session.antmailFocused = true; - session.antmailStatus = antmailStatus(operation); - } - } - String displayStatus = session.antmailStatus; - if (!displayStatus.isEmpty()) drawBottomWrapped(g, displayStatus, x, y, h, 208, PALE_GREEN); - } - - private String antmailStatus(AntmailResultPayload result) { - if (result.status() == 0) return "DELIVERED"; - if (result.status() == 1) return "QUEUED"; - return "DELIVERY FAILED // " + (result.detail().isBlank() ? "SERVER REJECTED" : result.detail().toUpperCase()); - } - - private AntmailMailbox mailbox(AntmailResultPayload result) { - if (result == null || result.data().isBlank()) return null; - try { - return AntmailMailbox.fromTag(AntmailWire.decodeTag(result.data())); - } catch (RuntimeException ignored) { - return null; - } - } - - private List mailboxMessages(AntmailResultPayload result, boolean sent) { - AntmailMailbox mailbox = mailbox(result); - if (mailbox == null) return List.of(); - return sent ? mailbox.sent() : mailbox.inbox(); - } - - private int antmailTotal(AntmailResultPayload result, boolean sent) { - if (result == null || result.data().isBlank()) return 0; - try { - var tag = AntmailWire.decodeTag(result.data()); - return tag.getInt(sent ? "TotalSent" : "TotalInbox"); - } catch (RuntimeException ignored) { - return mailboxMessages(result, sent).size(); - } - } - - private AntmailMessage selectedAntmailMessage(AntmailResultPayload result) { - List messages = mailboxMessages(result, session.antmailSent); - if (session.antmailMessageIndex < 0 || session.antmailMessageIndex >= messages.size()) return null; - AntmailMessage summary = messages.get(session.antmailMessageIndex); - AntmailMessage detail = AntmailClientState.getMessage(position, summary.id()); - return detail == null ? summary : detail; - } - - private void renderGames(GuiGraphics g, Window window, int x, int y, int mouseX, int mouseY) { - g.drawString(font, Component.literal("INSTALLED GAMES"), x, y, GREEN, false); - if (window.gameOpen) { - if (window.gameId.equals("antman")) antman.render(g, font, x, y + 16, 214, 155); - else if (window.gameId.equals("blockle")) blockle.render(g, font, x, y + 16, 214, 155); - else basilisk.render(g, font, x, y + 16, 214, 155); - return; - } - List games = installedGameIds(); - for (int index = 0; index < games.size(); index++) { - int rowTop = y + 18 + index * 16; - if (inside(x - 4, rowTop, 218, 16, mouseX, mouseY)) box(g, x - 4, rowTop, x + 214, rowTop + 16, GREEN); - g.drawString(font, Component.literal(gameLabel(games.get(index))), x, y + 22 + index * 16, PALE_GREEN, false); - } - if (games.isEmpty()) { - g.drawString(font, Component.literal("NO GAMES INSTALLED"), x, y + 22, PALE_GREEN, false); - g.drawString(font, Component.literal(trimToWidth("FIND GAME DISKS TO INSTALL PROGRAMS", 214)), x, y + 40, PALE_GREEN, false); - } - } - - private List installedGameIds() { - List games = new ArrayList<>(); - if (basilisk.installed()) games.add("basilisk"); - if (antman.installed()) games.add("antman"); - if (blockle.installed()) games.add("blockle"); - return games; - } - - private String gameLabel(String gameId) { - return switch (gameId) { - case "basilisk" -> "BASILISK [ ENTER ]"; - case "antman" -> "ANTMAN.EXE [ ENTER ]"; - default -> "BLOCKLE [ ENTER ]"; - }; - } - - private void renderTrash(GuiGraphics g, int x, int y) { - g.drawString(font, Component.literal("DROP A DISK HERE TO EJECT"), x, y, GREEN, false); - g.drawString(font, Component.literal("EJECTED FILES RETURN TO PLAYER"), x, y + 22, PALE_GREEN, false); - } - - private int wrap(GuiGraphics g, String text, int x, int y, int width, int color) { return wrap(g, Component.literal(text), x, y, width, color); } - - private int wrap(GuiGraphics g, Component text, int x, int y, int width, int color) { - for (var line : font.split(text, width)) { g.drawString(font, line, x, y, color, false); y += 11; } - return y; - } - - private int wrapLimited(GuiGraphics g, String text, int x, int y, int width, int bottom, int color) { - for (var line : font.split(Component.literal(text), width)) { - if (y + 11 > bottom) break; - g.drawString(font, line, x, y, color, false); - y += 11; - } - return y; - } - - private void drawBottomWrapped(GuiGraphics g, String text, int x, int y, int h, int width, int color) { - List lines = font.split(Component.literal(text), width); - int lineY = y + h - 16 - Math.max(0, lines.size() - 1) * 11; - for (var line : lines) { - g.drawString(font, line, x, lineY, color, false); - lineY += 11; - } - } - - private void box(GuiGraphics g, int x1, int y1, int x2, int y2, int color) { - g.fill(x1, y1, x2, y1 + 2, color); - g.fill(x1, y2 - 2, x2, y2, color); - g.fill(x1, y1, x1 + 2, y2, color); - g.fill(x2 - 2, y1, x2, y2, color); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - float scale = uiScale(); - mouseX = (mouseX - width / 2.0F) / scale; - mouseY = (mouseY - height / 2.0F) / scale; - int l = -WIDTH / 2; - int t = -HEIGHT / 2; - if (!loggedIn) { - ComputerBlockEntity computer = computer(); - boolean setup = (accessResult != null && !accessResult.hasPassword()) || (computer != null && !computer.hasPassword()); - int formX = setup ? l + 110 : l + 242; - if (inside(formX, t + 181, setup ? 150 : 100, 24, mouseX, mouseY)) tryLogin(); - return true; - } - session.lastUse = System.currentTimeMillis(); - for (int i = session.windows.size() - 1; i >= 0; i--) { - Window window = session.windows.get(i); - if (window.minimized) continue; - int x = l + windowLocalX(window); - int y = t + windowLocalY(window); - int w = windowWidth(window); - int h = windowHeight(window); - int contentX = x + 8; - int contentY = y + 28; - int contentW = w - 16; - int contentH = h - 34; - if (inside(x, y + TITLE_BAR_HEIGHT, w, h - TITLE_BAR_HEIGHT, mouseX, mouseY)) { - activeWindow = window; - session.windows.remove(i); - session.windows.add(window); - if (window.type.equals("SETTINGS") && inside(contentX, contentY + 105, Math.min(205, contentW), 51, mouseX, mouseY)) { - List disks = physicalDisks(); - int index = ((int) mouseY - (contentY + 105)) / 17; - if (index >= 0 && index < disks.size() && index < 3) selectedDisk = disks.get(index); - return true; - } - if (window.type.equals("ARCHIVE")) { - List entries = filterArchiveEntries(ComputerGuideData.entriesFor(computer() == null ? List.of() : computer().diskIds())); - int archiveX = x + 8; - int archiveY = y + 28; - if (session.archiveEntryId != null) { - if (inside(archiveX, archiveY, w - 16, 20, mouseX, mouseY)) { - session.archiveEntryId = null; - session.archiveDetailScroll = 0; - } - } else { - if (inside(archiveX, archiveY + 14, w - 16, 21, mouseX, mouseY)) { - session.archiveSearchFocused = true; - return true; - } - session.archiveSearchFocused = false; - int columns = window.maximized ? 3 : 1; - int cellHeight = window.maximized ? 72 : 38; - int visibleRows = Math.max(1, (contentH - 58) / cellHeight); - int totalRows = (entries.size() + columns - 1) / columns; - int startRow = Math.max(0, Math.min(session.archiveScroll, Math.max(0, totalRows - visibleRows))); - int gridTop = archiveY + 40; - int relativeRow = ((int) mouseY - gridTop) / cellHeight; - int relativeColumn = Math.min(columns - 1, Math.max(0, ((int) mouseX - archiveX) / Math.max(1, (contentW - 8) / columns))); - int index = (startRow + relativeRow) * columns + relativeColumn; - int cellWidth = (contentW - 8) / columns; - int cellX = archiveX + relativeColumn * cellWidth; - int rowTop = gridTop + relativeRow * cellHeight; - if (relativeRow >= 0 && relativeRow < visibleRows && index < entries.size() - && inside(cellX, rowTop, cellWidth - 2, cellHeight - 2, mouseX, mouseY)) { - ComputerGuideData.Entry openedEntry = entries.get(index); - session.archiveEntryId = openedEntry.id(); - session.archiveDetailScroll = 0; - if (!openedEntry.structureId().isBlank()) { - com.craisinlord.antarchy.content.client.ComputerStructureLocatorClientState.searching(position, openedEntry.dimensionId()); - ComputerNetworking.locateStructure(position, openedEntry.id()); - } - } - } - return true; - } - if (window.type.equals("FILES")) { - var files = ComputerFileSystemClientState.get(position).files(); - if (!session.fileExplorerDirectory.equals("/") && mouseY >= contentY && mouseY < contentY + 12 && mouseX > x + w - 48) { - session.fileExplorerDirectory = parentDirectory(session.fileExplorerDirectory); - session.fileExplorerScroll = 0; - return true; - } - int row = 0; - for (String file : files) { - String[] fields = file.split("\\t", 3); - if (fields.length < 3 || !isDirectChild(fields[1], session.fileExplorerDirectory)) continue; - int rowIndex = row++; - if (rowIndex < session.fileExplorerScroll) continue; - int rowTop = contentY + 34 + (rowIndex - session.fileExplorerScroll) * 14; - if (rowTop > contentY + contentH - 32) break; - if (!inside(contentX, rowTop - 2, contentW, 14, mouseX, mouseY)) continue; - session.fileExplorerSelected = fields[1]; - if (fields[0].equals("DIRECTORY")) { - session.fileExplorerDirectory = fields[1]; - session.fileExplorerScroll = 0; - } - else if (fields[0].equals("TEXT") || fields[0].equals("IMAGE")) { - ComputerNetworking.openFile(position, fields[1]); - open(fields[1].endsWith(".antpaint") ? "PAINT" : "TEXT"); - } - break; - } - return true; - } - if (window.type.equals("PAINT")) { - int canvasX = contentX; - int canvasY = contentY + 30; - int canvasHeight = Math.max(1, contentH - 32); - if (inside(canvasX, canvasY, contentW, canvasHeight, mouseX, mouseY)) { - if (button == 0 || button == 1) { - paintStrokeActive = session.paintTool == AntPaintTool.PENCIL || session.paintTool == AntPaintTool.ERASER; - paintStrokeButton = button; - lastPaintPixelX = -1; - lastPaintPixelY = -1; - if (paintStrokeActive) session.paintHistory.record(session.paintCanvas); - } - if (button == 0 || button == 1) paintAt(mouseX, mouseY, canvasX, canvasY, contentW, canvasHeight); - } - else if (mouseY >= contentY + 11 && mouseY < contentY + 30 && mouseX >= contentX && mouseX < contentX + Math.min(196, contentW)) { - paintStrokeActive = false; - paintToolbarAction(((int) mouseX - contentX) / 28); - } - return true; - } - if (window.type.equals("TEXT")) { - if (inside(contentX, contentY + 12, contentW, 26, mouseX, mouseY)) { - int toolbarX = (int) mouseX - contentX; - if (toolbarX < 30) newText(); - else if (toolbarX < 60) { session.textListing = true; session.textPickerScroll = 0; ComputerNetworking.listFiles(position); } - else if (toolbarX < 102) writeText(); - else if (toolbarX < 138) saveText(); - else if (toolbarX < 210) beginSaveAs(); - } else if (session.textListing) { - var files = ComputerFileSystemClientState.get(position).files(); - int index = session.textPickerScroll + ((int) mouseY - (contentY + 62)) / 14; - int textIndex = 0; - for (String file : files) { - String[] fields = file.split("\\t", 3); - if (fields.length < 3 || !fields[0].equals("TEXT")) continue; - if (textIndex++ == index) { - session.textListing = false; - ComputerNetworking.openFile(position, fields[1]); - break; - } - } - } else { - session.textFocused = true; - setTextCursorFromMouse(mouseX - (contentX + 10), mouseY - (contentY + 46), contentW - 12); - } - return true; - } - if (window.type.equals("ANTMAIL")) { - var result = AntmailClientState.getMailbox(position); - if (result == null && !antmailUnconfigured()) { - if (inside(contentX, contentY + 56, Math.min(150, contentW), 23, mouseX, mouseY)) { - session.antmailStateWaitTicks = 0; - requestAntmailState(true); - } - return true; - } else if (result == null || result.address().isBlank()) { - if (inside(contentX, contentY + 56, Math.min(206, contentW), 23, mouseX, mouseY)) { - session.antmailFocused = true; - return true; - } - if (inside(contentX, contentY + 84, contentW, 24, mouseX, mouseY)) { - setupAntmail(); - return true; - } - } else if (inside(contentX, contentY + 30, 54, 24, mouseX, mouseY)) { - session.antmailMode = "inbox"; - session.antmailSent = false; - session.antmailPage = 0; - session.antmailMessageIndex = -1; - requestAntmailState(true); - } else if (inside(contentX + 54, contentY + 30, 46, 24, mouseX, mouseY)) { - session.antmailMode = "sent"; - session.antmailSent = true; - session.antmailPage = 0; - session.antmailMessageIndex = -1; - requestAntmailState(true); - } else if (inside(contentX + 100, contentY + 30, 54, 24, mouseX, mouseY)) { - session.antmailMode = "drafts"; - session.antmailSent = false; - session.antmailPage = 0; - session.antmailMessageIndex = -1; - requestAntmailState(true); - } else if (inside(contentX + 158, contentY + 30, Math.min(56, Math.max(0, contentW - 158)), 24, mouseX, mouseY)) { - session.antmailMode = "compose"; - session.antmailFocused = true; - session.antmailField = 1; - restoreLatestDraft(result); - } else if (session.antmailMode.equals("inbox") || session.antmailMode.equals("sent")) { - if (mouseY >= contentY + contentH - 48 && mouseY < contentY + contentH - 28) { - if (mouseX < contentX + 52 && session.antmailPage > 0) { - session.antmailPage--; - requestAntmailState(true); - } else if (mouseX >= contentX + 142 && (session.antmailPage + 1) * AntmailMailbox.PAGE_SIZE < antmailTotal(result, session.antmailSent)) { - session.antmailPage++; - requestAntmailState(true); - } - return true; - } - int index = ((int) mouseY - (contentY + 80)) / 14; - List messages = mailboxMessages(result, session.antmailSent); - if (index >= 0 && index < messages.size()) { - session.antmailMessageIndex = index; - session.antmailMode = "message"; - AntmailNetworking.requestMessage(position, messages.get(index).id()); - if (!session.antmailSent) AntmailNetworking.markRead(position, messages.get(index).id()); - } - } else if (session.antmailMode.equals("drafts")) { - int index = ((int) mouseY - (contentY + 80)) / 14; - AntmailMailbox mailbox = mailbox(result); - if (mailbox != null && index >= 0 && index < mailbox.drafts().size()) { - restoreDraft(mailbox.drafts().get(index)); - session.antmailMode = "compose"; - session.antmailFocused = true; - session.antmailField = 1; - } - } else if (session.antmailMode.equals("message")) { - AntmailMessage message = selectedAntmailMessage(result); - if (session.antmailSent && message != null && !message.deliveryStatus().equals("DELIVERED") - && session.antmailRetryMessageId.isBlank() - && mouseY >= contentY + contentH - 50 && mouseY < contentY + contentH - 32) { - session.antmailRetryBaseline = AntmailClientState.get(position); - session.antmailRetryMessageId = message.id().toString(); - session.antmailRetryTicks = 100; - AntmailNetworking.retry(position, message.id()); - session.antmailStatus = "RETRYING DELIVERY"; - return true; - } - if (mouseY >= contentY + contentH - 32 && mouseY < contentY + contentH - 8) { - if (message != null) AntmailNetworking.delete(position, message.id(), session.antmailSent); - session.antmailMode = session.antmailSent ? "sent" : "inbox"; - session.antmailMessageIndex = -1; - return true; - } - if (message != null && session.antmailAttachmentStartLine >= 0 - && mouseY >= session.antmailAttachmentStartLine - && mouseY < session.antmailAttachmentStartLine + session.antmailVisibleAttachmentCount * 14 - && !message.attachments().isEmpty()) { - int attachmentIndex = ((int) mouseY - session.antmailAttachmentStartLine) / 14; - saveAntmailAttachment(message.attachments().get(attachmentIndex)); - return true; - } - if (mouseY < contentY + 72) { - session.antmailMode = session.antmailSent ? "sent" : "inbox"; - session.antmailMessageIndex = -1; - } - } else if (session.antmailMode.equals("compose")) { - if (session.antmailPickingAttachment) { - int row = 0; - for (String file : ComputerFileSystemClientState.get(position).files()) { - String[] fields = file.split("\\t", 3); - if (fields.length < 2 || (!fields[0].equals("TEXT") && !fields[0].equals("IMAGE"))) continue; - int rowTop = contentY + 64 + row++ * 14; - if (rowTop > contentY + contentH - 52) break; - if (mouseY >= rowTop - 2 && mouseY < rowTop + 12) { - session.fileExplorerSelected = fields[1]; - session.antmailAttachText = fields[0].equals("TEXT"); - session.antmailAttachPaint = fields[0].equals("IMAGE"); - session.antmailPickingAttachment = false; - if (session.antmailAttachText) { - session.textPath = fields[1]; - session.antmailTextPath = fields[1]; - } else { - session.antmailPaintPath = fields[1]; - session.paintName = fields[1].substring(fields[1].lastIndexOf('/') + 1); - session.antmailPaintAttachment = null; - session.antmailPaintLoadPendingPath = fields[1]; - } - ComputerNetworking.openFile(position, fields[1]); - return true; - } - } - return true; - } - if (mouseY >= contentY + 116 && mouseY < contentY + 141) { - session.antmailPickingAttachment = true; - return true; - } - if (mouseY >= contentY + 42 && mouseY < contentY + 59) { - session.antmailField = 1; - session.antmailFocused = true; - } else if (mouseY >= contentY + 59 && mouseY < contentY + 75) { - session.antmailField = 2; - session.antmailFocused = true; - } else if (mouseY >= contentY + 75 && mouseY < contentY + 116) { - session.antmailField = 3; - session.antmailFocused = true; - } else if (mouseY >= contentY + contentH - 38 && mouseY < contentY + contentH - 8) { - sendAntmail(); - } - } - return true; - } - if (window.type.equals("GAMES") && !window.gameOpen) { - List games = installedGameIds(); - int index = ((int) mouseY - (contentY + 18)) / 16; - if (index >= 0 && index < games.size() && mouseY < contentY + 18 + games.size() * 16) { - window.gameId = games.get(index); - window.gameOpen = true; - activeWindow = window; - return true; - } - } - if (window.type.equals("TERMINAL")) { session.terminalFocused = true; return true; } - } - if (window.type.equals("SETTINGS") && inside(contentX, contentY + 156, Math.min(205, contentW), 20, mouseX, mouseY)) { - ejectSelected(); - return true; - } - if (window.type.equals("SETTINGS") && inside(contentX, contentY + 70, 90, 18, mouseX, mouseY)) { - ComputerNetworking.logout(position); - loggedIn = false; - session.authenticated = false; - session.windows.clear(); - activeWindow = null; - return true; - } - if (inside(x, y + TITLE_BAR_HEIGHT, w, h - TITLE_BAR_HEIGHT, mouseX, mouseY)) return true; - if (inside(x, y, w, TITLE_BAR_HEIGHT, mouseX, mouseY)) { - int controlsLeft = x + w - WINDOW_CONTROL_WIDTH * WINDOW_CONTROL_COUNT; - if (inside(controlsLeft + WINDOW_CONTROL_WIDTH * 2, y, WINDOW_CONTROL_WIDTH, TITLE_BAR_HEIGHT, mouseX, mouseY)) { - if (window.type.equals("ANTMAIL") && session.antmailMode.equals("compose")) saveAntmailDraft(); - session.windows.remove(i); - if (activeWindow == window) activeWindow = null; - } - else if (inside(controlsLeft + WINDOW_CONTROL_WIDTH, y, WINDOW_CONTROL_WIDTH, TITLE_BAR_HEIGHT, mouseX, mouseY)) toggleMaximized(window); - else if (inside(controlsLeft, y, WINDOW_CONTROL_WIDTH, TITLE_BAR_HEIGHT, mouseX, mouseY)) { - window.minimized = true; - if (activeWindow == window) activeWindow = null; - } - else if (!window.maximized) { dragging = window; dragX = (int) mouseX - x; dragY = (int) mouseY - y; session.windows.remove(i); session.windows.add(window); } - return true; - } - } - for (int i = 0; i < ICONS.length; i++) { - int x = l + 24 + i % 4 * 94; - int y = t + 48 + i / 4 * 76; - if (inside(x - 6, y - 6, 76, 57, mouseX, mouseY)) { open(ICONS[i]); return true; } - } - List disks = physicalDisks().stream().filter(id -> !id.getPath().equals("blockle_game")).toList(); - for (int i = 0; i < disks.size() && i < 3; i++) { - int x = l + 24 + (i + 1) * 94; - int y = t + 200 + i / 4 * 54; - if (inside(x - 6, y - 6, 76, 48, mouseX, mouseY)) { - draggedDisk = disks.get(i).toString(); - selectedDisk = disks.get(i); - return true; - } - } - return true; - } - - @Override - public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) { - float scale = uiScale(); - mouseX = (mouseX - width / 2.0F) / scale; - mouseY = (mouseY - height / 2.0F) / scale; - if (dragging != null) { - int l = -WIDTH / 2; - int t = -HEIGHT / 2; - dragging.x = Math.max(12, Math.min(WIDTH - dragging.renderWidth - 12, (int) mouseX - l - this.dragX)); - dragging.y = Math.max(34, Math.min(HEIGHT - dragging.renderHeight - 12, (int) mouseY - t - this.dragY)); - } - if (paintStrokeActive && button == paintStrokeButton && activeWindow != null && activeWindow.type.equals("PAINT")) { - int l = -WIDTH / 2; - int t = -HEIGHT / 2; - int x = l + windowLocalX(activeWindow) + 8; - int y = t + windowLocalY(activeWindow) + 58; - paintAt(mouseX, mouseY, x, y, activeWindow.renderWidth - 16, Math.max(1, activeWindow.renderHeight - 66)); - } - return true; - } - - @Override - public boolean mouseReleased(double mouseX, double mouseY, int button) { - float scale = uiScale(); - mouseX = (mouseX - width / 2.0F) / scale; - mouseY = (mouseY - height / 2.0F) / scale; - if (draggedDisk != null) { - int l = -WIDTH / 2; - int t = -HEIGHT / 2; - int trashX = l + 24 + (TRASH_ICON_INDEX % 4) * 94; - int trashY = t + 48 + (TRASH_ICON_INDEX / 4) * 76; - if (inside(trashX - 6, trashY - 6, 76, 57, mouseX, mouseY)) ejectSelected(); - draggedDisk = null; - } - dragging = null; - if (button == paintStrokeButton) { - paintStrokeActive = false; - paintStrokeButton = -1; - lastPaintPixelX = -1; - lastPaintPixelY = -1; - } - return true; - } - - @Override - public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) { - Window hoveredWindow = windowAt(mouseX, mouseY); - if (!loggedIn || hoveredWindow == null) return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY); - activeWindow = hoveredWindow; - if (activeWindow.type.equals("FILES")) { - session.fileExplorerScroll = Math.max(0, session.fileExplorerScroll - (int) Math.signum(scrollY)); - return true; - } - if (activeWindow.type.equals("TEXT") && session.textListing) { - session.textPickerScroll = Math.max(0, session.textPickerScroll - (int) Math.signum(scrollY)); - return true; - } - if (activeWindow.type.equals("TEXT")) { - session.textScroll = Math.max(0, session.textScroll - (int) Math.signum(scrollY)); - return true; - } - if (activeWindow.type.equals("ARCHIVE")) { - if (session.archiveEntryId == null) session.archiveScroll = Math.max(0, session.archiveScroll - (int) Math.signum(scrollY)); - else session.archiveDetailScroll = Math.max(0, session.archiveDetailScroll - (int) Math.signum(scrollY)); - return true; - } - return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY); - } - - @Override - public boolean charTyped(char codePoint, int modifiers) { - if (!loggedIn && codePoint >= 32) { - if (confirmingPassword) { - if (confirmation.length() < 32) confirmation += Character.toUpperCase(codePoint); - } else if (password.length() < 32) { - password += Character.toUpperCase(codePoint); - } - } - if (loggedIn && activeWindow != null && activeWindow.type.equals("FILES") && session.fileExplorerRenaming && codePoint >= 32 && codePoint != '/' && codePoint != '\\') { - if (session.fileExplorerRename.length() < 48) session.fileExplorerRename += codePoint; - return true; - } - if (loggedIn && activeWindow != null && activeWindow.type.equals("ARCHIVE") && session.archiveSearchFocused && codePoint >= 32 && session.archiveSearch.length() < 48) { - session.archiveSearch += codePoint; - session.archiveScroll = 0; - return true; - } - if (loggedIn && activeWindow != null && activeWindow.type.equals("FILES") && session.fileExplorerCreatingDirectory && codePoint >= 32 && codePoint != '/' && codePoint != '\\') { - if (session.fileExplorerRename.length() < 48) session.fileExplorerRename += codePoint; - return true; - } - if (loggedIn && activeWindow != null && activeWindow.type.equals("FILES") && session.fileExplorerMoving && codePoint >= 32 && codePoint != '\\') { - if (session.fileExplorerRename.length() < 64) session.fileExplorerRename += codePoint; - return true; - } - if (loggedIn && activeWindow != null && activeWindow.type.equals("TEXT") && session.textRenaming && codePoint >= 32 && codePoint != '/' && codePoint != '\\') { - if (session.textRename.length() < 48) session.textRename += codePoint; - return true; - } - if (loggedIn && activeWindow != null) { - if (activeWindow.type.equals("GAMES") && activeWindow.gameOpen && activeWindow.gameId.equals("blockle") && blockle.charTyped(codePoint)) return true; - if (activeWindow.type.equals("TEXT") && session.textFocused && codePoint >= 32) { - session.textContent = session.textContent.substring(0, session.textCursor) + codePoint + session.textContent.substring(session.textCursor); - session.textCursor++; - session.textDirty = true; - return true; - } - if (activeWindow.type.equals("TERMINAL") && session.terminalFocused && codePoint >= 32) { session.terminalInput += codePoint; return true; } - if (activeWindow.type.equals("ANTMAIL") && session.antmailFocused && codePoint >= 32) { - if (session.antmailMode.equals("compose")) { - if (session.antmailField == 1 && session.antmailRecipient.length() < 64) session.antmailRecipient += codePoint; - else if (session.antmailField == 2 && session.antmailSubject.length() < 64) session.antmailSubject += codePoint; - else if (session.antmailField == 3 && session.antmailBody.length() < 16384) session.antmailBody += codePoint; - } else if (Character.isLetterOrDigit(codePoint) || codePoint == '_') session.antmailUsername += Character.toLowerCase(codePoint); - return true; - } - } - return true; - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (!loggedIn) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { onClose(); return true; } - if (keyCode == 259) { - if (confirmingPassword && !confirmation.isEmpty()) confirmation = confirmation.substring(0, confirmation.length() - 1); - else if (!password.isEmpty()) password = password.substring(0, password.length() - 1); - } - else if (keyCode == 257 || keyCode == 335) tryLogin(); - return true; - } - if (activeWindow != null && activeWindow.type.equals("GAMES") && activeWindow.gameOpen) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { - activeWindow.gameOpen = false; - return true; - } - if (activeWindow.gameId.equals("antman") ? antman.keyPressed(keyCode) : activeWindow.gameId.equals("blockle") ? blockle.keyPressed(keyCode) : basilisk.keyPressed(keyCode)) return true; - } - if (activeWindow != null && activeWindow.type.equals("FILES")) { - if (session.fileExplorerDeleteConfirm) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { session.fileExplorerDeleteConfirm = false; return true; } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { session.fileExplorerDeleteConfirm = false; deleteSelectedFile(); return true; } - } - if (session.fileExplorerCreatingDirectory) { - if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { if (!session.fileExplorerRename.isEmpty()) session.fileExplorerRename = session.fileExplorerRename.substring(0, session.fileExplorerRename.length() - 1); return true; } - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { session.fileExplorerCreatingDirectory = false; session.fileExplorerRename = ""; return true; } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { - if (!session.fileExplorerRename.isBlank()) { - String folder = session.fileExplorerRename.replace("\\", "\\\\").replace("\"", "\\\""); - ComputerNetworking.terminalCommand(position, session.fileExplorerDirectory, "mkdir \"" + folder + "\""); - } - session.fileExplorerCreatingDirectory = false; - session.fileExplorerRename = ""; - ComputerNetworking.listFiles(position); - return true; - } - } - if (session.fileExplorerMoving) { - if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { if (!session.fileExplorerRename.isEmpty()) session.fileExplorerRename = session.fileExplorerRename.substring(0, session.fileExplorerRename.length() - 1); return true; } - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { session.fileExplorerMoving = false; session.fileExplorerRename = ""; return true; } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { - if (!session.fileExplorerRename.isBlank() && !session.fileExplorerSelected.isBlank()) { - String destination = session.fileExplorerRename.startsWith("/") - ? ComputerFileSystem.normalize(session.fileExplorerRename) - : ComputerFileSystem.normalize(session.fileExplorerDirectory + "/" + session.fileExplorerRename); - ComputerNetworking.moveFile(position, session.fileExplorerSelected, destination); - } - session.fileExplorerMoving = false; - session.fileExplorerRename = ""; - ComputerNetworking.listFiles(position); - return true; - } - } - if (session.fileExplorerRenaming) { - if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { - if (!session.fileExplorerRename.isEmpty()) session.fileExplorerRename = session.fileExplorerRename.substring(0, session.fileExplorerRename.length() - 1); - return true; - } - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { - session.fileExplorerRenaming = false; - session.fileExplorerRename = ""; - return true; - } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { - renameSelectedFile(); - return true; - } - } else if (keyCode == GLFW.GLFW_KEY_R && hasControl(modifiers)) { - beginRenameSelectedFile(); - return true; - } else if (keyCode == GLFW.GLFW_KEY_DELETE) { - session.fileExplorerDeleteConfirm = true; - return true; - } else if (keyCode == GLFW.GLFW_KEY_N && hasControl(modifiers)) { - session.fileExplorerCreatingDirectory = true; - session.fileExplorerRename = ""; - return true; - } else if (keyCode == GLFW.GLFW_KEY_M && hasControl(modifiers) && !session.fileExplorerSelected.isBlank()) { - session.fileExplorerMoving = true; - session.fileExplorerRename = ""; - return true; - } - } - if (activeWindow != null && activeWindow.type.equals("TERMINAL") && session.terminalFocused) { - if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { if (!session.terminalInput.isEmpty()) session.terminalInput = session.terminalInput.substring(0, session.terminalInput.length() - 1); return true; } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { executeTerminal(); return true; } - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { session.terminalFocused = false; return true; } - } - - if (activeWindow != null && activeWindow.type.equals("ANTMAIL") && session.antmailFocused) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE && session.antmailPickingAttachment) { - session.antmailPickingAttachment = false; - return true; - } - if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { - if (session.antmailMode.equals("compose")) { - if (session.antmailField == 1 && !session.antmailRecipient.isEmpty()) session.antmailRecipient = session.antmailRecipient.substring(0, session.antmailRecipient.length() - 1); - else if (session.antmailField == 2 && !session.antmailSubject.isEmpty()) session.antmailSubject = session.antmailSubject.substring(0, session.antmailSubject.length() - 1); - else if (session.antmailField == 3 && !session.antmailBody.isEmpty()) session.antmailBody = session.antmailBody.substring(0, session.antmailBody.length() - 1); - } else if (!session.antmailUsername.isEmpty()) session.antmailUsername = session.antmailUsername.substring(0, session.antmailUsername.length() - 1); - return true; - } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { - if (session.antmailMode.equals("compose")) sendAntmail(); else setupAntmail(); - return true; - } - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { session.antmailFocused = false; return true; } - } - if (activeWindow != null && activeWindow.type.equals("TEXT") && session.textFocused) { - if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { if (session.textCursor > 0) { session.textContent = session.textContent.substring(0, session.textCursor - 1) + session.textContent.substring(session.textCursor); session.textCursor--; session.textDirty = true; } return true; } - if (keyCode == GLFW.GLFW_KEY_DELETE && !hasControl(modifiers)) { if (session.textCursor < session.textContent.length()) { session.textContent = session.textContent.substring(0, session.textCursor) + session.textContent.substring(session.textCursor + 1); session.textDirty = true; } return true; } - if (keyCode == GLFW.GLFW_KEY_LEFT) { session.textCursor = Math.max(0, session.textCursor - 1); return true; } - if (keyCode == GLFW.GLFW_KEY_RIGHT) { session.textCursor = Math.min(session.textContent.length(), session.textCursor + 1); return true; } - if (keyCode == GLFW.GLFW_KEY_UP || keyCode == GLFW.GLFW_KEY_DOWN) { moveTextCursorVertically(keyCode == GLFW.GLFW_KEY_UP); return true; } - if (keyCode == GLFW.GLFW_KEY_HOME) { session.textCursor = session.textContent.lastIndexOf('\n', Math.max(0, session.textCursor - 1)) + 1; return true; } - if (keyCode == GLFW.GLFW_KEY_END) { int next = session.textContent.indexOf('\n', session.textCursor); session.textCursor = next < 0 ? session.textContent.length() : next; return true; } - if (keyCode == GLFW.GLFW_KEY_ENTER) { session.textContent = session.textContent.substring(0, session.textCursor) + "\n" + session.textContent.substring(session.textCursor); session.textCursor++; session.textDirty = true; return true; } - if (keyCode == GLFW.GLFW_KEY_S && hasControl(modifiers) && (modifiers & GLFW.GLFW_MOD_SHIFT) == 0) { saveText(); return true; } - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { session.textFocused = false; return true; } - } - if (activeWindow != null && activeWindow.type.equals("TEXT")) { - if (session.textDeleteConfirm) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { session.textDeleteConfirm = false; return true; } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { session.textDeleteConfirm = false; deleteTextFile(); return true; } - } - if (keyCode == GLFW.GLFW_KEY_DELETE && hasControl(modifiers)) { session.textDeleteConfirm = true; session.textFocused = false; return true; } - if (keyCode == GLFW.GLFW_KEY_S && hasControl(modifiers) && (modifiers & GLFW.GLFW_MOD_SHIFT) != 0) { - beginSaveAs(); - return true; - } - if (keyCode == GLFW.GLFW_KEY_R && hasControl(modifiers) && !session.textPath.isBlank()) { - int slash = session.textPath.lastIndexOf('/'); - session.textRename = session.textPath.substring(slash + 1); - session.textRenaming = true; - return true; - } - if (session.textRenaming) { - if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { - if (!session.textRename.isEmpty()) session.textRename = session.textRename.substring(0, session.textRename.length() - 1); - return true; - } - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { - session.textRenaming = false; - session.textRename = ""; - return true; - } - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { - renameTextFile(); - return true; - } - } - } - if (activeWindow != null && activeWindow.type.equals("ARCHIVE") && session.archiveEntryId != null && keyCode == GLFW.GLFW_KEY_ESCAPE) { - session.archiveEntryId = null; - session.archiveDetailScroll = 0; - return true; - } - if (activeWindow != null && activeWindow.type.equals("ARCHIVE") && session.archiveSearchFocused) { - if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { - if (!session.archiveSearch.isEmpty()) session.archiveSearch = session.archiveSearch.substring(0, session.archiveSearch.length() - 1); - session.archiveScroll = 0; - return true; - } - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { - session.archiveSearchFocused = false; - return true; - } - } - return super.keyPressed(keyCode, scanCode, modifiers); - } - - private void tryLogin() { - if (password.isBlank()) return; - if (accessResult != null && !accessResult.hasPassword()) { - if (!confirmingPassword) { - confirmingPassword = true; - confirmation = ""; - loginMessage = "RETYPE PASSWORD // CONFIRMATION REQUIRED"; - loginMessageTicks = 100; - return; - } - if (!password.equals(confirmation)) { - password = ""; - confirmation = ""; - confirmingPassword = false; - loginMessage = "SETUP ERROR // PASSWORDS DO NOT MATCH"; - loginMessageTicks = 100; - return; - } - ComputerNetworking.setup(position, password); - loginMessage = "INITIALIZING ACCESS // WAITING"; - } else { - ComputerNetworking.login(position, password); - loginMessage = "AUTHENTICATING // WAITING"; - } - loginMessageTicks = 40; - password = ""; - confirmation = ""; - confirmingPassword = false; - } - - private void executeTerminal() { - String input = session.terminalInput.trim(); - if (input.isEmpty()) return; - session.terminalOutput.add(session.terminalDirectory + "> " + input); - ComputerNetworking.terminalCommand(position, session.terminalDirectory, input); - session.terminalInput = ""; - } - - private void requestAntmailState() { - requestAntmailState(false); - } - - private boolean antmailUnconfigured() { - AntmailResultPayload mailbox = AntmailClientState.getMailbox(position); - if (mailbox != null) return mailbox.address().isBlank(); - AntmailResultPayload response = AntmailClientState.get(position); - return response != null && ("unconfigured".equals(response.detail()) - || "address_not_found".equals(response.detail()) - || response.detail().startsWith("registration_failed:")); - } - - private void requestAntmailState(boolean force) { - int folder = session.antmailMode.equals("sent") ? com.craisinlord.antarchy.content.network.AntmailStateRequestPayload.SENT - : session.antmailMode.equals("drafts") ? com.craisinlord.antarchy.content.network.AntmailStateRequestPayload.DRAFTS - : com.craisinlord.antarchy.content.network.AntmailStateRequestPayload.INBOX; - AntmailNetworking.requestState(position, folder, session.antmailPage, force ? 0L : AntmailClientState.getVersion(position)); - if (force) session.antmailStateWaitTicks = 0; - } - - private void setupAntmail() { - if (!loggedIn) { - session.antmailStatus = "SETUP ERROR // SIGN IN TO THE COMPUTER FIRST"; - return; - } - if (!AntmailAddress.isValidUsername(session.antmailUsername)) { - session.antmailStatus = "SETUP ERROR // USE 3-16 LOWERCASE LETTERS, NUMBERS, OR _"; - session.antmailFocused = true; - return; - } - AntmailClientState.clear(position); - AntmailNetworking.setup(position, session.antmailUsername); - session.antmailStatus = "REGISTERING ADDRESS"; - session.antmailRegistrationPending = true; - session.antmailFocused = false; - } - - private void sendAntmail() { - if (!AntmailAddress.isValidAddress(session.antmailRecipient)) { - session.antmailStatus = "SEND ERROR // INVALID RECIPIENT"; - return; - } - if (session.antmailSubject.isBlank() || session.antmailBody.isBlank()) { - session.antmailStatus = "SEND ERROR // SUBJECT AND BODY REQUIRED"; - return; - } - if (session.antmailAttachText && (session.textDirty || session.textPath.isBlank() || session.textPath.contains("untitled") || !session.textPath.equals(session.antmailTextPath))) { - session.antmailStatus = "SEND ERROR // SAVE TEXT FILE FIRST"; - return; - } - if (session.antmailAttachPaint && !session.antmailPaintLoadPendingPath.isBlank()) { - session.antmailStatus = "ATTACHMENT STILL LOADING // TRY AGAIN"; - return; - } - if (session.antmailAttachPaint && session.antmailPaintAttachment == null - && (session.paintDirty || session.paintName.isBlank() || session.paintName.startsWith("UNTITLED") - || !session.antmailPaintPath.endsWith(session.paintName))) { - session.antmailStatus = "SEND ERROR // SAVE PAINTING FIRST"; - return; - } - List attachments = composeAttachments(); - session.antmailSendBaseline = AntmailClientState.get(position); - AntmailNetworking.send(position, session.antmailRecipient, session.antmailSubject, session.antmailBody, attachments); - session.antmailStatus = "SENDING MESSAGE"; - session.antmailSendPending = true; - session.antmailFocused = false; - session.antmailDraftId = null; - session.antmailDraftLoaded = false; - session.antmailPickingAttachment = false; - } - - private void restoreLatestDraft(AntmailResultPayload result) { - if (session.antmailDraftLoaded) return; - AntmailMailbox mailbox = mailbox(result); - if (mailbox == null || mailbox.drafts().isEmpty()) { - session.antmailDraftLoaded = true; - return; - } - restoreDraft(mailbox.drafts().getLast()); - session.antmailStatus = "DRAFT RECOVERED"; - session.antmailDraftLoaded = true; - } - - private void restoreDraft(AntmailDraft draft) { - session.antmailDraftId = draft.id(); - session.antmailRecipient = draft.recipient() == null ? "" : draft.recipient().fullAddress(); - session.antmailSubject = draft.subject(); - session.antmailBody = draft.body(); - session.antmailAttachText = false; - session.antmailAttachPaint = false; - session.antmailPickingAttachment = false; - for (AntmailAttachment attachment : draft.attachments()) { - if (attachment instanceof AntmailAttachment.TextFile text) { - session.antmailAttachText = true; - session.textPath = "/documents/" + text.fileName(); - session.antmailTextPath = session.textPath; - session.textContent = text.contents(); - } else if (attachment instanceof AntmailAttachment.PaintImage paint) { - session.antmailAttachPaint = true; - session.paintName = paint.fileName(); - session.antmailPaintPath = "/pictures/" + paint.fileName(); - session.antmailPaintAttachment = paint; - session.antmailPaintLoadPendingPath = ""; - AntPaintCanvas canvas = new AntPaintCanvas(); - byte[] pixels = paint.pixels(); - for (int index = 0; index < pixels.length; index++) if (pixels[index] == 1) canvas.set(index % AntPaintCanvas.WIDTH, index / AntPaintCanvas.WIDTH, true); - session.paintCanvas = canvas; - } - } - } - - private void saveAntmailDraft() { - try { - if (session.antmailRecipient.isBlank() && session.antmailSubject.isBlank() && session.antmailBody.isBlank()) return; - AntmailResultPayload result = AntmailClientState.getMailbox(position); - AntmailMailbox mailbox = mailbox(result); - if (mailbox == null) return; - AntmailDraft draft = new AntmailDraft(session.antmailDraftId, AntmailAddress.parse(mailbox.address().fullAddress()), - session.antmailRecipient.isBlank() ? null : AntmailAddress.parse(session.antmailRecipient), session.antmailSubject, session.antmailBody, composeAttachments()); - AntmailNetworking.saveDraft(position, draft); - } catch (RuntimeException ignored) { - session.antmailStatus = "DRAFT SAVE ERROR // CHECK RECIPIENT"; - } - } - - private List composeAttachments() { - List attachments = new ArrayList<>(); - if (session.antmailAttachText) attachments.add(new AntmailAttachment.TextFile(session.textPath.substring(session.textPath.lastIndexOf('/') + 1), session.textContent)); - if (session.antmailAttachPaint && session.antmailPaintAttachment != null) { - attachments.add(session.antmailPaintAttachment); - } else if (session.antmailAttachPaint) { - byte[] pixels = new byte[AntPaintCanvas.WIDTH * AntPaintCanvas.HEIGHT]; - for (int py = 0; py < AntPaintCanvas.HEIGHT; py++) for (int px = 0; px < AntPaintCanvas.WIDTH; px++) pixels[py * AntPaintCanvas.WIDTH + px] = (byte) (session.paintCanvas.get(px, py) ? 1 : 0); - attachments.add(new AntmailAttachment.PaintImage(session.paintName, AntPaintCanvas.WIDTH, AntPaintCanvas.HEIGHT, pixels)); - } - return attachments; - } - - private void syncAntmailPaintAttachment() { - String requestedPath = session.antmailPaintLoadPendingPath; - if (requestedPath.isBlank()) return; - ComputerFileSystemClientState.State fileState = ComputerFileSystemClientState.get(position); - if (!requestedPath.equals(fileState.openedPath()) || fileState.openedContents().isBlank()) return; - try { - AntPaintFile file = AntPaintFileCodec.decode(Base64.getDecoder().decode(fileState.openedContents())); - session.paintName = file.filename(); - session.antmailPaintPath = requestedPath; - session.antmailPaintAttachment = AntmailAttachmentFiles.fromPaintFile(file); - session.antmailPaintLoadPendingPath = ""; - session.antmailStatus = "PAINTING ATTACHED // " + file.filename(); - } catch (RuntimeException exception) { - session.antmailPaintLoadPendingPath = ""; - session.antmailStatus = "ATTACHMENT ERROR // INVALID PAINT FILE"; - } - } - - private void saveAntmailAttachment(AntmailAttachment attachment) { - try { - if (attachment instanceof AntmailAttachment.TextFile text) { - String path = "/documents/" + text.fileName(); - if (computerFileExists(path)) ComputerNetworking.saveFile(position, path, text.contents()); - else ComputerNetworking.createFile(position, path, text.contents()); - session.antmailStatus = "ATTACHMENT SAVED // " + text.fileName(); - } else if (attachment instanceof AntmailAttachment.PaintImage paint) { - String name = paint.fileName().endsWith(".antpaint") ? paint.fileName() : paint.fileName() + ".antpaint"; - AntPaintFile file = AntmailAttachmentFiles.toPaintFile(paint, UUID.randomUUID().toString(), 0L, 0L); - String encoded = Base64.getEncoder().encodeToString(AntPaintFileCodec.encode(file)); - String path = "/pictures/" + name; - if (computerFileExists(path)) ComputerNetworking.saveFile(position, path, encoded); - else ComputerNetworking.createFile(position, path, encoded); - session.antmailStatus = "ATTACHMENT SAVED // " + name; - } - ComputerNetworking.listFiles(position); - } catch (RuntimeException exception) { - session.antmailStatus = "ATTACHMENT ERROR // INVALID FILE"; - } - } - - private boolean computerFileExists(String path) { - return ComputerFileSystemClientState.get(position).files().stream().anyMatch(entry -> entry.contains("\t" + path + "\t")); - } - - private void newText() { - session.textPath = "/documents/untitled.txt"; - session.textContent = ""; - session.textCursor = 0; - session.textScroll = 0; - session.textDirty = true; - session.textListing = false; - session.textFocused = true; - } - - private void beginSaveAs() { - int slash = session.textPath.lastIndexOf('/'); - session.textRename = session.textPath.substring(slash + 1); - session.textSaveAs = true; - session.textRenaming = true; - session.textFocused = false; - } - - private void setTextCursorFromMouse(double mouseX, double mouseY, int width) { - if (session.textContent.isEmpty()) { session.textCursor = 0; return; } - StringBuilder line = new StringBuilder(); - int cursor = 0; - int targetLine = Math.max(0, (int) (mouseY / 11)); - int currentLine = 0; - for (int index = 0; index < session.textContent.length(); index++) { - char character = session.textContent.charAt(index); - if (character == '\n' || font.width(line.toString() + character) > width) { - if (currentLine == targetLine) { - session.textCursor = index; - return; - } - currentLine++; - line.setLength(0); - if (character == '\n') continue; - } - line.append(character); - cursor = index + 1; - } - session.textCursor = cursor; - } - - private void moveTextCursorVertically(boolean up) { - int lineStart = session.textContent.lastIndexOf('\n', Math.max(0, session.textCursor - 1)) + 1; - int column = session.textCursor - lineStart; - int target = up ? lineStart - 1 : session.textContent.indexOf('\n', session.textCursor); - if (target < 0) return; - int targetStart = up ? session.textContent.lastIndexOf('\n', target - 1) + 1 : target + 1; - int targetEnd = session.textContent.indexOf('\n', targetStart); - if (targetEnd < 0) targetEnd = session.textContent.length(); - session.textCursor = Math.min(targetStart + column, targetEnd); - } - - private void writeText() { - boolean exists = ComputerFileSystemClientState.get(position).files().stream().anyMatch(entry -> entry.contains("\t" + session.textPath + "\t")); - if (exists) ComputerNetworking.saveFile(position, session.textPath, session.textContent); - else ComputerNetworking.createFile(position, session.textPath, session.textContent); - ComputerNetworking.listFiles(position); - } - - private void saveText() { - boolean exists = ComputerFileSystemClientState.get(position).files().stream().anyMatch(entry -> entry.contains("\t" + session.textPath + "\t")); - if (exists) ComputerNetworking.saveFile(position, session.textPath, session.textContent); - else ComputerNetworking.createFile(position, session.textPath, session.textContent); - ComputerNetworking.listFiles(position); - } - - private void renameTextFile() { - if (!session.textRename.isBlank() && !session.textRename.contains("/") && !ComputerFileSystem.isProtected(session.textPath)) { - String parent = parentDirectory(session.textPath); - String destination = parent.equals("/") ? "/" + session.textRename : parent + "/" + session.textRename; - if (session.textSaveAs) { - session.textSaveAsPendingPath = destination; - ComputerNetworking.createFile(position, destination, session.textContent); - } else { - ComputerNetworking.moveFile(position, session.textPath, destination); - session.textMovePendingPath = destination; - } - ComputerNetworking.listFiles(position); - } - session.textSaveAs = false; - session.textRenaming = false; - session.textRename = ""; - } - - private void savePaint() { - String path = "/pictures/" + session.paintName; - if (!path.endsWith(".antpaint")) path += ".antpaint"; - session.paintName = path.substring(path.lastIndexOf('/') + 1); - AntPaintFile file = new AntPaintFile(UUID.randomUUID().toString(), session.paintName, session.paintCanvas.copy(), 0L, 0L); - String encoded = Base64.getEncoder().encodeToString(AntPaintFileCodec.encode(file)); - String savedPath = path; - boolean exists = ComputerFileSystemClientState.get(position).files().stream().anyMatch(entry -> entry.contains("\t" + savedPath + "\t")); - if (exists) ComputerNetworking.saveFile(position, savedPath, encoded); else ComputerNetworking.createFile(position, savedPath, encoded); - session.antmailPaintPath = savedPath; - session.antmailPaintAttachment = null; - session.antmailPaintLoadPendingPath = ""; - session.paintDirty = false; - ComputerNetworking.listFiles(position); - } - - private void paintTool(int offset) { - session.paintTool = offset < 52 ? AntPaintTool.PENCIL : offset < 108 ? AntPaintTool.ERASER : AntPaintTool.FILL; - } - - private void paintToolbarAction(int index) { - if (index == 0) session.paintTool = AntPaintTool.PENCIL; - else if (index == 1) session.paintTool = AntPaintTool.ERASER; - else if (index == 2) session.paintTool = AntPaintTool.FILL; - else if (index == 3) { - session.paintHistory.record(session.paintCanvas); - session.paintCanvas.clear(); - session.paintDirty = true; - } else if (index == 4 && session.paintHistory.canUndo()) { - session.paintCanvas = session.paintHistory.undo(session.paintCanvas); - session.paintDirty = true; - } else if (index == 5 && session.paintHistory.canRedo()) { - session.paintCanvas = session.paintHistory.redo(session.paintCanvas); - session.paintDirty = true; - } else if (index == 6) { - savePaint(); - } - } - - private void paintAt(double mouseX, double mouseY, int x, int y, int w, int h) { - int size = Math.min(w / AntPaintCanvas.WIDTH, h / AntPaintCanvas.HEIGHT); - if (size <= 0) return; - int canvasX = x + (w - size * AntPaintCanvas.WIDTH) / 2; - int px = (int) ((mouseX - canvasX) / size); - int canvasY = y + Math.max(0, (h - size * AntPaintCanvas.HEIGHT) / 2); - int py = (int) ((mouseY - canvasY) / size); - if (px < 0 || py < 0 || px >= AntPaintCanvas.WIDTH || py >= AntPaintCanvas.HEIGHT) { - lastPaintPixelX = -1; - lastPaintPixelY = -1; - return; - } - if (session.paintTool == AntPaintTool.FILL) { - session.paintHistory.record(session.paintCanvas); - if (session.paintCanvas.floodFill(px, py, true) > 0) session.paintDirty = true; - return; - } - if (lastPaintPixelX < 0 || lastPaintPixelY < 0) applyPaintPixel(px, py); - else { - int dx = Math.abs(px - lastPaintPixelX); - int dy = Math.abs(py - lastPaintPixelY); - int stepX = lastPaintPixelX < px ? 1 : -1; - int stepY = lastPaintPixelY < py ? 1 : -1; - int error = dx - dy; - int currentX = lastPaintPixelX; - int currentY = lastPaintPixelY; - while (currentX != px || currentY != py) { - int twiceError = error * 2; - if (twiceError > -dy) { error -= dy; currentX += stepX; } - if (twiceError < dx) { error += dx; currentY += stepY; } - applyPaintPixel(currentX, currentY); - } - } - lastPaintPixelX = px; - lastPaintPixelY = py; - } - - private void applyPaintPixel(int x, int y) { - boolean changed = session.paintTool == AntPaintTool.PENCIL - ? session.paintCanvas.draw(x, y) - : session.paintCanvas.erase(x, y); - if (changed) session.paintDirty = true; - } - - private boolean hasControl(int modifiers) { - return (modifiers & GLFW.GLFW_MOD_CONTROL) != 0; - } - - private ComputerBlockEntity computer() { - if (Minecraft.getInstance().level == null) return null; - return Minecraft.getInstance().level.getBlockEntity(position) instanceof ComputerBlockEntity computer ? computer : null; - } - - private List physicalDisks() { - ComputerBlockEntity computer = computer(); - if (computer == null) return List.of(); - return computer.diskIds().stream() - .filter(id -> !id.equals(ResourceLocation.fromNamespaceAndPath("antarchy", "introduction"))) - .toList(); - } - - private void ejectSelected() { - if (selectedDisk == null) return; - ComputerNetworking.eject(position, selectedDisk); - loginMessage = "EJECTING DISK // WAITING"; - loginMessageTicks = 100; - selectedDisk = null; - } - - private void beginRenameSelectedFile() { - if (session.fileExplorerSelected.isEmpty() || session.fileExplorerSelected.equals("/") || ComputerFileSystem.isProtected(session.fileExplorerSelected)) return; - int slash = session.fileExplorerSelected.lastIndexOf('/'); - session.fileExplorerRename = session.fileExplorerSelected.substring(slash + 1); - session.fileExplorerRenaming = true; - } - - private void renameSelectedFile() { - if (!session.fileExplorerRename.isBlank() && !session.fileExplorerRename.contains("/")) { - String parent = parentDirectory(session.fileExplorerSelected); - String destination = parent.equals("/") ? "/" + session.fileExplorerRename : parent + "/" + session.fileExplorerRename; - ComputerNetworking.moveFile(position, session.fileExplorerSelected, destination); - session.fileExplorerSelected = destination; - ComputerNetworking.listFiles(position); - } - session.fileExplorerRenaming = false; - session.fileExplorerRename = ""; - } - - private void deleteSelectedFile() { - if (session.fileExplorerSelected.isEmpty() || session.fileExplorerSelected.equals("/") || ComputerFileSystem.isProtected(session.fileExplorerSelected)) return; - ComputerNetworking.deleteFile(position, session.fileExplorerSelected); - session.fileExplorerSelected = ""; - ComputerNetworking.listFiles(position); - } - - private void deleteTextFile() { - if (session.textPath.isBlank() || ComputerFileSystem.isProtected(session.textPath)) return; - ComputerNetworking.deleteFile(position, session.textPath); - session.textPath = "/documents/untitled.txt"; - session.textContent = ""; - session.textCursor = 0; - session.textDirty = false; - ComputerNetworking.listFiles(position); - } - - private void open(String type) { - for (int index = 0; index < session.windows.size(); index++) { - Window window = session.windows.get(index); - if (!window.type.equals(type)) continue; - window.minimized = false; - session.windows.remove(index); - session.windows.add(window); - activeWindow = window; - if (type.equals("FILES")) ComputerNetworking.listFiles(position); - return; - } - Window window = new Window(type, TITLES.get(type), 112 + session.windows.size() * 12, 52 + session.windows.size() * 10); - session.windows.add(window); - activeWindow = window; - if (type.equals("ANTMAIL")) requestAntmailState(); - else if (type.equals("FILES")) ComputerNetworking.listFiles(position); - } - - private Window windowAt(double mouseX, double mouseY) { - float scale = uiScale(); - double localMouseX = (mouseX - width / 2.0F) / scale; - double localMouseY = (mouseY - height / 2.0F) / scale; - int left = -WIDTH / 2; - int top = -HEIGHT / 2; - for (int index = session.windows.size() - 1; index >= 0; index--) { - Window window = session.windows.get(index); - if (window.minimized) continue; - int windowX = left + windowLocalX(window); - int windowY = top + windowLocalY(window); - int renderedWidth = windowWidth(window); - int renderedHeight = windowHeight(window); - if (inside(windowX, windowY + TITLE_BAR_HEIGHT, renderedWidth, renderedHeight - TITLE_BAR_HEIGHT, localMouseX, localMouseY)) return window; - } - return null; - } - - private static boolean inside(int x, int y, int w, int h, double mx, double my) { return mx >= x && mx < x + w && my >= y && my < y + h; } - - @Override - public boolean isPauseScreen() { return false; } - - @Override - public void onClose() { - ComputerNetworking.close(position); - super.onClose(); - } - - private static final class Session { - private boolean authenticated; - private long lastUse; - private final List windows = new ArrayList<>(); - private final List terminalOutput = new ArrayList<>(List.of("ANTINTOSH TERMINAL [READY]", "TYPE HELP FOR COMMANDS")); - private String terminalInput = ""; - private String terminalDirectory = "/"; - private String terminalResponse = ""; - private boolean terminalFocused; - private String textPath = "/documents/untitled.txt"; - private String textContent = ""; - private int textCursor; - private int textScroll; - private boolean textDirty; - private boolean textFocused; - private boolean textListing; - private int textPickerScroll; - private boolean textRenaming; - private boolean textSaveAs; - private String textSaveAsPendingPath = ""; - private String textMovePendingPath = ""; - private boolean textJustSavedAs; - private boolean textDeleteConfirm; - private String textRename = ""; - private ResourceLocation archiveEntryId; - private int archiveScroll; - private String archiveSearch = ""; - private boolean archiveSearchFocused; - private int fileExplorerScroll; - private boolean fileExplorerDeleteConfirm; - private boolean fileExplorerCreatingDirectory; - private boolean fileExplorerMoving; - private int archiveDetailScroll; - private int archiveDetailMaxScroll; - private int archiveMouseX; - private int archiveMouseY; - private int mouseX; - private int mouseY; - private AntPaintCanvas paintCanvas = new AntPaintCanvas(); - private final AntPaintHistory paintHistory = new AntPaintHistory(); - private AntPaintTool paintTool = AntPaintTool.PENCIL; - private String paintName = "UNTITLED.ANTPAINT"; - private boolean paintDirty; - private String antmailUsername = ""; - private String antmailStatus = ""; - private boolean antmailSendPending; - private AntmailResultPayload antmailSendBaseline; - private String antmailRetryMessageId = ""; - private AntmailResultPayload antmailRetryBaseline; - private int antmailRetryTicks; - private boolean antmailRegistrationPending; - private int antmailRefreshTicks; - private int antmailStateWaitTicks; - private boolean antmailFocused; - private String antmailMode = "inbox"; - private String antmailRecipient = ""; - private String antmailSubject = ""; - private String antmailBody = ""; - private int antmailField; - private boolean antmailSent; - private int antmailMessageIndex = -1; - private int antmailPage; - private int antmailAttachmentStartLine = -1; - private int antmailVisibleAttachmentCount; - private boolean antmailAttachText; - private boolean antmailAttachPaint; - private String antmailTextPath = ""; - private String antmailPaintPath = ""; - private AntmailAttachment.PaintImage antmailPaintAttachment; - private String antmailPaintLoadPendingPath = ""; - private UUID antmailDraftId; - private boolean antmailDraftLoaded; - private boolean antmailPickingAttachment; - private String fileExplorerDirectory = "/"; - private String fileExplorerSelected = ""; - private String fileExplorerRename = ""; - private boolean fileExplorerRenaming; - private String wallpaperId = ComputerDesktopState.DEFAULT_WALLPAPER.toString(); - private List wallpapers = List.of(ComputerDesktopState.DEFAULT_WALLPAPER.toString()); - private boolean desktopRequested; - private int bootTicks = -1; - private String basiliskResponse = ""; - private boolean basiliskStateRequested; - private String antmanResponse = ""; - private boolean antmanStateRequested; - private boolean blockleStateRequested; - } - - private static final class Window { - private final String type; - private final String title; - private final int width = 230; - private final int height = 210; - private int x; - private int y; - private int restoreX; - private int restoreY; - private int renderWidth = width; - private int renderHeight = height; - private boolean minimized; - private boolean maximized; - private boolean gameOpen; - private String gameId = "basilisk"; - - private Window(String type, String title, int x, int y) { - this.type = type; - this.title = title; - this.x = x; - this.y = y; - this.restoreX = x; - this.restoreY = y; - } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/AntarchyGameSavedData.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/AntarchyGameSavedData.java new file mode 100644 index 00000000..ca9e45f8 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/AntarchyGameSavedData.java @@ -0,0 +1,75 @@ +package com.craisinlord.antarchy.content.computer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.saveddata.SavedData; + +/** Persistent Antarchy game progress keyed by AntOS computer location. */ +public final class AntarchyGameSavedData extends SavedData { + private static final String ID = "antarchy_computer_games"; + private final Map games = new HashMap<>(); + + public static AntarchyGameSavedData create() { return new AntarchyGameSavedData(); } + + public static AntarchyGameSavedData load(CompoundTag tag, HolderLookup.Provider registries) { + AntarchyGameSavedData data = new AntarchyGameSavedData(); + for (net.minecraft.nbt.Tag raw : tag.getList("Games", net.minecraft.nbt.Tag.TAG_COMPOUND)) { + CompoundTag item = (CompoundTag) raw; + GameState state = new GameState(); + state.basiliskScore = item.getInt("BasiliskScore"); + state.basiliskHighScore = item.getInt("BasiliskHighScore"); + state.antmanScore = item.getInt("AntmanScore"); + state.antmanHighScore = item.getInt("AntmanHighScore"); + state.blockleDay = item.getLong("BlockleDay"); + for (net.minecraft.nbt.Tag guess : item.getList("BlockleGuesses", net.minecraft.nbt.Tag.TAG_STRING)) state.blockleGuesses.add(guess.getAsString()); + data.games.put(item.getString("Computer"), state); + } + return data; + } + + @Override public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) { + ListTag list = new ListTag(); + games.forEach((key, state) -> { + CompoundTag item = new CompoundTag(); + item.putString("Computer", key); + item.putInt("BasiliskScore", state.basiliskScore); + item.putInt("BasiliskHighScore", state.basiliskHighScore); + item.putInt("AntmanScore", state.antmanScore); + item.putInt("AntmanHighScore", state.antmanHighScore); + item.putLong("BlockleDay", state.blockleDay); + ListTag guesses = new ListTag(); + state.blockleGuesses.forEach(guess -> guesses.add(net.minecraft.nbt.StringTag.valueOf(guess))); + item.put("BlockleGuesses", guesses); + list.add(item); + }); + tag.put("Games", list); + return tag; + } + + public static GameState get(MinecraftServer server, String key) { + AntarchyGameSavedData data = server.overworld().getDataStorage().computeIfAbsent( + new SavedData.Factory<>(AntarchyGameSavedData::create, AntarchyGameSavedData::load, null), ID); + return data.games.computeIfAbsent(key, ignored -> new GameState()); + } + + public static void changed(MinecraftServer server) { + server.overworld().getDataStorage().computeIfAbsent( + new SavedData.Factory<>(AntarchyGameSavedData::create, AntarchyGameSavedData::load, null), ID).setDirty(); + } + + public static String key(net.minecraft.resources.ResourceLocation dimension, net.minecraft.core.BlockPos pos) { + return dimension + "|" + pos.asLong(); + } + + public static final class GameState { + public int basiliskScore, basiliskHighScore, antmanScore, antmanHighScore; + public long blockleDay = Long.MIN_VALUE; + public final List blockleGuesses = new ArrayList<>(); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerDesktopState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerDesktopState.java deleted file mode 100644 index 87a45062..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerDesktopState.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.craisinlord.antarchy.content.computer; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.resources.ResourceLocation; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -public final class ComputerDesktopState { - public static final ResourceLocation DEFAULT_WALLPAPER = ResourceLocation.fromNamespaceAndPath("antarchy", "grid_ant"); - public static final int MAX_UNLOCKED_WALLPAPERS = 64; - public static final int MAX_INSTALLED_PROGRAMS = 64; - public static final int MAX_ICON_POSITIONS = 64; - public static final int MAX_ID_LENGTH = 128; - public static final int MIN_ICON_COORDINATE = 0; - public static final int MAX_ICON_COORDINATE = 4096; - private static final String SELECTED_WALLPAPER_TAG = "SelectedWallpaper"; - private static final String UNLOCKED_WALLPAPERS_TAG = "UnlockedWallpapers"; - private static final String INSTALLED_PROGRAMS_TAG = "InstalledPrograms"; - private static final String ICON_POSITIONS_TAG = "IconPositions"; - private static final String ID_TAG = "Id"; - private static final String X_TAG = "X"; - private static final String Y_TAG = "Y"; - private ResourceLocation selectedWallpaper; - private final Set unlockedWallpaperIds = new LinkedHashSet<>(); - private final Set installedProgramIds = new LinkedHashSet<>(); - private final Map iconPositions = new LinkedHashMap<>(); - - public ResourceLocation selectedWallpaper() { - return selectedWallpaper; - } - - public Set unlockedWallpaperIds() { - return Collections.unmodifiableSet(new LinkedHashSet<>(unlockedWallpaperIds)); - } - - public Set installedProgramIds() { - return Collections.unmodifiableSet(new LinkedHashSet<>(installedProgramIds)); - } - - public Map iconPositions() { - return Collections.unmodifiableMap(new LinkedHashMap<>(iconPositions)); - } - - public boolean hasWallpaper(ResourceLocation id) { - return id != null && unlockedWallpaperIds.contains(id); - } - - public boolean hasProgram(ResourceLocation id) { - return id != null && installedProgramIds.contains(id); - } - - public IconPosition iconPosition(ResourceLocation id) { - return iconPositions.get(id); - } - - public boolean unlockWallpaper(ResourceLocation id) { - if (!validId(id) || unlockedWallpaperIds.contains(id)) { - return false; - } - if (unlockedWallpaperIds.size() >= MAX_UNLOCKED_WALLPAPERS) { - return false; - } - return unlockedWallpaperIds.add(id); - } - - public boolean installProgram(ResourceLocation id) { - if (!validId(id) || installedProgramIds.size() >= MAX_INSTALLED_PROGRAMS && !installedProgramIds.contains(id)) { - return false; - } - return installedProgramIds.add(id); - } - - public boolean uninstallProgram(ResourceLocation id) { - if (id == null || !installedProgramIds.remove(id)) { - return false; - } - iconPositions.remove(id); - return true; - } - - public boolean selectWallpaper(ResourceLocation id) { - if (id != null && !unlockedWallpaperIds.contains(id)) { - return false; - } - if (id == null ? selectedWallpaper == null : id.equals(selectedWallpaper)) { - return false; - } - selectedWallpaper = id; - return true; - } - - public boolean setIconPosition(ResourceLocation id, int x, int y) { - if (!validId(id) || x < MIN_ICON_COORDINATE || x > MAX_ICON_COORDINATE || y < MIN_ICON_COORDINATE || y > MAX_ICON_COORDINATE) { - return false; - } - IconPosition position = new IconPosition(x, y); - if (position.equals(iconPositions.get(id))) { - return false; - } - if (!iconPositions.containsKey(id) && iconPositions.size() >= MAX_ICON_POSITIONS) { - return false; - } - iconPositions.put(id, position); - return true; - } - - public boolean removeIconPosition(ResourceLocation id) { - return id != null && iconPositions.remove(id) != null; - } - - public void clear() { - unlockedWallpaperIds.clear(); - installedProgramIds.clear(); - iconPositions.clear(); - unlockedWallpaperIds.add(DEFAULT_WALLPAPER); - selectedWallpaper = DEFAULT_WALLPAPER; - } - - public CompoundTag save() { - CompoundTag tag = new CompoundTag(); - if (selectedWallpaper != null) { - tag.putString(SELECTED_WALLPAPER_TAG, selectedWallpaper.toString()); - } - tag.put(UNLOCKED_WALLPAPERS_TAG, saveIds(unlockedWallpaperIds)); - tag.put(INSTALLED_PROGRAMS_TAG, saveIds(installedProgramIds)); - ListTag positions = new ListTag(); - for (Map.Entry entry : iconPositions.entrySet()) { - CompoundTag position = new CompoundTag(); - position.putString(ID_TAG, entry.getKey().toString()); - position.putInt(X_TAG, entry.getValue().x()); - position.putInt(Y_TAG, entry.getValue().y()); - positions.add(position); - } - tag.put(ICON_POSITIONS_TAG, positions); - return tag; - } - - public void load(CompoundTag tag) { - clear(); - loadIds(tag.getList(UNLOCKED_WALLPAPERS_TAG, 8), unlockedWallpaperIds, MAX_UNLOCKED_WALLPAPERS); - ResourceLocation storedSelection = parseId(tag.getString(SELECTED_WALLPAPER_TAG)); - selectedWallpaper = storedSelection != null && unlockedWallpaperIds.contains(storedSelection) ? storedSelection : DEFAULT_WALLPAPER; - loadIds(tag.getList(INSTALLED_PROGRAMS_TAG, 8), installedProgramIds, MAX_INSTALLED_PROGRAMS); - ListTag positions = tag.getList(ICON_POSITIONS_TAG, 10); - for (int index = 0; index < positions.size() && iconPositions.size() < MAX_ICON_POSITIONS; index++) { - CompoundTag stored = positions.getCompound(index); - ResourceLocation id = parseId(stored.getString(ID_TAG)); - int x = stored.getInt(X_TAG); - int y = stored.getInt(Y_TAG); - if (id != null && x >= MIN_ICON_COORDINATE && x <= MAX_ICON_COORDINATE && y >= MIN_ICON_COORDINATE && y <= MAX_ICON_COORDINATE) { - iconPositions.putIfAbsent(id, new IconPosition(x, y)); - } - } - } - - private static ListTag saveIds(Collection ids) { - ListTag list = new ListTag(); - for (ResourceLocation id : ids) { - list.add(StringTag.valueOf(id.toString())); - } - return list; - } - - private static void loadIds(ListTag list, Set destination, int limit) { - for (int index = 0; index < list.size() && destination.size() < limit; index++) { - ResourceLocation id = parseId(list.getString(index)); - if (id != null) { - destination.add(id); - } - } - } - - private static ResourceLocation parseId(String value) { - if (value == null || value.isBlank() || value.length() > MAX_ID_LENGTH) { - return null; - } - try { - return ResourceLocation.parse(value); - } catch (Exception ignored) { - return null; - } - } - - private static boolean validId(ResourceLocation id) { - return id != null && id.toString().length() <= MAX_ID_LENGTH; - } - - public record IconPosition(int x, int y) { - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerFileSystem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerFileSystem.java deleted file mode 100644 index 3cc24eba..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerFileSystem.java +++ /dev/null @@ -1,220 +0,0 @@ -package com.craisinlord.antarchy.content.computer; - -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.StringTag; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -public final class ComputerFileSystem { - public static final int MAX_FILE_SIZE = 16 * 1024; - public static final int MAX_TOTAL_SIZE = 64 * 1024; - public static final int MAX_NODES = 128; - public static final int MAX_PATH_LENGTH = 64; - public static final String ROOT = "/"; - public static final String DOCUMENTS = "/documents"; - public static final String PICTURES = "/pictures"; - public static final String SYSTEM = "/system"; - private static final String FILES_TAG = "Files"; - private static final String PATH_TAG = "Path"; - private static final String TYPE_TAG = "Type"; - private static final String CONTENTS_TAG = "Contents"; - private final Map nodes = new LinkedHashMap<>(); - private int totalBytes; - - public ComputerFileSystem() { - reset(); - } - - public void reset() { - nodes.clear(); - nodes.put(ROOT, ComputerFile.directory(ROOT)); - nodes.put(DOCUMENTS, ComputerFile.directory(DOCUMENTS)); - nodes.put(PICTURES, ComputerFile.directory(PICTURES)); - nodes.put(SYSTEM, ComputerFile.directory(SYSTEM)); - totalBytes = 0; - } - - public ComputerFile get(String path) { - return nodes.get(normalize(path)); - } - - public List list() { - return List.copyOf(nodes.values()); - } - - public List listTextFiles() { - return nodes.values().stream().filter(file -> file.type() == ComputerFile.Type.TEXT).toList(); - } - - public Result createDirectory(String path) { - String normalized = normalize(path); - if (normalized.isEmpty() || ROOT.equals(normalized) || isProtected(normalized) || nodes.containsKey(normalized) || nodes.size() >= MAX_NODES) return Result.failure("invalid_path"); - int slash = normalized.lastIndexOf('/'); - if (slash > 0 && !nodesDirectory(normalized.substring(0, slash))) return Result.failure("missing_directory"); - nodes.put(normalized, ComputerFile.directory(normalized)); - return Result.ok(); - } - - public Result createTextFile(String path, String contents) { - String normalized = normalize(path); - Result validation = validateTextPath(normalized, contents); - if (!validation.successful()) return validation; - if (nodes.containsKey(normalized)) return Result.failure("already_exists"); - if (nodes.size() >= MAX_NODES) return Result.failure("node_limit"); - int size = contents.length(); - if (totalBytes + size > MAX_TOTAL_SIZE) return Result.failure("storage_limit"); - nodes.put(normalized, ComputerFile.text(normalized, contents)); - totalBytes += size; - return Result.ok(); - } - - public Result writeTextFile(String path, String contents) { - String normalized = normalize(path); - Result validation = validateTextPath(normalized, contents); - if (!validation.successful()) return validation; - ComputerFile current = nodes.get(normalized); - if (current == null) return Result.failure("not_found"); - if (current.type() != ComputerFile.Type.TEXT && current.type() != ComputerFile.Type.IMAGE) return Result.failure("not_file"); - int replacement = totalBytes - current.contents().length() + contents.length(); - if (replacement > MAX_TOTAL_SIZE) return Result.failure("storage_limit"); - nodes.put(normalized, ComputerFile.text(normalized, contents)); - totalBytes = replacement; - return Result.ok(); - } - - public Result createOrWriteTextFile(String path, String contents) { - return nodes.containsKey(normalize(path)) ? writeTextFile(path, contents) : createTextFile(path, contents); - } - - public Result move(String source, String destination) { - String from = normalize(source); - String to = normalize(destination); - if (from.isEmpty() || to.isEmpty() || ROOT.equals(from) || ROOT.equals(to) || isProtected(from) || isProtected(to)) return Result.failure("invalid_path"); - ComputerFile file = nodes.get(from); - if (file == null) return Result.failure("not_found"); - if (nodes.containsKey(to)) return Result.failure("already_exists"); - int slash = to.lastIndexOf('/'); - if (slash > 0 && !nodesDirectory(to.substring(0, slash))) return Result.failure("missing_directory"); - if (file.type() == ComputerFile.Type.DIRECTORY && to.startsWith(from + "/")) return Result.failure("invalid_path"); - if (file.type() == ComputerFile.Type.DIRECTORY) { - List descendants = nodes.keySet().stream() - .filter(path -> path.startsWith(from + "/")) - .toList(); - for (String descendant : descendants) { - String movedPath = to + descendant.substring(from.length()); - if (nodes.containsKey(movedPath)) return Result.failure("already_exists"); - } - Map moved = new LinkedHashMap<>(); - moved.put(to, ComputerFile.directory(to)); - for (String descendant : descendants) { - ComputerFile child = nodes.get(descendant); - String movedPath = to + descendant.substring(from.length()); - moved.put(movedPath, child.type() == ComputerFile.Type.DIRECTORY - ? ComputerFile.directory(movedPath) - : ComputerFile.text(movedPath, child.contents())); - } - nodes.keySet().removeIf(path -> path.equals(from) || path.startsWith(from + "/")); - nodes.putAll(moved); - } else { - nodes.remove(from); - nodes.put(to, ComputerFile.text(to, file.contents())); - } - return Result.ok(); - } - - public Result delete(String path) { - String normalized = normalize(path); - if (isProtected(normalized)) return Result.failure("protected"); - ComputerFile file = nodes.get(normalized); - if (file == null) return Result.failure("not_found"); - if (file.type() == ComputerFile.Type.DIRECTORY && nodes.keySet().stream().anyMatch(key -> !key.equals(normalized) && key.startsWith(normalized + "/"))) { - return Result.failure("not_empty"); - } - nodes.remove(normalized); - totalBytes -= file.contents().length(); - return Result.ok(); - } - - public static String normalize(String path) { - if (path == null || path.isBlank()) return ""; - String value = path.trim().replace('\\', '/'); - if (!value.startsWith("/")) value = "/" + value; - while (value.contains("//")) value = value.replace("//", "/"); - if (value.length() > 1 && value.endsWith("/")) value = value.substring(0, value.length() - 1); - return value; - } - - public static boolean isProtected(String path) { - String normalized = normalize(path); - return normalized.equals(SYSTEM) || normalized.startsWith(SYSTEM + "/"); - } - - public void save(CompoundTag tag, HolderLookup.Provider registries) { - ListTag files = new ListTag(); - for (ComputerFile file : nodes.values()) { - if (ROOT.equals(file.path())) continue; - CompoundTag stored = new CompoundTag(); - stored.putString(PATH_TAG, file.path()); - stored.putString(TYPE_TAG, file.type().name()); - if (file.type() == ComputerFile.Type.TEXT || file.type() == ComputerFile.Type.IMAGE) stored.putString(CONTENTS_TAG, file.contents()); - files.add(stored); - } - tag.put(FILES_TAG, files); - } - - public void load(CompoundTag tag, HolderLookup.Provider registries) { - reset(); - ListTag files = tag.getList(FILES_TAG, 10); - for (int index = 0; index < files.size(); index++) { - CompoundTag stored = files.getCompound(index); - String path = normalize(stored.getString(PATH_TAG)); - if (path.isEmpty() || nodes.containsKey(path) || path.length() > MAX_PATH_LENGTH || isProtected(path)) continue; - String type = stored.getString(TYPE_TAG); - if ("DIRECTORY".equals(type)) { - if (nodes.size() < MAX_NODES) nodes.put(path, ComputerFile.directory(path)); - } else if ("TEXT".equals(type) || "IMAGE".equals(type)) { - String contents = stored.getString(CONTENTS_TAG); - if (contents.length() <= MAX_FILE_SIZE && nodes.size() < MAX_NODES && totalBytes + contents.length() <= MAX_TOTAL_SIZE) { - nodes.put(path, "IMAGE".equals(type) ? ComputerFile.image(path, contents) : ComputerFile.text(path, contents)); - totalBytes += contents.length(); - } - } - } - } - - private Result validateTextPath(String path, String contents) { - if (path.isEmpty() || path.length() > MAX_PATH_LENGTH || ROOT.equals(path) || isProtected(path)) return Result.failure("invalid_path"); - if (contents == null || contents.length() > MAX_FILE_SIZE) return Result.failure("file_limit"); - if (path.contains("..") || path.indexOf('\0') >= 0 || path.indexOf('\n') >= 0 || path.indexOf('\r') >= 0) return Result.failure("invalid_path"); - int slash = path.lastIndexOf('/'); - if (slash > 0 && !nodesDirectory(path.substring(0, slash))) return Result.failure("missing_directory"); - return Result.ok(); - } - - private boolean nodesDirectory(String path) { - ComputerFile parent = nodes.get(path); - return parent != null && parent.type() == ComputerFile.Type.DIRECTORY; - } - - public int totalBytes() { - return totalBytes; - } - - public record Result(boolean successful, String error) { - public static Result ok() { return new Result(true, ""); } - public static Result failure(String error) { return new Result(false, error); } - } - - public record ComputerFile(String path, Type type, String contents) { - public static ComputerFile directory(String path) { return new ComputerFile(path, Type.DIRECTORY, ""); } - public static ComputerFile text(String path, String contents) { return path.endsWith(".antpaint") ? image(path, contents) : new ComputerFile(path, Type.TEXT, contents); } - public static ComputerFile image(String path, String contents) { return new ComputerFile(path, Type.IMAGE, contents); } - public enum Type { DIRECTORY, TEXT, IMAGE } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintCanvas.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintCanvas.java deleted file mode 100644 index 1e1ff637..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintCanvas.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.craisinlord.antarchy.content.computer.paint; - -import java.util.ArrayDeque; -import java.util.Arrays; - -public final class AntPaintCanvas { - public static final int WIDTH = 32; - public static final int HEIGHT = 24; - public static final int PIXEL_COUNT = WIDTH * HEIGHT; - public static final int PACKED_LENGTH = (PIXEL_COUNT + 7) / 8; - - private final boolean[] pixels; - - public AntPaintCanvas() { - this.pixels = new boolean[PIXEL_COUNT]; - } - - private AntPaintCanvas(boolean[] pixels) { - this.pixels = pixels; - } - - public boolean get(int x, int y) { - return pixels[index(x, y)]; - } - - public void set(int x, int y, boolean value) { - pixels[index(x, y)] = value; - } - - public boolean draw(int x, int y) { - return setIfChanged(x, y, true); - } - - public boolean erase(int x, int y) { - return setIfChanged(x, y, false); - } - - public void clear() { - Arrays.fill(pixels, false); - } - - public int floodFill(int x, int y, boolean value) { - int start = index(x, y); - boolean original = pixels[start]; - if (original == value) { - return 0; - } - ArrayDeque queue = new ArrayDeque<>(); - boolean[] visited = new boolean[PIXEL_COUNT]; - queue.add(start); - int changed = 0; - while (!queue.isEmpty()) { - int current = queue.removeFirst(); - if (visited[current] || pixels[current] != original) { - continue; - } - visited[current] = true; - pixels[current] = value; - changed++; - int px = current % WIDTH; - int py = current / WIDTH; - if (px > 0) queue.add(current - 1); - if (px + 1 < WIDTH) queue.add(current + 1); - if (py > 0) queue.add(current - WIDTH); - if (py + 1 < HEIGHT) queue.add(current + WIDTH); - } - return changed; - } - - public byte[] pack() { - byte[] packed = new byte[PACKED_LENGTH]; - for (int i = 0; i < PIXEL_COUNT; i++) { - if (pixels[i]) { - packed[i / 8] |= (byte) (1 << (i % 8)); - } - } - return packed; - } - - public static AntPaintCanvas unpack(byte[] packed) { - if (packed == null || packed.length != PACKED_LENGTH) { - throw new IllegalArgumentException("Invalid AntPaint pixel data length"); - } - boolean[] pixels = new boolean[PIXEL_COUNT]; - for (int i = 0; i < PIXEL_COUNT; i++) { - pixels[i] = (packed[i / 8] & (1 << (i % 8))) != 0; - } - return new AntPaintCanvas(pixels); - } - - public AntPaintCanvas copy() { - return new AntPaintCanvas(Arrays.copyOf(pixels, pixels.length)); - } - - public boolean[] copyPixels() { - return Arrays.copyOf(pixels, pixels.length); - } - - private boolean setIfChanged(int x, int y, boolean value) { - int index = index(x, y); - if (pixels[index] == value) { - return false; - } - pixels[index] = value; - return true; - } - - private static int index(int x, int y) { - if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) { - throw new IndexOutOfBoundsException("AntPaint pixel outside canvas"); - } - return y * WIDTH + x; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFile.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFile.java deleted file mode 100644 index 3c528337..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFile.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.craisinlord.antarchy.content.computer.paint; - -import java.util.Objects; - -public record AntPaintFile(String fileId, String filename, AntPaintCanvas canvas, long createdTime, long modifiedTime) { - public AntPaintFile { - Objects.requireNonNull(fileId, "fileId"); - filename = AntPaintFilename.normalize(filename); - Objects.requireNonNull(canvas, "canvas"); - if (fileId.isBlank()) { - throw new IllegalArgumentException("fileId cannot be blank"); - } - } - - public AntPaintFile copy() { - return new AntPaintFile(fileId, filename, canvas.copy(), createdTime, modifiedTime); - } - - public AntPaintFile withCanvas(AntPaintCanvas value, long modified) { - return new AntPaintFile(fileId, filename, value, createdTime, modified); - } - - public AntPaintFile withFilename(String value, long modified) { - return new AntPaintFile(fileId, value, canvas, createdTime, modified); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFileCodec.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFileCodec.java deleted file mode 100644 index 960c1acf..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFileCodec.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.craisinlord.antarchy.content.computer.paint; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -public final class AntPaintFileCodec { - public static final int VERSION = 1; - public static final int MAX_ENCODED_BYTES = 1024; - private static final int MAGIC = 0x41505431; - - private AntPaintFileCodec() { - } - - public static byte[] encode(AntPaintFile file) { - try { - ByteArrayOutputStream output = new ByteArrayOutputStream(MAX_ENCODED_BYTES); - DataOutputStream data = new DataOutputStream(output); - data.writeInt(MAGIC); - data.writeByte(VERSION); - writeString(data, file.fileId()); - writeString(data, file.filename()); - data.writeLong(file.createdTime()); - data.writeLong(file.modifiedTime()); - data.writeByte(AntPaintCanvas.WIDTH); - data.writeByte(AntPaintCanvas.HEIGHT); - data.write(file.canvas().pack()); - data.flush(); - byte[] encoded = output.toByteArray(); - if (encoded.length > MAX_ENCODED_BYTES) { - throw new IllegalArgumentException("AntPaint file exceeds encoded size limit"); - } - return encoded; - } catch (IOException exception) { - throw new IllegalStateException("Unable to encode AntPaint file", exception); - } - } - - public static AntPaintFile decode(byte[] encoded) { - if (encoded == null || encoded.length > MAX_ENCODED_BYTES) { - throw new IllegalArgumentException("Invalid AntPaint file size"); - } - try { - DataInputStream data = new DataInputStream(new ByteArrayInputStream(encoded)); - if (data.readInt() != MAGIC || data.readUnsignedByte() != VERSION) { - throw new IllegalArgumentException("Unsupported AntPaint file format"); - } - String fileId = readString(data, 128); - String filename = readString(data, AntPaintFilename.MAX_LENGTH + AntPaintFilename.EXTENSION.length()); - long created = data.readLong(); - long modified = data.readLong(); - if (data.readUnsignedByte() != AntPaintCanvas.WIDTH || data.readUnsignedByte() != AntPaintCanvas.HEIGHT) { - throw new IllegalArgumentException("Unsupported AntPaint canvas size"); - } - byte[] pixels = data.readNBytes(AntPaintCanvas.PACKED_LENGTH); - if (pixels.length != AntPaintCanvas.PACKED_LENGTH || data.available() != 0) { - throw new IllegalArgumentException("Invalid AntPaint pixel payload"); - } - return new AntPaintFile(fileId, filename, AntPaintCanvas.unpack(pixels), created, modified); - } catch (IOException exception) { - throw new IllegalArgumentException("Invalid AntPaint file data", exception); - } - } - - private static void writeString(DataOutputStream data, String value) throws IOException { - byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - if (bytes.length > 128) { - throw new IllegalArgumentException("AntPaint string exceeds size limit"); - } - data.writeShort(bytes.length); - data.write(bytes); - } - - private static String readString(DataInputStream data, int maxBytes) throws IOException { - int length = data.readUnsignedShort(); - if (length > maxBytes) { - throw new IllegalArgumentException("AntPaint string exceeds size limit"); - } - byte[] bytes = new byte[length]; - data.readFully(bytes); - return new String(bytes, StandardCharsets.UTF_8); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFilename.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFilename.java deleted file mode 100644 index 3beefbcb..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintFilename.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.craisinlord.antarchy.content.computer.paint; - -import java.util.Locale; -import java.util.regex.Pattern; - -public final class AntPaintFilename { - public static final int MAX_LENGTH = 32; - public static final String EXTENSION = ".antpaint"; - private static final Pattern SAFE_NAME = Pattern.compile("[A-Za-z0-9 _.-]+"); - - private AntPaintFilename() { - } - - public static boolean isValid(String filename) { - if (filename == null || filename.isBlank() || filename.length() > MAX_LENGTH) { - return false; - } - if (filename.equals(".") || filename.equals("..") || filename.contains("..")) { - return false; - } - return SAFE_NAME.matcher(filename).matches(); - } - - public static String normalize(String filename) { - if (filename == null) { - throw new IllegalArgumentException("Filename cannot be null"); - } - String value = filename.trim(); - if (value.toLowerCase(Locale.ROOT).endsWith(EXTENSION)) { - value = value.substring(0, value.length() - EXTENSION.length()); - } - value = value.trim(); - if (!isValid(value)) { - throw new IllegalArgumentException("Invalid AntPaint filename"); - } - return value + EXTENSION; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintHistory.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintHistory.java deleted file mode 100644 index 9e7b0b58..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintHistory.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.craisinlord.antarchy.content.computer.paint; - -import java.util.ArrayDeque; - -public final class AntPaintHistory { - public static final int MAX_STATES = 32; - - private final ArrayDeque undo = new ArrayDeque<>(); - private final ArrayDeque redo = new ArrayDeque<>(); - - public void record(AntPaintCanvas canvas) { - undo.addLast(canvas.copy()); - while (undo.size() > MAX_STATES) { - undo.removeFirst(); - } - redo.clear(); - } - - public boolean canUndo() { - return !undo.isEmpty(); - } - - public boolean canRedo() { - return !redo.isEmpty(); - } - - public AntPaintCanvas undo(AntPaintCanvas current) { - if (undo.isEmpty()) { - return current; - } - redo.addLast(current.copy()); - return undo.removeLast().copy(); - } - - public AntPaintCanvas redo(AntPaintCanvas current) { - if (redo.isEmpty()) { - return current; - } - undo.addLast(current.copy()); - return redo.removeLast().copy(); - } - - public void clear() { - undo.clear(); - redo.clear(); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintRequests.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintRequests.java deleted file mode 100644 index a2830775..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintRequests.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.craisinlord.antarchy.content.computer.paint; - -import java.util.Objects; - -public final class AntPaintRequests { - private AntPaintRequests() { - } - - public record Save(String computerId, AntPaintFile file) { - public Save { - Objects.requireNonNull(computerId, "computerId"); - Objects.requireNonNull(file, "file"); - } - } - - public record Load(String computerId, String fileId) { - public Load { - Objects.requireNonNull(computerId, "computerId"); - Objects.requireNonNull(fileId, "fileId"); - } - } - - public record List(String computerId) { - public List { - Objects.requireNonNull(computerId, "computerId"); - } - } - - public record Delete(String computerId, String fileId) { - public Delete { - Objects.requireNonNull(computerId, "computerId"); - Objects.requireNonNull(fileId, "fileId"); - } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintResults.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintResults.java deleted file mode 100644 index d5ca9707..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintResults.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.craisinlord.antarchy.content.computer.paint; - -import java.util.List; - -public final class AntPaintResults { - private AntPaintResults() { - } - - public enum Code { - SUCCESS, - UNAUTHENTICATED, - OUT_OF_RANGE, - INVALID_FILENAME, - INVALID_FILE, - FILE_NOT_FOUND, - DUPLICATE_FILE, - STORAGE_FULL, - PAYLOAD_TOO_LARGE, - BUSY, - UNKNOWN_ERROR - } - - public record Save(Code code, String fileId) { - } - - public record Load(Code code, AntPaintFile file) { - } - - public record List(Code code, java.util.List files) { - public List { - files = files == null ? java.util.List.of() : java.util.List.copyOf(files); - } - } - - public record Delete(Code code, String fileId) { - } - - public record AntPaintFileMetadata(String fileId, String filename, long modifiedTime) { - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintTool.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintTool.java deleted file mode 100644 index 68c6088e..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/paint/AntPaintTool.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.craisinlord.antarchy.content.computer.paint; - -public enum AntPaintTool { - PENCIL, - ERASER, - FILL -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommand.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommand.java deleted file mode 100644 index b4a79272..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommand.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.craisinlord.antarchy.content.computer.terminal; - -import java.util.List; - -public record TerminalCommand(String name, List arguments, String rawInput) { - public TerminalCommand { - name = name.toLowerCase(java.util.Locale.ROOT); - arguments = List.copyOf(arguments); - rawInput = rawInput == null ? "" : rawInput; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommandParser.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommandParser.java deleted file mode 100644 index 330f82fa..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommandParser.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.craisinlord.antarchy.content.computer.terminal; - -import java.util.ArrayList; -import java.util.List; - -public final class TerminalCommandParser { - private static final int MAX_INPUT_LENGTH = 256; - - private TerminalCommandParser() { - } - - public static ParseResult parse(String input) { - if (input == null) { - return ParseResult.error("ERROR: EMPTY INPUT"); - } - if (input.length() > MAX_INPUT_LENGTH) { - return ParseResult.error("ERROR: COMMAND TOO LONG"); - } - String trimmed = input.trim(); - if (trimmed.isEmpty()) { - return ParseResult.error("ERROR: EMPTY INPUT"); - } - List tokens = new ArrayList<>(); - StringBuilder token = new StringBuilder(); - Character quote = null; - boolean escaped = false; - for (int index = 0; index < trimmed.length(); index++) { - char character = trimmed.charAt(index); - if (escaped) { - token.append(character); - escaped = false; - } else if (character == '\\') { - escaped = true; - } else if (quote != null) { - if (character == quote) { - quote = null; - } else { - token.append(character); - } - } else if (character == '\'' || character == '"') { - quote = character; - } else if (Character.isWhitespace(character)) { - if (!token.isEmpty()) { - tokens.add(token.toString()); - token.setLength(0); - } - } else { - token.append(character); - } - } - if (escaped) { - token.append('\\'); - } - if (quote != null) { - return ParseResult.error("ERROR: UNTERMINATED QUOTE"); - } - if (!token.isEmpty()) { - tokens.add(token.toString()); - } - if (tokens.isEmpty()) { - return ParseResult.error("ERROR: EMPTY INPUT"); - } - return ParseResult.success(new TerminalCommand(tokens.get(0), tokens.subList(1, tokens.size()), input)); - } - - public record ParseResult(TerminalCommand command, String error) { - public boolean isSuccess() { - return command != null; - } - - public static ParseResult success(TerminalCommand command) { - return new ParseResult(command, null); - } - - public static ParseResult error(String message) { - return new ParseResult(null, message); - } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommandService.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommandService.java deleted file mode 100644 index 1fbe556b..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalCommandService.java +++ /dev/null @@ -1,207 +0,0 @@ -package com.craisinlord.antarchy.content.computer.terminal; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; -import java.util.Optional; - -public final class TerminalCommandService { - private static final int MAX_ARGUMENT_LENGTH = 128; - private static final List COMMANDS = List.of( - "help", "clear", "pwd", "ls", "cd", "mkdir", "touch", "cat", "write", "rm", "rmdir", "mv", "open" - ); - - public TerminalResult execute(TerminalFileSystem fileSystem, String workingDirectory, String input) { - String currentDirectory = normalizeWorkingDirectory(fileSystem, workingDirectory); - TerminalCommandParser.ParseResult parsed = TerminalCommandParser.parse(input); - if (!parsed.isSuccess()) { - return TerminalResult.error(currentDirectory, parsed.error()); - } - TerminalCommand command = parsed.command(); - if (command.arguments().stream().anyMatch(argument -> argument.length() > MAX_ARGUMENT_LENGTH)) { - return TerminalResult.error(currentDirectory, "ERROR: ARGUMENT TOO LONG"); - } - return switch (command.name()) { - case "help" -> help(currentDirectory, command); - case "clear" -> requireArguments(currentDirectory, command, 0, TerminalResult.clear(currentDirectory)); - case "pwd" -> requireArguments(currentDirectory, command, 0, TerminalResult.success(currentDirectory, currentDirectory)); - case "ls" -> list(fileSystem, currentDirectory, command); - case "cd" -> changeDirectory(fileSystem, currentDirectory, command); - case "mkdir" -> createDirectory(fileSystem, currentDirectory, command); - case "touch" -> createFile(fileSystem, currentDirectory, command); - case "cat" -> readFile(fileSystem, currentDirectory, command); - case "write" -> writeFile(fileSystem, currentDirectory, command); - case "rm" -> removeFile(fileSystem, currentDirectory, command); - case "rmdir" -> removeDirectory(fileSystem, currentDirectory, command); - case "mv" -> move(fileSystem, currentDirectory, command); - case "open" -> open(fileSystem, currentDirectory, command); - default -> TerminalResult.error(currentDirectory, "ERROR: INVALID COMMAND: " + command.name()); - }; - } - - private TerminalResult help(String workingDirectory, TerminalCommand command) { - if (command.arguments().isEmpty()) { - return TerminalResult.success(workingDirectory, - "AVAILABLE COMMANDS", - "HELP CLEAR PWD LS CD MKDIR TOUCH CAT WRITE RM RMDIR MV OPEN"); - } - if (command.arguments().size() != 1) { - return usage(workingDirectory, "help [command]"); - } - return switch (command.arguments().get(0).toLowerCase(Locale.ROOT)) { - case "help" -> TerminalResult.success(workingDirectory, "HELP [COMMAND] // SHOW COMMAND HELP"); - case "clear" -> TerminalResult.success(workingDirectory, "CLEAR // CLEAR TERMINAL OUTPUT"); - case "pwd" -> TerminalResult.success(workingDirectory, "PWD // PRINT WORKING DIRECTORY"); - case "ls" -> TerminalResult.success(workingDirectory, "LS [PATH] // LIST DIRECTORY CONTENTS"); - case "cd" -> TerminalResult.success(workingDirectory, "CD [PATH] // CHANGE WORKING DIRECTORY"); - case "mkdir" -> TerminalResult.success(workingDirectory, "MKDIR // CREATE DIRECTORY"); - case "touch" -> TerminalResult.success(workingDirectory, "TOUCH // CREATE EMPTY TEXT FILE"); - case "cat" -> TerminalResult.success(workingDirectory, "CAT // PRINT TEXT FILE"); - case "write" -> TerminalResult.success(workingDirectory, "WRITE [TEXT] // REPLACE TEXT FILE CONTENTS"); - case "rm" -> TerminalResult.success(workingDirectory, "RM // DELETE TEXT FILE"); - case "rmdir" -> TerminalResult.success(workingDirectory, "RMDIR // DELETE EMPTY DIRECTORY"); - case "mv" -> TerminalResult.success(workingDirectory, "MV // MOVE OR RENAME"); - case "open" -> TerminalResult.success(workingDirectory, "OPEN // OPEN TEXT FILE IN ANTTEXT"); - default -> TerminalResult.error(workingDirectory, "ERROR: NO HELP FOR " + command.arguments().get(0)); - }; - } - - private TerminalResult list(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() > 1) { - return usage(workingDirectory, "ls [path]"); - } - String path = command.arguments().isEmpty() ? workingDirectory : command.arguments().get(0); - Optional entry = find(fileSystem, path, workingDirectory); - if (entry.isEmpty()) { - return missing(workingDirectory, path); - } - if (!entry.get().isDirectory()) { - return TerminalResult.success(workingDirectory, entry.get().name()); - } - List entries = new ArrayList<>(fileSystem.list(path, workingDirectory)); - entries.sort(Comparator.comparing(TerminalFileSystem.Entry::isDirectory).reversed().thenComparing(TerminalFileSystem.Entry::name)); - if (entries.isEmpty()) { - return TerminalResult.success(workingDirectory, "DIRECTORY EMPTY"); - } - return new TerminalResult(TerminalResult.Status.SUCCESS, entries.stream() - .map(value -> (value.isDirectory() ? "[DIR] " : " ") + value.name()).toList(), workingDirectory, false, null); - } - - private TerminalResult changeDirectory(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() > 1) { - return usage(workingDirectory, "cd [path]"); - } - String path = command.arguments().isEmpty() ? TerminalFileSystem.ROOT_PATH : command.arguments().get(0); - Optional entry = find(fileSystem, path, workingDirectory); - if (entry.isEmpty() || !entry.get().isDirectory()) { - return missing(workingDirectory, path); - } - return TerminalResult.success(entry.get().path(), entry.get().path()); - } - - private TerminalResult createDirectory(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() != 1) { - return usage(workingDirectory, "mkdir "); - } - return fileSystem.createDirectory(command.arguments().get(0), workingDirectory) - ? TerminalResult.success(workingDirectory) - : TerminalResult.error(workingDirectory, "ERROR: CANNOT CREATE DIRECTORY"); - } - - private TerminalResult createFile(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() != 1) { - return usage(workingDirectory, "touch "); - } - return fileSystem.createFile(command.arguments().get(0), workingDirectory) - ? TerminalResult.success(workingDirectory) - : TerminalResult.error(workingDirectory, "ERROR: CANNOT CREATE FILE"); - } - - private TerminalResult readFile(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() != 1) { - return usage(workingDirectory, "cat "); - } - Optional contents = fileSystem.readText(command.arguments().get(0), workingDirectory); - return contents.map(value -> TerminalResult.success(workingDirectory, value.split("\\R", -1))) - .orElseGet(() -> missing(workingDirectory, command.arguments().get(0))); - } - - private TerminalResult writeFile(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() < 1 || command.arguments().size() > 2) { - return usage(workingDirectory, "write [text]"); - } - String contents = command.arguments().size() == 2 ? command.arguments().get(1) : ""; - return fileSystem.writeText(command.arguments().get(0), workingDirectory, contents) - ? TerminalResult.success(workingDirectory) - : TerminalResult.error(workingDirectory, "ERROR: CANNOT WRITE FILE"); - } - - private TerminalResult removeFile(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() != 1) { - return usage(workingDirectory, "rm "); - } - return fileSystem.deleteFile(command.arguments().get(0), workingDirectory) - ? TerminalResult.success(workingDirectory) - : TerminalResult.error(workingDirectory, "ERROR: CANNOT DELETE FILE"); - } - - private TerminalResult removeDirectory(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() != 1) { - return usage(workingDirectory, "rmdir "); - } - return fileSystem.deleteEmptyDirectory(command.arguments().get(0), workingDirectory) - ? TerminalResult.success(workingDirectory) - : TerminalResult.error(workingDirectory, "ERROR: DIRECTORY NOT EMPTY OR NOT FOUND"); - } - - private TerminalResult move(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() != 2) { - return usage(workingDirectory, "mv "); - } - return fileSystem.move(command.arguments().get(0), command.arguments().get(1), workingDirectory) - ? TerminalResult.success(workingDirectory) - : TerminalResult.error(workingDirectory, "ERROR: CANNOT MOVE PATH"); - } - - private TerminalResult open(TerminalFileSystem fileSystem, String workingDirectory, TerminalCommand command) { - if (command.arguments().size() != 1) { - return usage(workingDirectory, "open "); - } - Optional entry = find(fileSystem, command.arguments().get(0), workingDirectory); - if (entry.isEmpty() || entry.get().isDirectory()) { - return missing(workingDirectory, command.arguments().get(0)); - } - return TerminalResult.open(workingDirectory, entry.get().path()); - } - - private Optional find(TerminalFileSystem fileSystem, String path, String workingDirectory) { - try { - return fileSystem.find(path, workingDirectory); - } catch (RuntimeException ignored) { - return Optional.empty(); - } - } - - private String normalizeWorkingDirectory(TerminalFileSystem fileSystem, String workingDirectory) { - try { - String normalized = fileSystem.normalize(workingDirectory, TerminalFileSystem.ROOT_PATH); - Optional entry = fileSystem.find(normalized, TerminalFileSystem.ROOT_PATH); - return entry.filter(TerminalFileSystem.Entry::isDirectory).map(TerminalFileSystem.Entry::path).orElse(TerminalFileSystem.ROOT_PATH); - } catch (RuntimeException ignored) { - return TerminalFileSystem.ROOT_PATH; - } - } - - private TerminalResult missing(String workingDirectory, String path) { - return TerminalResult.error(workingDirectory, "ERROR: PATH NOT FOUND: " + path); - } - - private TerminalResult usage(String workingDirectory, String usage) { - return TerminalResult.error(workingDirectory, "USAGE: " + usage); - } - - private TerminalResult requireArguments(String workingDirectory, TerminalCommand command, int count, TerminalResult result) { - return command.arguments().size() == count ? result : usage(workingDirectory, command.name() + " requires " + count + " arguments"); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalFileSystem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalFileSystem.java deleted file mode 100644 index c1ff3701..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalFileSystem.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.craisinlord.antarchy.content.computer.terminal; - -import java.util.List; -import java.util.Optional; - -public interface TerminalFileSystem { - String ROOT_PATH = "/"; - - Optional find(String path, String workingDirectory); - - List list(String path, String workingDirectory); - - boolean createDirectory(String path, String workingDirectory); - - boolean createFile(String path, String workingDirectory); - - Optional readText(String path, String workingDirectory); - - boolean writeText(String path, String workingDirectory, String contents); - - boolean deleteFile(String path, String workingDirectory); - - boolean deleteEmptyDirectory(String path, String workingDirectory); - - boolean move(String source, String destination, String workingDirectory); - - String normalize(String path, String workingDirectory); - - record Entry(String name, String path, EntryType type) { - public boolean isDirectory() { - return type == EntryType.DIRECTORY; - } - } - - enum EntryType { - FILE, - DIRECTORY - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalResult.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalResult.java deleted file mode 100644 index 02304226..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/terminal/TerminalResult.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.craisinlord.antarchy.content.computer.terminal; - -import java.util.List; - -public record TerminalResult(Status status, List lines, String workingDirectory, boolean clearOutput, String openPath) { - public TerminalResult { - status = status == null ? Status.ERROR : status; - lines = List.copyOf(lines); - workingDirectory = workingDirectory == null || workingDirectory.isBlank() ? TerminalFileSystem.ROOT_PATH : workingDirectory; - } - - public static TerminalResult success(String workingDirectory, String... lines) { - return new TerminalResult(Status.SUCCESS, List.of(lines), workingDirectory, false, null); - } - - public static TerminalResult error(String workingDirectory, String... lines) { - return new TerminalResult(Status.ERROR, List.of(lines), workingDirectory, false, null); - } - - public static TerminalResult clear(String workingDirectory) { - return new TerminalResult(Status.SUCCESS, List.of(), workingDirectory, true, null); - } - - public static TerminalResult open(String workingDirectory, String path) { - return new TerminalResult(Status.OPEN, List.of("OPENING " + path), workingDirectory, false, path); - } - - public enum Status { - SUCCESS, - ERROR, - OPEN - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java index 4f833207..5f96df3d 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java @@ -38,6 +38,8 @@ public final class CreativeTabContents { } private static final List CATALOG = List.of( + entry("bug_spray", "tools"), + entry("ant_trap", "building_blocks"), entry("ouranwood_log", "building_blocks"), entry("ouranwood_wood", "building_blocks"), entry("mossy_ouranwood_log", "building_blocks"), @@ -395,8 +397,6 @@ public final class CreativeTabContents { entry("lucid_anchor", "tools_and_utilities"), entry("undertrial_spawner", "tools_and_utilities"), entry("undervault", "tools_and_utilities"), - entry("computer", "functional_blocks"), - entry("floppy_disk", "tools_and_utilities"), entry("undertrial_key", "tools_and_utilities"), entry("reverie_bottle", "tools_and_utilities"), entry("glimmer_bottle", "tools_and_utilities"), @@ -641,8 +641,8 @@ public final class CreativeTabContents { } for (String diskId : List.of("antarchy:introduction", "antarchy:chronosphere", "antarchy:toreterror_arena", "antarchy:high_fructose_corn_syrup", "antarchy:elythia", "antarchy:flying_squirrel", "antarchy:cavaryn", "antarchy:thoraxis", "antarchy:temporal_fields", "antarchy:bloodglass", "antarchy:vortex")) { - ItemStack disk = new ItemStack(AntarchyObjects.FLOPPY_DISK.get()); - disk.set(AntarchyObjects.FLOPPY_DISK_COMPONENT.get(), ResourceLocation.parse(diskId)); + ItemStack disk = new ItemStack(com.craisinlord.antos.content.AntOSObjects.FLOPPY_DISK.get()); + disk.set(com.craisinlord.antos.content.AntOSObjects.FLOPPY_DISK_COMPONENT.get(), ResourceLocation.parse(diskId)); output.accept(disk); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/InvertedMobEffect.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/InvertedMobEffect.java index b3bb5f15..e9175ed5 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/InvertedMobEffect.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/effect/InvertedMobEffect.java @@ -6,6 +6,7 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectCategory; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.EntityType; public final class InvertedMobEffect extends MobEffect { private static final AntarchyGravityTransition EFFECT_TRANSITION = new AntarchyGravityTransition(12); @@ -17,6 +18,14 @@ public final class InvertedMobEffect extends MobEffect { @Override public boolean applyEffectTick(LivingEntity entity, int amplifier) { + // Vex flight steering is authored around vanilla gravity and ground contact. Rotating its + // gravity frame makes the native flight controller drive it into terrain. + if (entity.getType() == EntityType.VEX) { + if (!entity.level().isClientSide()) { + AntarchyGravityApi.clearForcedGravity(entity); + } + return true; + } if (!entity.level().isClientSide()) { if (AntarchyGravityApi.getGravityDirection(entity) != AntarchyGravityDirection.UP || !AntarchyGravityApi.isGravityForced(entity)) { AntarchyGravityApi.setForcedGravityDirection(entity, AntarchyGravityDirection.UP, EFFECT_TRANSITION); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/HoverboardEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/HoverboardEntity.java index b27dfda6..7ac87777 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/HoverboardEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/HoverboardEntity.java @@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.entity; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.config.AntarchySettings; +import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; @@ -67,6 +68,14 @@ public class HoverboardEntity extends PathfinderMob implements GeoEntity { this.setPersistenceRequired(); } + @Override + public void tick() { + super.tick(); + if (!this.level().isClientSide && this.isVehicle() && this.tickCount % 100 == 0) { + this.playSound(AntarchySoundEvents.HOVERBOARD_IDLE.get(), 0.55F, 1.0F); + } + } + @Override public boolean canBeAffected(MobEffectInstance effectInstance) { return false; @@ -139,12 +148,21 @@ public class HoverboardEntity extends PathfinderMob implements GeoEntity { if (!this.isVehicle() && !player.isSecondaryUseActive()) { if (!this.level().isClientSide) { player.startRiding(this); + this.playSound(AntarchySoundEvents.HOVERBOARD_MOUNT.get(), 1.0F, 1.0F); } return InteractionResult.sidedSuccess(this.level().isClientSide); } return super.mobInteract(player, hand); } + @Override + protected void removePassenger(Entity passenger) { + super.removePassenger(passenger); + if (!this.level().isClientSide) { + this.playSound(AntarchySoundEvents.HOVERBOARD_DISMOUNT.get(), 1.0F, 1.0F); + } + } + @Override @Nullable public LivingEntity getControllingPassenger() { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/TermiteEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/TermiteEntity.java index 69a7bcc3..591b0bc9 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/TermiteEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/ant/TermiteEntity.java @@ -16,6 +16,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.Item; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.RotatedPillarBlock; import net.minecraft.world.level.block.SoundType; @@ -32,6 +33,8 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity { private static final int WOOD_REPATH_INTERVAL_TICKS = 6; private static final int WOOD_SEARCH_RADIUS_HORIZONTAL = 12; private static final int WOOD_SEARCH_RADIUS_VERTICAL = 4; + private static final int LIGHT_FLEE_SEARCH_RADIUS = 6; + private static final int LIGHT_FLEE_SEARCH_INTERVAL_TICKS = 10; private static final Vec3i[] IMMEDIATE_WOOD_OFFSETS = { new Vec3i(0, 1, 0), new Vec3i(0, 0, -1), @@ -48,6 +51,7 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity { private int nextWoodBiteTick; private int nextWoodSearchTick; private int nextWoodRepathTick; + private int nextLightFleeSearchTick; public TermiteEntity(EntityType entityType, Level level) { super(entityType, level); @@ -113,6 +117,12 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity { @Override protected boolean handlePriorityForaging() { + if (this.isBlockLit(this.blockPosition())) { + this.targetWoodPos = null; + this.fleeBlockLight(); + return true; + } + if (!this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || !this.level().getGameRules().getBoolean(AntarchyGameRules.RULE_DO_TERMITE_GREIFING)) { this.targetWoodPos = null; @@ -142,6 +152,45 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity { return true; } + private void fleeBlockLight() { + if (this.tickCount < this.nextLightFleeSearchTick && !this.getNavigation().isDone()) { + return; + } + this.nextLightFleeSearchTick = this.tickCount + LIGHT_FLEE_SEARCH_INTERVAL_TICKS; + + BlockPos origin = this.blockPosition(); + BlockPos bestPos = null; + int bestLight = this.level().getBrightness(LightLayer.BLOCK, origin); + double bestDistance = -1.0D; + BlockPos.MutableBlockPos candidate = new BlockPos.MutableBlockPos(); + for (int dx = -LIGHT_FLEE_SEARCH_RADIUS; dx <= LIGHT_FLEE_SEARCH_RADIUS; dx++) { + for (int dz = -LIGHT_FLEE_SEARCH_RADIUS; dz <= LIGHT_FLEE_SEARCH_RADIUS; dz++) { + if (dx * dx + dz * dz > LIGHT_FLEE_SEARCH_RADIUS * LIGHT_FLEE_SEARCH_RADIUS) continue; + for (int dy = -2; dy <= 2; dy++) { + candidate.set(origin.getX() + dx, origin.getY() + dy, origin.getZ() + dz); + if (!this.isWalkableStandPos(candidate)) continue; + int light = this.level().getBrightness(LightLayer.BLOCK, candidate); + double distance = candidate.distSqr(origin); + if (light > bestLight || (light == bestLight && distance <= bestDistance)) continue; + bestPos = candidate.immutable(); + bestLight = light; + bestDistance = distance; + } + } + } + + if (bestPos == null) { + this.getNavigation().stop(); + return; + } + Path path = this.getNavigation().createPath(bestPos, 0); + if (path == null || !path.canReach()) { + this.getNavigation().stop(); + return; + } + this.getNavigation().moveTo(bestPos.getX() + 0.5D, bestPos.getY(), bestPos.getZ() + 0.5D, 1.2D); + } + private boolean tryEatNearbyWood() { if (this.tickCount < this.nextWoodBiteTick) { return false; @@ -151,7 +200,7 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity { for (Vec3i offset : IMMEDIATE_WOOD_OFFSETS) { BlockPos targetPos = basePos.offset(offset); BlockState targetState = this.level().getBlockState(targetPos); - if (!this.isTermiteEdible(targetState)) { + if (this.isBlockLit(targetPos) || !this.isTermiteEdible(targetState)) { continue; } @@ -244,7 +293,12 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity { } private boolean isValidWoodTarget(BlockPos targetPos) { - return this.isTermiteEdible(this.level().getBlockState(targetPos)); + return !this.isBlockLit(targetPos) && this.isTermiteEdible(this.level().getBlockState(targetPos)); + } + + private boolean isBlockLit(BlockPos pos) { + return AntarchySettings.termitesFearBlockLight() + && this.level().getBrightness(LightLayer.BLOCK, pos) > 0; } private boolean isTermiteEdible(BlockState state) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java index 49129815..46f02b4a 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/KingEntity.java @@ -14,6 +14,8 @@ import net.minecraft.sounds.SoundSource; import net.minecraft.network.chat.Component; import net.minecraft.ChatFormatting; import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.core.BlockPos; +import net.minecraft.tags.BlockTags; import net.minecraft.core.particles.DustParticleOptions; import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket; import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket; @@ -252,7 +254,7 @@ public class KingEntity extends RoyalBossEntity { return true; } LivingEntity target = this.getTarget(); - if (target == null || this.activeDecree != null || this.royalChargeOpeningDelayTicks > 0 + if (target == null || this.royalChargeOpeningDelayTicks > 0 || this.kingLandingStage != KingLandingStage.AERIAL || !this.attackScheduler.ready("royal_charge", RoyalAttackLane.MOVEMENT)) { return false; @@ -543,6 +545,9 @@ public class KingEntity extends RoyalBossEntity { return; } this.tickRoyalBoundary((ServerLevel) this.level()); + if (this.tickCount % 10 == 0 && this.isRoyalFlying() && this.getDeltaMovement().horizontalDistanceSqr() > 0.01D) { + this.clearLeavesAhead((ServerLevel) this.level()); + } this.tickComeNoCloserIndicator(); this.tickRoyalPunishment(); this.tickPendingExileAnimation(); @@ -762,6 +767,28 @@ public class KingEntity extends RoyalBossEntity { return true; } + private void clearLeavesAhead(ServerLevel level) { + if (!level.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING)) { + return; + } + Vec3 movement = this.getDeltaMovement().normalize().scale(1.5D); + AABB forward = this.getBoundingBox().move(movement).inflate(0.2D, 0.0D, 0.2D); + BlockPos min = BlockPos.containing(forward.minX, forward.minY, forward.minZ); + BlockPos max = BlockPos.containing(forward.maxX, forward.maxY, forward.maxZ); + BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos(); + for (int y = min.getY(); y <= max.getY(); y++) { + for (int x = min.getX(); x <= max.getX(); x++) { + for (int z = min.getZ(); z <= max.getZ(); z++) { + cursor.set(x, y, z); + if (level.getBlockState(cursor).is(BlockTags.LEAVES)) { + level.destroyBlock(cursor, true, this); + return; + } + } + } + } + } + private void tickKingCombatMovement(LivingEntity target) { if (this.royalChargeActive) { this.getNavigation().stop(); @@ -1102,7 +1129,9 @@ public class KingEntity extends RoyalBossEntity { public RoyalDecree.Evaluation evaluateCloseQuarters(LivingEntity target) { UUID targetId = target.getUUID(); - double distanceSquared = target.distanceToSqr(this); + double dx = target.getX() - this.getX(); + double dz = target.getZ() - this.getZ(); + double distanceSquared = dx * dx + dz * dz; if (distanceSquared <= CLOSE_QUARTERS_RADIUS * CLOSE_QUARTERS_RADIUS) { this.closeQuartersEntered.add(targetId); this.closeQuartersOutsideTicks.remove(targetId); @@ -1719,8 +1748,8 @@ public class KingEntity extends RoyalBossEntity { } @Override public void onActive(int elapsedTicks) { Vec3 origin = KingEntity.this.headAnchor(KingEntity.this.royalHead(RoyalHead.Slot.LEFT)); - Vec3 predicted = target.position().add(target.getDeltaMovement().scale(10.0D)); - Vec3 direction = predicted.add(0.0D, target.getBbHeight() * 0.5D, 0.0D).subtract(origin).normalize(); + Vec3 direction = RoyalElementalProjectileEntity.aimDirection( + origin, target, RoyalBeamElement.FIRE); RoyalElementalProjectileEntity fireball = RoyalElementalProjectileEntity.create(level, KingEntity.this, RoyalBeamElement.FIRE, origin, direction); level.addFreshEntity(fireball); @@ -1739,7 +1768,8 @@ public class KingEntity extends RoyalBossEntity { } @Override public void onActive(int elapsedTicks) { Vec3 origin = KingEntity.this.headAnchor(KingEntity.this.royalHead(RoyalHead.Slot.RIGHT)); - Vec3 direction = target.getEyePosition().subtract(origin).normalize(); + Vec3 direction = RoyalElementalProjectileEntity.aimDirection( + origin, target, RoyalBeamElement.ICE); RoyalElementalProjectileEntity iceball = RoyalElementalProjectileEntity.create(level, KingEntity.this, RoyalBeamElement.ICE, origin, direction); level.addFreshEntity(iceball); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/QueenEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/QueenEntity.java index 9952333f..782013f6 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/QueenEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/QueenEntity.java @@ -10,6 +10,8 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition; +import com.craisinlord.antarchy.content.time.TimeDilationApi; +import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity; import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager; import java.util.HashMap; import java.util.Iterator; @@ -73,6 +75,9 @@ public class QueenEntity extends RoyalBossEntity { private static final double CRUSHING_GRAVITY_RADIUS = 18.0D; private static final double CRUSHING_GRAVITY_STRENGTH = 0.12D; private static final int ACCELERATION_COOLDOWN = 500; + private static final int QUEEN_CHRONOSPHERE_COOLDOWN = 700; + private static final int QUEEN_CHRONOSPHERE_DURATION = 200; + private static final double QUEEN_CHRONOSPHERE_RADIUS = 20.0D; private static final int ACCELERATION_DURATION = 140; private static final int BLACK_HOLE_COOLDOWN = 340; private static final double QUEEN_BITE_COOLDOWN_MULTIPLIER = 1.5D; @@ -83,10 +88,10 @@ public class QueenEntity extends RoyalBossEntity { private static final int ACCELERATION_TELEGRAPH_TICKS = 32; private static final int LANDING_TELEGRAPH_TICKS = 30; private static final int LANDING_IMPACT_TICKS = 18; - private static final int GROUND_COMBAT_TICKS = 140; + private static final int GROUND_COMBAT_TICKS = 160; private static final int TAKEOFF_TICKS = 30; - private static final int LANDING_COOLDOWN_MIN = 360; - private static final int LANDING_COOLDOWN_VARIANCE = 240; + private static final int LANDING_COOLDOWN_MIN = 300; + private static final int LANDING_COOLDOWN_VARIANCE = 120; private static final int BLACK_HOLE_FOLLOW_UP_DELAY = 22; private static final int CRUSHING_LANDING_DELAY = 40; private static final int MOMENTUM_VOLLEY_DELAY = MOMENTUM_LOCK_DURATION + 5; @@ -99,12 +104,14 @@ public class QueenEntity extends RoyalBossEntity { private int momentumLockCooldownTicks; private int crushingGravityCooldownTicks; private int accelerationCooldownTicks; + private int queenChronosphereCooldownTicks; private int blackHoleCooldownTicks; private int gravityStompTelegraphTicks; private int blackHoleTelegraphTicks; private int momentumLockTelegraphTicks; private int crushingGravityTelegraphTicks; private int accelerationTelegraphTicks; + private int queenChronosphereTelegraphTicks; @Nullable private Vec3 pendingBlackHoleAnchor; private int queenLandingCooldownTicks = 300; @@ -238,6 +245,11 @@ public class QueenEntity extends RoyalBossEntity { return 1.55D; } + @Override + protected double combatStandoffDistance() { + return Math.min(super.combatStandoffDistance(), 14.0D); + } + @Override protected int maxConcurrentHeadAttacks(Phase phase) { return switch (phase) { @@ -851,6 +863,7 @@ public class QueenEntity extends RoyalBossEntity { if (this.momentumLockCooldownTicks > 0) this.momentumLockCooldownTicks--; if (this.crushingGravityCooldownTicks > 0) this.crushingGravityCooldownTicks--; if (this.accelerationCooldownTicks > 0) this.accelerationCooldownTicks--; + if (this.queenChronosphereCooldownTicks > 0) this.queenChronosphereCooldownTicks--; if (this.blackHoleCooldownTicks > 0) this.blackHoleCooldownTicks--; if (!(this.level() instanceof ServerLevel level)) { @@ -871,7 +884,7 @@ public class QueenEntity extends RoyalBossEntity { String[] candidates = switch (phase) { case ONE -> new String[] {"gravity_stomp", "black_hole"}; case TWO -> new String[] {"gravity_stomp", "black_hole", "momentum_lock", "crushing_gravity"}; - case THREE -> new String[] {"gravity_stomp", "black_hole", "momentum_lock", "crushing_gravity", "acceleration"}; + case THREE -> new String[] {"gravity_stomp", "black_hole", "momentum_lock", "crushing_gravity", "acceleration", "queen_chronosphere"}; }; String forcedAttack = this.pendingPhaseSignature == Phase.TWO ? "momentum_lock" : this.pendingPhaseSignature == Phase.THREE ? "acceleration" : null; @@ -888,12 +901,14 @@ public class QueenEntity extends RoyalBossEntity { case "black_hole" -> BLACK_HOLE_COOLDOWN; case "momentum_lock" -> MOMENTUM_LOCK_COOLDOWN; case "crushing_gravity" -> CRUSHING_GRAVITY_COOLDOWN; + case "queen_chronosphere" -> QUEEN_CHRONOSPHERE_COOLDOWN; default -> ACCELERATION_COOLDOWN; }; RoyalAttackLane lane = "black_hole".equals(selected) ? RoyalAttackLane.CENTER_HEAD : RoyalAttackLane.HAZARD; int actionWindup = this.queenAbilityWindup(selected); int actionAnimationTicks = "momentum_lock".equals(selected) - || "crushing_gravity".equals(selected) || "acceleration".equals(selected) ? 55 : 25; + || "crushing_gravity".equals(selected) || "acceleration".equals(selected) + || "queen_chronosphere".equals(selected) ? 55 : 25; int actionRecovery = this.animationRecovery(actionAnimationTicks, actionWindup, 1, 14); if (!this.beginRoyalAttack(selected, lane, this.cooldown(cooldown), actionWindup, 1, actionRecovery, new com.craisinlord.antarchy.content.entity.royal.attack.RoyalAttackScheduler.Action() { @@ -902,7 +917,8 @@ public class QueenEntity extends RoyalBossEntity { QueenEntity.this.triggerAnim("body_action", "stomp"); } else if ("momentum_lock".equals(selected) || "crushing_gravity".equals(selected) - || "acceleration".equals(selected)) { + || "acceleration".equals(selected) + || "queen_chronosphere".equals(selected)) { QueenEntity.this.triggerAnim("wing_action", "wing_gust"); } else if ("black_hole".equals(selected)) { QueenEntity.this.royalHead(RoyalHead.Slot.CENTER).startShoot(); @@ -965,6 +981,13 @@ public class QueenEntity extends RoyalBossEntity { this.startRoyalAcceleration(); this.queueQueenFollowUp(QueenFollowUp.ACCELERATION_VOLLEY, ACCELERATION_VOLLEY_DELAY); } + case "queen_chronosphere" -> { + this.queenChronosphereCooldownTicks = cooldown; + TimeDilationFieldEntity field = TimeDilationApi.createField(level, this.position(), + QUEEN_CHRONOSPHERE_RADIUS, 0.08D, QUEEN_CHRONOSPHERE_DURATION, this.getUUID()); + field.configureQueenChronosphere(this); + this.startRoyalRecovery(35); + } } } @@ -998,6 +1021,7 @@ public class QueenEntity extends RoyalBossEntity { case "momentum_lock" -> !this.royalEffects.active("momentum_lock") && this.momentumLockCooldownTicks <= 0; case "crushing_gravity" -> phase != Phase.ONE && !this.royalEffects.active("crushing_gravity") && this.crushingGravityCooldownTicks <= 0; case "acceleration" -> phase == Phase.THREE && !this.royalEffects.active("acceleration") && this.accelerationCooldownTicks <= 0; + case "queen_chronosphere" -> phase == Phase.THREE && this.queenChronosphereCooldownTicks <= 0; default -> false; }; } @@ -1013,6 +1037,7 @@ public class QueenEntity extends RoyalBossEntity { }; case THREE -> switch (id) { case "acceleration" -> 7; + case "queen_chronosphere" -> 6; case "black_hole", "crushing_gravity" -> 5; case "gravity_stomp" -> 4; case "momentum_lock" -> 2; @@ -1084,6 +1109,7 @@ public class QueenEntity extends RoyalBossEntity { case "momentum_lock" -> MOMENTUM_LOCK_TELEGRAPH_TICKS; case "crushing_gravity" -> CRUSHING_GRAVITY_TELEGRAPH_TICKS; case "acceleration" -> ACCELERATION_TELEGRAPH_TICKS; + case "queen_chronosphere" -> 40; default -> 10; }; } @@ -1111,6 +1137,10 @@ public class QueenEntity extends RoyalBossEntity { this.accelerationTelegraphTicks = ACCELERATION_TELEGRAPH_TICKS; this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 1.22F); } + case "queen_chronosphere" -> { + this.queenChronosphereTelegraphTicks = 40; + this.playRoyalSound(AntarchySoundEvents.QUEEN_ROAR.get(), 0.85F); + } } } @@ -1121,13 +1151,14 @@ public class QueenEntity extends RoyalBossEntity { case "momentum_lock" -> this.momentumLockTelegraphTicks = 0; case "crushing_gravity" -> this.crushingGravityTelegraphTicks = 0; case "acceleration" -> this.accelerationTelegraphTicks = 0; + case "queen_chronosphere" -> this.queenChronosphereTelegraphTicks = 0; } } private boolean isQueenAbilityTelegraphActive() { return this.gravityStompTelegraphTicks > 0 || this.blackHoleTelegraphTicks > 0 || this.momentumLockTelegraphTicks > 0 || this.crushingGravityTelegraphTicks > 0 - || this.accelerationTelegraphTicks > 0; + || this.accelerationTelegraphTicks > 0 || this.queenChronosphereTelegraphTicks > 0; } private void clearQueenAbilityTelegraphs() { @@ -1136,6 +1167,7 @@ public class QueenEntity extends RoyalBossEntity { this.momentumLockTelegraphTicks = 0; this.crushingGravityTelegraphTicks = 0; this.accelerationTelegraphTicks = 0; + this.queenChronosphereTelegraphTicks = 0; this.pendingBlackHoleAnchor = null; } @@ -1150,6 +1182,7 @@ public class QueenEntity extends RoyalBossEntity { this.tickMomentumLockTelegraph(level); this.tickCrushingGravityTelegraph(level); this.tickAccelerationTelegraph(level); + this.tickQueenChronosphereTelegraph(level); } private void tickGravityStompTelegraph(ServerLevel level) { @@ -1235,6 +1268,18 @@ public class QueenEntity extends RoyalBossEntity { this.accelerationTelegraphTicks--; } + private void tickQueenChronosphereTelegraph(ServerLevel level) { + if (this.queenChronosphereTelegraphTicks <= 0) return; + int elapsed = 40 - this.queenChronosphereTelegraphTicks; + Vec3 center = this.position().add(0.0D, this.getBbHeight() * 0.5D, 0.0D); + if (this.tickCount % 2 == 0) { + double radius = Mth.lerp(elapsed / 40.0D, 3.0D, QUEEN_CHRONOSPHERE_RADIUS); + this.spawnQueenTelegraphRing(level, center, radius, 96, ParticleTypes.REVERSE_PORTAL); + } + this.playQueenTelegraphBeat(level, this.queenChronosphereTelegraphTicks, 40, 0.5F); + this.queenChronosphereTelegraphTicks--; + } + private void spawnQueenTelegraphSpokes(ServerLevel level, Vec3 center, double radius) { int spokes = 10; int segments = 6; @@ -1524,6 +1569,7 @@ public class QueenEntity extends RoyalBossEntity { tag.putInt("MomentumLockCooldownTicks", this.momentumLockCooldownTicks); tag.putInt("CrushingGravityCooldownTicks", this.crushingGravityCooldownTicks); tag.putInt("AccelerationCooldownTicks", this.accelerationCooldownTicks); + tag.putInt("QueenChronosphereCooldownTicks", this.queenChronosphereCooldownTicks); tag.putInt("BlackHoleCooldownTicks", this.blackHoleCooldownTicks); tag.putInt("QueenLandingCooldownTicks", this.queenLandingCooldownTicks); tag.putString("QueuedQueenFollowUp", this.queuedQueenFollowUp.name()); @@ -1547,6 +1593,7 @@ public class QueenEntity extends RoyalBossEntity { this.momentumLockCooldownTicks = Math.max(0, tag.getInt("MomentumLockCooldownTicks")); this.crushingGravityCooldownTicks = Math.max(0, tag.getInt("CrushingGravityCooldownTicks")); this.accelerationCooldownTicks = Math.max(0, tag.getInt("AccelerationCooldownTicks")); + this.queenChronosphereCooldownTicks = Math.max(0, tag.getInt("QueenChronosphereCooldownTicks")); this.blackHoleCooldownTicks = Math.max(0, tag.getInt("BlackHoleCooldownTicks")); this.queenLandingCooldownTicks = tag.contains("QueenLandingCooldownTicks") ? Math.max(0, tag.getInt("QueenLandingCooldownTicks")) : 300; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java index fea0d22d..a592a18a 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalBossEntity.java @@ -850,8 +850,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult Vec3 awayFromTarget = horizontalDistance > 1.0E-4D ? new Vec3(-dx / horizontalDistance, 0.0D, -dz / horizontalDistance) : this.getViewVector(1.0F).multiply(-1.0D, 0.0D, -1.0D).normalize(); - double standoff = Math.max(this.getBbWidth() * 0.55D, - BEAM_MUZZLE_FORWARD - this.biteReach() * 0.65D + 1.0D); + double standoff = this.combatStandoffDistance(); Vec3 wanted = target.position().add(awayFromTarget.scale(standoff)); boolean atStandoff = this.position().multiply(1.0D, 0.0D, 1.0D) .distanceToSqr(wanted.multiply(1.0D, 0.0D, 1.0D)) <= 4.0D; @@ -908,6 +907,11 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult return false; } + protected double combatStandoffDistance() { + return Math.max(this.getBbWidth() * 0.55D, + BEAM_MUZZLE_FORWARD - this.biteReach() * 0.65D + 1.0D); + } + private void tickCombatLocomotionMode(LivingEntity target) { if (!this.isFlyingBoss() || this.managesOwnCombatLocomotion()) { return; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java index c73b07e6..193a41f2 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalElementalProjectileEntity.java @@ -42,6 +42,11 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen private static final int ICEBALL = 1; private static final int DREAM_FIREBALL = 2; private static final int MAX_LIFETIME = 100; + private static final float FIREBALL_SPEED = 1.5F; + private static final float ICEBALL_SPEED = 1.2F; + private static final float DREAM_FIREBALL_SPEED = 1.05F; + private static final double PROJECTILE_INERTIA = 0.985D; + private static final float DREAM_FIREBALL_INERTIA = 0.96F; private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); public RoyalElementalProjectileEntity(EntityType type, Level level) { @@ -57,10 +62,38 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen projectile.setPos(position.x, position.y, position.z); projectile.setAttackType(element == RoyalBeamElement.ICE ? ICEBALL : element == RoyalBeamElement.DREAM_FIRE ? DREAM_FIREBALL : FIREBALL); - projectile.shoot(direction.x, direction.y, direction.z, element == RoyalBeamElement.ICE ? 0.8F : 1.05F, 0.0F); + float speed = element == RoyalBeamElement.ICE ? ICEBALL_SPEED + : element == RoyalBeamElement.DREAM_FIRE ? DREAM_FIREBALL_SPEED : FIREBALL_SPEED; + projectile.shoot(direction.x, direction.y, direction.z, speed, 0.0F); return projectile; } + /** Solves a discrete ballistic lead against the projectile's drag and gravity. */ + public static Vec3 aimDirection(Vec3 origin, LivingEntity target, RoyalBeamElement element) { + double speed = element == RoyalBeamElement.ICE ? ICEBALL_SPEED : FIREBALL_SPEED; + double gravity = element == RoyalBeamElement.ICE ? 0.006D : 0.018D; + double inertia = PROJECTILE_INERTIA; + Vec3 bestDirection = target.getEyePosition().subtract(origin).normalize(); + double bestError = Double.MAX_VALUE; + + for (int ticks = 1; ticks < MAX_LIFETIME; ticks++) { + double dragDistance = (1.0D - Math.pow(inertia, ticks)) / (1.0D - inertia); + Vec3 predictedTarget = target.getEyePosition().add(target.getDeltaMovement().scale(ticks)); + Vec3 displacement = predictedTarget.subtract(origin); + double gravityCompensation = gravity * (ticks - dragDistance) / (1.0D - inertia); + Vec3 initialVelocity = new Vec3( + displacement.x / dragDistance, + (displacement.y + gravityCompensation) / dragDistance, + displacement.z / dragDistance); + double error = Math.abs(initialVelocity.length() - speed); + if (error < bestError) { + bestError = error; + bestDirection = initialVelocity.normalize(); + } + } + return bestDirection; + } + @Override protected void defineSynchedData(SynchedEntityData.Builder builder) { builder.define(ATTACK_TYPE, FIREBALL); @@ -80,11 +113,11 @@ public class RoyalElementalProjectileEntity extends ThrowableProjectile implemen @Override protected double getDefaultGravity() { - return this.isIceball() ? 0.012D : 0.018D; + return this.isIceball() ? 0.006D : 0.018D; } protected float getInertia() { - return this.isIceball() ? 0.985F : 0.96F; + return this.isDreamFireball() ? DREAM_FIREBALL_INERTIA : (float) PROJECTILE_INERTIA; } @Override diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java index 9004cc5f..e0e85fea 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/RoyalMountEntity.java @@ -10,7 +10,7 @@ import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode; import com.craisinlord.antarchy.content.effect.RoyalEffectEligibility; import com.craisinlord.antarchy.content.effect.RoyalEffectHooks; import com.craisinlord.antarchy.content.time.TimeDilationApi; -import java.util.UUID; +import net.minecraft.resources.ResourceLocation; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; @@ -34,6 +34,7 @@ import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.TamableAnimal; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.control.FlyingMoveControl; import net.minecraft.world.entity.ai.control.MoveControl; @@ -71,6 +72,7 @@ import software.bernie.geckolib.animation.RawAnimation; import software.bernie.geckolib.util.GeckoLibUtil; public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntity, FlyingAnimal { + private static final ResourceLocation GROWTH_HEALTH_MODIFIER = ResourceLocation.fromNamespaceAndPath("antarchy", "royal_mount_growth_health"); public static final int ANIM_IDLE = 0; public static final int ANIM_WALK = 1; public static final int ANIM_FLY = 2; @@ -309,6 +311,9 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit this.navigation = flying ? this.flyingNavigation : this.groundNavigation; this.setNoGravity(flying); } + if (GROWTH_PROGRESS.equals(key)) { + this.refreshDimensions(); + } } public void setFlying(boolean flying) { @@ -350,11 +355,30 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit public void setGrowthProgress(float progress) { float clamped = Mth.clamp(progress, 0.0F, 1.0F); if (this.getGrowthProgress() != clamped) { + float oldHealth = this.getHealth(); this.entityData.set(GROWTH_PROGRESS, clamped); + var maxHealth = this.getAttribute(Attributes.MAX_HEALTH); + if (maxHealth != null) { + maxHealth.removeModifier(GROWTH_HEALTH_MODIFIER); + if (clamped > 0.0F) { + maxHealth.addTransientModifier(new AttributeModifier(GROWTH_HEALTH_MODIFIER, + this.getGrowthScale() - 1.0D, AttributeModifier.Operation.ADD_MULTIPLIED_BASE)); + } + this.setHealth(Math.min(oldHealth, this.getMaxHealth())); + } this.refreshDimensions(); } } + @Override + public boolean hurt(DamageSource source, float amount) { + Entity attacker = source.getEntity(); + if (attacker != null && this.hasPassenger(attacker)) { + return false; + } + return super.hurt(source, amount); + } + @Override public EntityDimensions getDefaultDimensions(Pose pose) { return super.getDefaultDimensions(pose).scale(this.getGrowthScale()); @@ -369,19 +393,37 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack stack = player.getItemInHand(hand); - if (this.isFood(stack)) { + if (stack.is(Items.GOLDEN_APPLE) && !this.isTame()) { + if (!this.level().isClientSide) { + if (!player.getAbilities().instabuild) { + stack.shrink(1); + } + this.tame(player); + this.heal(8.0F); + this.level().broadcastEntityEvent(this, (byte) 7); + } + return InteractionResult.sidedSuccess(this.level().isClientSide); + } + + if (stack.is(Items.ENCHANTED_GOLDEN_APPLE) && this.isTame() && this.isOwnedBy(player)) { if (!this.level().isClientSide) { if (!player.getAbilities().instabuild) { stack.shrink(1); } this.heal(8.0F); this.setGrowthProgress(this.getGrowthProgress() + (1.0F - this.getGrowthProgress()) * 0.1F); - if (!this.isTame() && this.random.nextInt(3) == 0) { - this.tame(player); - this.level().broadcastEntityEvent(this, (byte) 7); - } else { - this.level().broadcastEntityEvent(this, (byte) 6); + this.level().broadcastEntityEvent(this, (byte) 6); + } + return InteractionResult.sidedSuccess(this.level().isClientSide); + } + + if (this.isFood(stack)) { + if (!this.level().isClientSide && this.isTame() && this.isOwnedBy(player) && this.getHealth() < this.getMaxHealth()) { + if (!player.getAbilities().instabuild) { + stack.shrink(1); } + this.heal(8.0F); + this.level().broadcastEntityEvent(this, (byte) 6); } return InteractionResult.sidedSuccess(this.level().isClientSide); } @@ -399,12 +441,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit } if (stack.isEmpty() && player.isSecondaryUseActive()) { - if (this.isSaddled()) { - if (!this.level().isClientSide) { - this.setSaddled(false); - this.spawnAtLocation(new ItemStack(Items.SADDLE)); - } - } else if (!this.level().isClientSide) { + if (!this.level().isClientSide) { this.setOrderedToSit(!this.isOrderedToSit()); this.setInSittingPose(this.isOrderedToSit()); this.navigation.stop(); @@ -762,8 +799,10 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit if (!this.hasPassenger(passenger)) { return; } - double yOffset = (this.isFlying() ? 1.55D : 1.35D) * this.getGrowthScale() - 1.0D; - Vec3 back = this.getLookAngle().scale(-0.35D * this.getGrowthScale()); + // Seat near the back of the torso, outside the scaled body instead of inside its growing hitbox. + double yOffset = Math.max(this.getBbHeight() * 0.82D, this.getBbHeight() - passenger.getBbHeight() * 0.65D); + double backDistance = this.getBbWidth() * 0.5D + passenger.getBbWidth() * 0.5D + 0.15D; + Vec3 back = this.getLookAngle().scale(-backDistance); moveFunction.accept(passenger, this.getX() + back.x, this.getY() + yOffset, this.getZ() + back.z); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandYourGroundDecree.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandYourGroundDecree.java index 622ff408..1bc33fbd 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandYourGroundDecree.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/royal/decree/StandYourGroundDecree.java @@ -11,9 +11,14 @@ public final class StandYourGroundDecree implements RoyalDecree { private final Map anchors = new HashMap<>(); public String translationKey() { return "decree.antarchy.stand_your_ground"; } public void apply(ServerLevel level, KingEntity king, LivingEntity target) { - Vec3 anchor = this.anchors.computeIfAbsent(target.getUUID(), ignored -> target.position()); + this.anchors.computeIfAbsent(target.getUUID(), ignored -> target.position()); } @Override public Evaluation evaluate(ServerLevel level, KingEntity king, LivingEntity target) { + if (target instanceof net.minecraft.server.level.ServerPlayer player && player.isFallFlying()) { + // Gliding carries unavoidable momentum; begin measuring again from the landing point. + this.anchors.put(target.getUUID(), target.position()); + return Evaluation.COMPLIANT; + } Vec3 anchor = this.anchors.computeIfAbsent(target.getUUID(), ignored -> target.position()); return target.position().distanceToSqr(anchor) > ALLOWED_DRIFT * ALLOWED_DRIFT ? Evaluation.VIOLATED : Evaluation.COMPLIANT; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/trades/ComputerScientistTradeManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/trades/ComputerScientistTradeManager.java index e67bd15b..55d7b394 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/trades/ComputerScientistTradeManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/trades/ComputerScientistTradeManager.java @@ -1,6 +1,6 @@ package com.craisinlord.antarchy.content.entity.trades; -import com.craisinlord.antarchy.content.AntarchyObjects; +import com.craisinlord.antos.content.AntOSObjects; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -118,8 +118,8 @@ public final class ComputerScientistTradeManager extends SimpleJsonResourceReloa ResourceLocation diskId = diskType.equals("random_general") ? GENERAL_DISKS.get(random.nextInt(GENERAL_DISKS.size())) : ResourceLocation.parse(diskType); - ItemStack disk = new ItemStack(AntarchyObjects.FLOPPY_DISK.get(), count); - disk.set(AntarchyObjects.FLOPPY_DISK_COMPONENT.get(), diskId); + ItemStack disk = new ItemStack(AntOSObjects.FLOPPY_DISK.get(), count); + disk.set(AntOSObjects.FLOPPY_DISK_COMPONENT.get(), diskId); return disk; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/trades/PestControlTradeManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/trades/PestControlTradeManager.java new file mode 100644 index 00000000..20dfbf7d --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/trades/PestControlTradeManager.java @@ -0,0 +1,76 @@ +package com.craisinlord.antarchy.content.entity.trades; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; +import net.minecraft.util.RandomSource; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.entity.npc.VillagerTrades; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.trading.ItemCost; +import net.minecraft.world.item.trading.MerchantOffer; + +public final class PestControlTradeManager extends SimpleJsonResourceReloadListener { + private static final Gson GSON = new Gson(); + private static volatile List trades = List.of(); + + public PestControlTradeManager() { + super(GSON, "pest_control_trades"); + } + + public static int[] slotsPerLevel() { + return new int[] {2, 2, 2, 1, 1}; + } + + public static VillagerTrades.ItemListing listing(int level, int index) { + return (villager, random) -> trades.stream().filter(entry -> entry.level == level && entry.index == index) + .findFirst().map(entry -> entry.offer(random)).orElse(null); + } + + @Override + protected void apply(Map resources, ResourceManager manager, ProfilerFiller profiler) { + List parsed = new ArrayList<>(); + for (JsonElement resource : resources.values()) { + if (!resource.isJsonObject()) continue; + JsonObject root = resource.getAsJsonObject(); + if (root.has("trades")) { + for (JsonElement element : root.getAsJsonArray("trades")) if (element.isJsonObject()) parsed.add(Entry.parse(element.getAsJsonObject())); + } else parsed.add(Entry.parse(root)); + } + trades = List.copyOf(parsed); + } + + private record Entry(int level, int index, String buy, int buyCount, String sell, int sellCount, + String secondBuy, int secondBuyCount, int maxUses, int xp) { + private MerchantOffer offer(RandomSource random) { + var buyItem = BuiltInRegistries.ITEM.get(ResourceLocation.parse(buy)); + var sellItem = BuiltInRegistries.ITEM.get(ResourceLocation.parse(sell)); + var secondCost = secondBuy == null ? java.util.Optional.empty() + : java.util.Optional.of(new ItemCost(BuiltInRegistries.ITEM.get(ResourceLocation.parse(secondBuy)), secondBuyCount)); + return new MerchantOffer(new ItemCost(buyItem, buyCount), secondCost, new ItemStack(sellItem, sellCount), maxUses, xp, 0.05F); + } + + private static Entry parse(JsonObject json) { + JsonObject buy = json.getAsJsonObject("buy"); + JsonObject sell = json.getAsJsonObject("sell"); + JsonObject second = json.has("second_buy") ? json.getAsJsonObject("second_buy") : null; + return new Entry(json.get("level").getAsInt(), json.get("index").getAsInt(), buy.get("item").getAsString(), + count(buy), sell.get("item").getAsString(), count(sell), second == null ? null : second.get("item").getAsString(), + second == null ? 0 : count(second), json.has("max_uses") ? json.get("max_uses").getAsInt() : 12, + json.has("villager_xp") ? json.get("villager_xp").getAsInt() : 5); + } + + private static int count(JsonObject stack) { + if (stack.has("min") && stack.has("max")) return stack.get("min").getAsInt(); + return stack.has("count") ? stack.get("count").getAsInt() : stack.get("min").getAsInt(); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/guide/ComputerGuideData.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/guide/ComputerGuideData.java deleted file mode 100644 index 53b72158..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/guide/ComputerGuideData.java +++ /dev/null @@ -1,290 +0,0 @@ -package com.craisinlord.antarchy.content.guide; - -import com.craisinlord.antarchy.Antarchy; -import com.craisinlord.antarchy.config.AntarchySettings; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.server.packs.resources.SimplePreparableReloadListener; -import net.minecraft.util.profiling.ProfilerFiller; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -public final class ComputerGuideData extends SimplePreparableReloadListener { - private static final ComputerGuideData INSTANCE = new ComputerGuideData(); - private static final ResourceLocation INTRODUCTION = ResourceLocation.fromNamespaceAndPath("antarchy", "introduction"); - private static volatile Map entries = Map.of(); - private static volatile Map disks = Map.of(); - - private ComputerGuideData() { - } - - public static ComputerGuideData instance() { - return INSTANCE; - } - - public static Entry entry(ResourceLocation id) { - return entries.get(id); - } - - public static Disk disk(ResourceLocation id) { - return disks.get(id); - } - - public static List entriesFor(List diskIds) { - if (AntarchySettings.unlockAllArchives()) { - Map result = new LinkedHashMap<>(); - result.put(INTRODUCTION, introduction()); - result.putAll(entries); - return result.values().stream().sorted(Comparator.comparing(entry -> entry.titleKey().toString())).toList(); - } - Map result = new LinkedHashMap<>(); - for (ResourceLocation diskId : diskIds) { - if (INTRODUCTION.equals(diskId)) { - result.put(INTRODUCTION, introduction()); - continue; - } - Disk disk = disks.get(diskId); - if (disk == null) { - continue; - } - for (ResourceLocation entryId : disk.entries()) { - Entry entry = entries.get(entryId); - if (entry != null) { - result.putIfAbsent(entryId, entry); - } - } - } - return result.values().stream().sorted(Comparator.comparing(entry -> entry.titleKey().toString())).toList(); - } - - private static Entry introduction() { - return new Entry(INTRODUCTION, "article", "general", "guide.antarchy.entry.introduction.title", - "guide.antarchy.entry.introduction.subtitle", List.of("guide.antarchy.entry.introduction.description"), - "", "", "", "", "", "", "", 0, "", "", "", 0.0F, 1.0F); - } - - /** Encodes the server data-pack view so remote clients do not need a duplicate asset copy. */ - public static String encodeNetworkSnapshot() { - JsonObject root = new JsonObject(); - JsonArray entryArray = new JsonArray(); - entries.values().stream().sorted(Comparator.comparing(entry -> entry.id().toString())).forEach(entry -> { - JsonObject object = new JsonObject(); - object.addProperty("id", entry.id().toString()); - object.addProperty("type", entry.type()); - object.addProperty("category", entry.category()); - object.addProperty("title", entry.titleKey()); - object.addProperty("subtitle", entry.subtitleKey()); - JsonArray descriptions = new JsonArray(); - entry.descriptionKeys().forEach(descriptions::add); - object.add("description", descriptions); - object.addProperty("item", entry.itemId()); - object.addProperty("entity", entry.entityId()); - object.addProperty("enchantment", entry.enchantmentId()); - object.addProperty("recipe", entry.recipeId()); - object.addProperty("structure", entry.structureId()); - object.addProperty("structure_tag", entry.structureTagId()); - object.addProperty("dimension", entry.dimensionId()); - object.addProperty("search_radius", entry.searchRadius()); - object.addProperty("cover_item", entry.coverItemId()); - object.addProperty("cover_entity", entry.coverEntityId()); - object.addProperty("cover_potion", entry.coverPotionId()); - object.addProperty("rotation", entry.rotation()); - object.addProperty("render_scale", entry.renderScale()); - entryArray.add(object); - }); - root.add("entries", entryArray); - - JsonArray diskArray = new JsonArray(); - disks.values().stream().sorted(Comparator.comparing(disk -> disk.id().toString())).forEach(disk -> { - JsonObject object = new JsonObject(); - object.addProperty("id", disk.id().toString()); - object.addProperty("category", disk.category()); - object.addProperty("title", disk.titleKey()); - JsonArray diskEntries = new JsonArray(); - disk.entries().forEach(id -> diskEntries.add(id.toString())); - object.add("entries", diskEntries); - object.addProperty("wallpaper", disk.wallpaper()); - diskArray.add(object); - }); - root.add("disks", diskArray); - return root.toString(); - } - - /** Applies the authoritative server snapshot on a multiplayer client. */ - public static void applyNetworkSnapshot(String json) { - try { - JsonObject root = JsonParser.parseString(json).getAsJsonObject(); - Map loadedEntries = new HashMap<>(); - Map loadedDisks = new HashMap<>(); - JsonArray entryArray = root.getAsJsonArray("entries"); - if (entryArray != null) { - for (JsonElement element : entryArray) { - JsonObject object = element.getAsJsonObject(); - parseEntry(ResourceLocation.parse(string(object, "id", "")), object, loadedEntries); - } - } - JsonArray diskArray = root.getAsJsonArray("disks"); - if (diskArray != null) { - for (JsonElement element : diskArray) { - JsonObject object = element.getAsJsonObject(); - parseDisk(ResourceLocation.parse(string(object, "id", "")), object, loadedDisks); - } - } - entries = Map.copyOf(loadedEntries); - disks = Map.copyOf(loadedDisks); - Antarchy.LOGGER.info("Loaded {} computer archive entries and {} disks from the server", entries.size(), disks.size()); - } catch (RuntimeException exception) { - Antarchy.LOGGER.error("Failed to decode computer archive data from the server", exception); - } - } - - @Override - protected LoadedData prepare(ResourceManager resourceManager, ProfilerFiller profiler) { - Map loadedEntries = new HashMap<>(); - Map loadedDisks = new HashMap<>(); - for (Map.Entry resource : resourceManager.listResources("computer/entry", path -> path.getPath().endsWith(".json")).entrySet()) { - ResourceLocation id = resourceId(resource.getKey(), "computer/entry"); - try { - JsonElement json = JsonParser.parseReader(resource.getValue().openAsReader()); - parseEntry(id, json, loadedEntries); - } catch (Exception exception) { - Antarchy.LOGGER.error("Failed to load computer archive entry {}", resource.getKey(), exception); - } - } - for (Map.Entry resource : resourceManager.listResources("computer/disk", path -> path.getPath().endsWith(".json")).entrySet()) { - ResourceLocation id = resourceId(resource.getKey(), "computer/disk"); - try { - JsonElement json = JsonParser.parseReader(resource.getValue().openAsReader()); - parseDisk(id, json, loadedDisks); - } catch (Exception exception) { - Antarchy.LOGGER.error("Failed to load computer archive disk {}", resource.getKey(), exception); - } - } - return new LoadedData(Map.copyOf(loadedEntries), Map.copyOf(loadedDisks)); - } - - @Override - protected void apply(LoadedData data, ResourceManager resourceManager, ProfilerFiller profiler) { - entries = data.entries(); - disks = data.disks(); - } - - private static ResourceLocation resourceId(ResourceLocation path, String directory) { - String prefix = directory + "/"; - String value = path.getPath().startsWith(prefix) ? path.getPath().substring(prefix.length()) : path.getPath(); - if (value.endsWith(".json")) { - value = value.substring(0, value.length() - 5); - } - return ResourceLocation.fromNamespaceAndPath(path.getNamespace(), value); - } - - private static void parseEntry(ResourceLocation id, JsonElement element, Map target) { - if (!element.isJsonObject()) { - return; - } - JsonObject object = element.getAsJsonObject(); - String type = string(object, "type", "article"); - String category = string(object, "category", "general"); - String title = string(object, "title", "guide.antarchy.missing_title"); - String subtitle = string(object, "subtitle", ""); - List description = strings(object.get("description")); - String item = string(object, "item", ""); - String entity = string(object, "entity", ""); - String enchantment = string(object, "enchantment", ""); - String recipe = string(object, "recipe", ""); - String structure = string(object, "structure", ""); - String structureTag = string(object, "structure_tag", ""); - String dimension = string(object, "dimension", ""); - int searchRadius = object.has("search_radius") && object.get("search_radius").isJsonPrimitive() - ? Math.max(0, Math.min(1000, object.get("search_radius").getAsInt())) : 0; - String coverItem = string(object, "cover_item", ""); - String coverEntity = string(object, "cover_entity", ""); - String coverPotion = string(object, "cover_potion", ""); - float rotation = object.has("rotation") && object.get("rotation").isJsonPrimitive() - ? object.get("rotation").getAsFloat() : 0.0F; - float renderScale = object.has("render_scale") && object.get("render_scale").isJsonPrimitive() - ? object.get("render_scale").getAsFloat() : 1.0F; - renderScale = Math.max(0.1F, Math.min(3.0F, renderScale)); - target.put(id, new Entry(id, type, category, title, subtitle, description, item, entity, enchantment, recipe, structure, structureTag, dimension, searchRadius, - coverItem, coverEntity, coverPotion, rotation, renderScale)); - } - - private static void parseDisk(ResourceLocation id, JsonElement element, Map target) { - if (!element.isJsonObject()) { - return; - } - JsonObject object = element.getAsJsonObject(); - String category = string(object, "category", "general"); - String title = string(object, "title", "guide.antarchy.missing_disk_title"); - List entryIds = new ArrayList<>(); - JsonElement entriesElement = object.get("entries"); - if (entriesElement != null && entriesElement.isJsonArray()) { - for (JsonElement entry : entriesElement.getAsJsonArray()) { - if (entry.isJsonPrimitive() && entry.getAsJsonPrimitive().isString()) { - try { - entryIds.add(ResourceLocation.parse(entry.getAsString())); - } catch (Exception ignored) { - } - } - } - } - String wallpaper = string(object, "wallpaper", ""); - target.put(id, new Disk(id, category, title, List.copyOf(entryIds), wallpaper)); - } - - private static String string(JsonObject object, String key, String fallback) { - JsonElement element = object.get(key); - return element != null && element.isJsonPrimitive() ? element.getAsString() : fallback; - } - - private static List strings(JsonElement element) { - if (element == null) { - return List.of(); - } - if (element.isJsonPrimitive()) { - return List.of(element.getAsString()); - } - if (!element.isJsonArray()) { - return List.of(); - } - List values = new ArrayList<>(); - JsonArray array = element.getAsJsonArray(); - for (JsonElement value : array) { - if (value.isJsonPrimitive()) { - values.add(value.getAsString()); - } else if (value.isJsonObject()) { - JsonObject render = value.getAsJsonObject(); - String item = string(render, "item", ""); - String entity = string(render, "entity", ""); - String enchantment = string(render, "enchantment", ""); - String recipe = string(render, "recipe", ""); - if (!item.isBlank()) values.add("@item:" + item); - else if (!entity.isBlank()) values.add("@entity:" + entity); - else if (!enchantment.isBlank()) values.add("@enchantment:" + enchantment); - else if (!recipe.isBlank()) values.add("@recipe:" + recipe); - } - } - return List.copyOf(values); - } - - public record Entry(ResourceLocation id, String type, String category, String titleKey, String subtitleKey, - List descriptionKeys, String itemId, String entityId, String enchantmentId, - String recipeId, String structureId, String structureTagId, String dimensionId, int searchRadius, String coverItemId, - String coverEntityId, String coverPotionId, float rotation, float renderScale) { - } - - public record Disk(ResourceLocation id, String category, String titleKey, List entries, String wallpaper) { - } - - record LoadedData(Map entries, Map disks) { - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BigBerthaItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BigBerthaItem.java index 094859a7..8f4ba348 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BigBerthaItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BigBerthaItem.java @@ -110,8 +110,7 @@ public class BigBerthaItem extends SwordItem implements GeoItem { public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { ItemStack stack = player.getItemInHand(hand); if (player.isShiftKeyDown()) { - cycleMode(level, player, stack); - return InteractionResultHolder.sidedSuccess(stack, level.isClientSide); + return InteractionResultHolder.pass(stack); } BossMode mode = getMode(stack); @@ -390,8 +389,8 @@ public class BigBerthaItem extends SwordItem implements GeoItem { } } - public boolean tryCycleModeWhileCoolingDown(Level level, Player player, ItemStack stack) { - if (player.getMainHandItem() != stack || !player.isShiftKeyDown() || !player.getCooldowns().isOnCooldown(this)) { + public boolean tryCycleModeFromInput(Level level, Player player, ItemStack stack) { + if (player.getMainHandItem() != stack) { return false; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BugSprayItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BugSprayItem.java new file mode 100644 index 00000000..472cdd34 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BugSprayItem.java @@ -0,0 +1,60 @@ +package com.craisinlord.antarchy.content.item; + +import com.craisinlord.antarchy.content.AntarchyObjects; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.Mob; +import net.minecraft.tags.EntityTypeTags; +import net.minecraft.tags.TagKey; +import net.minecraft.resources.ResourceLocation; +import com.craisinlord.antarchy.content.entity.ant.BaseAntEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public final class BugSprayItem extends Item { + private static final TagKey> ANARCHY_ARTHROPODS = TagKey.create( + net.minecraft.core.registries.Registries.ENTITY_TYPE, ResourceLocation.fromNamespaceAndPath("antarchy", "arthropods")); + + public BugSprayItem(Properties properties) { + super(properties); + } + + @Override + public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); + if (!level.isClientSide && level instanceof ServerLevel serverLevel) { + Vec3 look = player.getLookAngle().normalize(); + Vec3 origin = player.getEyePosition().add(look.scale(0.6D)); + var sprayParticle = AntarchyObjects.BUG_SPRAY_PARTICLE.get(); + serverLevel.sendParticles(sprayParticle, origin.x, origin.y, origin.z, 18, + look.x * 0.45D, look.y * 0.45D, look.z * 0.45D, 0.06D); + AABB bounds = new AABB(origin, origin.add(look.scale(7.0D))).inflate(2.2D); + for (Entity entity : level.getEntities(player, bounds, candidate -> candidate instanceof Mob mob + && (mob.getType().is(EntityTypeTags.SENSITIVE_TO_BANE_OF_ARTHROPODS) + || mob.getType().is(ANARCHY_ARTHROPODS) || mob instanceof BaseAntEntity))) { + if (!(entity instanceof Mob mob)) continue; + Vec3 toward = mob.position().subtract(origin); + double distance = toward.length(); + if (distance > 9.0D || distance < 0.001D || toward.normalize().dot(look) < 0.55D) continue; + Vec3 flee = mob.position().subtract(player.position()).normalize(); + mob.getNavigation().moveTo(mob.getX() + flee.x * 6.0D, mob.getY(), mob.getZ() + flee.z * 6.0D, 1.25D); + mob.setTarget(null); + mob.setLastHurtByMob(null); + mob.setDeltaMovement(mob.getDeltaMovement().add(flee.scale(0.55D)).add(0.0D, 0.15D, 0.0D)); + mob.hurtMarked = true; + serverLevel.sendParticles(sprayParticle, mob.getX(), mob.getY() + mob.getBbHeight() * 0.5D, mob.getZ(), 5, 0.12D, 0.12D, 0.12D, 0.02D); + } + level.playSound(null, player.blockPosition(), SoundEvents.FIRE_EXTINGUISH, SoundSource.PLAYERS, 0.7F, 1.2F); + stack.shrink(1); + } + return InteractionResultHolder.sidedSuccess(stack, level.isClientSide); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/ComputerItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/ComputerItem.java deleted file mode 100644 index 78a5c6ee..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/ComputerItem.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import com.craisinlord.antarchy.content.block.ComputerBlock; -import net.minecraft.world.item.BlockItem; - -public final class ComputerItem extends BlockItem { - public ComputerItem(ComputerBlock block, Properties properties) { - super(block, properties); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/FloppyDiskItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/FloppyDiskItem.java deleted file mode 100644 index a8b2d7e2..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/FloppyDiskItem.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import com.craisinlord.antarchy.content.AntarchyObjects; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; - -import java.util.List; - -public final class FloppyDiskItem extends Item { - public FloppyDiskItem(Properties properties) { - super(properties.stacksTo(1)); - } - - public static float categoryModelProperty(ItemStack stack) { - ResourceLocation id = stack.get(AntarchyObjects.FLOPPY_DISK_COMPONENT.get()); - if (id == null) return 0.0F; - var disk = com.craisinlord.antarchy.content.guide.ComputerGuideData.disk(id); - if (disk == null) return 0.0F; - return switch (disk.category()) { - case "elythia" -> 1.0F; - case "cavaryn" -> 2.0F; - case "thoraxis" -> 3.0F; - default -> 0.0F; - }; - } - - @Override - public Component getName(ItemStack stack) { - ResourceLocation id = stack.get(AntarchyObjects.FLOPPY_DISK_COMPONENT.get()); - if (id != null) { - var disk = com.craisinlord.antarchy.content.guide.ComputerGuideData.disk(id); - if (disk != null) { - return Component.translatable(disk.titleKey()); - } - } - return Component.translatable("item.antarchy.floppy_disk"); - } - - @Override - public void appendHoverText(ItemStack stack, TooltipContext context, List tooltip, TooltipFlag flag) { - tooltip.add(Component.translatable("item.antarchy.floppy_disk.tooltip")); - ResourceLocation id = stack.get(AntarchyObjects.FLOPPY_DISK_COMPONENT.get()); - if (id != null) { - var disk = com.craisinlord.antarchy.content.guide.ComputerGuideData.disk(id); - if (disk != null) { - tooltip.add(Component.translatable("guide.antarchy.category." + disk.category())); - } - } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalGuardianSwordAbilities.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalGuardianSwordAbilities.java index 16615bd9..9661d1bd 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalGuardianSwordAbilities.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalGuardianSwordAbilities.java @@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.item; import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.content.effect.CommandedEntityAccess; +import com.craisinlord.antarchy.content.AntarchySoundEvents; import com.craisinlord.antarchy.content.entity.royal.RoyalIceSpikeEntity; import java.util.HashSet; import java.util.Set; @@ -126,21 +127,23 @@ public final class RoyalGuardianSwordAbilities { if (forward.lengthSqr() < 1.0E-5D) forward = new Vec3(0.0D, 0.0D, 1.0D); forward = forward.normalize(); Vec3 side = new Vec3(-forward.z, 0.0D, forward.x); - for (int distanceStep = 1; distanceStep <= 4; distanceStep++) { - double distance = 2.5D + distanceStep * 2.7D; + for (int distanceStep = 1; distanceStep <= 5; distanceStep++) { + double distance = distanceStep * 4.0D; for (int lane = -1; lane <= 1; lane++) { - Vec3 point = primary.position().add(forward.scale(distance)).add(side.scale(lane * 1.8D)); - BlockPos surface = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, BlockPos.containing(point)); - Vec3 spawn = new Vec3(point.x, surface.getY() + 0.1D, point.z); + Vec3 point = owner.position().add(forward.scale(distance)) + .add(side.scale(lane * (distanceStep % 2 == 0 ? 2.0D : 0.0D))); + int groundY = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, + net.minecraft.util.Mth.floor(point.x), net.minecraft.util.Mth.floor(point.z)); + Vec3 spawn = new Vec3(point.x, groundY + 0.1D, point.z); RoyalIceSpikeEntity spike = RoyalIceSpikeEntity.create(level, spawn, owner, - (float) AntarchySettings.royalGuardianSwordFrostSpikeDamage(), 1.55D, 100, 60, 1); + (float) AntarchySettings.royalGuardianSwordFrostSpikeDamage(), 2.2D, 100, 0, 0); level.addFreshEntity(spike); } } Vec3 center = primary.position().add(0.0D, 0.8D, 0.0D); level.sendParticles(ParticleTypes.SNOWFLAKE, center.x, center.y, center.z, 70, 1.3D, 1.1D, 1.3D, 0.1D); level.sendParticles(ParticleTypes.SNOWFLAKE, owner.getX(), owner.getY() + 1.0D, owner.getZ(), 18, 0.7D, 0.5D, 0.7D, 0.04D); - level.playSound(null, primary.blockPosition(), SoundEvents.GLASS_BREAK, SoundSource.PLAYERS, 1.0F, 0.6F); + level.playSound(null, primary.blockPosition(), AntarchySoundEvents.KING_ICE_SPIKES.get(), SoundSource.PLAYERS, 0.8F, 0.95F); } private static void chainLightning(ServerLevel level, Player owner, LivingEntity primary) { @@ -187,9 +190,10 @@ public final class RoyalGuardianSwordAbilities { double length = direction.length(); if (length > 0.001D) { Vec3 step = direction.scale(1.0D / length); - for (double d = 0.0D; d < length; d += 1.2D) { + for (double d = 0.0D; d < length; d += 0.35D) { Vec3 point = start.add(step.scale(d)); - level.sendParticles(STORM_GOLD, point.x, point.y, point.z, 2, 0.08D, 0.08D, 0.08D, 0.04D); + level.sendParticles(ParticleTypes.ELECTRIC_SPARK, point.x, point.y, point.z, 1, 0.025D, 0.025D, 0.025D, 0.0D); + level.sendParticles(STORM_GOLD, point.x, point.y, point.z, 1, 0.025D, 0.025D, 0.025D, 0.0D); } } level.sendParticles(ParticleTypes.ELECTRIC_SPARK, end.x, end.y, end.z, 18, 0.25D, 0.4D, 0.25D, 0.08D); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalGuardianSwordItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalGuardianSwordItem.java index d56f9d1a..1637ee43 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalGuardianSwordItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/RoyalGuardianSwordItem.java @@ -77,11 +77,11 @@ public class RoyalGuardianSwordItem extends SwordItem implements GeoItem { public InteractionResultHolder use(net.minecraft.world.level.Level level, Player player, InteractionHand hand) { ItemStack stack = player.getItemInHand(hand); if (!player.isShiftKeyDown()) return InteractionResultHolder.pass(stack); - cycleMode(level, player, stack); - return InteractionResultHolder.sidedSuccess(stack, level.isClientSide); + return InteractionResultHolder.pass(stack); } - private void cycleMode(net.minecraft.world.level.Level level, Player player, ItemStack stack) { + public boolean tryCycleModeFromInput(net.minecraft.world.level.Level level, Player player, ItemStack stack) { + if (player.getMainHandItem() != stack) return false; Mode next = getMode(stack).next(); setMode(stack, next); if (!level.isClientSide) { @@ -89,6 +89,7 @@ public class RoyalGuardianSwordItem extends SwordItem implements GeoItem { Component.translatable(next.translationKey)), true); level.playSound(null, player.blockPosition(), SoundEvents.AMETHYST_BLOCK_CHIME, SoundSource.PLAYERS, 0.8F, 0.8F + next.ordinal() * 0.2F); } + return true; } private static void setMode(ItemStack stack, Mode mode) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameNetworkHandler.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameNetworkHandler.java new file mode 100644 index 00000000..bea035cb --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameNetworkHandler.java @@ -0,0 +1,121 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.config.AntarchySettings; +import com.craisinlord.antarchy.content.computer.AntarchyGameSavedData; +import com.craisinlord.antarchy.content.computer.blockle.BlockleAnswers; +import com.craisinlord.antarchy.content.computer.blockle.BlockleDictionary; +import com.craisinlord.antarchy.content.computer.blockle.BlockleGame; +import java.util.function.BiConsumer; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; + +/** Handles Antarchy-specific game progress sent by AntOS game extensions. */ +public final class AntarchyGameNetworkHandler { + private static BiConsumer resultSender = (player, result) -> { }; + + private AntarchyGameNetworkHandler() { } + + public static void setResultSender(BiConsumer sender) { + resultSender = sender; + } + + public static void handle(ServerPlayer player, AntarchyGamePayload payload) { + BlockPos pos = payload.pos(); + if (payload.value().length() > 65536 || player.level().isClientSide || !player.serverLevel().hasChunkAt(pos) + || player.distanceToSqr(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D) > 64.0D) return; + + var blockEntity = player.serverLevel().getBlockEntity(pos); + if (!(blockEntity instanceof com.craisinlord.antos.content.block.entity.ComputerBlockEntity computer) + || !computer.canUseFileSystem(player)) { + send(player, payload, false, "", "unauthorized"); + return; + } + String key = AntarchyGameSavedData.key(player.serverLevel().dimension().location(), pos); + AntarchyGameSavedData.GameState state = AntarchyGameSavedData.get(player.server, key); + switch (payload.action()) { + case AntarchyGamePayload.BASILISK_STATE -> basilisk(player, payload, state); + case AntarchyGamePayload.ANTMAN_STATE -> antman(player, payload, state); + case AntarchyGamePayload.BLOCKLE_STATE -> blockle(player, computer, payload, state, false); + case AntarchyGamePayload.BLOCKLE_GUESS -> blockle(player, computer, payload, state, true); + default -> send(player, payload, false, "", "invalid"); + } + } + + private static void basilisk(ServerPlayer player, AntarchyGamePayload payload, AntarchyGameSavedData.GameState state) { + if (!payload.value().isBlank()) { + Integer score = parseScore(payload.value()); + if (score == null) { send(player, payload, false, "", "invalid_score"); return; } + state.basiliskScore = score; + state.basiliskHighScore = Math.max(state.basiliskHighScore, score); + AntarchyGameSavedData.changed(player.server); + } + send(player, payload, true, state.basiliskScore + "\0" + state.basiliskHighScore, ""); + } + + private static void antman(ServerPlayer player, AntarchyGamePayload payload, AntarchyGameSavedData.GameState state) { + if (!payload.value().isBlank()) { + Integer score = parseScore(payload.value()); + if (score == null) { send(player, payload, false, "", "invalid_score"); return; } + state.antmanScore = score; + state.antmanHighScore = Math.max(state.antmanHighScore, score); + AntarchyGameSavedData.changed(player.server); + } + send(player, payload, true, state.antmanScore + "\0" + state.antmanHighScore, ""); + } + + private static Integer parseScore(String value) { + try { return Math.min(1_000_000, Math.max(0, Integer.parseInt(value))); } + catch (NumberFormatException ignored) { return null; } + } + + private static void blockle(ServerPlayer player, + com.craisinlord.antos.content.block.entity.ComputerBlockEntity computer, + AntarchyGamePayload payload, AntarchyGameSavedData.GameState state, boolean submit) { + ResourceLocation disk = ResourceLocation.fromNamespaceAndPath("antarchy", "blockle_game"); + if (!AntarchySettings.unlockAllArchives() && !computer.diskIds().contains(disk)) { + send(player, payload, false, "", "not_installed"); return; + } + ServerLevel overworld = player.server.getLevel(Level.OVERWORLD); + if (overworld == null) { send(player, payload, false, "", "overworld_unavailable"); return; } + long day = overworld.getDayTime() / 24000L; + if (state.blockleDay != day) { + state.blockleDay = day; + state.blockleGuesses.clear(); + AntarchyGameSavedData.changed(player.server); + } + BlockleAnswers.Answer answer = BlockleAnswers.answerForDay(day); + if (answer == null) { send(player, payload, false, "", "answers_unavailable"); return; } + if (submit) { + String guess = payload.value().toLowerCase(java.util.Locale.ROOT); + if (guess.length() != 5 || !guess.chars().allMatch(value -> value >= 'a' && value <= 'z') || !BlockleDictionary.contains(guess)) { + send(player, payload, false, "", "invalid_word"); return; + } + if (state.blockleGuesses.size() >= 6 || state.blockleGuesses.contains(answer.word())) { + send(player, payload, false, "", "game_over"); return; + } + state.blockleGuesses.add(guess); + AntarchyGameSavedData.changed(player.server); + } + send(player, payload, true, encodeBlockle(day, answer, state.blockleGuesses), ""); + } + + private static String encodeBlockle(long day, BlockleAnswers.Answer answer, java.util.List guesses) { + StringBuilder result = new StringBuilder(Long.toString(day)); + boolean solved = false; + for (String guess : guesses) { + result.append('|').append(guess).append(',').append(BlockleGame.evaluate(answer.word(), guess)); + if (guess.equals(answer.word())) solved = true; + } + if (solved || guesses.size() >= 6) result.append('|').append(solved ? "SOLVED" : "FAILED").append('|').append(answer.word()).append('|').append(answer.itemId()); + else result.append('|').append("PLAYING"); + return result.toString(); + } + + private static void send(ServerPlayer player, AntarchyGamePayload payload, boolean success, String data, String error) { + resultSender.accept(player, new AntarchyGameResultPayload(payload.pos(), success, + payload.action() + "\0" + error + "\0" + data)); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameNetworking.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameNetworking.java new file mode 100644 index 00000000..82172cee --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameNetworking.java @@ -0,0 +1,20 @@ +package com.craisinlord.antarchy.content.network; + +import net.minecraft.core.BlockPos; +import java.util.function.Consumer; + +/** Small Antarchy game-progress bridge over the AntOS computer session. */ +public final class AntarchyGameNetworking { + private static Consumer sender = payload -> { }; + private AntarchyGameNetworking() { } + + public static void setSender(Consumer sender) { AntarchyGameNetworking.sender = sender; } + public static void requestBasiliskState(BlockPos pos) { send(pos, AntarchyGamePayload.BASILISK_STATE, ""); } + public static void saveBasiliskScore(BlockPos pos, int score) { send(pos, AntarchyGamePayload.BASILISK_STATE, Integer.toString(Math.max(0, score))); } + public static void requestAntmanState(BlockPos pos) { send(pos, AntarchyGamePayload.ANTMAN_STATE, ""); } + public static void saveAntmanScore(BlockPos pos, int score) { send(pos, AntarchyGamePayload.ANTMAN_STATE, Integer.toString(Math.max(0, score))); } + public static void requestBlockleState(BlockPos pos) { send(pos, AntarchyGamePayload.BLOCKLE_STATE, ""); } + public static void submitBlockleGuess(BlockPos pos, String guess) { send(pos, AntarchyGamePayload.BLOCKLE_GUESS, guess); } + + private static void send(BlockPos pos, int action, String value) { sender.accept(new AntarchyGamePayload(pos, action, value)); } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGamePayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGamePayload.java new file mode 100644 index 00000000..da300daf --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGamePayload.java @@ -0,0 +1,32 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.Antarchy; +import net.minecraft.core.BlockPos; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record AntarchyGamePayload(BlockPos pos, int action, String value) implements CustomPacketPayload { + public static final int BASILISK_STATE = 16; + public static final int ANTMAN_STATE = 17; + public static final int BLOCKLE_STATE = 18; + public static final int BLOCKLE_GUESS = 19; + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antarchy_game")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, AntarchyGamePayload::pos, + ByteBufCodecs.VAR_INT, AntarchyGamePayload::action, + ByteBufCodecs.stringUtf8(65536), AntarchyGamePayload::value, + AntarchyGamePayload::new + ); + + public AntarchyGamePayload(BlockPos pos, int action) { + this(pos, action, ""); + } + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameResultPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameResultPayload.java new file mode 100644 index 00000000..e1271351 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntarchyGameResultPayload.java @@ -0,0 +1,24 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.Antarchy; +import net.minecraft.core.BlockPos; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record AntarchyGameResultPayload(BlockPos pos, boolean success, String data) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antarchy_game_result")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, AntarchyGameResultPayload::pos, + ByteBufCodecs.BOOL, AntarchyGameResultPayload::success, + ByteBufCodecs.stringUtf8(65536), AntarchyGameResultPayload::data, + AntarchyGameResultPayload::new + ); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailDeletePayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailDeletePayload.java deleted file mode 100644 index a2dceb8d..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailDeletePayload.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record AntmailDeletePayload(BlockPos pos, String messageId, boolean sent) implements CustomPacketPayload { - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_delete")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailDeletePayload::pos, ByteBufCodecs.stringUtf8(64), AntmailDeletePayload::messageId, ByteBufCodecs.BOOL, AntmailDeletePayload::sent, AntmailDeletePayload::new); - @Override public Type type() { return TYPE; } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailDraftPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailDraftPayload.java deleted file mode 100644 index dde362ef..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailDraftPayload.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record AntmailDraftPayload(BlockPos pos, int action, String data) implements CustomPacketPayload { - public static final int SAVE = 0; - public static final int DELETE = 1; - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_draft")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailDraftPayload::pos, ByteBufCodecs.VAR_INT, AntmailDraftPayload::action, ByteBufCodecs.stringUtf8(65536), AntmailDraftPayload::data, AntmailDraftPayload::new); - @Override public Type type() { return TYPE; } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailMessageRequestPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailMessageRequestPayload.java deleted file mode 100644 index 366c07c4..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailMessageRequestPayload.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record AntmailMessageRequestPayload(BlockPos pos, String messageId) implements CustomPacketPayload { - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_message_request")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, AntmailMessageRequestPayload::pos, - ByteBufCodecs.stringUtf8(64), AntmailMessageRequestPayload::messageId, - AntmailMessageRequestPayload::new - ); - - @Override - public Type type() { - return TYPE; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailNetworking.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailNetworking.java deleted file mode 100644 index ff9a0cc6..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailNetworking.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.content.antmail.AntmailAttachment; -import com.craisinlord.antarchy.content.antmail.AntmailDebug; -import com.craisinlord.antarchy.content.antmail.AntmailDraft; -import com.craisinlord.antarchy.content.antmail.AntmailWire; -import net.minecraft.core.BlockPos; - -import java.util.List; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -public final class AntmailNetworking { - private static Consumer sender = payload -> { }; - private static volatile boolean senderConfigured; - private static final AtomicBoolean senderErrorLogged = new AtomicBoolean(); - - private AntmailNetworking() { - } - - public static void setSender(Consumer sender) { - AntmailNetworking.sender = sender; - senderConfigured = sender != null; - } - - public static void setup(BlockPos pos, String username) { - AntmailDebug.log("C2S setup request pos=" + pos + " usernameLength=" + username.length()); - sendPayload(new AntmailSetupPayload(pos, username)); - } - - public static void requestState(BlockPos pos) { - requestState(pos, AntmailStateRequestPayload.INBOX, 0, 0L); - } - - public static void requestState(BlockPos pos, int folder, int page) { - requestState(pos, folder, page, 0L); - } - - public static void requestState(BlockPos pos, int folder, int page, long knownVersion) { - AntmailDebug.log("C2S state request pos=" + pos + " folder=" + folder + " page=" + page + " knownVersion=" + knownVersion); - sendPayload(new AntmailStateRequestPayload(pos, folder, page, knownVersion)); - } - - public static void requestMessage(BlockPos pos, UUID messageId) { - sendPayload(new AntmailMessageRequestPayload(pos, messageId.toString())); - } - - public static void send(BlockPos pos, String recipient, String subject, String body, List attachments) { - sendPayload(new AntmailSendPayload(pos, recipient, subject, body, AntmailWire.encodeAttachments(attachments))); - } - - public static void markRead(BlockPos pos, UUID messageId) { - sendPayload(new AntmailReadPayload(pos, messageId.toString(), true)); - } - - public static void markUnread(BlockPos pos, UUID messageId) { - sendPayload(new AntmailReadPayload(pos, messageId.toString(), false)); - } - - public static void delete(BlockPos pos, UUID messageId, boolean sent) { - sendPayload(new AntmailDeletePayload(pos, messageId.toString(), sent)); - } - - public static void saveDraft(BlockPos pos, AntmailDraft draft) { - sendPayload(new AntmailDraftPayload(pos, AntmailDraftPayload.SAVE, AntmailWire.encodeTag(draft.toTag()))); - } - - public static void deleteDraft(BlockPos pos, UUID draftId) { - sendPayload(new AntmailDraftPayload(pos, AntmailDraftPayload.DELETE, draftId.toString())); - } - - public static void retry(BlockPos pos, UUID messageId) { - sendPayload(new AntmailRetryPayload(pos, messageId.toString())); - } - - private static void sendPayload(Object payload) { - if (!senderConfigured) { - if (senderErrorLogged.compareAndSet(false, true)) AntmailDebug.error("Network sender is not configured; dropping " + payload.getClass().getSimpleName(), new IllegalStateException("AntmailNetworking.setSender was not called")); - } - else sender.accept(payload); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailPayloadTypes.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailPayloadTypes.java deleted file mode 100644 index db991df6..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailPayloadTypes.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - -import java.util.List; - -public final class AntmailPayloadTypes { - private AntmailPayloadTypes() { - } - - public static List> all() { - return List.of( - AntmailSetupPayload.TYPE, - AntmailStateRequestPayload.TYPE, - AntmailMessageRequestPayload.TYPE, - AntmailSendPayload.TYPE, - AntmailReadPayload.TYPE, - AntmailDeletePayload.TYPE, - AntmailDraftPayload.TYPE, - AntmailRetryPayload.TYPE, - AntmailResultPayload.TYPE - ); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailReadPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailReadPayload.java deleted file mode 100644 index 017e7f6c..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailReadPayload.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record AntmailReadPayload(BlockPos pos, String messageId, boolean read) implements CustomPacketPayload { - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_read")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailReadPayload::pos, ByteBufCodecs.stringUtf8(64), AntmailReadPayload::messageId, ByteBufCodecs.BOOL, AntmailReadPayload::read, AntmailReadPayload::new); - @Override public Type type() { return TYPE; } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailResultPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailResultPayload.java deleted file mode 100644 index 8a4860cc..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailResultPayload.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record AntmailResultPayload(BlockPos pos, int status, String address, String messageId, String detail, String data, long version) implements CustomPacketPayload { - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_result")); - public static final StreamCodec STREAM_CODEC = StreamCodec.of( - (buf, payload) -> { - BlockPos.STREAM_CODEC.encode(buf, payload.pos()); - ByteBufCodecs.VAR_INT.encode(buf, payload.status()); - ByteBufCodecs.stringUtf8(64).encode(buf, payload.address()); - ByteBufCodecs.stringUtf8(64).encode(buf, payload.messageId()); - ByteBufCodecs.stringUtf8(128).encode(buf, payload.detail()); - ByteBufCodecs.stringUtf8(65536).encode(buf, payload.data()); - ByteBufCodecs.VAR_LONG.encode(buf, payload.version()); - }, - buf -> new AntmailResultPayload( - BlockPos.STREAM_CODEC.decode(buf), - ByteBufCodecs.VAR_INT.decode(buf), - ByteBufCodecs.stringUtf8(64).decode(buf), - ByteBufCodecs.stringUtf8(64).decode(buf), - ByteBufCodecs.stringUtf8(128).decode(buf), - ByteBufCodecs.stringUtf8(65536).decode(buf), - ByteBufCodecs.VAR_LONG.decode(buf) - )); - @Override public Type type() { return TYPE; } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailRetryPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailRetryPayload.java deleted file mode 100644 index 71c9d724..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailRetryPayload.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record AntmailRetryPayload(BlockPos pos, String messageId) implements CustomPacketPayload { - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_retry")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailRetryPayload::pos, ByteBufCodecs.stringUtf8(64), AntmailRetryPayload::messageId, AntmailRetryPayload::new); - @Override public Type type() { return TYPE; } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailSendPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailSendPayload.java deleted file mode 100644 index a34221b2..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailSendPayload.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record AntmailSendPayload(BlockPos pos, String recipient, String subject, String body, String attachments) implements CustomPacketPayload { - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_send")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailSendPayload::pos, ByteBufCodecs.stringUtf8(64), AntmailSendPayload::recipient, ByteBufCodecs.stringUtf8(64), AntmailSendPayload::subject, ByteBufCodecs.stringUtf8(16384), AntmailSendPayload::body, ByteBufCodecs.stringUtf8(65536), AntmailSendPayload::attachments, AntmailSendPayload::new); - @Override public Type type() { return TYPE; } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailServerHandler.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailServerHandler.java deleted file mode 100644 index 5230e6c6..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailServerHandler.java +++ /dev/null @@ -1,266 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.content.antmail.AntmailAddress; -import com.craisinlord.antarchy.content.antmail.AntmailDebug; -import com.craisinlord.antarchy.content.antmail.AntmailAttachment; -import com.craisinlord.antarchy.content.antmail.AntmailDeliveryResult; -import com.craisinlord.antarchy.content.antmail.AntmailDraft; -import com.craisinlord.antarchy.content.antmail.AntmailMessage; -import com.craisinlord.antarchy.content.antmail.AntmailMailbox; -import com.craisinlord.antarchy.content.antmail.AntmailServerData; -import com.craisinlord.antarchy.content.antmail.AntmailValidation; -import com.craisinlord.antarchy.content.antmail.AntmailWire; -import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.block.entity.BlockEntity; - -import java.util.List; -import java.util.UUID; -import java.util.function.BiConsumer; - -public final class AntmailServerHandler { - private static BiConsumer resultSender = (player, payload) -> { }; - private static volatile boolean resultSenderConfigured; - - private AntmailServerHandler() { - } - - public static void setResultSender(BiConsumer sender) { - resultSender = sender; - resultSenderConfigured = sender != null; - } - - public static void handle(ServerPlayer player, AntmailSetupPayload payload) { - AntmailDebug.log("C2S setup received player=" + player.getGameProfile().getName() + " pos=" + payload.pos() - + " usernameLength=" + payload.username().length()); - ComputerBlockEntity computer = computer(player, payload.pos()); - if (computer == null || !computer.canUseFileSystem(player)) { - AntmailDebug.log("setup rejected: computer missing, out of reach, unauthenticated, or owned by another user"); - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", "", "registration_failed:unauthorized", ""); - return; - } - AntmailServerData data = AntmailServerData.access(player.server); - AntmailServerData.Registration registration = data.register(player.server, player.serverLevel(), payload.pos(), payload.username()); - if (registration.valid()) data.setLastUsedAddress(player, registration.address()); - String mailbox = registration.valid() ? AntmailWire.encodeTag(data.mailboxOrCreate(registration.address()).toTag(false)) : ""; - String detail = registration.valid() ? "registration_success" : "registration_failed:" + registration.reason(); - result(player, payload.pos(), registration.valid() ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), registration.valid() ? registration.address().fullAddress() : "", "", detail, mailbox); - } - - public static void handle(ServerPlayer player, AntmailStateRequestPayload payload) { - AntmailDebug.log("state request received player=" + player.getGameProfile().getName() + " pos=" + payload.pos() - + " folder=" + payload.folder() + " page=" + payload.page() + " knownVersion=" + payload.knownVersion()); - ComputerBlockEntity computer = authorizedComputer(player, payload.pos()); - if (computer == null) { - AntmailDebug.log("state request rejected: computer missing, out of reach, unauthenticated, or owned by another user"); - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", "", "unauthorized", ""); - return; - } - AntmailServerData data = AntmailServerData.access(player.server); - data.drainAddress(player.server, player.serverLevel(), payload.pos()); - AntmailAddress address = data.addressAt(player.serverLevel().dimension().location(), payload.pos()); - if (address == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", "", "unconfigured", ""); - return; - } - AntmailMailbox mailbox = data.mailboxOrCreate(address); - if (payload.knownVersion() == data.mailboxVersion(address)) { - result(player, payload.pos(), AntmailDeliveryResult.Status.DELIVERED.ordinal(), address.fullAddress(), "", "unchanged", ""); - return; - } - int folder = payload.folder() < AntmailStateRequestPayload.INBOX || payload.folder() > AntmailStateRequestPayload.DRAFTS - ? AntmailStateRequestPayload.INBOX : payload.folder(); - int page = Math.max(0, Math.min(payload.page(), AntmailValidation.MAX_MAILBOX_MESSAGES / AntmailMailbox.PAGE_SIZE)); - try { - String encoded = AntmailWire.encodeTag(mailbox.toPageTag(folder, page)); - result(player, payload.pos(), AntmailDeliveryResult.Status.DELIVERED.ordinal(), address.fullAddress(), "", "", encoded); - } catch (RuntimeException exception) { - AntmailDebug.error("Failed to encode state for " + address.fullAddress() + " at " + payload.pos(), exception); - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), "", "state_encode_failed", ""); - } - } - - public static void handle(ServerPlayer player, AntmailMessageRequestPayload payload) { - ComputerBlockEntity computer = authorizedComputer(player, payload.pos()); - if (computer == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", payload.messageId(), "unauthorized", ""); - return; - } - AntmailServerData data = AntmailServerData.access(player.server); - AntmailAddress address = data.addressAt(player.serverLevel().dimension().location(), payload.pos()); - if (address == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", payload.messageId(), "unconfigured", ""); - return; - } - try { - UUID id = UUID.fromString(payload.messageId()); - AntmailMailbox mailbox = data.mailboxOrCreate(address); - AntmailMessage message = mailbox.findInbox(id); - if (message == null) message = mailbox.findSent(id); - if (message == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), "not_found", ""); - return; - } - result(player, payload.pos(), AntmailDeliveryResult.Status.DELIVERED.ordinal(), address.fullAddress(), payload.messageId(), "message_detail", AntmailWire.encodeTag(message.toTag())); - } catch (IllegalArgumentException exception) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), "invalid_id", ""); - } - } - - public static void handle(ServerPlayer player, AntmailSendPayload payload) { - ComputerBlockEntity computer = authorizedComputer(player, payload.pos()); - if (computer == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", "", "unauthorized", ""); - return; - } - AntmailServerData data = AntmailServerData.access(player.server); - AntmailAddress sender = data.addressAt(player.serverLevel().dimension().location(), payload.pos()); - if (sender == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", "", "unconfigured", ""); - return; - } - try { - AntmailAddress recipient = AntmailAddress.parse(payload.recipient()); - List attachments = AntmailWire.decodeAttachments(payload.attachments()); - AntmailMessage message = AntmailMessage.create(sender, recipient, payload.subject(), payload.body(), player.serverLevel().getGameTime(), attachments); - AntmailDeliveryResult delivery = data.deliver(player.server, message); - result(player, payload.pos(), delivery.status().ordinal(), sender.fullAddress(), message.id().toString(), delivery.detail(), AntmailWire.encodeTag(data.mailboxOrCreate(sender).toTag(false))); - } catch (RuntimeException exception) { - result(player, payload.pos(), AntmailDeliveryResult.Status.MESSAGE_INVALID.ordinal(), "", "", "invalid_message", ""); - } - } - - public static void handle(ServerPlayer player, AntmailReadPayload payload) { - ComputerBlockEntity computer = authorizedComputer(player, payload.pos()); - if (computer == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", payload.messageId(), "unauthorized", ""); - return; - } - AntmailServerData data = AntmailServerData.access(player.server); - AntmailAddress address = data.addressAt(player.serverLevel().dimension().location(), payload.pos()); - if (address == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", payload.messageId(), "unconfigured", ""); - return; - } - try { - UUID id = UUID.fromString(payload.messageId()); - AntmailMailbox mailbox = data.mailboxOrCreate(address); - boolean changed = payload.read() ? mailbox.markRead(id) : markUnread(mailbox, id); - if (changed) data.touchMailbox(address); - result(player, payload.pos(), changed ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), changed ? "" : "not_found", AntmailWire.encodeTag(mailbox.toTag(false))); - } catch (IllegalArgumentException exception) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), "invalid_id", ""); - } - } - - public static void handle(ServerPlayer player, AntmailDeletePayload payload) { - ComputerBlockEntity computer = authorizedComputer(player, payload.pos()); - if (computer == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", payload.messageId(), "unauthorized", ""); - return; - } - AntmailServerData data = AntmailServerData.access(player.server); - AntmailAddress address = data.addressAt(player.serverLevel().dimension().location(), payload.pos()); - if (address == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", payload.messageId(), "unconfigured", ""); - return; - } - try { - UUID id = UUID.fromString(payload.messageId()); - AntmailMailbox mailbox = data.mailboxOrCreate(address); - boolean changed = payload.sent() ? mailbox.removeSent(id) : mailbox.removeInbox(id); - if (changed) data.touchMailbox(address); - result(player, payload.pos(), changed ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), changed ? "" : "not_found", AntmailWire.encodeTag(mailbox.toTag(false))); - } catch (IllegalArgumentException exception) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), "invalid_id", ""); - } - } - - public static void handle(ServerPlayer player, AntmailDraftPayload payload) { - ComputerBlockEntity computer = authorizedComputer(player, payload.pos()); - if (computer == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", "", "unauthorized", ""); - return; - } - AntmailServerData data = AntmailServerData.access(player.server); - AntmailAddress address = data.addressAt(player.serverLevel().dimension().location(), payload.pos()); - if (address == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", "", "unconfigured", ""); - return; - } - AntmailMailbox mailbox = data.mailboxOrCreate(address); - try { - if (payload.action() == AntmailDraftPayload.SAVE) { - AntmailDraft draft = AntmailDraft.fromTag(AntmailWire.decodeTag(payload.data())); - AntmailDraft stored = new AntmailDraft(draft.id(), address, draft.recipient(), draft.subject(), draft.body(), draft.attachments()); - if (!mailbox.saveDraft(stored)) throw new IllegalArgumentException("draft_limit"); - } else if (payload.action() == AntmailDraftPayload.DELETE) { - if (!mailbox.removeDraft(UUID.fromString(payload.data()))) throw new IllegalArgumentException("not_found"); - } else { - throw new IllegalArgumentException("invalid_action"); - } - data.touchMailbox(address); - result(player, payload.pos(), AntmailDeliveryResult.Status.DELIVERED.ordinal(), address.fullAddress(), "", "draft_saved", AntmailWire.encodeTag(mailbox.toTag(false))); - } catch (RuntimeException exception) { - result(player, payload.pos(), AntmailDeliveryResult.Status.MESSAGE_INVALID.ordinal(), address.fullAddress(), "", "draft_failed", AntmailWire.encodeTag(mailbox.toTag(false))); - } - } - - public static void handle(ServerPlayer player, AntmailRetryPayload payload) { - ComputerBlockEntity computer = authorizedComputer(player, payload.pos()); - if (computer == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", payload.messageId(), "unauthorized", ""); - return; - } - AntmailServerData data = AntmailServerData.access(player.server); - AntmailAddress sender = data.addressAt(player.serverLevel().dimension().location(), payload.pos()); - if (sender == null) { - result(player, payload.pos(), AntmailDeliveryResult.Status.ADDRESS_NOT_FOUND.ordinal(), "", payload.messageId(), "unconfigured", ""); - return; - } - try { - AntmailDeliveryResult delivery = data.retry(player.server, sender, UUID.fromString(payload.messageId())); - AntmailMailbox mailbox = data.mailboxOrCreate(sender); - result(player, payload.pos(), delivery == null ? AntmailDeliveryResult.Status.MESSAGE_INVALID.ordinal() : delivery.status().ordinal(), sender.fullAddress(), payload.messageId(), delivery == null ? "not_found" : delivery.detail(), AntmailWire.encodeTag(mailbox.toTag(false))); - } catch (IllegalArgumentException exception) { - result(player, payload.pos(), AntmailDeliveryResult.Status.MESSAGE_INVALID.ordinal(), sender.fullAddress(), payload.messageId(), "invalid_id", ""); - } - } - - private static ComputerBlockEntity authorizedComputer(ServerPlayer player, BlockPos pos) { - ComputerBlockEntity computer = computer(player, pos); - return computer != null && computer.canUseFileSystem(player) ? computer : null; - } - - private static boolean markUnread(AntmailMailbox mailbox, UUID id) { - for (com.craisinlord.antarchy.content.antmail.AntmailMessage message : mailbox.inbox()) { - if (message.id().equals(id)) { - message.markUnread(); - return true; - } - } - return false; - } - - private static ComputerBlockEntity computer(ServerPlayer player, BlockPos pos) { - if (!player.serverLevel().hasChunkAt(pos) || player.distanceToSqr(pos.getX() + .5D, pos.getY() + .5D, pos.getZ() + .5D) > 64D) return null; - BlockEntity blockEntity = player.serverLevel().getBlockEntity(pos); - return blockEntity instanceof ComputerBlockEntity computer ? computer : null; - } - - private static void result(ServerPlayer player, BlockPos pos, int status, String address, String messageId, String detail, String data) { - long version = 0L; - try { - if (!address.isBlank()) version = AntmailServerData.access(player.server).mailboxVersion(AntmailAddress.parse(address)); - } catch (IllegalArgumentException ignored) { - } - AntmailDebug.log("sending result player=" + player.getGameProfile().getName() + " pos=" + pos + " status=" + status - + " detail=" + detail + " addressPresent=" + !address.isBlank() + " dataChars=" + data.length() + " version=" + version); - if (!resultSenderConfigured) { - AntmailDebug.error("Result sender is not configured; dropping response at " + pos, new IllegalStateException("AntmailServerHandler.setResultSender was not called")); - return; - } - resultSender.accept(player, new AntmailResultPayload(pos, status, address, messageId, detail, data, version)); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailSetupPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailSetupPayload.java deleted file mode 100644 index 8c93d6ce..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailSetupPayload.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record AntmailSetupPayload(BlockPos pos, String username) implements CustomPacketPayload { - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_setup")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite(BlockPos.STREAM_CODEC, AntmailSetupPayload::pos, ByteBufCodecs.stringUtf8(32), AntmailSetupPayload::username, AntmailSetupPayload::new); - @Override public Type type() { return TYPE; } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailStateRequestPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailStateRequestPayload.java deleted file mode 100644 index 095b54e7..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailStateRequestPayload.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record AntmailStateRequestPayload(BlockPos pos, int folder, int page, long knownVersion) implements CustomPacketPayload { - public static final int INBOX = 0; - public static final int SENT = 1; - public static final int DRAFTS = 2; - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_state_request")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, AntmailStateRequestPayload::pos, - ByteBufCodecs.VAR_INT, AntmailStateRequestPayload::folder, - ByteBufCodecs.VAR_INT, AntmailStateRequestPayload::page, - ByteBufCodecs.VAR_LONG, AntmailStateRequestPayload::knownVersion, - AntmailStateRequestPayload::new); - @Override public Type type() { return TYPE; } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessHandler.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessHandler.java deleted file mode 100644 index 7aaa3c8b..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessHandler.java +++ /dev/null @@ -1,455 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; -import com.craisinlord.antarchy.content.computer.ComputerFileSystem; -import com.craisinlord.antarchy.content.computer.terminal.TerminalCommandService; -import com.craisinlord.antarchy.content.computer.terminal.TerminalFileSystem; -import com.craisinlord.antarchy.content.computer.terminal.TerminalResult; -import com.craisinlord.antarchy.config.AntarchySettings; -import com.craisinlord.antarchy.content.antmail.AntmailWire; -import com.craisinlord.antarchy.content.computer.blockle.BlockleAnswers; -import com.craisinlord.antarchy.content.computer.blockle.BlockleDictionary; -import com.craisinlord.antarchy.content.computer.blockle.BlockleGame; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.core.registries.Registries; -import net.minecraft.tags.TagKey; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.block.entity.BlockEntity; - -import java.util.function.BiConsumer; - -public final class ComputerAccessHandler { - private static BiConsumer resultSender = (player, result) -> { - }; - - private ComputerAccessHandler() { - } - - public static void setResultSender(BiConsumer sender) { - resultSender = sender; - } - - public static void handle(ServerPlayer player, ComputerAccessPayload payload) { - if (payload.value().length() > 65536 || player.level().isClientSide || !player.serverLevel().hasChunkAt(payload.pos()) || - player.distanceToSqr(payload.pos().getX() + 0.5D, payload.pos().getY() + 0.5D, payload.pos().getZ() + 0.5D) > 64.0D) { - return; - } - BlockEntity blockEntity = player.serverLevel().getBlockEntity(payload.pos()); - if (!(blockEntity instanceof ComputerBlockEntity computer)) { - send(player, payload, ComputerAccessResultPayload.INVALID); - return; - } - switch (payload.action()) { - case ComputerAccessPayload.OPEN -> open(player, computer, payload); - case ComputerAccessPayload.SETUP -> setup(player, computer, payload); - case ComputerAccessPayload.LOGIN -> login(player, computer, payload); - case ComputerAccessPayload.LOGOUT -> { - computer.logout(); - send(player, payload, ComputerAccessResultPayload.READY); - } - case ComputerAccessPayload.CLOSE -> { - computer.releaseUser(player); - send(player, payload, computer.isAuthenticated() ? ComputerAccessResultPayload.SUCCESS : ComputerAccessResultPayload.READY); - } - case ComputerAccessPayload.CHANGE_PASSWORD -> { - boolean changed = computer.setPassword(player, payload.value()); - send(player, payload, changed ? ComputerAccessResultPayload.SUCCESS : ComputerAccessResultPayload.INVALID_PASSWORD); - } - case ComputerAccessPayload.EJECT -> eject(player, computer, payload); - case ComputerAccessPayload.FILE_LIST -> fileList(player, computer, payload); - case ComputerAccessPayload.FILE_OPEN -> fileOpen(player, computer, payload); - case ComputerAccessPayload.FILE_CREATE -> fileCreate(player, computer, payload); - case ComputerAccessPayload.FILE_SAVE -> fileSave(player, computer, payload); - case ComputerAccessPayload.FILE_DELETE -> fileDelete(player, computer, payload); - case ComputerAccessPayload.FILE_MOVE -> fileMove(player, computer, payload); - case ComputerAccessPayload.TERMINAL_COMMAND -> terminalCommand(player, computer, payload); - case ComputerAccessPayload.DESKTOP_STATE -> desktopState(player, computer, payload); - case ComputerAccessPayload.DESKTOP_WALLPAPER -> desktopWallpaper(player, computer, payload); - case ComputerAccessPayload.BASILISK_STATE -> basiliskState(player, computer, payload); - case ComputerAccessPayload.ANTMAN_STATE -> antmanState(player, computer, payload); - case ComputerAccessPayload.BLOCKLE_STATE -> blockleState(player, computer, payload, false); - case ComputerAccessPayload.BLOCKLE_GUESS -> blockleState(player, computer, payload, true); - case ComputerAccessPayload.LOCATE_STRUCTURE -> locateStructure(player, computer, payload); - default -> send(player, payload, ComputerAccessResultPayload.INVALID); - } - } - - private static void open(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (computer.hasActiveUser() && !computer.isAuthenticatedBy(player)) { - send(player, payload, ComputerAccessResultPayload.BUSY); - sendArchive(player, computer, payload); - return; - } - if (!computer.hasPassword()) { - computer.claimUser(player); - send(player, payload, ComputerAccessResultPayload.SETUP_REQUIRED); - } else if (computer.isAuthenticated()) { - computer.claimUser(player); - send(player, payload, ComputerAccessResultPayload.SUCCESS); - } else { - send(player, payload, ComputerAccessResultPayload.READY); - } - sendArchive(player, computer, payload); - } - - private static void sendArchive(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - resultSender.accept(player, new ComputerAccessResultPayload(payload.pos(), ComputerAccessResultPayload.SUCCESS, - computer.hasPassword(), computer.isAuthenticated(), ComputerAccessPayload.ARCHIVE_STATE + "\0\0" - + com.craisinlord.antarchy.content.guide.ComputerGuideData.encodeNetworkSnapshot())); - } - - private static void setup(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.hasPassword() && computer.initializePassword(player, payload.value())) { - send(player, payload, ComputerAccessResultPayload.SUCCESS); - } else { - send(player, payload, computer.hasActiveUser() ? ComputerAccessResultPayload.BUSY : ComputerAccessResultPayload.INVALID_PASSWORD); - } - } - - private static void login(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (computer.hasActiveUser() && !computer.isAuthenticatedBy(player)) { - send(player, payload, ComputerAccessResultPayload.BUSY); - } else if (computer.authenticate(player, payload.value())) { - com.craisinlord.antarchy.content.antmail.AntmailServerData data = com.craisinlord.antarchy.content.antmail.AntmailServerData.access(player.server); - net.minecraft.resources.ResourceLocation dimension = player.serverLevel().dimension().location(); - com.craisinlord.antarchy.content.antmail.AntmailAddress address = data.addressAt(dimension, computer.getBlockPos()); - if (address != null) data.setLastUsedAddress(player, address); - send(player, payload, ComputerAccessResultPayload.SUCCESS); - } else { - send(player, payload, ComputerAccessResultPayload.INVALID_PASSWORD); - } - } - - private static void eject(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - try { - ResourceLocation diskId = ResourceLocation.parse(payload.value()); - boolean ejected = computer.ejectOne(player, diskId); - send(player, payload, ejected ? ComputerAccessResultPayload.SUCCESS : ComputerAccessResultPayload.INVALID); - } catch (Exception ignored) { - send(player, payload, ComputerAccessResultPayload.INVALID); - } - } - - private static void fileList(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - StringBuilder data = new StringBuilder(); - for (ComputerFileSystem.ComputerFile file : computer.fileSystem().list()) { - if (!data.isEmpty()) data.append('\n'); - data.append(file.type().name()).append('\t').append(file.path()).append('\t').append(file.contents().length()); - } - sendFile(player, payload, true, data.toString(), ""); - } - - private static void fileOpen(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - ComputerFileSystem.ComputerFile file = computer.fileSystem().get(payload.value()); - if (file == null) { - sendFile(player, payload, false, "", "not_found"); - } else if (file.type() != ComputerFileSystem.ComputerFile.Type.TEXT && file.type() != ComputerFileSystem.ComputerFile.Type.IMAGE) { - sendFile(player, payload, false, "", "not_file"); - } else { - sendFile(player, payload, true, file.path() + "\0" + file.contents(), ""); - } - } - - private static void fileCreate(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - String[] request = splitFileRequest(payload.value()); - if (request == null) { - sendFile(player, payload, false, "", "invalid_request"); - return; - } - ComputerFileSystem.Result result = computer.fileSystem().createTextFile(request[0], request[1]); - if (result.successful()) computer.setChanged(); - sendFile(player, payload, result.successful(), request[0], result.error()); - } - - private static void fileSave(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - String[] request = splitFileRequest(payload.value()); - if (request == null) { - sendFile(player, payload, false, "", "invalid_request"); - return; - } - ComputerFileSystem.Result result = computer.fileSystem().writeTextFile(request[0], request[1]); - if (result.successful()) computer.setChanged(); - sendFile(player, payload, result.successful(), request[0], result.error()); - } - - private static void fileDelete(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - ComputerFileSystem.Result result = computer.fileSystem().delete(payload.value()); - if (result.successful()) computer.setChanged(); - sendFile(player, payload, result.successful(), payload.value(), result.error()); - } - - private static void fileMove(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - String[] request = splitFileRequest(payload.value()); - if (request == null) { - sendFile(player, payload, false, "", "invalid_request"); - return; - } - ComputerFileSystem.Result result = computer.fileSystem().move(request[0], request[1]); - if (result.successful()) computer.setChanged(); - sendFile(player, payload, result.successful(), request[1], result.error()); - } - - private static void terminalCommand(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - String[] request = splitFileRequest(payload.value()); - if (request == null) { - sendTerminal(player, payload, TerminalResult.error("/", "ERROR: INVALID REQUEST")); - return; - } - TerminalResult result = new TerminalCommandService().execute(new ComputerTerminalFileSystem(computer.fileSystem()), request[0], request[1]); - if (result.status() != TerminalResult.Status.SUCCESS || !result.lines().isEmpty() || result.clearOutput() || result.openPath() != null) computer.setChanged(); - sendTerminal(player, payload, result); - } - - private static void sendTerminal(ServerPlayer player, ComputerAccessPayload payload, TerminalResult result) { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - tag.putString("Directory", result.workingDirectory()); - tag.putBoolean("Clear", result.clearOutput()); - tag.putString("Open", result.openPath() == null ? "" : result.openPath()); - net.minecraft.nbt.ListTag lines = new net.minecraft.nbt.ListTag(); - for (String line : result.lines()) lines.add(net.minecraft.nbt.StringTag.valueOf(line)); - tag.put("Lines", lines); - resultSender.accept(player, new ComputerAccessResultPayload(payload.pos(), result.status() == TerminalResult.Status.ERROR ? ComputerAccessResultPayload.INVALID : ComputerAccessResultPayload.SUCCESS, - true, true, payload.action() + "\0" + AntmailWire.encodeTag(tag))); - } - - private static final class ComputerTerminalFileSystem implements TerminalFileSystem { - private final ComputerFileSystem fileSystem; - - private ComputerTerminalFileSystem(ComputerFileSystem fileSystem) { this.fileSystem = fileSystem; } - - @Override public java.util.Optional find(String path, String workingDirectory) { - ComputerFileSystem.ComputerFile file = fileSystem.get(resolve(path, workingDirectory)); - return file == null ? java.util.Optional.empty() : java.util.Optional.of(new Entry(file.path(), file.path(), file.type() == ComputerFileSystem.ComputerFile.Type.DIRECTORY ? EntryType.DIRECTORY : EntryType.FILE)); - } - @Override public java.util.List list(String path, String workingDirectory) { - String directory = resolve(path, workingDirectory); - return fileSystem.list().stream().filter(file -> { - String parent = file.path().lastIndexOf('/') <= 0 ? "/" : file.path().substring(0, file.path().lastIndexOf('/')); - return parent.equals(directory) && !file.path().equals(directory); - }).map(file -> new Entry(file.path().substring(file.path().lastIndexOf('/') + 1), file.path(), file.type() == ComputerFileSystem.ComputerFile.Type.DIRECTORY ? EntryType.DIRECTORY : EntryType.FILE)).toList(); - } - @Override public boolean createDirectory(String path, String workingDirectory) { return fileSystem.createDirectory(resolve(path, workingDirectory)).successful(); } - @Override public boolean createFile(String path, String workingDirectory) { return fileSystem.createTextFile(resolve(path, workingDirectory), "").successful(); } - @Override public java.util.Optional readText(String path, String workingDirectory) { ComputerFileSystem.ComputerFile file = fileSystem.get(resolve(path, workingDirectory)); return file != null && file.type() == ComputerFileSystem.ComputerFile.Type.TEXT ? java.util.Optional.of(file.contents()) : java.util.Optional.empty(); } - @Override public boolean writeText(String path, String workingDirectory, String contents) { return fileSystem.createOrWriteTextFile(resolve(path, workingDirectory), contents).successful(); } - @Override public boolean deleteFile(String path, String workingDirectory) { return fileSystem.delete(resolve(path, workingDirectory)).successful(); } - @Override public boolean deleteEmptyDirectory(String path, String workingDirectory) { return fileSystem.delete(resolve(path, workingDirectory)).successful(); } - @Override public boolean move(String source, String destination, String workingDirectory) { return fileSystem.move(resolve(source, workingDirectory), resolve(destination, workingDirectory)).successful(); } - @Override public String normalize(String path, String workingDirectory) { return resolve(path, workingDirectory); } - - private String resolve(String path, String workingDirectory) { - if (path == null || path.isBlank()) return ComputerFileSystem.normalize(workingDirectory); - return ComputerFileSystem.normalize(path.startsWith("/") ? path : ComputerFileSystem.normalize(workingDirectory) + "/" + path); - } - } - - private static void desktopState(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendDesktop(player, payload, false, "unauthorized"); - return; - } - sendDesktop(player, payload, true, AntmailWire.encodeTag(computer.desktopState().save())); - } - - private static void desktopWallpaper(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendDesktop(player, payload, false, "unauthorized"); - return; - } - try { - boolean changed = computer.selectWallpaper(ResourceLocation.parse(payload.value())); - sendDesktop(player, payload, changed, changed ? "" : "wallpaper_locked"); - } catch (RuntimeException exception) { - sendDesktop(player, payload, false, "invalid_wallpaper"); - } - } - - private static void basiliskState(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - if (!payload.value().isBlank()) { - try { - computer.setBasiliskScore(Integer.parseInt(payload.value())); - } catch (NumberFormatException ignored) { - sendFile(player, payload, false, "", "invalid_score"); - return; - } - } - sendFile(player, payload, true, computer.basiliskScore() + "\0" + computer.basiliskHighScore(), ""); - } - - private static void antmanState(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - if (!computer.canUseFileSystem(player)) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - if (!payload.value().isBlank()) { - try { - computer.setAntmanScore(Integer.parseInt(payload.value())); - } catch (NumberFormatException ignored) { - sendFile(player, payload, false, "", "invalid_score"); - return; - } - } - sendFile(player, payload, true, computer.antmanScore() + "\0" + computer.antmanHighScore(), ""); - } - - private static void blockleState(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload, boolean submit) { - if (!computer.canUseFileSystem(player)) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - ResourceLocation disk = ResourceLocation.fromNamespaceAndPath("antarchy", "blockle_game"); - if (!AntarchySettings.unlockAllArchives() && !computer.diskIds().contains(disk)) { - sendFile(player, payload, false, "", "not_installed"); - return; - } - ServerLevel overworld = player.server.getLevel(Level.OVERWORLD); - if (overworld == null) { - sendFile(player, payload, false, "", "overworld_unavailable"); - return; - } - long day = overworld.getDayTime() / 24000L; - computer.resetBlockle(day); - BlockleAnswers.Answer answer = BlockleAnswers.answerForDay(day); - if (answer == null) { - sendFile(player, payload, false, "", "answers_unavailable"); - return; - } - if (submit) { - String guess = payload.value() == null ? "" : payload.value().toLowerCase(java.util.Locale.ROOT); - if (guess.length() != 5 || !guess.chars().allMatch(value -> value >= 'a' && value <= 'z')) { - sendFile(player, payload, false, "", "invalid_word"); - return; - } - if (!BlockleDictionary.contains(guess)) { - sendFile(player, payload, false, "", "invalid_word"); - return; - } - if (computer.blockleGuesses().size() >= 6 || computer.blockleGuesses().stream().anyMatch(value -> value.equals(answer.word()))) { - sendFile(player, payload, false, "", "game_over"); - return; - } - computer.addBlockleGuess(guess); - } - sendFile(player, payload, true, encodeBlockle(day, answer, computer.blockleGuesses()), ""); - } - - private static void locateStructure(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { - ResourceLocation entryId; - try { - entryId = ResourceLocation.parse(payload.value()); - } catch (RuntimeException exception) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - var entry = com.craisinlord.antarchy.content.guide.ComputerGuideData.entry(entryId); - boolean unlocked = com.craisinlord.antarchy.content.guide.ComputerGuideData.entriesFor(computer.diskIds()).stream() - .anyMatch(candidate -> candidate.id().equals(entryId)); - if (!computer.canUseFileSystem(player) || !unlocked || entry == null || entry.structureId().isBlank() - || entry.structureTagId().isBlank() || entry.dimensionId().isBlank() || entry.searchRadius() <= 0) { - sendFile(player, payload, false, "", "unauthorized"); - return; - } - - ResourceLocation dimensionId; - ResourceLocation structureTagId; - try { - dimensionId = ResourceLocation.parse(entry.dimensionId()); - structureTagId = ResourceLocation.parse(entry.structureTagId()); - } catch (RuntimeException exception) { - sendFile(player, payload, false, "", "dimension_unavailable"); - return; - } - ServerLevel targetLevel = player.server.getLevel(net.minecraft.resources.ResourceKey.create(Registries.DIMENSION, dimensionId)); - if (targetLevel == null) { - sendFile(player, payload, false, "", "dimension_unavailable"); - return; - } - - TagKey structures = TagKey.create(Registries.STRUCTURE, structureTagId); - net.minecraft.core.BlockPos origin = computer.getBlockPos(); - var nearest = targetLevel.findNearestMapStructure(structures, origin, entry.searchRadius(), false); - if (nearest == null) { - sendFile(player, payload, false, "", "not_found"); - return; - } - - sendFile(player, payload, true, nearest.getX() + ", " + nearest.getY() + ", " + nearest.getZ(), ""); - } - - private static String encodeBlockle(long day, BlockleAnswers.Answer answer, java.util.List guesses) { - StringBuilder result = new StringBuilder(Long.toString(day)); - boolean solved = false; - for (String guess : guesses) { - String colors = BlockleGame.evaluate(answer.word(), guess); - if (guess.equals(answer.word())) solved = true; - result.append('|').append(guess).append(',').append(colors); - } - if (solved || guesses.size() >= 6) { - result.append('|').append(solved ? "SOLVED" : "FAILED").append('|').append(answer.word()).append('|').append(answer.itemId()); - } else { - result.append('|').append("PLAYING"); - } - return result.toString(); - } - - private static void sendDesktop(ServerPlayer player, ComputerAccessPayload payload, boolean success, String data) { - BlockEntity blockEntity = player.serverLevel().getBlockEntity(payload.pos()); - boolean hasPassword = blockEntity instanceof ComputerBlockEntity computer && computer.hasPassword(); - boolean authenticated = blockEntity instanceof ComputerBlockEntity computer && computer.isAuthenticated(); - resultSender.accept(player, new ComputerAccessResultPayload(payload.pos(), success ? ComputerAccessResultPayload.SUCCESS : ComputerAccessResultPayload.INVALID, - hasPassword, authenticated, payload.action() + "\0\0" + data)); - } - - private static String[] splitFileRequest(String value) { - if (value == null) return null; - int separator = value.indexOf('\0'); - if (separator < 0) return null; - return new String[]{value.substring(0, separator), value.substring(separator + 1)}; - } - - private static void send(ServerPlayer player, ComputerAccessPayload payload, int result) { - BlockEntity blockEntity = player.serverLevel().getBlockEntity(payload.pos()); - boolean hasPassword = blockEntity instanceof ComputerBlockEntity computer && computer.hasPassword(); - boolean authenticated = blockEntity instanceof ComputerBlockEntity computer && computer.isAuthenticated(); - resultSender.accept(player, new ComputerAccessResultPayload(payload.pos(), result, hasPassword, authenticated)); - } - - private static void sendFile(ServerPlayer player, ComputerAccessPayload payload, boolean success, String data, String error) { - BlockEntity blockEntity = player.serverLevel().getBlockEntity(payload.pos()); - boolean hasPassword = blockEntity instanceof ComputerBlockEntity computer && computer.hasPassword(); - boolean authenticated = blockEntity instanceof ComputerBlockEntity computer && computer.isAuthenticated(); - resultSender.accept(player, new ComputerAccessResultPayload(payload.pos(), success ? ComputerAccessResultPayload.SUCCESS : ComputerAccessResultPayload.INVALID, - hasPassword, authenticated, payload.action() + "\0" + error + "\0" + data)); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessPayload.java deleted file mode 100644 index 8f310b86..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessPayload.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record ComputerAccessPayload(BlockPos pos, int action, String value) implements CustomPacketPayload { - public static final int OPEN = 0; - public static final int SETUP = 1; - public static final int LOGIN = 2; - public static final int LOGOUT = 3; - public static final int CHANGE_PASSWORD = 4; - public static final int EJECT = 5; - public static final int CLOSE = 6; - public static final int FILE_LIST = 7; - public static final int FILE_OPEN = 8; - public static final int FILE_CREATE = 9; - public static final int FILE_SAVE = 10; - public static final int DESKTOP_STATE = 11; - public static final int DESKTOP_WALLPAPER = 12; - public static final int FILE_DELETE = 13; - public static final int FILE_MOVE = 14; - public static final int TERMINAL_COMMAND = 15; - public static final int BASILISK_STATE = 16; - public static final int ANTMAN_STATE = 17; - public static final int BLOCKLE_STATE = 18; - public static final int BLOCKLE_GUESS = 19; - public static final int ARCHIVE_STATE = 20; - public static final int LOCATE_STRUCTURE = 21; - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "computer_access")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, ComputerAccessPayload::pos, - ByteBufCodecs.VAR_INT, ComputerAccessPayload::action, - ByteBufCodecs.stringUtf8(65536), ComputerAccessPayload::value, - ComputerAccessPayload::new - ); - - public ComputerAccessPayload(BlockPos pos, int action) { - this(pos, action, ""); - } - - @Override - public Type type() { - return TYPE; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessResultPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessResultPayload.java deleted file mode 100644 index 10059c69..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessResultPayload.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import com.craisinlord.antarchy.Antarchy; -import net.minecraft.core.BlockPos; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; - -public record ComputerAccessResultPayload(BlockPos pos, int result, boolean hasPassword, boolean authenticated, String data) implements CustomPacketPayload { - public static final int SETUP_REQUIRED = 0; - public static final int READY = 1; - public static final int SUCCESS = 2; - public static final int INVALID_PASSWORD = 3; - public static final int BUSY = 4; - public static final int INVALID = 5; - public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "computer_access_result")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, ComputerAccessResultPayload::pos, - ByteBufCodecs.VAR_INT, ComputerAccessResultPayload::result, - ByteBufCodecs.BOOL, ComputerAccessResultPayload::hasPassword, - ByteBufCodecs.BOOL, ComputerAccessResultPayload::authenticated, - ByteBufCodecs.stringUtf8(65536), ComputerAccessResultPayload::data, - ComputerAccessResultPayload::new - ); - - public ComputerAccessResultPayload(BlockPos pos, int result, boolean hasPassword, boolean authenticated) { - this(pos, result, hasPassword, authenticated, ""); - } - - @Override - public Type type() { - return TYPE; - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerNetworking.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerNetworking.java deleted file mode 100644 index 05159994..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerNetworking.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.craisinlord.antarchy.content.network; - -import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; - -import java.util.function.Consumer; - -public final class ComputerNetworking { - private static Consumer sender = payload -> { - }; - - private ComputerNetworking() { - } - - public static void setSender(Consumer sender) { - ComputerNetworking.sender = sender; - } - - public static void open(BlockPos pos) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.OPEN)); - } - - public static void setup(BlockPos pos, String password) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.SETUP, password)); - } - - public static void login(BlockPos pos, String password) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.LOGIN, password)); - } - - public static void logout(BlockPos pos) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.LOGOUT)); - } - - public static void close(BlockPos pos) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.CLOSE)); - } - - public static void changePassword(BlockPos pos, String password) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.CHANGE_PASSWORD, password)); - } - - public static void eject(BlockPos pos, ResourceLocation diskId) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.EJECT, diskId.toString())); - } - - public static void listFiles(BlockPos pos) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.FILE_LIST)); - } - - public static void openFile(BlockPos pos, String path) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.FILE_OPEN, path)); - } - - public static void createFile(BlockPos pos, String path, String contents) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.FILE_CREATE, path + "\0" + contents)); - } - - public static void saveFile(BlockPos pos, String path, String contents) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.FILE_SAVE, path + "\0" + contents)); - } - - public static void deleteFile(BlockPos pos, String path) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.FILE_DELETE, path)); - } - - public static void moveFile(BlockPos pos, String source, String destination) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.FILE_MOVE, source + "\0" + destination)); - } - - public static void terminalCommand(BlockPos pos, String directory, String command) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.TERMINAL_COMMAND, directory + "\0" + command)); - } - - public static void requestDesktopState(BlockPos pos) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.DESKTOP_STATE)); - } - - public static void selectWallpaper(BlockPos pos, ResourceLocation id) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.DESKTOP_WALLPAPER, id.toString())); - } - - public static void requestBasiliskState(BlockPos pos) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.BASILISK_STATE)); - } - - public static void saveBasiliskScore(BlockPos pos, int score) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.BASILISK_STATE, Integer.toString(Math.max(0, score)))); - } - - public static void requestAntmanState(BlockPos pos) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.ANTMAN_STATE)); - } - - public static void saveAntmanScore(BlockPos pos, int score) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.ANTMAN_STATE, Integer.toString(Math.max(0, score)))); - } - - public static void requestBlockleState(BlockPos pos) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.BLOCKLE_STATE)); - } - - public static void submitBlockleGuess(BlockPos pos, String guess) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.BLOCKLE_GUESS, guess)); - } - - public static void locateStructure(BlockPos pos, ResourceLocation entryId) { - sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.LOCATE_STRUCTURE, entryId.toString())); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/RoyalGuardianSwordModeCyclePayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/RoyalGuardianSwordModeCyclePayload.java new file mode 100644 index 00000000..f80a4483 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/RoyalGuardianSwordModeCyclePayload.java @@ -0,0 +1,19 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.Antarchy; +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record RoyalGuardianSwordModeCyclePayload() implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "royal_guardian_sword_mode_cycle")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.unit(new RoyalGuardianSwordModeCyclePayload()); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java index 610db543..0b5c166a 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java @@ -36,6 +36,7 @@ public class TimeDilationFieldEntity extends Entity { private int missingAnchorTicks; private boolean visual = true; private boolean chronosphere; + private boolean queenChronosphere; public TimeDilationFieldEntity(EntityType entityType, Level level) { super(entityType, level); @@ -79,19 +80,25 @@ public class TimeDilationFieldEntity extends Entity { this.chronosphere = true; } + public void configureQueenChronosphere(QueenEntity owner) { + this.ownerId = owner.getUUID(); + this.attachTo(owner); + this.queenChronosphere = true; + } + public boolean isChronosphere() { return this.chronosphere; } public boolean affects(Entity entity) { - if (entity instanceof RoyalBlackHoleEntity || this.isOwnedBy(entity) + if (!this.queenChronosphere && entity instanceof RoyalBlackHoleEntity || this.isOwnedBy(entity) || this.anchorId != null && this.anchorId.equals(entity.getUUID())) { return false; } if (this.chronosphere && !(entity instanceof LivingEntity || entity instanceof Projectile || entity instanceof ItemEntity)) { return false; } - if (this.ownerId != null && this.level() instanceof ServerLevel level + if (!this.queenChronosphere && this.ownerId != null && this.level() instanceof ServerLevel level && level.getEntity(this.ownerId) instanceof QueenEntity) { return !entity.getType().is(AntarchyTags.Entities.QUEEN_DOES_NOT_ATTACK); } @@ -202,6 +209,7 @@ public class TimeDilationFieldEntity extends Entity { this.anchorId = tag.hasUUID("AnchorUuid") ? tag.getUUID("AnchorUuid") : null; this.visual = !tag.contains("Visual") || tag.getBoolean("Visual"); this.chronosphere = tag.getBoolean("Chronosphere"); + this.queenChronosphere = tag.getBoolean("QueenChronosphere"); } @Override @@ -218,5 +226,6 @@ public class TimeDilationFieldEntity extends Entity { } tag.putBoolean("Visual", this.visual); tag.putBoolean("Chronosphere", this.chronosphere); + tag.putBoolean("QueenChronosphere", this.queenChronosphere); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/VillagePoolAdditions.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/VillagePoolAdditions.java index 33d00fb9..679d197c 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/VillagePoolAdditions.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/VillagePoolAdditions.java @@ -1,7 +1,6 @@ package com.craisinlord.antarchy.content.worldgen; import com.craisinlord.antarchy.Antarchy; -import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.mixins.PoiTypesAccessor; import com.craisinlord.integrated_api.mixins.structures.StructurePoolAccessor; import com.google.gson.Gson; @@ -59,7 +58,9 @@ public final class VillagePoolAdditions { ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, poiId)); Map> stateMap = PoiTypesAccessor.antarchy$getTypeByState(); - for (BlockState state : AntarchyObjects.COMPUTER.get().getStateDefinition().getPossibleStates()) { + net.minecraft.world.level.block.Block computer = BuiltInRegistries.BLOCK.get( + ResourceLocation.fromNamespaceAndPath("antos", "computer")); + for (BlockState state : computer.getStateDefinition().getPossibleStates()) { stateMap.put(state, poi); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/KingsTreeGridFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/KingsTreeGridFeature.java index 5d4c7dbd..1dfec542 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/KingsTreeGridFeature.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/elythia/KingsTreeGridFeature.java @@ -144,9 +144,8 @@ public final class KingsTreeGridFeature extends Feature callback) { - if (callback.getReturnValueZ() && player.getAdvancements().getOrStartProgress(advancement).isDone()) AntmailEventData.onAdvancement(player, advancement); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PoiTypesMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PoiTypesMixin.java index ef9e77e9..4e212645 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PoiTypesMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PoiTypesMixin.java @@ -1,6 +1,5 @@ package com.craisinlord.antarchy.mixins; -import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.Antarchy; import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; @@ -23,12 +22,14 @@ public abstract class PoiTypesMixin { private static void antarchy$computerPoi(BlockState state, CallbackInfoReturnable>> callback) { try { - if (!state.is(AntarchyObjects.COMPUTER.get())) { + boolean computer = state.is(BuiltInRegistries.BLOCK.get(ResourceLocation.fromNamespaceAndPath("antos", "computer"))); + boolean antTrap = state.is(BuiltInRegistries.BLOCK.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "ant_trap"))); + if (!computer && !antTrap) { return; } ResourceKey key = ResourceKey.create( Registries.POINT_OF_INTEREST_TYPE, - ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "computer")); + ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, computer ? "computer" : "pest_control")); BuiltInRegistries.POINT_OF_INTEREST_TYPE.getHolder(key) .ifPresent(holder -> callback.setReturnValue(Optional.of(holder))); } catch (IllegalStateException ignored) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/TemporalContractionLivingRendererMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/TemporalContractionLivingRendererMixin.java index cd597a1a..3968ef2f 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/TemporalContractionLivingRendererMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/TemporalContractionLivingRendererMixin.java @@ -6,18 +6,23 @@ import net.minecraft.client.model.EntityModel; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.client.renderer.entity.layers.RenderLayer; import net.minecraft.world.entity.LivingEntity; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(LivingEntityRenderer.class) public abstract class TemporalContractionLivingRendererMixin> { + @Invoker("addLayer") + protected abstract boolean antarchy$addLayer(RenderLayer layer); + @Inject(method = "", at = @At("RETURN")) private void antarchy$addTemporalContractionLayer(EntityRendererProvider.Context context, M model, float shadowRadius, CallbackInfo callbackInfo) { LivingEntityRenderer renderer = (LivingEntityRenderer) (Object) this; - renderer.addLayer(new TemporalContractionAfterimageLayer<>(renderer)); + this.antarchy$addLayer(new TemporalContractionAfterimageLayer<>(renderer)); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesCandyCaneHookMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesCandyCaneHookMixin.java index e809f64e..e8168256 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesCandyCaneHookMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesCandyCaneHookMixin.java @@ -8,6 +8,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyArg; @@ -15,6 +16,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** Handles Alex's Caves' manually-launched Candy Cane Hook. */ @Mixin(targets = "com.github.alexmodguy.alexscaves.server.entity.item.CandyCaneHookEntity") +@Pseudo public abstract class AlexsCavesCandyCaneHookMixin { private boolean antarchy$launchAdjusted; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesDepthChargeMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesDepthChargeMixin.java index 410b00fd..8f0239dc 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesDepthChargeMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesDepthChargeMixin.java @@ -5,11 +5,13 @@ import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManag import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.Projectile; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; /** Corrects Depth Charge's custom tick, which bypasses ThrowableProjectile.tick. */ @Mixin(targets = "com.github.alexmodguy.alexscaves.server.entity.item.DepthChargeEntity") +@Pseudo public abstract class AlexsCavesDepthChargeMixin { @ModifyArg( method = "tick", diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesTephraMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesTephraMixin.java index 374bb783..0f96c5d6 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesTephraMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesTephraMixin.java @@ -5,11 +5,13 @@ import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManag import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.Projectile; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; /** Corrects Tephra's explicit -0.1 Y gravity adjustment. */ @Mixin(targets = "com.github.alexmodguy.alexscaves.server.entity.item.TephraEntity") +@Pseudo public abstract class AlexsCavesTephraMixin { @ModifyArg( method = "tick", diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesWaterBoltMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesWaterBoltMixin.java index 329169ae..7bbcccd3 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesWaterBoltMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/alexscaves/AlexsCavesWaterBoltMixin.java @@ -5,11 +5,13 @@ import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManag import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.Projectile; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; /** Corrects Water Bolt's explicit -0.07 Y gravity adjustment. */ @Mixin(targets = "com.github.alexmodguy.alexscaves.server.entity.item.WaterBoltEntity") +@Pseudo public abstract class AlexsCavesWaterBoltMixin { @ModifyArg( method = "tick", diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/ars_nouveau/ArsNouveauProjectileGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/ars_nouveau/ArsNouveauProjectileGravityMixin.java index d938dd91..fa90dabb 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/ars_nouveau/ArsNouveauProjectileGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/ars_nouveau/ArsNouveauProjectileGravityMixin.java @@ -7,12 +7,14 @@ import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManag import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.Projectile; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** Handles Ars Nouveau's explicit tickNextPosition gravity adjustment. */ @Mixin(targets = "com.hollingsworth.arsnouveau.common.entity.EntityProjectileSpell") +@Pseudo public abstract class ArsNouveauProjectileGravityMixin { @Inject(method = "tick", at = @At("HEAD")) private void antarchy$prepareArsProjectile(CallbackInfo ci) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/irons_spellbooks/IronsSpellbooksProjectileGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/irons_spellbooks/IronsSpellbooksProjectileGravityMixin.java index 2abf065f..d44dd439 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/irons_spellbooks/IronsSpellbooksProjectileGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/irons_spellbooks/IronsSpellbooksProjectileGravityMixin.java @@ -9,6 +9,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -16,6 +17,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** Handles Iron's Spellbooks' custom travel() gravity path. */ @Mixin(targets = "io.redspace.ironsspellbooks.entity.spells.AbstractMagicProjectile") +@Pseudo public abstract class IronsSpellbooksProjectileGravityMixin { @Shadow public abstract float getSpeed(); @Shadow protected abstract double getDefaultGravity(); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsArrowGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsArrowGravityMixin.java index fbe65aef..94ec1463 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsArrowGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsArrowGravityMixin.java @@ -4,12 +4,14 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager; import net.minecraft.world.entity.projectile.AbstractArrow; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** Corrects Scorched Guns arrow-derived projectiles with custom tick gravity. */ @Mixin(targets = "top.ribs.scguns.entity.projectile.BrassBoltEntity") +@Pseudo public abstract class ScgunsArrowGravityMixin { @Inject(method = "tick", at = @At("TAIL")) private void antarchy$invertScgunsArrowGravity(CallbackInfo ci) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityGravityMixin.java index f76ed2a2..f5e1f051 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityGravityMixin.java @@ -10,6 +10,7 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -25,6 +26,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; "top.ribs.scguns.entity.projectile.OsborneSlugProjectileEntity", "top.ribs.scguns.entity.projectile.ShotballProjectileEntity" }) +@Pseudo public abstract class ScgunsProjectileEntityGravityMixin { @Shadow @Nullable protected LivingEntity shooter; @Shadow protected double modifiedGravity; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityMoveTimeDilationMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityMoveTimeDilationMixin.java index 4db808f1..af8ac9f0 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityMoveTimeDilationMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityMoveTimeDilationMixin.java @@ -5,6 +5,7 @@ import com.craisinlord.antarchy.content.time.TimeDilationEntityAccess; import com.craisinlord.antarchy.content.time.TimeDilationMath; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.MoverType; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -31,6 +32,9 @@ public abstract class EntityMoveTimeDilationMixin { @ModifyVariable(method = "move", at = @At("HEAD"), argsOnly = true, ordinal = 0) private Vec3 antarchy$scaleTimeDilatedMovement(Vec3 movement) { Entity entity = (Entity) (Object) this; + if (entity instanceof Player && !entity.level().isClientSide) { + return movement; + } double rate = TimeDilationApi.getRate(entity); if (Math.abs(rate - TimeDilationMath.NORMAL_RATE) < 0.001D) { return movement; diff --git a/common-1.21.1/src/main/resources/antarchy-common.mixins.json b/common-1.21.1/src/main/resources/antarchy-common.mixins.json index 0bcd2a72..ef3ea3f1 100644 --- a/common-1.21.1/src/main/resources/antarchy-common.mixins.json +++ b/common-1.21.1/src/main/resources/antarchy-common.mixins.json @@ -57,7 +57,6 @@ "ServerPlayerRoyalBoundaryMixin", "ProjectileRoyalBoundaryMixin", "RoyalBoundaryOwnerCleanupMixin", - "PlayerAdvancementsMixin", "SpongeAntiwaterMixin", "SquidzookaEnchantMixin", "SquidzookaMultishotMixin", diff --git a/common-1.21.1/src/main/resources/assets/antarchy/animations/computer.animation.json b/common-1.21.1/src/main/resources/assets/antarchy/animations/computer.animation.json deleted file mode 100644 index 1ca9b1f3..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/animations/computer.animation.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "format_version": "1.8.0", - "animations": { - "on_state": { - "loop": true, - "bones": { - "on": {"scale": {"vector": [1, 1, 1]}}, - "white_flash": {"scale": {"vector": [0, 0, 0]}}, - "off": {"scale": {"vector": [0, 0, 0]}}, - "computer_on": {"scale": {"vector": [1, 1, 1]}} - } - }, - "off_state": { - "loop": true, - "bones": { - "on": {"scale": {"vector": [0, 0, 0]}}, - "white_flash": {"scale": {"vector": [0, 0, 0]}}, - "off": {"scale": {"vector": [1, 1, 1]}}, - "computer_on": {"scale": {"vector": [0, 0, 0]}} - } - }, - "turn_on": { - "loop": "hold_on_last_frame", - "animation_length": 0.4583, - "bones": { - "on": {"scale": {"0.0": {"vector": [1, 0, 0]}, "0.125": {"vector": [1, 0, 0]}, "0.2917": {"vector": [1, 1, 1], "easing": "easeInOutCubic"}}}, - "white_flash": {"scale": {"0.0": {"vector": [1, 0, 0]}, "0.0417": {"vector": [1, 0.2, 0.2]}, "0.2083": {"vector": [1, 1, 1], "easing": "easeInOutQuint"}}}, - "off": {"scale": {"0.0": {"vector": [1, 1, 1]}}}, - "computer_on": {"scale": {"0.0": {"vector": [0, 0, 0]}, "0.0417": {"vector": [1, 1, 1]}}} - } - }, - "turn_off": { - "loop": "hold_on_last_frame", - "animation_length": 0.4583, - "bones": { - "on": {"scale": {"0.0": {"vector": [1, 1, 1]}, "0.25": {"vector": [1, 0, 1], "easing": "easeInOutQuint"}}}, - "white_flash": {"scale": {"0.0": {"vector": [1, 1, 0]}, "0.125": {"vector": [1, 1, 0]}, "0.375": {"vector": [1, 0, 0], "easing": "easeInOutQuart"}}}, - "off": {"scale": {"0.0": {"vector": [0, 0, 0]}, "0.0417": {"vector": [1, 1, 1]}}}, - "computer_on": {"scale": {"0.0": {"vector": [1, 1, 1]}, "0.25": {"vector": [1, 1, 1]}, "0.2917": {"vector": [0, 0, 0]}}} - } - } - } -} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/blockstates/ant_trap.json b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/ant_trap.json new file mode 100644 index 00000000..48f6872d --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/ant_trap.json @@ -0,0 +1 @@ +{"variants":{"":{"model":"antarchy:block/ant_trap"}}} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/blockstates/computer.json b/common-1.21.1/src/main/resources/assets/antarchy/blockstates/computer.json deleted file mode 100644 index 0cce1c7f..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/blockstates/computer.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "multipart": [ - { - "apply": { - "model": "antarchy:block/computer" - } - } - ] -} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/geo/computer.geo.json b/common-1.21.1/src/main/resources/assets/antarchy/geo/computer.geo.json deleted file mode 100644 index 94959b9d..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/geo/computer.geo.json +++ /dev/null @@ -1,189 +0,0 @@ -{ - "format_version": "1.12.0", - "minecraft:geometry": [ - { - "description": { - "identifier": "geometry.unknown", - "texture_width": 128, - "texture_height": 128, - "visible_bounds_width": 2, - "visible_bounds_height": 2.5, - "visible_bounds_offset": [0, 0.75, 0] - }, - "bones": [ - { - "name": "computer", - "pivot": [-5, 3, 1], - "cubes": [ - { - "origin": [-7, 0, -7], - "size": [14, 4, 14], - "uv": { - "north": {"uv": [14, 14], "uv_size": [14, 4]}, - "east": {"uv": [0, 14], "uv_size": [14, 4]}, - "south": {"uv": [42, 14], "uv_size": [14, 4]}, - "west": {"uv": [28, 14], "uv_size": [14, 4]}, - "up": {"uv": [14, 0], "uv_size": [14, 14]}, - "down": {"uv": [28, 14], "uv_size": [14, -14]} - } - } - ] - }, - { - "name": "computer_on", - "parent": "computer", - "pivot": [0, -1, 0.95], - "cubes": [ - { - "origin": [-6, 2, -7.05], - "size": [3, 1, 0], - "uv": { - "north": {"uv": [97, 14], "uv_size": [3, 1]}, - "east": {"uv": [83, 14], "uv_size": [0, 1]}, - "south": {"uv": [125, 14], "uv_size": [3, 1]}, - "west": {"uv": [111, 14], "uv_size": [0, 1]}, - "up": {"uv": [97, 0], "uv_size": [3, 0]}, - "down": {"uv": [111, 0], "uv_size": [3, 0]} - } - } - ] - }, - { - "name": "cables", - "parent": "computer", - "pivot": [-5, 3, 1], - "cubes": [ - { - "origin": [-2, 1, 5], - "size": [0, 8, 3], - "uv": { - "north": {"uv": [13, 66], "uv_size": [0, 8]}, - "east": {"uv": [0, 66], "uv_size": [3, 8]}, - "south": {"uv": [26, 66], "uv_size": [0, 8]}, - "west": {"uv": [13, 66], "uv_size": [13, 8]}, - "up": {"uv": [13, 53], "uv_size": [0, 13]}, - "down": {"uv": [13, 66], "uv_size": [0, -13]} - } - }, - { - "origin": [2, 1, 5], - "size": [0, 8, 3], - "uv": { - "north": {"uv": [45, 51], "uv_size": [0, 8]}, - "east": {"uv": [32, 51], "uv_size": [3, 8]}, - "south": {"uv": [58, 51], "uv_size": [0, 8]}, - "west": {"uv": [45, 51], "uv_size": [13, 8]}, - "up": {"uv": [45, 38], "uv_size": [0, 13]}, - "down": {"uv": [45, 51], "uv_size": [0, -13]} - } - } - ] - }, - { - "name": "monitor", - "parent": "computer", - "pivot": [0, 3, 0], - "cubes": [ - { - "origin": [-6, 4, -6], - "size": [12, 11, 4], - "uv": { - "north": {"uv": [4, 42], "uv_size": [12, 11]}, - "east": {"uv": [0, 42], "uv_size": [4, 11]}, - "south": {"uv": [20, 42], "uv_size": [12, 11]}, - "west": {"uv": [16, 42], "uv_size": [4, 11]}, - "up": {"uv": [4, 38], "uv_size": [12, 4]} - } - }, - { - "origin": [-5, 4, -5], - "size": [10, 10, 10], - "uv": { - "north": {"uv": [10, 28], "uv_size": [10, 10]}, - "east": {"uv": [0, 28], "uv_size": [10, 10]}, - "south": {"uv": [30, 28], "uv_size": [10, 10]}, - "west": {"uv": [20, 28], "uv_size": [10, 10]}, - "up": {"uv": [10, 18], "uv_size": [10, 10]}, - "down": {"uv": [20, 28], "uv_size": [10, -10]} - } - } - ] - }, - { - "name": "screens", - "parent": "monitor", - "pivot": [0.5, 2.9, 0.5] - }, - { - "name": "on", - "parent": "screens", - "pivot": [0, 9.4, -5.625], - "cubes": [ - { - "origin": [-5.5, 3.9, -5.6], - "size": [11, 11, 0], - "uv": { - "north": {"uv": [77, 33], "uv_size": [17, 17]}, - "east": {"uv": [40, 18], "uv_size": [0, 11]}, - "south": {"uv": [51, 18], "uv_size": [11, 11]}, - "west": {"uv": [51, 18], "uv_size": [0, 11]}, - "up": {"uv": [40, 18], "uv_size": [11, 0]}, - "down": {"uv": [51, 18], "uv_size": [11, 0]} - } - }, - { - "origin": [-5.5, 3.9, -5.65], - "size": [11, 11, 0], - "uv": { - "north": {"uv": [98, 31.5], "uv_size": [17, 17]}, - "east": {"uv": [40, 18], "uv_size": [0, 11]}, - "south": {"uv": [51, 18], "uv_size": [11, 11]}, - "west": {"uv": [51, 18], "uv_size": [0, 11]}, - "up": {"uv": [40, 18], "uv_size": [11, 0]}, - "down": {"uv": [51, 18], "uv_size": [11, 0]} - } - } - ] - }, - { - "name": "off", - "parent": "screens", - "pivot": [0, 9.4, -5.4], - "cubes": [ - { - "origin": [-5.5, 3.9, -5.4], - "size": [11, 11, 0], - "uv": { - "north": {"uv": [77, 50], "uv_size": [17, 17]}, - "east": {"uv": [58, 29], "uv_size": [0, 11]}, - "south": {"uv": [62, 46], "uv_size": [11, 11]}, - "west": {"uv": [69, 29], "uv_size": [0, 11]}, - "up": {"uv": [58, 29], "uv_size": [11, 0]}, - "down": {"uv": [69, 29], "uv_size": [11, 0]} - } - } - ] - }, - { - "name": "white_flash", - "parent": "screens", - "pivot": [0, 9.4, -5.55], - "cubes": [ - { - "origin": [-5.5, 3.9, -5.55], - "size": [11, 11, 0], - "uv": { - "north": {"uv": [56, 0], "uv_size": [11, 11]}, - "east": {"uv": [56, 0], "uv_size": [0, 11]}, - "south": {"uv": [67, 0], "uv_size": [11, 11]}, - "west": {"uv": [67, 0], "uv_size": [0, 11]}, - "up": {"uv": [56, 0], "uv_size": [11, 0]}, - "down": {"uv": [67, 0], "uv_size": [11, 0]} - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/de_de.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/de_de.json index 9eecf3e6..68d01114 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/de_de.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/de_de.json @@ -963,5 +963,8 @@ "advancements.antarchy.beetle_buddies.title": "Käferkumpel", "advancements.antarchy.beetle_buddies.description": "Zähme einen Herkuleskäfer.", "advancements.antarchy.herculean_task.title": "Herkulesaufgabe", - "advancements.antarchy.herculean_task.description": "Erschlage einen Herkuleskäfer." + "advancements.antarchy.herculean_task.description": "Erschlage einen Herkuleskäfer.", + "subtitles.antarchy.hoverboard_mount": "Hoverboard startet", + "subtitles.antarchy.hoverboard_dismount": "Hoverboard stoppt", + "subtitles.antarchy.hoverboard_idle": "Hoverboard summt" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json index 903dc8d7..7d40bdd8 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json @@ -883,6 +883,10 @@ "advancements.antarchy.water_cannon_craft.description": "Obtain a Water Cannon.", "advancements.antarchy.kill_toreterror.title": "More like Tore-Terrible Aim!", "advancements.antarchy.kill_toreterror.description": "Defeat the Toreterror.", + "advancements.antarchy.down_with_the_king.title": "Down with the King", + "advancements.antarchy.down_with_the_king.description": "Defeat the King.", + "advancements.antarchy.down_with_the_queen.title": "Down with the Queen", + "advancements.antarchy.down_with_the_queen.description": "Defeat the Queen.", "advancements.antarchy.challenge_games.title": "Challenge Games!", "advancements.antarchy.challenge_games.description": "Enter the Ruined Arena.", "advancements.antarchy.bugged_out.title": "Bugged Out", @@ -1441,7 +1445,7 @@ ,"decree.antarchy.kneel": "KNEEL" ,"decree.antarchy.kneel.subtitle": "Crouch within five seconds." ,"decree.antarchy.close_quarters": "CLOSE QUARTERS" - ,"decree.antarchy.close_quarters.subtitle": "Enter the royal circle. Projectiles are forbidden." + ,"decree.antarchy.close_quarters.subtitle": "Reach the King's 16-block circle within 6 seconds. Projectiles are forbidden." ,"decree.antarchy.violated": "DECREE VIOLATED" ,"decree.antarchy.violated.subtitle": "Your hearts are halved for 30. Face the King's judgment." ,"effect.antarchy.royal_vitality_penalty": "Royal Vitality Diminished" @@ -1462,12 +1466,10 @@ ,"menu.antarchy.royal_judgment": "Royal Judgment" ,"enchantment.antarchy.feather_rising": "Feather Rising" ,"enchantment.antarchy.chronosphere": "Chronosphere" - ,"block.antarchy.computer": "Computer" ,"entity.minecraft.villager.antarchy.computer_scientist": "Game Developer" - ,"item.antarchy.computer": "Computer" - ,"item.antarchy.floppy_disk": "Floppy Disk" - ,"item.antarchy.floppy_disk.tooltip": "Put in a computer to find out more" - ,"screen.antarchy.computer": "ANTINTOSH OS" + ,"block.antarchy.ant_trap": "Ant Trap" + ,"item.antarchy.bug_spray": "Bug Spray" + ,"entity.minecraft.villager.antarchy.pest_control": "Pest Control" ,"guide.antarchy.category.general": "GENERAL" ,"guide.antarchy.category.elythia": "ELYTHIA" ,"guide.antarchy.category.thoraxis": "THORAXIS" @@ -1545,21 +1547,13 @@ ,"guide.antarchy.entry.queen.title": "Queen" ,"guide.antarchy.entry.queen.subtitle": "Thoraxis royal entity record" ,"guide.antarchy.entry.queen.description": "Follow a Queen Havoc Trail through Thoraxis's underside to its chamber; the Queen appears when you approach." - ,"screen.antarchy.computer.first_use.title": "INITIAL PASSWORD SETUP" - ,"screen.antarchy.computer.first_use.prompt": "CREATE A PASSWORD TO CONTINUE" - ,"screen.antarchy.computer.first_use.confirm": "CONFIRM PASSWORD" - ,"screen.antarchy.computer.busy": "COMPUTER IN USE" - ,"screen.antarchy.computer.eject_denied": "ACCESS DENIED: SETTINGS REQUIRED" - ,"screen.antarchy.computer.ejected": "DISK EJECTED" - ,"antintosh.antarchy.desktop.archive": "ARCHIVE" - ,"antintosh.antarchy.desktop.settings": "SETTINGS" - ,"antintosh.antarchy.desktop.terminal": "TERMINAL" - ,"antintosh.antarchy.desktop.antpaint": "ANTPAINT" - ,"antintosh.antarchy.desktop.antmail": "ANTMAIL" - ,"antintosh.antarchy.desktop.text_editor": "TEXT EDITOR" - ,"antintosh.antarchy.antmail.compose": "COMPOSE MESSAGE" - ,"antintosh.antarchy.antmail.attach_text": "ATTACH TEXT FILE" - ,"antintosh.antarchy.antmail.attach_image": "ATTACH ANTPAINT IMAGE" - ,"antintosh.antarchy.antmail.save_attachment": "SAVE ATTACHMENT" - ,"antintosh.antarchy.antmail.address": "COMPUTER ADDRESS" + ,"guide.antarchy.entry.prince.title": "Prince" + ,"guide.antarchy.entry.prince.subtitle": "Royal mount record" + ,"guide.antarchy.entry.prince.description": "Prince Eggs hatch into small Princes that grow when fed golden items; they can be tamed, saddled to fly, fire elemental bolts, and command nearby hostile mobs." + ,"guide.antarchy.entry.princess.title": "Princess" + ,"guide.antarchy.entry.princess.subtitle": "Royal mount record" + ,"guide.antarchy.entry.princess.description": "Princess Eggs hatch into small Princesses that grow when fed golden items; they can be tamed, saddled to fly, fire elemental bolts, and create time-dilation fields around enemies." + ,"subtitles.antarchy.hoverboard_mount": "Hoverboard starts" + ,"subtitles.antarchy.hoverboard_dismount": "Hoverboard stops" + ,"subtitles.antarchy.hoverboard_idle": "Hoverboard hums" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/ant_trap.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/ant_trap.json new file mode 100644 index 00000000..c98a1c83 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/block/ant_trap.json @@ -0,0 +1,77 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [32, 32], + "textures": { + "0": "antarchy:block/ant_trap", + "particle": "antarchy:block/ant_trap" + }, + "elements": [ + { + "from": [3, 0, 3], + "to": [13, 1, 13], + "faces": { + "north": {"uv": [9, 6, 14, 6.5], "texture": "#0"}, + "east": {"uv": [9, 6.5, 14, 7], "texture": "#0"}, + "south": {"uv": [9, 7, 14, 7.5], "texture": "#0"}, + "west": {"uv": [9, 7.5, 14, 8], "texture": "#0"}, + "up": {"uv": [5, 5, 0, 0], "texture": "#0"}, + "down": {"uv": [5, 5, 0, 10], "texture": "#0"} + } + }, + { + "from": [4, 1, 4], + "to": [12, 5, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 1, 0]}, + "faces": { + "north": {"uv": [5, 8, 9, 10], "texture": "#0"}, + "east": {"uv": [9, 0, 13, 2], "texture": "#0"}, + "south": {"uv": [9, 2, 13, 4], "texture": "#0"}, + "west": {"uv": [9, 4, 13, 6], "texture": "#0"}, + "up": {"uv": [9, 4, 5, 0], "texture": "#0"}, + "down": {"uv": [9, 4, 5, 8], "texture": "#0"} + } + } + ], + "display": { + "thirdperson_righthand": { + "translation": [0, 4, 0], + "scale": [0.5, 0.5, 0.5] + }, + "thirdperson_lefthand": { + "translation": [0, 4, 0], + "scale": [0.5, 0.5, 0.5] + }, + "firstperson_righthand": { + "translation": [0, 6, 0], + "scale": [0.8, 0.8, 0.8] + }, + "firstperson_lefthand": { + "translation": [0, 6, 0], + "scale": [0.8, 0.8, 0.8] + }, + "ground": { + "translation": [0, 1.5, 0], + "scale": [0.4, 0.4, 0.4] + }, + "gui": { + "rotation": [35, -130, 0], + "translation": [0, 3.5, 0] + }, + "head": { + "translation": [0, 14.5, 0] + }, + "fixed": { + "translation": [0, 4, 0] + } + }, + "groups": [ + { + "name": "anttrap", + "origin": [0, 0, 0], + "scope": 0, + "color": 0, + "children": [0, 1] + } + ] +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/computer.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/computer.json deleted file mode 100644 index 90f14fc1..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/block/computer.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:block/cube_all", - "textures": { - "all": "antarchy:block/computer/computer" - } -} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/block/verdant_nyxium.json b/common-1.21.1/src/main/resources/assets/antarchy/models/block/verdant_nyxium.json index 6afbf9dc..09e697b8 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/block/verdant_nyxium.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/block/verdant_nyxium.json @@ -1,7 +1,7 @@ { "parent": "minecraft:block/cube_bottom_top", "textures": { - "bottom": "antarchy:block/typhonite", + "bottom": "antarchy:block/typhonite/typhonite", "side": "antarchy:block/verdant_nyxium", "top": "antarchy:block/verdant_nyxium_top" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/item/ant_trap.json b/common-1.21.1/src/main/resources/assets/antarchy/models/item/ant_trap.json new file mode 100644 index 00000000..c6bf80c0 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/item/ant_trap.json @@ -0,0 +1 @@ +{"parent":"antarchy:block/ant_trap"} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/item/bug_spray.json b/common-1.21.1/src/main/resources/assets/antarchy/models/item/bug_spray.json new file mode 100644 index 00000000..2c57a55e --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/item/bug_spray.json @@ -0,0 +1 @@ +{"parent":"minecraft:item/generated","textures":{"layer0":"antarchy:item/bug_spray"},"overrides":[{"predicate":{"minecraft:using_item":1},"model":"antarchy:item/bug_spray_spraying"}]} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/item/bug_spray_spraying.json b/common-1.21.1/src/main/resources/assets/antarchy/models/item/bug_spray_spraying.json new file mode 100644 index 00000000..54c1e225 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/models/item/bug_spray_spraying.json @@ -0,0 +1 @@ +{"parent":"minecraft:item/generated","textures":{"layer0":"antarchy:item/bug_spray_spraying"}} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/item/computer.json b/common-1.21.1/src/main/resources/assets/antarchy/models/item/computer.json deleted file mode 100644 index 7b1a8ed5..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/item/computer.json +++ /dev/null @@ -1,232 +0,0 @@ -{ - "format_version": "1.21.11", - "credit": "Made with Blockbench", - "texture_size": [128, 128], - "textures": { - "0": "antarchy:item/computer", - "particle": "antarchy:item/computer" - }, - "elements": [ - { - "from": [1, 0, 1], - "to": [15, 4, 15], - "rotation": {"angle": 0, "axis": "y", "origin": [8, -1, 9]}, - "faces": { - "north": {"uv": [1.75, 1.75, 3.5, 2.25], "texture": "#0"}, - "east": {"uv": [0, 1.75, 1.75, 2.25], "texture": "#0"}, - "south": {"uv": [5.25, 1.75, 7, 2.25], "texture": "#0"}, - "west": {"uv": [3.5, 1.75, 5.25, 2.25], "texture": "#0"}, - "up": {"uv": [3.5, 1.75, 1.75, 0], "texture": "#0"}, - "down": {"uv": [5.25, 0, 3.5, 1.75], "texture": "#0"} - } - }, - { - "from": [11, 2, 0.95], - "to": [14, 3, 0.95], - "rotation": {"angle": 0, "axis": "y", "origin": [8, -1, 8.95]}, - "faces": { - "north": {"uv": [12.125, 1.75, 12.5, 1.875], "texture": "#0"}, - "east": {"uv": [10.375, 1.75, 10.375, 1.875], "texture": "#0"}, - "south": {"uv": [15.625, 1.75, 16, 1.875], "texture": "#0"}, - "west": {"uv": [13.875, 1.75, 13.875, 1.875], "texture": "#0"}, - "up": {"uv": [12.5, 0, 12.125, 0], "texture": "#0"}, - "down": {"uv": [14.25, 0, 13.875, 0], "texture": "#0"} - } - }, - { - "from": [10, 1, 13], - "to": [10, 9, 16], - "rotation": {"angle": 0, "axis": "y", "origin": [13, 3, 9]}, - "faces": { - "north": {"uv": [1.625, 8.25, 1.625, 9.25], "texture": "#0"}, - "east": {"uv": [0, 8.25, 0.375, 9.25], "texture": "#0"}, - "south": {"uv": [3.25, 8.25, 3.25, 9.25], "texture": "#0"}, - "west": {"uv": [1.625, 8.25, 3.25, 9.25], "texture": "#0"}, - "up": {"uv": [1.625, 8.25, 1.625, 6.625], "texture": "#0"}, - "down": {"uv": [1.625, 6.625, 1.625, 8.25], "texture": "#0"} - } - }, - { - "from": [6, 1, 13], - "to": [6, 9, 16], - "rotation": {"angle": 0, "axis": "y", "origin": [9, 3, 9]}, - "faces": { - "north": {"uv": [5.625, 6.375, 5.625, 7.375], "texture": "#0"}, - "east": {"uv": [4, 6.375, 4.375, 7.375], "texture": "#0"}, - "south": {"uv": [7.25, 6.375, 7.25, 7.375], "texture": "#0"}, - "west": {"uv": [5.625, 6.375, 7.25, 7.375], "texture": "#0"}, - "up": {"uv": [5.625, 6.375, 5.625, 4.75], "texture": "#0"}, - "down": {"uv": [5.625, 4.75, 5.625, 6.375], "texture": "#0"} - } - }, - { - "from": [2, 4, 2], - "to": [14, 15, 6], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 3, 8]}, - "faces": { - "north": {"uv": [0.5, 5.25, 2, 6.625], "texture": "#0"}, - "east": {"uv": [0, 5.25, 0.5, 6.625], "texture": "#0"}, - "south": {"uv": [2.5, 5.25, 4, 6.625], "texture": "#0"}, - "west": {"uv": [2, 5.25, 2.5, 6.625], "texture": "#0"}, - "up": {"uv": [2, 5.25, 0.5, 4.75], "texture": "#0"} - } - }, - { - "from": [3, 4, 3], - "to": [13, 14, 13], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 3, 9]}, - "faces": { - "north": {"uv": [1.25, 3.5, 2.5, 4.75], "texture": "#0"}, - "east": {"uv": [0, 3.5, 1.25, 4.75], "texture": "#0"}, - "south": {"uv": [3.75, 3.5, 5, 4.75], "texture": "#0"}, - "west": {"uv": [2.5, 3.5, 3.75, 4.75], "texture": "#0"}, - "up": {"uv": [2.5, 3.5, 1.25, 2.25], "texture": "#0"}, - "down": {"uv": [3.75, 2.25, 2.5, 3.5], "texture": "#0"} - } - }, - { - "from": [2.5, 3.9, 2.4], - "to": [13.5, 14.9, 2.4], - "rotation": {"angle": 0, "axis": "y", "origin": [7.5, 2.9, 8.4]}, - "faces": { - "north": {"uv": [9.625, 4.125, 11.75, 6.25], "texture": "#0"}, - "east": {"uv": [5, 2.25, 5, 3.625], "texture": "#0"}, - "south": {"uv": [6.375, 2.25, 7.75, 3.625], "texture": "#0"}, - "west": {"uv": [6.375, 2.25, 6.375, 3.625], "texture": "#0"}, - "up": {"uv": [6.375, 2.25, 5, 2.25], "texture": "#0"}, - "down": {"uv": [7.75, 2.25, 6.375, 2.25], "texture": "#0"} - } - }, - { - "from": [2.5, 3.9, 2.35], - "to": [13.5, 14.9, 2.35], - "rotation": {"angle": 0, "axis": "y", "origin": [7.5, 2.9, 8.35]}, - "faces": { - "north": {"uv": [12.25, 3.9375, 14.375, 6.0625], "texture": "#0"}, - "east": {"uv": [5, 2.25, 5, 3.625], "texture": "#0"}, - "south": {"uv": [6.375, 2.25, 7.75, 3.625], "texture": "#0"}, - "west": {"uv": [6.375, 2.25, 6.375, 3.625], "texture": "#0"}, - "up": {"uv": [6.375, 2.25, 5, 2.25], "texture": "#0"}, - "down": {"uv": [7.75, 2.25, 6.375, 2.25], "texture": "#0"} - } - }, - { - "from": [2.5, 3.9, 2.6], - "to": [13.5, 14.9, 2.6], - "rotation": {"angle": 0, "axis": "y", "origin": [7.5, 2.9, 8.6]}, - "faces": { - "north": {"uv": [9.625, 6.25, 11.75, 8.375], "texture": "#0"}, - "east": {"uv": [7.25, 3.625, 7.25, 5], "texture": "#0"}, - "south": {"uv": [7.75, 5.75, 9.125, 7.125], "texture": "#0"}, - "west": {"uv": [8.625, 3.625, 8.625, 5], "texture": "#0"}, - "up": {"uv": [8.625, 3.625, 7.25, 3.625], "texture": "#0"}, - "down": {"uv": [10, 3.625, 8.625, 3.625], "texture": "#0"} - } - }, - { - "from": [2.5, 3.9, 2.45], - "to": [13.5, 14.9, 2.45], - "rotation": {"angle": 0, "axis": "y", "origin": [7.5, 2.9, 8.45]}, - "faces": { - "north": {"uv": [7, 0, 8.375, 1.375], "texture": "#0"}, - "east": {"uv": [7, 0, 7, 1.375], "texture": "#0"}, - "south": {"uv": [8.375, 0, 9.75, 1.375], "texture": "#0"}, - "west": {"uv": [8.375, 0, 8.375, 1.375], "texture": "#0"}, - "up": {"uv": [8.375, 0, 7, 0], "texture": "#0"}, - "down": {"uv": [9.75, 0, 8.375, 0], "texture": "#0"} - } - } - ], - "groups": [ - { - "name": "computer", - "origin": [13, 3, 9], - "scope": 0, - "color": 0, - "children": [ - 0, - { - "name": "computer_on", - "origin": [8, -1, 8.95], - "scope": 0, - "color": 0, - "children": [1] - }, - { - "name": "cables", - "origin": [13, 3, 9], - "scope": 0, - "color": 0, - "children": [2, 3] - }, - { - "name": "monitor", - "origin": [8, 3, 8], - "scope": 0, - "color": 0, - "children": [ - 4, - 5, - { - "name": "screens", - "origin": [7.5, 2.9, 8.5], - "scope": 0, - "color": 0, - "children": [ - { - "name": "on", - "origin": [8, 9.4, 2.375], - "scope": 0, - "color": 0, - "children": [6, 7] - }, - { - "name": "off", - "origin": [8, 9.4, 2.6], - "scope": 0, - "color": 0, - "children": [8] - }, - { - "name": "white_flash", - "origin": [8, 9.4, 2.45], - "scope": 0, - "color": 0, - "children": [9] - } - ] - } - ] - } - ] - } - ], - "display": { - "thirdperson_righthand": { - "rotation": [75, 45, 0], - "translation": [0, 2.5, 0], - "scale": [0.375, 0.375, 0.375] - }, - "thirdperson_lefthand": { - "rotation": [75, 45, 0], - "translation": [0, 2.5, 0], - "scale": [0.375, 0.375, 0.375] - }, - "firstperson_righthand": { - "rotation": [0, 45, 0], - "scale": [0.4, 0.4, 0.4] - }, - "firstperson_lefthand": { - "rotation": [0, 225, 0], - "scale": [0.4, 0.4, 0.4] - }, - "gui": { - "rotation": [30, 225, 0], - "scale": [0.625, 0.625, 0.625] - }, - "fixed": { - "rotation": [0, 180, 0], - "scale": [0.5, 0.5, 0.5] - } - } -} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk.json b/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk.json deleted file mode 100644 index 20b0be13..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "antarchy:item/floppy_disk/floppy_disk" - }, - "overrides": [ - { - "predicate": {"antarchy:disk_category": 1.0}, - "model": "antarchy:item/floppy_disk/elythia_floppy_disk" - }, - { - "predicate": {"antarchy:disk_category": 2.0}, - "model": "antarchy:item/floppy_disk/cavaryn_floppy_disk" - }, - { - "predicate": {"antarchy:disk_category": 3.0}, - "model": "antarchy:item/floppy_disk/thoraxis_floppy_disk" - } - ] -} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/cavaryn_floppy_disk.json b/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/cavaryn_floppy_disk.json deleted file mode 100644 index 3360a214..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/cavaryn_floppy_disk.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "antarchy:item/floppy_disk/cavaryn_floppy_disk" - } -} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/elythia_floppy_disk.json b/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/elythia_floppy_disk.json deleted file mode 100644 index fbe4e65b..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/elythia_floppy_disk.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "antarchy:item/floppy_disk/elythia_floppy_disk" - } -} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/thoraxis_floppy_disk.json b/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/thoraxis_floppy_disk.json deleted file mode 100644 index 8bf4e26e..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/item/floppy_disk/thoraxis_floppy_disk.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "antarchy:item/floppy_disk/thoraxis_floppy_disk" - } -} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/particles/bug_spray.json b/common-1.21.1/src/main/resources/assets/antarchy/particles/bug_spray.json new file mode 100644 index 00000000..ba25042c --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/particles/bug_spray.json @@ -0,0 +1 @@ +{"textures":["antarchy:bug_spray/bug_spray_particle1","antarchy:bug_spray/bug_spray_particle2","antarchy:bug_spray/bug_spray_particle3","antarchy:bug_spray/bug_spray_particle4"]} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds.json b/common-1.21.1/src/main/resources/assets/antarchy/sounds.json index 1a9fee58..5ceebe06 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/sounds.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/sounds.json @@ -3111,6 +3111,27 @@ { "name": "antarchy:mob/princess/fly6" } ] }, + "hoverboard_mount": { + "subtitle": "subtitles.antarchy.hoverboard_mount", + "sounds": [ + { "name": "antarchy:hoverboard/mount1" }, + { "name": "antarchy:hoverboard/mount2" }, + { "name": "antarchy:hoverboard/mount3" }, + { "name": "antarchy:hoverboard/mount4" } + ] + }, + "hoverboard_dismount": { + "subtitle": "subtitles.antarchy.hoverboard_dismount", + "sounds": [ + { "name": "antarchy:hoverboard/dismount1" }, + { "name": "antarchy:hoverboard/dismount2" }, + { "name": "antarchy:hoverboard/dismount3" } + ] + }, + "hoverboard_idle": { + "subtitle": "subtitles.antarchy.hoverboard_idle", + "sounds": [ { "name": "antarchy:hoverboard/idle1" } ] + }, "princess_step": { "subtitle": "subtitles.antarchy.princess_step", "sounds": [ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/dismount1.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/dismount1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e15e221ba601ac06ed0ca68127fe1d39615f86fb GIT binary patch literal 31715 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 literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/dismount2.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/dismount2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..271c9f9c562813b121d83acecf883ebb2297333a GIT binary patch literal 32526 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 F`5)Q++@b&g literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/dismount3.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/dismount3.ogg new file mode 100644 index 0000000000000000000000000000000000000000..969f68d26f001c0b52229c7b73fdd5601e35e570 GIT binary patch literal 30728 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 F{{T-6GcN!D literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/idle1.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/idle1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..d9c060aabf20c61b947bf0575cf4bf1bae2a8762 GIT binary patch literal 40925 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 literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/mount4.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/hoverboard/mount4.ogg new file mode 100644 index 0000000000000000000000000000000000000000..85b87c540d970944146b88a23ddfb82566490a4a GIT binary patch literal 28931 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 S000000002+23W&eU=t1(d%a8m literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/block/ant_trap.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/block/ant_trap.png new file mode 100644 index 0000000000000000000000000000000000000000..f3dd846e75523552156e9047c7dd52491d4560b5 GIT binary patch literal 1011 zcmVp{iyIR5CWqg5F$u|E(C=T*oBvAsJZK^ z?P;Dl?|J=NO%}WJWuJG>nVB;)XJ+1WHY~3;;^&psxZG$&jYZ3O=9$j&%1XQppB5J4 z;?mMN9q}Z?1y6`Ef+P@4!-&1pPjiFhi2;0H|KaGrQ-N^n_8s&M+;;J;{(cOO+;x8D zy_%ZB>*;B{nVG>wPbdX)ZVo+=f>}D3%y^@tquAWqvJuefbWqBj1D|K!!NCEV%_iFI zwxduvM^XTCAm>K20VRdO7`7yKqVN3Uhj~;h6^PqtG_ba|=J@jU8wLgjaMa#+w0`g6 z=FGwd^gNb@j5(~yqjg^VR{g!7W&rRet-`U#4pMy3e&k!&7gY?d#*c!k< zkOrrTodZ%JO6N%^#roVt#fLpAgOO9eZoHEUg{Q=FhblSKTtUgXW|HaC6Eqgcq^PkR zdA;pyP(T^#3&~`o8-m+Nc3duF?o@#oG|vbjq%8R&_*e$WV*^?DB*i6xt(f!$wQ-UI554Dl}Q0U04_R?UtPd#9f)8zdLw>`rqj zGOgO@RvjJ&EK3mt7#x+sAxNe>Mt2R)Jf>A}Y)zT^f|Z@iUdMJVwgPc#jz}#6NTc?< zY<;xzrL>3RCk2of8_h~?GZ@2kPcBvv}oN?u&xO7ykBnk0U z0s)0>TF`@xtCx=p>UC3XZN9#~&W()? z$IHn{zL=Qc_}Ey9o&)H0;dQM`xxOxIJ=nPk;3fk%8Mw*7f6u_b&ZfTr00960@z9d& h00006NklNo%Z literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.animation.json b/common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.animation.json deleted file mode 100644 index 062e648c..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.animation.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "format_version": "1.8.0", - "animations": { - "on_state": { - "loop": true, - "bones": { - "on": { - "scale": { - "vector": [1, 1, 1] - } - }, - "white_flash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "off": { - "scale": { - "vector": [0, 0, 0] - } - }, - "computer_on": { - "scale": { - "vector": [1, 1, 1] - } - } - } - }, - "off_state": { - "loop": true, - "bones": { - "on": { - "scale": { - "vector": [0, 0, 0] - } - }, - "white_flash": { - "scale": { - "vector": [0, 0, 0] - } - }, - "off": { - "scale": { - "vector": [1, 1, 1] - } - }, - "computer_on": { - "scale": { - "vector": [0, 0, 0] - } - } - } - }, - "turn_on": { - "loop": "hold_on_last_frame", - "animation_length": 0.4583, - "bones": { - "on": { - "scale": { - "0.0": { - "vector": [1, 0, 0] - }, - "0.125": { - "vector": [1, 0, 0] - }, - "0.2917": { - "vector": [1, 1, 1], - "easing": "easeInOutCubic" - } - } - }, - "white_flash": { - "scale": { - "0.0": { - "vector": [1, 0, 0] - }, - "0.0417": { - "vector": [1, 0.2, 0.2] - }, - "0.2083": { - "vector": [1, 1, 1], - "easing": "easeInOutQuint" - } - } - }, - "off": { - "scale": { - "vector": [1, 1, 1] - } - }, - "computer_on": { - "scale": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.0417": { - "vector": [1, 1, 1] - } - } - } - } - }, - "turn_off": { - "loop": "hold_on_last_frame", - "animation_length": 0.4583, - "bones": { - "on": { - "scale": { - "0.0": { - "vector": [1, 1, 1] - }, - "0.25": { - "vector": [1, 0, 1], - "easing": "easeInOutQuint" - } - } - }, - "white_flash": { - "scale": { - "0.0": { - "vector": [1, 1, 0] - }, - "0.125": { - "vector": [1, 1, 0] - }, - "0.375": { - "vector": [1, 0, 0], - "easing": "easeInOutQuart" - } - } - }, - "off": { - "scale": { - "0.0": { - "vector": [0, 0, 0] - }, - "0.0417": { - "vector": [1, 1, 1] - } - } - }, - "computer_on": { - "scale": { - "0.0": { - "vector": [1, 1, 1] - }, - "0.25": { - "vector": [1, 1, 1] - }, - "0.2917": { - "vector": [0, 0, 0] - } - } - } - } - } - } -} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.png deleted file mode 100644 index e24c294c8bd18287ae760ad598d9e45dc336dde5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10764 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 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.png.mcmeta b/common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.png.mcmeta deleted file mode 100644 index 68df5399..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/textures/block/computer/computer.png.mcmeta +++ /dev/null @@ -1,5 +0,0 @@ -{ - "animation": { - "frametime": 5 - } -} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/villager/profession/pest_control.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/villager/profession/pest_control.png new file mode 100644 index 0000000000000000000000000000000000000000..ddb6ca141cf1fbc0c3a744d31c958758c8bac79e GIT binary patch literal 613 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1U2Ka=yT1c|^D6=`qvl>hBDGPG=YfF|nYsH%>Hg(hnhWc7rSsgySX5rK} z=i-?*8J$J07E3eybbL~q0~`(Ftf$1d#_1{mP4daz{TfJ#mjw9*|Hld#o;f^=11jMx z@Q5sCVBk9p!i>lBSEK+1B}!Z)N`mv#O3D+9QW*jgGxJLH{9Hp6O!W-)%)76Bw*-15 z$=2V)Fa~SiJI`tZtmr zyTO_u{%2#unoUz@1}r+lygHp}N0Y&D$6 zWqyh>o)7Q0-Trg>oc!bqm4>??2sarlEKX>XI<%})>wh>dJFTRq_v+}qofV(*vVs5a=>vMYzNv7@Kf{#oUuesfNi zlIh;mdKI;Vst0Ec`3AOHXW literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/villager/profession/pest_control.png.mcmeta b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/villager/profession/pest_control.png.mcmeta new file mode 100644 index 00000000..5571aa23 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/villager/profession/pest_control.png.mcmeta @@ -0,0 +1,5 @@ +{ + "villager": { + "hat": "none" + } +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/ATTRIBUTION.txt b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/ATTRIBUTION.txt deleted file mode 100644 index eba7c488..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/ATTRIBUTION.txt +++ /dev/null @@ -1,7 +0,0 @@ -The pencil, eraser, trash, undo, and redo icons are adapted from Pixelarticons -by Gerrit Halfmann (https://github.com/halfmage/pixelarticons), version 2.4.1. - -The source SVG files are included in this directory. The PNGs were rasterized -at 24x24, recolored to Antarchy green, and given a one-pixel black outline. - -These icons are licensed under the MIT License. See LICENSE-pixelarticons.txt. diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/LICENSE-pixelarticons.txt b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/LICENSE-pixelarticons.txt deleted file mode 100644 index 4be35e8f..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/LICENSE-pixelarticons.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Gerrit Halfmann - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/eraser.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/eraser.png deleted file mode 100644 index c08255a27d0c022316da735104a1d9a719400a48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233 zcmV \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/pencil.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/pencil.png deleted file mode 100644 index 561b0b85e334c3b456eaa9ac97fbc5cb595c2c5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252 zcmVuEJeA+zy!;wjIcZ;gB \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/redo.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/redo.png deleted file mode 100644 index 295aefb5ea5d0e3f6ba216080da5eca18e55e763..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj3q4&NLn`LHoqUn2L4n70d)F_2 z`<(xg20Zdj0_!hl=F5IieYUrJv*6#Fqr27{%C64)*wtZ(7(&mKX7qiGTmGuWTD!qb~-+x$n-ks(Cf{>ij$W`m5wlqxgzst<} z=pEmeplQyFRNt9p2+p%oDraB1f2rwDX63*wCnx1*eeb9ej&j&IFZI_#!MsU1{_Fb| SEer=bmci52&t;ucLK6UYZCC#Q diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/redo.svg b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/redo.svg deleted file mode 100644 index e3d850e2..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/redo.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/trash.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/trash.png deleted file mode 100644 index b8ba1b211ca96167bdca225aa05cd60c287bfa96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjGdx`!Ln`LHo$AfkpuppzDX>$W z{qek60=(QkE0?ZS%KM_L`Iq-(!kGkz;*-94OV53tbhPGzNPn?PvZl($&Z%9}M;T&d zOeQa%aKid{MYeE#Y82m`c<{$`c)FopHT - - diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/undo.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/undo.png deleted file mode 100644 index 6ad846e0bb33d681c44f0b7a94506b3c02c74826..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj3q4&NLn`LH4T_?3u0Y zjPG|GWa>B$-`th;J>5Y-KKD`tqghxZ`!l1o_qPK-6tHO|n{m!eX#Fb8C-F?GK8Ba-cd^T-i7&tWdUR{js%aXfe}tqv4yoN~5X?LD#@G9C SRI4J;u?(KBelF{r5}E)i=~-9+ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/undo.svg b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/undo.svg deleted file mode 100644 index f9fbac20..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint/undo.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/item/bug_spray.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/item/bug_spray.png new file mode 100644 index 0000000000000000000000000000000000000000..35c4c9129f459ac8b71de1f7e566031b732d10f9 GIT binary patch literal 335 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}B0G|+7Q&ZEWOP9vQ#WgiGdAhLt|Npn|J5Cv8#ito-D%Wl%QUf5 z*}{Z*#flY?!K?)Z1wgG=6wD6*DWQ@ezhIyiRKW0P|9&B$EN6j7WHAE+-(e7DJf6QI z1t=&{;u=vBoS#-wo>-L15RjOeSEA?V8lqsTXQ*f1eeJs?P)&lTi(`mJaBZJA-vI>< z7r_nJwCdi~-*hu+EC~!V?pNeFbN>jY|wHSM3%aVNepVXlT5kZlM%=lHvZGkDFNR zmwIj1*Ywg4E!pnSwbS!{9AnS57e8v5l1&Q|n!XyBK4V?Jn9nWQWm7%SE(T9mKbLh* G2~7aYgnQfo literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/item/bug_spray_spraying.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/item/bug_spray_spraying.png new file mode 100644 index 0000000000000000000000000000000000000000..571c8a49ab6ae40ee472684628ac0e77d8caa07d GIT binary patch literal 348 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G|;0G|-oIeStj@9{P@HC?)NX{Qdo3-LbiGb_T2N2`G$8M0ZW@phDGBlm25LbE4F3Ml1%dLM1s;*b3=DjS zK$uZf!>a)(C{f}XQ4*Y=R#Ki=l*$m0n3-3i=jR%tV5(=RXWo77yCqOfs;7%%h(>Vi zxk#}F1rF8=E_00BzwN)?=j`}#ag4k>U)8H0GE3_0@)R5e8|NMB)aB_&aA3%4HcFJ0 zl9zassa)lEL;Gpa&*=}mymy}2&Uo->)U8+Q4(qSJsq1x+m*!BYsNVT&m2}Z7{y%4) Sa$Nvg$l&Sf=d#Wzp$PyTi-Y6< literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/item/computer.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/item/computer.png deleted file mode 100644 index 8ebda321d0b0ee517f8746241a8a557b85fad2e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2156 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 Y01Ug!AyL@d`~Uy|07*qoM6N<$f|2+P*#H0l literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/particle/bug_spray/bug_spray_particle4.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/particle/bug_spray/bug_spray_particle4.png new file mode 100644 index 0000000000000000000000000000000000000000..ef70266c81f7679266e0090eddbc47f756e0a69b GIT binary patch literal 588 zcmV-S0<-;zP)SK{HV>ZcUeWItnW7h0GnFI)Ife@SG?i zdoix8SO6$Nq!gpck`4%kC&SZ0zb{6kAyzRi1_A2;00`1KH8e5k@9O?m=6koG>wvlk zLvK60|C`6A`Z^>IE0@Rnq_2nfOKV7+YrpooN zwf4uY3m@~ViyA7JMzxq^TLDNGwR7$LzBk&wmaPj|D{;X;4*&rF|7Mo1Dhf!yp2GZ(F}v@#(v;{V(f!1KKnTYv-$$jjt6NS< zLslNjZgqjaVMX`>;xh;#q2{(rg;Dp8DJ<>$kyu`ff@H)fz)3gf`Q8dC(q+D`Sb%3`$eV3de}`Er)Psu(^EOViREg+o<8LpO z7RG*vI-Fv8EfS(v*c=CO_C&?x zR7Tmn&;zHHO8!cmNOFh~l-L(L{!*_0Vn{}Ou|S__at{n%5NG#2W|@xpJBL-vk6qi* zVDs&YYwyquveqP^n-%Bx}b)l#(OtqZUKU zXMAh6W)=EigZ_N>%(H?|=T&)_mzaK2JXG&J?1zqskDL&}#0MUFa~I=cA1DWNo3>$2M&$y zY!*>l3Xxxu@`k_ZHhNB~jWm0#DZm{UeAe^8S0k0FDeIm_FM6QM_bAaCNPn&!ue*s| zLi+4Ak=oY=UEJU36FgJXV{EnZ2ULBrM=Zte=GX3=6#(&(ZB={PmT7hU(v8UoF}tQ$ z8_&vZnX>V{QQ64poMy98`t3$XzyO6SKWoye?zE97ZW_44E&AvLsrRWg&7wMz93Cu_ z+XorxXJZ@o7WVLKOOd;%gEQNE-Q>2I12@396_=3f#{zwp9P?1JP867zfbj4LaOzzB zjfrNC-p~W{X+=)k$or5zmKNayQRU1@<80pZI5xh6GP=W5A%W|<5j@?7wJ))xC!yav z8T030w1;VT-83@&LR@0IwEm-5C~pC*VX><0@kTlzYf#`z)wdEUA>&anw;cg1cU13k b-=x=kegPq!&61?klH!Dz{0?5G0s!zAL#N4( literal 1451 zcmV;c1yuSUiwFP!00000|Fu}lZrer_y?jZO?AT4v0BO-py9-dj`vOJkM0V;$9l^2l zLIGh!j-`o74ly&5qAuEQ3v|~X=(=B#4=B)oD9}}x-E^1U5lP9C#^eA+Xb6<3Gv}VU z_spGVCHszg0H9xOeEIs`fz~kL(Ut7tr)M4nQV~V*J&y2lE~^0!6qf_nFpe4H zT3|d2j1SFvf*Kyzq}erNTnmh6f$=T$)M@rCG*_59%@w9jbA_qXTw&@o`xcrjOr7Qm zQ>VGY)M>6TYnnIw6PPqRW=x%*D@>iAD@>iAD@>hcXZfmMJ5Gb<3e&U(od(SnrfCf} zX?87mJPRJrg2y-UcxFtK$F^b_YTBf^!ZdlTsa}Ce^9_2MJmy+>%(d{CYvD21!eg$5 z$C@;|7CfE>k7vQ-Tk!ZM9z(sFcmimyMTKedPg5-d3(d~*USD_5!atsce>@BScozO? z((GFBcosaK1&?pRcH)j47DT)jr)R+1WBte zZkvPEz<5}?#CEk8WBukjQ`kIZ#%TOVNeIn(Ii&%efSq!&Hrih5f^DiQv<`YGQ z2e+!h%YiGiR6*R_)`;y|lyaMR3Owo~GYX0EIA_D&Ad#Rn~fsYYhRe|xbe&2RrUyk`~ z$R}CMveA&gagbj>B1}LrNRM}lw=ar1`58$m++P-F%94bP=s{Jafs7a?lu0!h^NFNb zS;29x=z(GmNRjfGg8%gJ*-=sIk5ZP=NRUhQg;Zj)NcLdy)3ns9W4DnhrHaHv5iO)e zCTfg{aUM9Z$3__@_Xy)EwlMj%4DXnCh1N2|IFk`ou)mzskSoR05Q5hnlL~E;AU};g zxRWO&qS`QnTrjSaU1lZcJc0eyJZPG$c?dg~EF;OFkaEv7%_(PbmDNoJ7*NuLMml*L z0d?9wSn<7PBS~i3`0bn|R4J;hzl+4Dn)u_j$SlukJmBPNknm9#y0~s}J?q3Fp1ffa zocUr>i?MfCV3!Szy0~$c=Fr#hrj$Z`{VgvzyI*J z1G^{LbdqFL5NrL@GQ_8VMBRoanmQ;?^JEkb#zd)9MH0O>+Ji{~7Ldd>^{{W~6k`|T~ F004^p-L(J! diff --git a/common-1.21.1/src/main/resources/data/antarchy/tags/entity_type/arthropods.json b/common-1.21.1/src/main/resources/data/antarchy/tags/entity_type/arthropods.json new file mode 100644 index 00000000..e0f3a117 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/tags/entity_type/arthropods.json @@ -0,0 +1 @@ +{"replace":false,"values":["antarchy:alpha_mantis","antarchy:bed_bug","antarchy:bomber","antarchy:brutalfly","antarchy:butterfly","antarchy:caterpillar","antarchy:emperor_scorpion","antarchy:flytrap","antarchy:hercules_beetle","antarchy:mantis","antarchy:rolly_polly","antarchy:scorpion","antarchy:spit_bug","antarchy:springbug","antarchy:stink_bug","antarchy:wasp"]} diff --git a/common-1.21.1/src/main/resources/data/antarchy/tags/worldgen/biome/has_structure/trayaurus_lab_biomes.json b/common-1.21.1/src/main/resources/data/antarchy/tags/worldgen/biome/has_structure/trayaurus_lab_biomes.json index b4a27d0f..d78507aa 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/tags/worldgen/biome/has_structure/trayaurus_lab_biomes.json +++ b/common-1.21.1/src/main/resources/data/antarchy/tags/worldgen/biome/has_structure/trayaurus_lab_biomes.json @@ -4,4 +4,4 @@ "antarchy:elythia_meadow", "antarchy:truffalo_forest" ] -} \ No newline at end of file +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/dimensional_tears.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/dimensional_tears.json index f2b49a38..1763b0ce 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/dimensional_tears.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/dimensional_tears.json @@ -12,8 +12,12 @@ "type": "minecraft:height_range", "height": { "type": "minecraft:uniform", - "min_inclusive": { "absolute": 24 }, - "max_inclusive": { "absolute": 170 } + "min_inclusive": { + "absolute": 24 + }, + "max_inclusive": { + "absolute": 170 + } } }, { diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/fallen_ouranwood_tree.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/fallen_ouranwood_tree.json index de592a7f..306faa83 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/fallen_ouranwood_tree.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/fallen_ouranwood_tree.json @@ -1,9 +1,19 @@ { "feature": "antarchy:fallen_ouranwood_tree", "placement": [ - { "type": "minecraft:rarity_filter", "chance": 5 }, - { "type": "minecraft:in_square" }, - { "type": "minecraft:heightmap", "heightmap": "WORLD_SURFACE_WG" }, - { "type": "minecraft:biome" } + { + "type": "minecraft:rarity_filter", + "chance": 5 + }, + { + "type": "minecraft:in_square" + }, + { + "type": "minecraft:heightmap", + "heightmap": "WORLD_SURFACE_WG" + }, + { + "type": "minecraft:biome" + } ] } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/fungal_mushroom.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/fungal_mushroom.json index a720f1e1..2ca02158 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/fungal_mushroom.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/fungal_mushroom.json @@ -1,9 +1,19 @@ { "feature": "antarchy:fungal_mushroom", "placement": [ - { "type": "minecraft:rarity_filter", "chance": 4 }, - { "type": "minecraft:in_square" }, - { "type": "minecraft:heightmap", "heightmap": "WORLD_SURFACE_WG" }, - { "type": "minecraft:biome" } + { + "type": "minecraft:rarity_filter", + "chance": 4 + }, + { + "type": "minecraft:in_square" + }, + { + "type": "minecraft:heightmap", + "heightmap": "WORLD_SURFACE_WG" + }, + { + "type": "minecraft:biome" + } ] } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch.json index b1b0dd8d..28ec81e3 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch.json @@ -1,9 +1,19 @@ { "feature": "antarchy:giant_lily_pad_patch", "placement": [ - { "type": "minecraft:rarity_filter", "chance": 6 }, - { "type": "minecraft:in_square" }, - { "type": "minecraft:heightmap", "heightmap": "MOTION_BLOCKING_NO_LEAVES" }, - { "type": "minecraft:biome" } + { + "type": "minecraft:rarity_filter", + "chance": 6 + }, + { + "type": "minecraft:in_square" + }, + { + "type": "minecraft:heightmap", + "heightmap": "MOTION_BLOCKING_NO_LEAVES" + }, + { + "type": "minecraft:biome" + } ] } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_common.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_common.json index 6b49ad1f..153f630d 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_common.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_common.json @@ -1,9 +1,19 @@ { "feature": "antarchy:giant_lily_pad_patch", "placement": [ - { "type": "minecraft:rarity_filter", "chance": 2 }, - { "type": "minecraft:in_square" }, - { "type": "minecraft:heightmap", "heightmap": "MOTION_BLOCKING_NO_LEAVES" }, - { "type": "minecraft:biome" } + { + "type": "minecraft:rarity_filter", + "chance": 2 + }, + { + "type": "minecraft:in_square" + }, + { + "type": "minecraft:heightmap", + "heightmap": "MOTION_BLOCKING_NO_LEAVES" + }, + { + "type": "minecraft:biome" + } ] } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_meadow.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_meadow.json index 9e0694f1..2a71d4d9 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_meadow.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_meadow.json @@ -1,9 +1,19 @@ { "feature": "antarchy:giant_lily_pad_patch", "placement": [ - { "type": "minecraft:rarity_filter", "chance": 8 }, - { "type": "minecraft:in_square" }, - { "type": "minecraft:heightmap", "heightmap": "MOTION_BLOCKING_NO_LEAVES" }, - { "type": "minecraft:biome" } + { + "type": "minecraft:rarity_filter", + "chance": 8 + }, + { + "type": "minecraft:in_square" + }, + { + "type": "minecraft:heightmap", + "heightmap": "MOTION_BLOCKING_NO_LEAVES" + }, + { + "type": "minecraft:biome" + } ] } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_peach_forest.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_peach_forest.json index 176eba6c..1752b357 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_peach_forest.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/giant_lily_pad_patch_peach_forest.json @@ -1,9 +1,19 @@ { "feature": "antarchy:giant_lily_pad_patch_peach_forest", "placement": [ - { "type": "minecraft:rarity_filter", "chance": 6 }, - { "type": "minecraft:in_square" }, - { "type": "minecraft:heightmap", "heightmap": "MOTION_BLOCKING_NO_LEAVES" }, - { "type": "minecraft:biome" } + { + "type": "minecraft:rarity_filter", + "chance": 6 + }, + { + "type": "minecraft:in_square" + }, + { + "type": "minecraft:heightmap", + "heightmap": "MOTION_BLOCKING_NO_LEAVES" + }, + { + "type": "minecraft:biome" + } ] } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_blood_crystal_spires.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_blood_crystal_spires.json index 1b324284..a4dc2f79 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_blood_crystal_spires.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_blood_crystal_spires.json @@ -29,5 +29,3 @@ } ] } - - diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_large_blood_crystal_spires.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_large_blood_crystal_spires.json index e75e392d..1f386190 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_large_blood_crystal_spires.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_large_blood_crystal_spires.json @@ -29,5 +29,3 @@ } ] } - - diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_medium_blood_crystal_spires.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_medium_blood_crystal_spires.json index 39631321..80554ca0 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_medium_blood_crystal_spires.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/lucid_medium_blood_crystal_spires.json @@ -29,5 +29,3 @@ } ] } - - diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ore_magma.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ore_magma.json index acd5b565..51bb28fe 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ore_magma.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ore_magma.json @@ -10,11 +10,15 @@ }, { "type": "minecraft:height_range", - "height": { - "type": "minecraft:uniform", - "max_inclusive": { "absolute": 144 }, - "min_inclusive": { "absolute": 8 } + "height": { + "type": "minecraft:uniform", + "max_inclusive": { + "absolute": 144 + }, + "min_inclusive": { + "absolute": 8 } + } }, { "type": "minecraft:biome" diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ouranwood_trees.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ouranwood_trees.json index 850dde08..023da009 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ouranwood_trees.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ouranwood_trees.json @@ -1,9 +1,19 @@ { "feature": "antarchy:ouranwood_tree_selector", "placement": [ - { "type": "minecraft:count", "count": 1 }, - { "type": "minecraft:in_square" }, - { "type": "minecraft:heightmap", "heightmap": "WORLD_SURFACE_WG" }, - { "type": "minecraft:biome" } + { + "type": "minecraft:count", + "count": 1 + }, + { + "type": "minecraft:in_square" + }, + { + "type": "minecraft:heightmap", + "heightmap": "WORLD_SURFACE_WG" + }, + { + "type": "minecraft:biome" + } ] } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ouranwood_trees_sparse.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ouranwood_trees_sparse.json index 61755a74..f596753c 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ouranwood_trees_sparse.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/ouranwood_trees_sparse.json @@ -1,9 +1,19 @@ { "feature": "antarchy:ouranwood_tree_selector_sparse", "placement": [ - { "type": "minecraft:rarity_filter", "chance": 6 }, - { "type": "minecraft:in_square" }, - { "type": "minecraft:heightmap", "heightmap": "WORLD_SURFACE_WG" }, - { "type": "minecraft:biome" } + { + "type": "minecraft:rarity_filter", + "chance": 6 + }, + { + "type": "minecraft:in_square" + }, + { + "type": "minecraft:heightmap", + "heightmap": "WORLD_SURFACE_WG" + }, + { + "type": "minecraft:biome" + } ] } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_floor_rib_columns.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_floor_rib_columns.json index 6e7c25e7..b972bc92 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_floor_rib_columns.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_floor_rib_columns.json @@ -9,7 +9,9 @@ "type": "minecraft:height_range", "height": { "type": "minecraft:uniform", - "min_inclusive": { "absolute": 32 }, + "min_inclusive": { + "absolute": 32 + }, "max_inclusive": { "absolute": 84 } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_floor_spikes.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_floor_spikes.json index d3bf833e..2bd6a911 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_floor_spikes.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_floor_spikes.json @@ -12,7 +12,9 @@ "type": "minecraft:height_range", "height": { "type": "minecraft:uniform", - "min_inclusive": { "absolute": 24 }, + "min_inclusive": { + "absolute": 24 + }, "max_inclusive": { "absolute": 96 } diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_ichor_lake.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_ichor_lake.json index 5ef5b1e4..2d7e2fc3 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_ichor_lake.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/thoraxis_ichor_lake.json @@ -12,8 +12,12 @@ "type": "minecraft:height_range", "height": { "type": "minecraft:uniform", - "min_inclusive": { "absolute": 1 }, - "max_inclusive": { "absolute": 1 } + "min_inclusive": { + "absolute": 1 + }, + "max_inclusive": { + "absolute": 1 + } } }, { diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/typhonite_veined_ore.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/typhonite_veined_ore.json index 6f2d2b7d..cb067565 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/typhonite_veined_ore.json +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/placed_feature/typhonite_veined_ore.json @@ -16,8 +16,12 @@ "type": "minecraft:height_range", "height": { "type": "minecraft:uniform", - "min_inclusive": { "absolute": 8 }, - "max_inclusive": { "absolute": 220 } + "min_inclusive": { + "absolute": 8 + }, + "max_inclusive": { + "absolute": 220 + } } }, { diff --git a/common-1.21.1/src/main/resources/data/minecraft/tags/point_of_interest_type/acquirable_job_site.json b/common-1.21.1/src/main/resources/data/minecraft/tags/point_of_interest_type/acquirable_job_site.json index df36bb72..7587066e 100644 --- a/common-1.21.1/src/main/resources/data/minecraft/tags/point_of_interest_type/acquirable_job_site.json +++ b/common-1.21.1/src/main/resources/data/minecraft/tags/point_of_interest_type/acquirable_job_site.json @@ -1,6 +1 @@ -{ - "replace": false, - "values": [ - "antarchy:computer" - ] -} +{"replace":false,"values":["antarchy:pest_control"]} diff --git a/common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/bastion_remnant.json b/common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/bastion_remnant.json new file mode 100644 index 00000000..5d341ba2 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/bastion_remnant.json @@ -0,0 +1,4 @@ +{ + "replace": false, + "values": [] +} diff --git a/common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/desert_pyramid.json b/common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/desert_pyramid.json new file mode 100644 index 00000000..5d341ba2 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/desert_pyramid.json @@ -0,0 +1,4 @@ +{ + "replace": false, + "values": [] +} diff --git a/common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/end_city.json b/common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/end_city.json new file mode 100644 index 00000000..5d341ba2 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/minecraft/tags/worldgen/biome/has_structure/end_city.json @@ -0,0 +1,4 @@ +{ + "replace": false, + "values": [] +} diff --git a/common-1.21.1/src/main/resources/data/minecraft/worldgen/noise/nether/temperature.json b/common-1.21.1/src/main/resources/data/minecraft/worldgen/noise/nether/temperature.json deleted file mode 100644 index ab7b15e6..00000000 --- a/common-1.21.1/src/main/resources/data/minecraft/worldgen/noise/nether/temperature.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "amplitudes": [ - 1.0, - 1.0 - ], - "firstOctave": -7 -} diff --git a/common-1.21.1/src/main/resources/data/minecraft/worldgen/noise/nether/vegetation.json b/common-1.21.1/src/main/resources/data/minecraft/worldgen/noise/nether/vegetation.json deleted file mode 100644 index ab7b15e6..00000000 --- a/common-1.21.1/src/main/resources/data/minecraft/worldgen/noise/nether/vegetation.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "amplitudes": [ - 1.0, - 1.0 - ], - "firstOctave": -7 -} diff --git a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/README.md b/common-1.21.1/src/test/resources/antarchy/computer/fixtures/README.md deleted file mode 100644 index 2301fa00..00000000 --- a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Computer contract fixtures - -These fixtures describe expected computer state transitions for validation tooling. They are not runtime datapack content and are not loaded by the game. - -The repository currently has no computer-specific test harness. The fixtures cover first placement, duplicate disk consumption, protection of the built-in introduction module, authenticated ejection through Settings or Trash, rejection without authentication, and physical disk drops when the block is broken. diff --git a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/built_in_introduction_protection.json b/common-1.21.1/src/test/resources/antarchy/computer/fixtures/built_in_introduction_protection.json deleted file mode 100644 index 6383bd49..00000000 --- a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/built_in_introduction_protection.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "case": "built_in_introduction_protection", - "physical_disk_ids": ["antarchy:chronosphere"], - "built_in_modules": ["antarchy:introduction"], - "settings_ejection_candidates": ["antarchy:chronosphere"], - "expected_dropped_disk_ids": ["antarchy:chronosphere"], - "expected_ejectable_ids": ["antarchy:chronosphere"], - "expected_introduction_dropped": false, - "expected_introduction_ejectable": false -} diff --git a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/duplicate_disk.json b/common-1.21.1/src/test/resources/antarchy/computer/fixtures/duplicate_disk.json deleted file mode 100644 index 35ac5ed2..00000000 --- a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/duplicate_disk.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "case": "duplicate_disk", - "initial_physical_disk_ids": ["antarchy:chronosphere"], - "inserted_disk_id": "antarchy:chronosphere", - "expected_physical_disk_ids": ["antarchy:chronosphere"], - "expected_item_consumed": true, - "expected_archive_change": false -} diff --git a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/ejection_and_drop.json b/common-1.21.1/src/test/resources/antarchy/computer/fixtures/ejection_and_drop.json deleted file mode 100644 index 6d6540a6..00000000 --- a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/ejection_and_drop.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "case": "ejection_and_drop", - "physical_disk_ids": ["antarchy:chronosphere", "antarchy:toreterror_arena"], - "ejection": {"authenticated": true, "route": "settings_or_trash", "selected_disk_id": "antarchy:chronosphere", "expected_returned_item_id": "antarchy:floppy_disk", "expected_remaining_physical_disk_ids": ["antarchy:toreterror_arena"]}, - "drop_after_break": {"expected_dropped_disk_ids": ["antarchy:toreterror_arena"], "expected_introduction_dropped": false}, - "rejection": {"authenticated": false, "expected_ejection": false} -} diff --git a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/newly_placed.json b/common-1.21.1/src/test/resources/antarchy/computer/fixtures/newly_placed.json deleted file mode 100644 index 500d082c..00000000 --- a/common-1.21.1/src/test/resources/antarchy/computer/fixtures/newly_placed.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "case": "newly_placed", - "physical_disk_ids": [], - "built_in_modules": ["antarchy:introduction"], - "password": {"state": "unset"}, - "session": {"authenticated": false, "screen_occupied": false}, - "expectations": ["introduction_visible_before_login", "first_use_password_setup_required", "introduction_not_ejectable", "introduction_not_dropped"] -} diff --git a/fabric-1.21.1/build.gradle b/fabric-1.21.1/build.gradle index ec00d835..fa2c87eb 100644 --- a/fabric-1.21.1/build.gradle +++ b/fabric-1.21.1/build.gradle @@ -38,6 +38,8 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version_1_21_1}" modApi "net.fabricmc.fabric-api:fabric-api:${fabric_api_version_1_21_1}" modImplementation "software.bernie.geckolib:geckolib-fabric-1.21.1:${geckolib_version}" + // AntOS is developed beside Antarchy as a separate local project. + modImplementation files("$rootDir/../AntOS/fabric-1.21.1/build/libs/antos-fabric-1.21.1-0.1.0.jar") modCompileOnly("curse.maven:intapi-${intapi_curseforge_id}:${intapi_curseforge_file_id_fabric}") modLocalRuntime("curse.maven:intapi-${intapi_curseforge_id}:${intapi_curseforge_file_id_fabric}") modLocalRuntime("dev.architectury:architectury-fabric:${architectury_api_version}") diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabric.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabric.java index c0b568a8..7380c260 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabric.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabric.java @@ -160,34 +160,6 @@ public final class AntarchyFabric implements ModInitializer { Antarchy.init(); BloodCrystalShardItem.SYNC_BLOODGLASS = BloodglassManager::syncBloodglass; registerTradeReloadListener(); - ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new IdentifiableResourceReloadListener() { - @Override - public ResourceLocation getFabricId() { - return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "computer_guide"); - } - - @Override - public CompletableFuture reload(PreparableReloadListener.PreparationBarrier barrier, ResourceManager manager, - ProfilerFiller prepareProfiler, ProfilerFiller applyProfiler, - Executor prepareExecutor, Executor applyExecutor) { - return com.craisinlord.antarchy.content.guide.ComputerGuideData.instance().reload( - barrier, manager, prepareProfiler, applyProfiler, prepareExecutor, applyExecutor); - } - }); - ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new IdentifiableResourceReloadListener() { - @Override - public ResourceLocation getFabricId() { - return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antmail_events"); - } - - @Override - public CompletableFuture reload(PreparableReloadListener.PreparationBarrier barrier, ResourceManager manager, - ProfilerFiller prepareProfiler, ProfilerFiller applyProfiler, - Executor prepareExecutor, Executor applyExecutor) { - return com.craisinlord.antarchy.content.antmail.AntmailEventData.instance().reload( - barrier, manager, prepareProfiler, applyProfiler, prepareExecutor, applyExecutor); - } - }); ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new IdentifiableResourceReloadListener() { @Override public ResourceLocation getFabricId() { @@ -233,6 +205,20 @@ public final class AntarchyFabric implements ModInitializer { return computerScientistDelegate.reload(barrier, manager, prepareProfiler, applyProfiler, prepareExecutor, applyExecutor); } }); + com.craisinlord.antarchy.content.entity.trades.PestControlTradeManager pestControlDelegate = new com.craisinlord.antarchy.content.entity.trades.PestControlTradeManager(); + ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new IdentifiableResourceReloadListener() { + @Override + public ResourceLocation getFabricId() { + return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "pest_control_trades"); + } + + @Override + public CompletableFuture reload(PreparableReloadListener.PreparationBarrier barrier, ResourceManager manager, + ProfilerFiller prepareProfiler, ProfilerFiller applyProfiler, + Executor prepareExecutor, Executor applyExecutor) { + return pestControlDelegate.reload(barrier, manager, prepareProfiler, applyProfiler, prepareExecutor, applyExecutor); + } + }); } private static final TagKey CAVARYN_BIOMES = TagKey.create( diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricContent.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricContent.java index 44df8be9..23514f50 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricContent.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricContent.java @@ -229,6 +229,10 @@ public final class AntarchyFabricContent { AntarchyFabricSounds.MANTIS_HURT, AntarchyFabricSounds.MANTIS_ATTACK, AntarchyFabricSounds.MANTIS_FLY_LOOP, + AntarchyFabricSounds.MANTICORE_IDLE, + AntarchyFabricSounds.MANTICORE_WALK, + AntarchyFabricSounds.MANTICORE_FLY, + AntarchyFabricSounds.MANTICORE_BITE, AntarchyFabricSounds.GRAVITY_GUN_PICKUP, AntarchyFabricSounds.GRAVITY_GUN_DROP, AntarchyFabricSounds.GRAVITY_GUN_HOLD_LOOP, @@ -352,6 +356,7 @@ public final class AntarchyFabricContent { AntarchyFabricSounds.DIMENSIONAL_TEAR_CREATURE_LEAVES, AntarchyFabricSounds.DIMENSIONAL_TEAR_CLOSE ); + AntarchySoundEvents.bindHoverboardSounds(AntarchyFabricSounds.HOVERBOARD_MOUNT, AntarchyFabricSounds.HOVERBOARD_DISMOUNT, AntarchyFabricSounds.HOVERBOARD_IDLE); AntarchyObjects.setOctopusBomb(AntarchyFabricEntities.OCTOPUS_BOMB); AntarchyObjects.setTentacle(AntarchyFabricEntities.TENTACLE); @@ -402,10 +407,6 @@ public final class AntarchyFabricContent { AntarchyObjects.setCookedCorndog(() -> AntarchyFabricItems.COOKED_CORNDOG.get()); AntarchyObjects.setGlimmerBottle(() -> AntarchyFabricItems.GLIMMER_BOTTLE.get()); AntarchyObjects.setGlimmerVariantComponent(() -> AntarchyFabricMisc.GLIMMER_VARIANT.get()); - AntarchyObjects.setFloppyDiskComponent(() -> AntarchyFabricMisc.FLOPPY_DISK_COMPONENT.get()); - AntarchyObjects.setFloppyDisk(() -> AntarchyFabricItems.FLOPPY_DISK.get()); - AntarchyObjects.setComputer(() -> AntarchyFabricBlocks.COMPUTER.get()); - AntarchyObjects.setComputerBlockEntity(() -> AntarchyFabricBlocks.COMPUTER_BLOCK_ENTITY.get()); AntarchyObjects.setAmericanComponent(() -> AntarchyFabricMisc.AMERICAN.get()); AntarchyObjects.setLumen(() -> AntarchyFabricMisc.LUMEN.get()); AntarchyObjects.setFlowingLumen(() -> AntarchyFabricMisc.FLOWING_LUMEN.get()); @@ -414,6 +415,7 @@ public final class AntarchyFabricContent { AntarchyObjects.setLumenFroglight(() -> AntarchyFabricBlocks.LUMEN_FROGLIGHT.get()); AntarchyObjects.setGlimmeringReed(() -> AntarchyFabricBlocks.GLIMMERING_REED.get()); AntarchyObjects.setPeachLeavesParticle(() -> AntarchyFabricMisc.PEACH_LEAVES_PARTICLE.get()); + AntarchyObjects.setBugSprayParticle(() -> AntarchyFabricMisc.BUG_SPRAY_PARTICLE.get()); AntarchyObjects.setLotusPollen(() -> AntarchyFabricMisc.LOTUS_POLLEN.get()); AntarchyObjects.setNightmareFireFlame(() -> AntarchyFabricMisc.NIGHTMARE_FIRE_FLAME.get()); AntarchyObjects.setLucidBoltImpactSmall(() -> AntarchyFabricMisc.LUCID_BOLT_IMPACT_SMALL.get()); diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java index ae9fa330..7ac5117e 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java @@ -189,8 +189,6 @@ public final class AntarchyFabricEvents { }); ServerTickEvents.END_SERVER_TICK.register(server -> { - com.craisinlord.antarchy.content.antmail.AntmailServerData.access(server).drainAvailable(server); - com.craisinlord.antarchy.content.antmail.AntmailEventData.onTick(server); Set activeThisTick = new HashSet<>(); for (ServerLevel level : server.getAllLevels()) { CavarynHordeManager.tick(level); @@ -221,6 +219,15 @@ public final class AntarchyFabricEvents { factories -> factories.add(ComputerScientistTradeManager.listing(tradeLevel, tradeIndex))); } } + int[] pestControlSlots = com.craisinlord.antarchy.content.entity.trades.PestControlTradeManager.slotsPerLevel(); + for (int level = 1; level <= pestControlSlots.length; level++) { + final int tradeLevel = level; + for (int index = 0; index < pestControlSlots[level - 1]; index++) { + final int tradeIndex = index; + TradeOfferHelper.registerVillagerOffers(AntarchyFabricMisc.PEST_CONTROL.get(), tradeLevel, + factories -> factories.add(com.craisinlord.antarchy.content.entity.trades.PestControlTradeManager.listing(tradeLevel, tradeIndex))); + } + } TradeOfferHelper.registerVillagerOffers(VillagerProfession.FARMER, 1, factories -> factories.add((trader, random) -> new MerchantOffer( new ItemCost(AntarchyFabricItems.CORN.get(), 20), diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClient.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClient.java index a1f3f6b7..dbccf137 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClient.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClient.java @@ -1,8 +1,7 @@ package com.craisinlord.antarchy.fabric.client; import com.craisinlord.antarchy.Antarchy; -import com.craisinlord.antarchy.content.client.AntarchyClientHooks; -import com.craisinlord.antarchy.content.client.screen.ComputerScreen; +import com.craisinlord.antarchy.content.client.game.AntarchyComputerGames; import com.craisinlord.antarchy.fabric.network.AntarchyFabricClientNetworking; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; @@ -12,7 +11,6 @@ import net.minecraft.server.packs.PackType; import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.client.Minecraft; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -23,7 +21,15 @@ public final class AntarchyFabricClient implements ClientModInitializer { AntarchyFabricClientNetworking.register(); AntarchyFabricClientNetworking.bootstrapMultipartClient(); AntarchyFabricClientBootstrap.register(); - AntarchyClientHooks.setComputerOpener(pos -> Minecraft.getInstance().setScreen(new ComputerScreen(pos))); + AntarchyComputerGames.register(); + ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener(new IdentifiableResourceReloadListener() { + @Override public ResourceLocation getFabricId() { return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "antos_computer_data"); } + @Override public CompletableFuture reload(PreparableReloadListener.PreparationBarrier barrier, ResourceManager manager, + ProfilerFiller prepareProfiler, ProfilerFiller applyProfiler, Executor prepareExecutor, Executor applyExecutor) { + return com.craisinlord.antos.content.guide.ComputerGuideData.instance().reload( + barrier, manager, prepareProfiler, applyProfiler, prepareExecutor, applyExecutor); + } + }); ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener(new IdentifiableResourceReloadListener() { @Override public ResourceLocation getFabricId() { diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java index cdba72f8..d5d4d020 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java @@ -103,10 +103,6 @@ public final class AntarchyFabricClientBootstrap { (net.minecraft.world.level.block.entity.BlockEntityType) AntarchyFabricBlocks.UNDERVAULT_BLOCK_ENTITY.get(), (BlockEntityRendererProvider) UnderVaultRenderer::new ); - BlockEntityRendererRegistry.register( - (net.minecraft.world.level.block.entity.BlockEntityType) AntarchyFabricBlocks.COMPUTER_BLOCK_ENTITY.get(), - (BlockEntityRendererProvider) ComputerRenderer::new - ); EntityRendererRegistry.register(AntarchyFabricEntities.EASTER_BUNNY.get(), context -> withTemporalGeoLayer(new EasterBunnyRenderer(context))); EntityRendererRegistry.register(AntarchyFabricEntities.FLYING_SQUIRREL.get(), context -> withTemporalGeoLayer(new FlyingSquirrelRenderer(context))); @@ -205,7 +201,7 @@ public final class AntarchyFabricClientBootstrap { EntityModelLayerRegistry.registerModelLayer(PeachBoatRenderer.chestBoatLayer(), ChestBoatModel::createBodyModel); } - private static > R withTemporalGeoLayer(R renderer) { + private static > R withTemporalGeoLayer(R renderer) { renderer.addRenderLayer(new TemporalContractionGeoLayer<>(renderer)); return renderer; } @@ -328,6 +324,7 @@ public final class AntarchyFabricClientBootstrap { registry.register(AntarchyFabricMisc.STINKY_GAS.get(), HypnoticGasParticle.Provider::new); registry.register(AntarchyFabricMisc.STINKY_FLY.get(), FireflyParticle.Provider::new); registry.register(AntarchyFabricMisc.PEACH_LEAVES_PARTICLE.get(), PeachLeavesParticle.Provider::new); + registry.register(AntarchyFabricMisc.BUG_SPRAY_PARTICLE.get(), com.craisinlord.antarchy.content.client.particle.BugSprayParticle.Provider::new); registry.register(AntarchyFabricMisc.LOTUS_POLLEN.get(), com.craisinlord.antarchy.content.client.particle.LotusPollenParticle.Provider::new); registry.register(AntarchyFabricMisc.HYPNOTIC_GAS.get(), HypnoticGasParticle.Provider::new); registry.register(AntarchyFabricMisc.HYPNOTIC_GAS_DOWN.get(), sprites -> new HypnoticGasParticle.Provider(sprites, true)); @@ -516,11 +513,6 @@ public final class AntarchyFabricClientBootstrap { return cage.getItemState(stack); } ); - ItemProperties.register( - AntarchyFabricItems.FLOPPY_DISK.get(), - ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "disk_category"), - (stack, level, entity, seed) -> com.craisinlord.antarchy.content.item.FloppyDiskItem.categoryModelProperty(stack) - ); } @SuppressWarnings({"unchecked", "rawtypes"}) diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/BigBerthaClientHandler.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/BigBerthaClientHandler.java index 0a3cd5d3..85c90ef5 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/BigBerthaClientHandler.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/BigBerthaClientHandler.java @@ -1,7 +1,9 @@ package com.craisinlord.antarchy.fabric.client; import com.craisinlord.antarchy.content.item.BigBerthaItem; +import com.craisinlord.antarchy.content.item.RoyalGuardianSwordItem; import com.craisinlord.antarchy.content.network.BigBerthaModeCyclePayload; +import com.craisinlord.antarchy.content.network.RoyalGuardianSwordModeCyclePayload; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.minecraft.client.Minecraft; @@ -22,7 +24,9 @@ public final class BigBerthaClientHandler { return; } - if (!(mc.player.getMainHandItem().getItem() instanceof BigBerthaItem)) { + boolean bigBertha = mc.player.getMainHandItem().getItem() instanceof BigBerthaItem; + boolean royalGuardianSword = mc.player.getMainHandItem().getItem() instanceof RoyalGuardianSwordItem; + if (!bigBertha && !royalGuardianSword) { lastUseDown = false; return; } @@ -31,8 +35,9 @@ public final class BigBerthaClientHandler { if (useDown && !lastUseDown && mc.player.isShiftKeyDown() - && mc.player.getCooldowns().isOnCooldown(mc.player.getMainHandItem().getItem())) { - ClientPlayNetworking.send(new BigBerthaModeCyclePayload()); + ) { + if (bigBertha) ClientPlayNetworking.send(new BigBerthaModeCyclePayload()); + else ClientPlayNetworking.send(new RoyalGuardianSwordModeCyclePayload()); } lastUseDown = useDown; } diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java index 656e5566..165a2527 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java @@ -36,12 +36,9 @@ public final class AntarchyFabricClientNetworking { } public static void register() { - ClientPlayNetworking.registerGlobalReceiver(AntmailResultPayload.TYPE, (payload, context) -> - context.client().execute(() -> com.craisinlord.antarchy.content.client.AntmailClientState.update(payload))); - ClientPlayNetworking.registerGlobalReceiver(ComputerAccessResultPayload.TYPE, (payload, context) -> - context.client().execute(() -> com.craisinlord.antarchy.content.client.ComputerAccessClientState.update(payload))); - com.craisinlord.antarchy.content.network.ComputerNetworking.setSender(ServerPlayNetworking::send); - com.craisinlord.antarchy.content.network.AntmailNetworking.setSender(ServerPlayNetworking::send); + ClientPlayNetworking.registerGlobalReceiver(AntarchyGameResultPayload.TYPE, (payload, context) -> + context.client().execute(() -> com.craisinlord.antarchy.content.client.AntarchyGameClientState.update(payload))); + com.craisinlord.antarchy.content.network.AntarchyGameNetworking.setSender(ClientPlayNetworking::send); ClientPlayNetworking.registerGlobalReceiver(GravityStatePayload.TYPE, (payload, context) -> context.client().execute(() -> AntarchyFabricNetworking.handleGravityState(context.player(), payload))); ClientPlayNetworking.registerGlobalReceiver(BloodglassStatePayload.TYPE, (payload, context) -> diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java index ded60b8c..58626021 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java @@ -49,9 +49,7 @@ public final class AntarchyFabricNetworking { public static void register() { registerPayloadTypes(); registerServerReceivers(); - com.craisinlord.antarchy.content.network.ComputerAccessHandler.setResultSender(ServerPlayNetworking::send); - com.craisinlord.antarchy.content.network.AntmailServerHandler.setResultSender(ServerPlayNetworking::send); - com.craisinlord.antarchy.content.network.ComputerNetworking.setSender(ServerPlayNetworking::send); + com.craisinlord.antarchy.content.network.AntarchyGameNetworkHandler.setResultSender(ServerPlayNetworking::send); } public static void bootstrapMultipartCommon() { @@ -75,8 +73,7 @@ public final class AntarchyFabricNetworking { } private static void registerPayloadTypes() { - PayloadTypeRegistry.playS2C().register(ComputerAccessResultPayload.TYPE, ComputerAccessResultPayload.STREAM_CODEC); - PayloadTypeRegistry.playS2C().register(AntmailResultPayload.TYPE, AntmailResultPayload.STREAM_CODEC); + PayloadTypeRegistry.playS2C().register(AntarchyGameResultPayload.TYPE, AntarchyGameResultPayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(GravityStatePayload.TYPE, GravityStatePayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(BloodglassStatePayload.TYPE, BloodglassStatePayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(TigerEyeCamouflageStatePayload.TYPE, TigerEyeCamouflageStatePayload.STREAM_CODEC); @@ -97,6 +94,8 @@ public final class AntarchyFabricNetworking { PayloadTypeRegistry.playC2S().register(RoyalMountVerticalPayload.TYPE, RoyalMountVerticalPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(GravityGunScrollPayload.TYPE, GravityGunScrollPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(BigBerthaModeCyclePayload.TYPE, BigBerthaModeCyclePayload.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(com.craisinlord.antarchy.content.network.RoyalGuardianSwordModeCyclePayload.TYPE, + com.craisinlord.antarchy.content.network.RoyalGuardianSwordModeCyclePayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(ToggleTigerEyeCamouflagePayload.TYPE, ToggleTigerEyeCamouflagePayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(ToggleRoyalInversionPayload.TYPE, ToggleRoyalInversionPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(com.craisinlord.antarchy.content.network.ActivateRoyalBoundaryPayload.TYPE, com.craisinlord.antarchy.content.network.ActivateRoyalBoundaryPayload.STREAM_CODEC); @@ -111,36 +110,12 @@ public final class AntarchyFabricNetworking { PayloadTypeRegistry.playC2S().register(HerculesBeetleMountedChargePayload.TYPE, HerculesBeetleMountedChargePayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(MultipartAttackPayload.TYPE, MultipartAttackPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(MultipartInteractPayload.TYPE, MultipartInteractPayload.STREAM_CODEC); - PayloadTypeRegistry.playC2S().register(ComputerAccessPayload.TYPE, ComputerAccessPayload.STREAM_CODEC); - PayloadTypeRegistry.playC2S().register(AntmailSetupPayload.TYPE, AntmailSetupPayload.STREAM_CODEC); - PayloadTypeRegistry.playC2S().register(AntmailStateRequestPayload.TYPE, AntmailStateRequestPayload.STREAM_CODEC); - PayloadTypeRegistry.playC2S().register(AntmailMessageRequestPayload.TYPE, AntmailMessageRequestPayload.STREAM_CODEC); - PayloadTypeRegistry.playC2S().register(AntmailSendPayload.TYPE, AntmailSendPayload.STREAM_CODEC); - PayloadTypeRegistry.playC2S().register(AntmailReadPayload.TYPE, AntmailReadPayload.STREAM_CODEC); - PayloadTypeRegistry.playC2S().register(AntmailDeletePayload.TYPE, AntmailDeletePayload.STREAM_CODEC); - PayloadTypeRegistry.playC2S().register(AntmailDraftPayload.TYPE, AntmailDraftPayload.STREAM_CODEC); - PayloadTypeRegistry.playC2S().register(AntmailRetryPayload.TYPE, AntmailRetryPayload.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(AntarchyGamePayload.TYPE, AntarchyGamePayload.STREAM_CODEC); } private static void registerServerReceivers() { - ServerPlayNetworking.registerGlobalReceiver(ComputerAccessPayload.TYPE, (payload, context) -> - context.server().execute(() -> ComputerAccessHandler.handle(context.player(), payload))); - ServerPlayNetworking.registerGlobalReceiver(AntmailSetupPayload.TYPE, (payload, context) -> - context.server().execute(() -> AntmailServerHandler.handle(context.player(), payload))); - ServerPlayNetworking.registerGlobalReceiver(AntmailStateRequestPayload.TYPE, (payload, context) -> - context.server().execute(() -> AntmailServerHandler.handle(context.player(), payload))); - ServerPlayNetworking.registerGlobalReceiver(AntmailMessageRequestPayload.TYPE, (payload, context) -> - context.server().execute(() -> AntmailServerHandler.handle(context.player(), payload))); - ServerPlayNetworking.registerGlobalReceiver(AntmailSendPayload.TYPE, (payload, context) -> - context.server().execute(() -> AntmailServerHandler.handle(context.player(), payload))); - ServerPlayNetworking.registerGlobalReceiver(AntmailReadPayload.TYPE, (payload, context) -> - context.server().execute(() -> AntmailServerHandler.handle(context.player(), payload))); - ServerPlayNetworking.registerGlobalReceiver(AntmailDeletePayload.TYPE, (payload, context) -> - context.server().execute(() -> AntmailServerHandler.handle(context.player(), payload))); - ServerPlayNetworking.registerGlobalReceiver(AntmailDraftPayload.TYPE, (payload, context) -> - context.server().execute(() -> AntmailServerHandler.handle(context.player(), payload))); - ServerPlayNetworking.registerGlobalReceiver(AntmailRetryPayload.TYPE, (payload, context) -> - context.server().execute(() -> AntmailServerHandler.handle(context.player(), payload))); + ServerPlayNetworking.registerGlobalReceiver(AntarchyGamePayload.TYPE, (payload, context) -> + context.server().execute(() -> AntarchyGameNetworkHandler.handle(context.player(), payload))); ServerPlayNetworking.registerGlobalReceiver(GravityGunPrimaryPayload.TYPE, (payload, context) -> context.server().execute(() -> handleGravityGunPrimary(context.player(), payload))); ServerPlayNetworking.registerGlobalReceiver(PortalGunPrimaryPayload.TYPE, (payload, context) -> @@ -155,6 +130,8 @@ public final class AntarchyFabricNetworking { context.server().execute(() -> handleGravityGunScroll(context.player(), payload))); ServerPlayNetworking.registerGlobalReceiver(BigBerthaModeCyclePayload.TYPE, (payload, context) -> context.server().execute(() -> handleBigBerthaModeCycle(context.player(), payload))); + ServerPlayNetworking.registerGlobalReceiver(com.craisinlord.antarchy.content.network.RoyalGuardianSwordModeCyclePayload.TYPE, + (payload, context) -> context.server().execute(() -> handleRoyalGuardianSwordModeCycle(context.player()))); ServerPlayNetworking.registerGlobalReceiver(ToggleTigerEyeCamouflagePayload.TYPE, (payload, context) -> context.server().execute(() -> handleTigerEyeCamouflageToggle(context.player()))); ServerPlayNetworking.registerGlobalReceiver(ToggleRoyalInversionPayload.TYPE, (payload, context) -> @@ -322,7 +299,14 @@ public final class AntarchyFabricNetworking { if (!(player.getMainHandItem().getItem() instanceof BigBerthaItem bigBerthaItem)) { return; } - bigBerthaItem.tryCycleModeWhileCoolingDown(player.serverLevel(), player, player.getMainHandItem()); + bigBerthaItem.tryCycleModeFromInput(player.serverLevel(), player, player.getMainHandItem()); + } + + private static void handleRoyalGuardianSwordModeCycle(ServerPlayer player) { + if (!(player.getMainHandItem().getItem() instanceof com.craisinlord.antarchy.content.item.RoyalGuardianSwordItem sword)) { + return; + } + sword.tryCycleModeFromInput(player.serverLevel(), player, player.getMainHandItem()); } private static void handleDiamondMinecartInput(ServerPlayer player, DiamondMinecartInputPayload payload) { diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricBlocks.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricBlocks.java index b10eb392..391615cc 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricBlocks.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricBlocks.java @@ -6,7 +6,6 @@ import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.block.*; import com.craisinlord.antarchy.content.block.entity.AntNestBlockEntity; import com.craisinlord.antarchy.content.block.entity.CritterCageBlockEntity; -import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; import com.craisinlord.antarchy.content.block.entity.DreamCampfireBlockEntity; import com.craisinlord.antarchy.content.block.entity.HushweedBlockEntity; import com.craisinlord.antarchy.content.block.entity.LucidAnchorBlockEntity; @@ -1066,6 +1065,11 @@ public final class AntarchyFabricBlocks { public static final DeferredBlock TERMITE_NEST = BLOCKS.register("termite_nest", () -> new AntNestBlock(AntarchyFabricEntities.TERMITE, AntarchyFabricBlocks::antNestBlockEntityType, BlockBehaviour.Properties.ofFullCopy(Blocks.DIRT).noLootTable())); + public static final DeferredBlock ANT_TRAP = BLOCKS.register("ant_trap", + () -> new com.craisinlord.antarchy.content.block.AntTrapBlock( + () -> java.util.List.of(AntarchyFabricEntities.RED_ANT.get(), AntarchyFabricEntities.BROWN_ANT.get(), AntarchyFabricEntities.RAINBOW_ANT.get(), AntarchyFabricEntities.TERMITE.get()), + AntarchyFabricBlocks::antNestBlockEntityType, BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_PRESSURE_PLATE).noOcclusion())); + public static final DeferredBlock URANIUM_ORE = BLOCKS.register("uranium_ore", () -> createOre(Blocks.EMERALD_ORE, 4, 8, MapColor.COLOR_YELLOW)); @@ -1212,8 +1216,6 @@ public final class AntarchyFabricBlocks { public static final DeferredBlock UPPER = BLOCKS.register("upper", () -> new UpperBlock(AntarchyFabricBlocks::upperBlockEntityType, BlockBehaviour.Properties.ofFullCopy(Blocks.HOPPER))); - public static final DeferredBlock COMPUTER = BLOCKS.register("computer", - () -> new com.craisinlord.antarchy.content.block.ComputerBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).noOcclusion())); public static final DeferredBlock ANTIMETAL_RAIL = BLOCKS.register("antimetal_rail", () -> new com.craisinlord.antarchy.content.block.AntimetalRailBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.RAIL))); @@ -1456,7 +1458,8 @@ public final class AntarchyFabricBlocks { RED_ANT_NEST.get(), BROWN_ANT_NEST.get(), RAINBOW_ANT_NEST.get(), - TERMITE_NEST.get() + TERMITE_NEST.get(), + ANT_TRAP.get() ).build(null)); @@ -1531,11 +1534,6 @@ public final class AntarchyFabricBlocks { (pos, state) -> new DimensionalTearMarkerBlockEntity(pos, state, AntarchyFabricBlocks::dimensionalTearMarkerBlockEntityType), DIMENSIONAL_TEAR_MARKER.get() ).build(null)); - public static final DeferredHolder, BlockEntityType> COMPUTER_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("computer", - () -> BlockEntityType.Builder.of( - (pos, state) -> new ComputerBlockEntity(pos, state, AntarchyFabricBlocks::computerBlockEntityType), - COMPUTER.get() - ).build(null)); public static final DeferredHolder, BlockEntityType> QUEEN_TRAIL_SPAWN_MARKER_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("queen_trail_spawn_marker", () -> BlockEntityType.Builder.of( (pos, state) -> new QueenTrailSpawnMarkerBlockEntity(pos, state, AntarchyFabricBlocks::queenTrailSpawnMarkerBlockEntityType), @@ -1576,10 +1574,6 @@ public final class AntarchyFabricBlocks { return UPPER_BLOCK_ENTITY.get(); } - private static BlockEntityType computerBlockEntityType() { - return COMPUTER_BLOCK_ENTITY.get(); - } - private static BlockEntityType vortexLensBlockEntityType() { return VORTEX_LENS_BLOCK_ENTITY.get(); } diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricItems.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricItems.java index e3062ff3..1d4cfde0 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricItems.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricItems.java @@ -86,7 +86,6 @@ public final class AntarchyFabricItems { public static final DeferredItem NIGHTMARE_SCALE = ITEMS.registerSimpleItem("nightmare_scale", new Item.Properties().rarity(Rarity.RARE).fireResistant()); - public static final DeferredItem FLOPPY_DISK = ITEMS.register("floppy_disk", () -> new com.craisinlord.antarchy.content.item.FloppyDiskItem(new Item.Properties())); public static final DeferredItem MUSIC_DISC_UNDERSIDE = ITEMS.registerSimpleItem("music_disc_underside", new Item.Properties().stacksTo(1).rarity(Rarity.RARE) @@ -100,8 +99,6 @@ public final class AntarchyFabricItems { public static final DeferredItem DUPLICATOR_LOG_ITEM = ITEMS.registerSimpleBlockItem(AntarchyFabricBlocks.DUPLICATOR_LOG); - public static final DeferredItem COMPUTER_ITEM = ITEMS.register("computer", - () -> new ComputerItem(AntarchyFabricBlocks.COMPUTER.get(), new Item.Properties())); public static final DeferredItem OURANWOOD_LOG_ITEM = ITEMS.registerSimpleBlockItem(AntarchyFabricBlocks.OURANWOOD_LOG); @@ -509,6 +506,11 @@ public final class AntarchyFabricItems { public static final DeferredItem RAINBOW_ANT_NEST_ITEM = ITEMS.registerSimpleBlockItem(AntarchyFabricBlocks.RAINBOW_ANT_NEST); public static final DeferredItem TERMITE_NEST_ITEM = ITEMS.registerSimpleBlockItem(AntarchyFabricBlocks.TERMITE_NEST); + public static final DeferredItem ANT_TRAP_ITEM = ITEMS.registerSimpleBlockItem(AntarchyFabricBlocks.ANT_TRAP); + + public static final DeferredItem BUG_SPRAY = ITEMS.register("bug_spray", + () -> new com.craisinlord.antarchy.content.item.BugSprayItem(new Item.Properties().stacksTo(16))); + public static final DeferredItem URANIUM_ORE_ITEM = ITEMS.registerSimpleBlockItem(AntarchyFabricBlocks.URANIUM_ORE, new Item.Properties().rarity(Rarity.UNCOMMON)); diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricMisc.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricMisc.java index 9066e779..f7d3b19a 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricMisc.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricMisc.java @@ -135,7 +135,7 @@ public final class AntarchyFabricMisc { public static final DeferredHolder COMPUTER_POI = POI_TYPES.register("computer", () -> new PoiType( - java.util.Set.copyOf(AntarchyFabricBlocks.COMPUTER.get().getStateDefinition().getPossibleStates()), + java.util.Set.copyOf(net.minecraft.world.level.block.Block.byItem(com.craisinlord.antos.content.AntOSObjects.COMPUTER_ITEM.get()).getStateDefinition().getPossibleStates()), 1, 1)); @@ -148,6 +148,13 @@ public final class AntarchyFabricMisc { com.google.common.collect.ImmutableSet.of(), SoundEvents.VILLAGER_WORK_LIBRARIAN)); + public static final DeferredHolder PEST_CONTROL_POI = POI_TYPES.register("pest_control", + () -> new PoiType(java.util.Set.copyOf(AntarchyFabricBlocks.ANT_TRAP.get().getStateDefinition().getPossibleStates()), 1, 1)); + + public static final DeferredHolder PEST_CONTROL = VILLAGER_PROFESSIONS.register("pest_control", + () -> new VillagerProfession("pest_control", holder -> holder.value() == PEST_CONTROL_POI.get(), + holder -> holder.value() == PEST_CONTROL_POI.get(), com.google.common.collect.ImmutableSet.of(), com.google.common.collect.ImmutableSet.of(), SoundEvents.VILLAGER_WORK_FLETCHER)); + public static final DeferredRegister ARMOR_MATERIALS = DeferredRegister.create(Registries.ARMOR_MATERIAL, Antarchy.MODID); @@ -212,13 +219,6 @@ public final class AntarchyFabricMisc { .networkSynchronized(com.craisinlord.antarchy.content.entity.glimmer.GlimmerVariant.STREAM_CODEC) .build()); - public static final DeferredHolder, net.minecraft.core.component.DataComponentType> FLOPPY_DISK_COMPONENT = - DATA_COMPONENT_TYPES.register("floppy_disk", - () -> net.minecraft.core.component.DataComponentType.builder() - .persistent(net.minecraft.resources.ResourceLocation.CODEC) - .networkSynchronized(net.minecraft.resources.ResourceLocation.STREAM_CODEC) - .build()); - public static final DeferredHolder, net.minecraft.core.component.DataComponentType> AMERICAN = DATA_COMPONENT_TYPES.register("american", @@ -358,6 +358,8 @@ public final class AntarchyFabricMisc { public static final DeferredHolder, SimpleParticleType> DREAM_FIRE_FLAME = PARTICLE_TYPES.register("dream_fire_flame", () -> simpleParticleType()); + public static final DeferredHolder, SimpleParticleType> BUG_SPRAY_PARTICLE = PARTICLE_TYPES.register("bug_spray", + () -> simpleParticleType()); public static final DeferredHolder, SimpleParticleType> NIGHTMARE_FIRE_FLAME = PARTICLE_TYPES.register("nightmare_fire_flame", () -> simpleParticleType()); diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java index 29eb0a49..dfdc91bb 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java @@ -305,6 +305,14 @@ public final class AntarchyFabricSounds { public static final DeferredHolder MANTIS_FLY_LOOP = registerSoundEvent("mantis_fly_loop"); + public static final DeferredHolder MANTICORE_IDLE = registerSoundEvent("manticore_idle"); + + public static final DeferredHolder MANTICORE_WALK = registerSoundEvent("manticore_walk"); + + public static final DeferredHolder MANTICORE_FLY = registerSoundEvent("manticore_fly"); + + public static final DeferredHolder MANTICORE_BITE = registerSoundEvent("manticore_bite"); + public static final DeferredHolder GRAVITY_GUN_PICKUP = registerSoundEvent("gravity_gun_pickup"); @@ -576,6 +584,9 @@ public final class AntarchyFabricSounds { public static final DeferredHolder PRINCESS_SHOOT = registerSoundEvent("princess_shoot"); public static final DeferredHolder PRINCESS_FLY = registerSoundEvent("princess_fly"); public static final DeferredHolder PRINCESS_STEP = registerSoundEvent("princess_step"); + public static final DeferredHolder HOVERBOARD_MOUNT = registerSoundEvent("hoverboard_mount"); + public static final DeferredHolder HOVERBOARD_DISMOUNT = registerSoundEvent("hoverboard_dismount"); + public static final DeferredHolder HOVERBOARD_IDLE = registerSoundEvent("hoverboard_idle"); diff --git a/fabric-1.21.1/src/main/resources/fabric.mod.json b/fabric-1.21.1/src/main/resources/fabric.mod.json index 03dbd314..ec2c4832 100644 --- a/fabric-1.21.1/src/main/resources/fabric.mod.json +++ b/fabric-1.21.1/src/main/resources/fabric.mod.json @@ -35,6 +35,7 @@ "minecraft": "~${minecraft_version}", "java": ">=21", "fabric-api": "*", + "antos": "*", "integrated_api": "*", "geckolib": "*", "sizeable_foliage": "*" diff --git a/neoforge-1.21.1/build.gradle b/neoforge-1.21.1/build.gradle index 593a1e7b..365eadf6 100644 --- a/neoforge-1.21.1/build.gradle +++ b/neoforge-1.21.1/build.gradle @@ -46,6 +46,9 @@ neoForge { } dependencies { + // AntOS is developed beside Antarchy as a separate local project. + compileOnly(files("$rootDir/../AntOS/neoforge-1.21.1/build/libs/antos-neoforge-1.21.1-0.1.0.jar")) + runtimeOnly(files("$rootDir/../AntOS/neoforge-1.21.1/build/libs/antos-neoforge-1.21.1-0.1.0.jar")) compileOnly("curse.maven:intapi-${intapi_curseforge_id}:${intapi_curseforge_file_id_neoforge}") runtimeOnly("curse.maven:intapi-${intapi_curseforge_id}:${intapi_curseforge_file_id_neoforge}") implementation "software.bernie.geckolib:geckolib-neoforge-1.21.1:${geckolib_version}" diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java index ca44e19c..66422232 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMobsConfig.java @@ -701,14 +701,14 @@ public final class AntarchyMobsConfig { b.push("royalMounts"); ROYAL_EGG_HATCH_CHANCE = b.comment("Chance denominator for a royal egg advancing one hatch stage on a random tick.").defineInRange("eggHatchChance", 3, 1, 100000); ROYAL_BOLT_DAMAGE = b.comment("Direct damage dealt by a Prince or Princess royal bolt.").defineInRange("boltDamage", 6.0D, 0.0D, 1024.0D); - PRINCE_HEALTH = b.comment("Prince base max health.").defineInRange("princeHealth", 120.0D, 1.0D, 32768.0D); + PRINCE_HEALTH = b.comment("Prince base max health.").defineInRange("princeHealth", 360.0D, 1.0D, 32768.0D); PRINCE_ATTACK_DAMAGE = b.comment("Prince bite damage.").defineInRange("princeAttackDamage", 14.0D, 0.0D, 1024.0D); PRINCE_MOVEMENT_SPEED = b.comment("Prince grounded movement speed.").defineInRange("princeMovementSpeed", 0.28D, 0.0D, 10.0D); PRINCE_FLYING_SPEED = b.comment("Prince flying movement speed.").defineInRange("princeFlyingSpeed", 0.9D, 0.0D, 10.0D); PRINCE_ARMOR = b.comment("Prince armor value.").defineInRange("princeArmor", 10.0D, 0.0D, 1024.0D); PRINCE_KNOCKBACK_RESISTANCE = b.comment("Prince knockback resistance.").defineInRange("princeKnockbackResistance", 0.6D, 0.0D, 1.0D); PRINCE_FOLLOW_RANGE = b.comment("Prince owner and target range.").defineInRange("princeFollowRange", 48.0D, 1.0D, 256.0D); - PRINCESS_HEALTH = b.comment("Princess base max health.").defineInRange("princessHealth", 110.0D, 1.0D, 32768.0D); + PRINCESS_HEALTH = b.comment("Princess base max health.").defineInRange("princessHealth", 330.0D, 1.0D, 32768.0D); PRINCESS_ATTACK_DAMAGE = b.comment("Princess bite damage.").defineInRange("princessAttackDamage", 10.0D, 0.0D, 1024.0D); PRINCESS_MOVEMENT_SPEED = b.comment("Princess grounded movement speed.").defineInRange("princessMovementSpeed", 0.28D, 0.0D, 10.0D); PRINCESS_FLYING_SPEED = b.comment("Princess flying movement speed.").defineInRange("princessFlyingSpeed", 0.95D, 0.0D, 10.0D); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java index 3882d215..49770204 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java @@ -247,6 +247,7 @@ public class AntarchyNeoforge { AntarchyNeoforgeSounds.DIMENSIONAL_TEAR_CREATURE_LEAVES, AntarchyNeoforgeSounds.DIMENSIONAL_TEAR_CLOSE ); + AntarchySoundEvents.bindHoverboardSounds(AntarchyNeoforgeSounds.HOVERBOARD_MOUNT, AntarchyNeoforgeSounds.HOVERBOARD_DISMOUNT, AntarchyNeoforgeSounds.HOVERBOARD_IDLE); bindCommonObjects(); ScorpionWhipTetherSync.setSink((player, targetId) -> PacketDistributor.sendToPlayersTrackingEntityAndSelf( player, @@ -288,13 +289,9 @@ public class AntarchyNeoforge { AntarchyNeoforgeCreativeModeTabs.register(modEventBus); AntarchyNeoforgeEntityAttributes.register(modEventBus); AntarchyNeoforgePayloadHandlers.register(modEventBus); - com.craisinlord.antarchy.content.network.ComputerNetworking.setSender(payload -> + com.craisinlord.antarchy.content.network.AntarchyGameNetworking.setSender(payload -> net.neoforged.neoforge.network.PacketDistributor.sendToServer(payload)); - com.craisinlord.antarchy.content.network.AntmailNetworking.setSender(payload -> - net.neoforged.neoforge.network.PacketDistributor.sendToServer((net.minecraft.network.protocol.common.custom.CustomPacketPayload) payload)); - com.craisinlord.antarchy.content.network.ComputerAccessHandler.setResultSender((player, payload) -> - net.neoforged.neoforge.network.PacketDistributor.sendToPlayer(player, payload)); - com.craisinlord.antarchy.content.network.AntmailServerHandler.setResultSender((player, payload) -> + com.craisinlord.antarchy.content.network.AntarchyGameNetworkHandler.setResultSender((player, payload) -> net.neoforged.neoforge.network.PacketDistributor.sendToPlayer(player, payload)); Antarchy.init(); } @@ -483,10 +480,6 @@ public class AntarchyNeoforge { AntarchyObjects.setCookedCorndog(() -> AntarchyNeoforgeItems.COOKED_CORNDOG.get()); AntarchyObjects.setGlimmerBottle(() -> AntarchyNeoforgeItems.GLIMMER_BOTTLE.get()); AntarchyObjects.setGlimmerVariantComponent(() -> AntarchyNeoforgeMisc.GLIMMER_VARIANT.get()); - AntarchyObjects.setFloppyDiskComponent(() -> AntarchyNeoforgeMisc.FLOPPY_DISK_COMPONENT.get()); - AntarchyObjects.setFloppyDisk(() -> AntarchyNeoforgeItems.FLOPPY_DISK.get()); - AntarchyObjects.setComputer(() -> AntarchyNeoforgeBlocks.COMPUTER.get()); - AntarchyObjects.setComputerBlockEntity(() -> AntarchyNeoforgeBlocks.COMPUTER_BLOCK_ENTITY.get()); AntarchyObjects.setAmericanComponent(() -> AntarchyNeoforgeMisc.AMERICAN.get()); AntarchyObjects.setLumen(() -> AntarchyNeoforgeMisc.LUMEN.get()); AntarchyObjects.setFlowingLumen(() -> AntarchyNeoforgeMisc.FLOWING_LUMEN.get()); @@ -495,6 +488,7 @@ public class AntarchyNeoforge { AntarchyObjects.setLumenFroglight(() -> AntarchyNeoforgeBlocks.LUMEN_FROGLIGHT.get()); AntarchyObjects.setGlimmeringReed(() -> AntarchyNeoforgeBlocks.GLIMMERING_REED.get()); AntarchyObjects.setPeachLeavesParticle(() -> AntarchyNeoforgeMisc.PEACH_LEAVES_PARTICLE.get()); + AntarchyObjects.setBugSprayParticle(() -> AntarchyNeoforgeMisc.BUG_SPRAY_PARTICLE.get()); AntarchyObjects.setLotusPollen(() -> AntarchyNeoforgeMisc.LOTUS_POLLEN.get()); AntarchyObjects.setNightmareFireFlame(() -> AntarchyNeoforgeMisc.NIGHTMARE_FIRE_FLAME.get()); AntarchyObjects.setLucidBoltImpactSmall(() -> AntarchyNeoforgeMisc.LUCID_BOLT_IMPACT_SMALL.get()); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforgeEvents.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforgeEvents.java index 7b61efb8..089dad02 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforgeEvents.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforgeEvents.java @@ -11,6 +11,8 @@ import com.craisinlord.antarchy.content.time.TimeDilationCommand; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.Component; @EventBusSubscriber(modid = Antarchy.MODID) public final class AntarchyNeoforgeEvents { diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyToolsConfig.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyToolsConfig.java index 02bbb003..942f04a6 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyToolsConfig.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyToolsConfig.java @@ -540,10 +540,10 @@ public final class AntarchyToolsConfig { BLOOD_CRYSTAL_LEGGINGS_DEFENSE = b.comment("Armor defense value for Blood Crystal Leggings. Default: 5 (iron).").defineInRange("leggingsDefense", 5, 0, 30); BLOOD_CRYSTAL_BOOTS_DEFENSE = b.comment("Armor defense value for Blood Crystal Boots. Default: 2 (iron).").defineInRange("bootsDefense", 2, 0, 30); BLOOD_CRYSTAL_ARMOR_TOUGHNESS = b.comment("Armor toughness for all Blood Crystal armor pieces. Default: 0.0 (iron).").defineInRange("armorToughness", 0.0, 0.0, 20.0); - BLOOD_CRYSTAL_HELMET_DURABILITY = b.comment("Max durability of Blood Crystal Helmet. Default: 77 (gold).").defineInRange("helmetDurability", 77, 1, 2000); - BLOOD_CRYSTAL_CHESTPLATE_DURABILITY = b.comment("Max durability of Blood Crystal Chestplate. Default: 112 (gold).").defineInRange("chestplateDurability", 112, 1, 2000); - BLOOD_CRYSTAL_LEGGINGS_DURABILITY = b.comment("Max durability of Blood Crystal Leggings. Default: 105 (gold).").defineInRange("leggingsDurability", 105, 1, 2000); - BLOOD_CRYSTAL_BOOTS_DURABILITY = b.comment("Max durability of Blood Crystal Boots. Default: 91 (gold).").defineInRange("bootsDurability", 91, 1, 2000); + BLOOD_CRYSTAL_HELMET_DURABILITY = b.comment("Max durability of Blood Crystal Helmet. Default: 92.").defineInRange("helmetDurability", 92, 1, 2000); + BLOOD_CRYSTAL_CHESTPLATE_DURABILITY = b.comment("Max durability of Blood Crystal Chestplate. Default: 134.").defineInRange("chestplateDurability", 134, 1, 2000); + BLOOD_CRYSTAL_LEGGINGS_DURABILITY = b.comment("Max durability of Blood Crystal Leggings. Default: 126.").defineInRange("leggingsDurability", 126, 1, 2000); + BLOOD_CRYSTAL_BOOTS_DURABILITY = b.comment("Max durability of Blood Crystal Boots. Default: 109.").defineInRange("bootsDurability", 109, 1, 2000); b.pop(); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java index 37a79002..03d2bb6e 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java @@ -2,8 +2,7 @@ package com.craisinlord.antarchy.neoforge.client; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.client.GoopedHudRenderer; -import com.craisinlord.antarchy.content.client.AntarchyClientHooks; -import com.craisinlord.antarchy.content.client.screen.ComputerScreen; +import com.craisinlord.antarchy.content.client.game.AntarchyComputerGames; import com.craisinlord.antarchy.content.client.HordeHudRenderer; import com.craisinlord.antarchy.content.client.TigerEyeClientHooks; import com.craisinlord.antarchy.content.client.hud.BloodglassHudRenderer; @@ -60,6 +59,7 @@ import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; import net.neoforged.neoforge.client.event.EntityRenderersEvent; import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.client.event.RegisterGuiLayersEvent; import net.neoforged.neoforge.client.gui.VanillaGuiLayers; import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; @@ -93,6 +93,11 @@ public final class AntarchyNeoForgeClient { private AntarchyNeoForgeClient() { } + @SubscribeEvent + public static void clientSetup(FMLClientSetupEvent event) { + event.enqueueWork(AntarchyComputerGames::register); + } + @SubscribeEvent public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) { event.registerBlockEntityRenderer(AntarchyNeoforgeBlocks.DREAM_CAMPFIRE_BLOCK_ENTITY.get(), CampfireRenderer::new); @@ -101,7 +106,6 @@ public final class AntarchyNeoForgeClient { event.registerBlockEntityRenderer(AntarchyNeoforgeBlocks.CRITTER_CAGE_BLOCK_ENTITY.get(), CritterCageRenderer::new); event.registerBlockEntityRenderer(AntarchyNeoforgeBlocks.UNDERTRIAL_SPAWNER_BLOCK_ENTITY.get(), UndertrialSpawnerRenderer::new); event.registerBlockEntityRenderer(AntarchyNeoforgeBlocks.UNDERVAULT_BLOCK_ENTITY.get(), UnderVaultRenderer::new); - event.registerBlockEntityRenderer(AntarchyNeoforgeBlocks.COMPUTER_BLOCK_ENTITY.get(), ComputerRenderer::new); event.registerEntityRenderer(AntarchyNeoforgeEntites.EASTER_BUNNY.get(), context -> withParalyzedGeoLayer(new EasterBunnyRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.FLYING_SQUIRREL.get(), context -> withParalyzedGeoLayer(new FlyingSquirrelRenderer(context))); event.registerEntityRenderer(AntarchyNeoforgeEntites.CATERPILLAR.get(), context -> withParalyzedGeoLayer(new CaterpillarRenderer(context))); @@ -205,7 +209,6 @@ public final class AntarchyNeoForgeClient { renderer.addLayer(new GoopedLivingLayer(renderer)); renderer.addLayer(new GlimmeringLivingLayer(renderer)); renderer.addLayer(new com.craisinlord.antarchy.content.client.renderer.RoyalIndicatorLivingLayer<>(renderer)); - renderer.addLayer(new RoyalAssailantAxeAfterimageLayer(renderer)); renderer.addLayer(new BrutalflyElytraLayer(renderer)); renderer.addLayer(new com.craisinlord.antarchy.content.client.renderer.ManticoreWingsLayer(renderer)); renderer.addLayer(new FallenKingCrownLayer(renderer)); @@ -326,6 +329,7 @@ public final class AntarchyNeoForgeClient { event.registerSpriteSet(AntarchyNeoforgeMisc.STINKY_GAS.get(), HypnoticGasParticle.Provider::new); event.registerSpriteSet(AntarchyNeoforgeMisc.STINKY_FLY.get(), FireflyParticle.Provider::new); event.registerSpriteSet(AntarchyNeoforgeMisc.PEACH_LEAVES_PARTICLE.get(), PeachLeavesParticle.Provider::new); + event.registerSpriteSet(AntarchyNeoforgeMisc.BUG_SPRAY_PARTICLE.get(), com.craisinlord.antarchy.content.client.particle.BugSprayParticle.Provider::new); event.registerSpriteSet(AntarchyNeoforgeMisc.LOTUS_POLLEN.get(), com.craisinlord.antarchy.content.client.particle.LotusPollenParticle.Provider::new); event.registerSpriteSet(AntarchyNeoforgeMisc.HYPNOTIC_GAS.get(), HypnoticGasParticle.Provider::new); event.registerSpriteSet(AntarchyNeoforgeMisc.HYPNOTIC_GAS_DOWN.get(), sprites -> new HypnoticGasParticle.Provider(sprites, true)); @@ -605,7 +609,6 @@ public final class AntarchyNeoForgeClient { @SubscribeEvent public static void onClientSetup(FMLClientSetupEvent event) { event.enqueueWork(() -> { - AntarchyClientHooks.setComputerOpener(pos -> Minecraft.getInstance().setScreen(new ComputerScreen(pos))); TigerEyeClientHooks.setCamouflageKeyTextSupplier(() -> AntarchyKeyBindings.TIGERS_EYE_CAMOUFLAGE.getTranslatedKeyMessage()); ItemBlockRenderTypes.setRenderLayer(AntarchyNeoforgeBlocks.UNDERTRIAL_SPAWNER.get(), RenderType.cutout()); ItemBlockRenderTypes.setRenderLayer(AntarchyNeoforgeBlocks.UNDERVAULT.get(), RenderType.cutout()); @@ -715,12 +718,12 @@ public final class AntarchyNeoForgeClient { ItemBlockRenderTypes.setRenderLayer(AntarchyNeoforgeBlocks.GLOWCAP_MUSHROOM.get(), RenderType.cutout()); registerUltimateBowProperties(); registerUltimateCrossbowProperties(); - registerFloppyDiskProperties(); }); } @SubscribeEvent public static void registerClientReloadListeners(RegisterClientReloadListenersEvent event) { + event.registerReloadListener(com.craisinlord.antos.content.guide.ComputerGuideData.instance()); event.registerReloadListener(new net.minecraft.server.packs.resources.SimplePreparableReloadListener() { @Override protected Void prepare(net.minecraft.server.packs.resources.ResourceManager resourceManager, net.minecraft.util.profiling.ProfilerFiller profiler) { @@ -734,14 +737,6 @@ public final class AntarchyNeoForgeClient { }); } - private static void registerFloppyDiskProperties() { - ItemProperties.register( - AntarchyNeoforgeItems.FLOPPY_DISK.get(), - ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "disk_category"), - (stack, level, entity, seed) -> com.craisinlord.antarchy.content.item.FloppyDiskItem.categoryModelProperty(stack) - ); - } - private static void registerUltimateBowProperties() { ItemProperties.register( AntarchyNeoforgeItems.ULTIMATE_BOW.get(), diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/BigBerthaClientHandler.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/BigBerthaClientHandler.java index cea5c426..34fa5930 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/BigBerthaClientHandler.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/BigBerthaClientHandler.java @@ -2,7 +2,9 @@ package com.craisinlord.antarchy.neoforge.client; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.item.BigBerthaItem; +import com.craisinlord.antarchy.content.item.RoyalGuardianSwordItem; import com.craisinlord.antarchy.content.network.BigBerthaModeCyclePayload; +import com.craisinlord.antarchy.content.network.RoyalGuardianSwordModeCyclePayload; import net.minecraft.client.Minecraft; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; @@ -25,7 +27,9 @@ public final class BigBerthaClientHandler { return; } - if (!(mc.player.getMainHandItem().getItem() instanceof BigBerthaItem)) { + boolean bigBertha = mc.player.getMainHandItem().getItem() instanceof BigBerthaItem; + boolean royalGuardianSword = mc.player.getMainHandItem().getItem() instanceof RoyalGuardianSwordItem; + if (!bigBertha && !royalGuardianSword) { lastUseDown = false; return; } @@ -34,8 +38,9 @@ public final class BigBerthaClientHandler { if (useDown && !lastUseDown && mc.player.isShiftKeyDown() - && mc.player.getCooldowns().isOnCooldown(mc.player.getMainHandItem().getItem())) { - PacketDistributor.sendToServer(new BigBerthaModeCyclePayload()); + ) { + if (bigBertha) PacketDistributor.sendToServer(new BigBerthaModeCyclePayload()); + else PacketDistributor.sendToServer(new RoyalGuardianSwordModeCyclePayload()); } lastUseDown = useDown; } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/network/AntarchyGravityNetworking.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/network/AntarchyGravityNetworking.java index cf76f499..b6899ab9 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/network/AntarchyGravityNetworking.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/network/AntarchyGravityNetworking.java @@ -7,6 +7,7 @@ import com.craisinlord.antarchy.content.item.EyeOfTheStormItem; import com.craisinlord.antarchy.content.item.GravityGunItem; import com.craisinlord.antarchy.content.item.PortalGunItem; import com.craisinlord.antarchy.content.network.BigBerthaModeCyclePayload; +import com.craisinlord.antarchy.content.network.RoyalGuardianSwordModeCyclePayload; import com.craisinlord.antarchy.content.network.EyeOfStormPrimaryPayload; import com.craisinlord.antarchy.content.network.GravityGunPrimaryPayload; import com.craisinlord.antarchy.content.network.GravityGunScrollPayload; @@ -54,6 +55,10 @@ public final class AntarchyGravityNetworking { BigBerthaModeCyclePayload.TYPE, BigBerthaModeCyclePayload.STREAM_CODEC, AntarchyGravityNetworking::handleBigBerthaModeCycle + ).playToServer( + RoyalGuardianSwordModeCyclePayload.TYPE, + RoyalGuardianSwordModeCyclePayload.STREAM_CODEC, + AntarchyGravityNetworking::handleRoyalGuardianSwordModeCycle ).playToServer( com.craisinlord.antarchy.content.network.DiamondMinecartInputPayload.TYPE, com.craisinlord.antarchy.content.network.DiamondMinecartInputPayload.STREAM_CODEC, @@ -149,7 +154,17 @@ public final class AntarchyGravityNetworking { return; } - bigBerthaItem.tryCycleModeWhileCoolingDown(serverPlayer.serverLevel(), serverPlayer, serverPlayer.getMainHandItem()); + bigBerthaItem.tryCycleModeFromInput(serverPlayer.serverLevel(), serverPlayer, serverPlayer.getMainHandItem()); + }); + } + + private static void handleRoyalGuardianSwordModeCycle(RoyalGuardianSwordModeCyclePayload payload, IPayloadContext context) { + context.enqueueWork(() -> { + if (!(context.player() instanceof ServerPlayer serverPlayer) + || !(serverPlayer.getMainHandItem().getItem() instanceof com.craisinlord.antarchy.content.item.RoyalGuardianSwordItem sword)) { + return; + } + sword.tryCycleModeFromInput(serverPlayer.serverLevel(), serverPlayer, serverPlayer.getMainHandItem()); }); } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java index a58be766..8f860f1f 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java @@ -263,6 +263,14 @@ public final class AntarchyNeoForgeEvents { .add(ComputerScientistTradeManager.listing(level, index)); } } + } else if (event.getType() == AntarchyNeoforgeMisc.PEST_CONTROL.get()) { + int[] slots = com.craisinlord.antarchy.content.entity.trades.PestControlTradeManager.slotsPerLevel(); + for (int level = 1; level <= slots.length; level++) { + for (int index = 0; index < slots[level - 1]; index++) { + event.getTrades().computeIfAbsent(level, ignored -> new java.util.ArrayList<>()) + .add(com.craisinlord.antarchy.content.entity.trades.PestControlTradeManager.listing(level, index)); + } + } } else if (event.getType() == VillagerProfession.FARMER) { event.getTrades().get(1).add(emeraldForItems(AntarchyObjects.CORN.get(), 20, 16, 2)); } else if (event.getType() == VillagerProfession.BUTCHER) { @@ -323,8 +331,6 @@ public final class AntarchyNeoForgeEvents { static void tickCavarynHordes(ServerTickEvent.Post event) { - com.craisinlord.antarchy.content.antmail.AntmailServerData.access(event.getServer()).drainAvailable(event.getServer()); - com.craisinlord.antarchy.content.antmail.AntmailEventData.onTick(event.getServer()); for (ServerLevel level : event.getServer().getAllLevels()) { CavarynHordeManager.tick(level); com.craisinlord.antarchy.content.horde.CavarynCreatureSpawner.tick(level); @@ -394,8 +400,7 @@ public final class AntarchyNeoForgeEvents { static void registerReloadListeners(AddReloadListenerEvent event) { event.addListener(new DrTrayaurusTradeManager()); event.addListener(new ComputerScientistTradeManager()); - event.addListener(com.craisinlord.antarchy.content.guide.ComputerGuideData.instance()); - event.addListener(com.craisinlord.antarchy.content.antmail.AntmailEventData.instance()); + event.addListener(new com.craisinlord.antarchy.content.entity.trades.PestControlTradeManager()); event.addListener(com.craisinlord.antarchy.content.computer.blockle.BlockleAnswers.instance()); } static void handleStartTracking(PlayerEvent.StartTracking event) { diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeBlocks.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeBlocks.java index 0a1101c1..12ec828b 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeBlocks.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeBlocks.java @@ -6,7 +6,6 @@ import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.block.*; import com.craisinlord.antarchy.content.block.entity.AntNestBlockEntity; import com.craisinlord.antarchy.content.block.entity.CritterCageBlockEntity; -import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; import com.craisinlord.antarchy.content.block.entity.DimensionalTearMarkerBlockEntity; import com.craisinlord.antarchy.content.block.entity.DreamCampfireBlockEntity; import com.craisinlord.antarchy.content.block.entity.HushweedBlockEntity; @@ -632,6 +631,11 @@ public final class AntarchyNeoforgeBlocks { () -> new AntNestBlock(AntarchyNeoforgeEntites.RAINBOW_ANT, AntarchyNeoforgeItems::antNestBlockEntityType, BlockBehaviour.Properties.ofFullCopy(Blocks.DIRT).noLootTable())); public static final DeferredBlock TERMITE_NEST = BLOCKS.register("termite_nest", () -> new AntNestBlock(AntarchyNeoforgeEntites.TERMITE, AntarchyNeoforgeItems::antNestBlockEntityType, BlockBehaviour.Properties.ofFullCopy(Blocks.DIRT).noLootTable())); + + public static final DeferredBlock ANT_TRAP = BLOCKS.register("ant_trap", + () -> new com.craisinlord.antarchy.content.block.AntTrapBlock( + () -> java.util.List.of(AntarchyNeoforgeEntites.RED_ANT.get(), AntarchyNeoforgeEntites.BROWN_ANT.get(), AntarchyNeoforgeEntites.RAINBOW_ANT.get(), AntarchyNeoforgeEntites.TERMITE.get()), + AntarchyNeoforgeItems::antNestBlockEntityType, BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_PRESSURE_PLATE).noOcclusion())); public static final DeferredBlock URANIUM_ORE = BLOCKS.register("uranium_ore", () -> createOre(Blocks.EMERALD_ORE, 4, 8, MapColor.COLOR_YELLOW)); public static final DeferredBlock DEEPSLATE_URANIUM_ORE = BLOCKS.register("deepslate_uranium_ore", @@ -717,8 +721,6 @@ public final class AntarchyNeoforgeBlocks { () -> new com.craisinlord.antarchy.content.block.AntimetalRailBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.RAIL))); public static final DeferredBlock UPPER = BLOCKS.register("upper", () -> new UpperBlock(AntarchyNeoforgeBlocks::upperBlockEntityType, BlockBehaviour.Properties.ofFullCopy(Blocks.HOPPER))); - public static final DeferredBlock COMPUTER = BLOCKS.register("computer", - () -> new com.craisinlord.antarchy.content.block.ComputerBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).noOcclusion())); public static final DeferredBlock ANTIMETAL_POWERED_RAIL = BLOCKS.register("antimetal_powered_rail", () -> new com.craisinlord.antarchy.content.block.AntimetalPoweredRailBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.POWERED_RAIL))); public static final DeferredBlock ANTIMETAL_DETECTOR_RAIL = BLOCKS.register("antimetal_detector_rail", @@ -884,7 +886,8 @@ public final class AntarchyNeoforgeBlocks { RED_ANT_NEST.get(), BROWN_ANT_NEST.get(), RAINBOW_ANT_NEST.get(), - TERMITE_NEST.get() + TERMITE_NEST.get(), + ANT_TRAP.get() ).build(null)); public static final DeferredHolder, BlockEntityType> DREAM_CAMPFIRE_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("dream_campfire", () -> BlockEntityType.Builder.of( @@ -951,11 +954,6 @@ public final class AntarchyNeoforgeBlocks { (pos, state) -> new DimensionalTearMarkerBlockEntity(pos, state, AntarchyNeoforgeBlocks::dimensionalTearMarkerBlockEntityType), DIMENSIONAL_TEAR_MARKER.get() ).build(null)); - public static final DeferredHolder, BlockEntityType> COMPUTER_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("computer", - () -> BlockEntityType.Builder.of( - (pos, state) -> new ComputerBlockEntity(pos, state, AntarchyNeoforgeBlocks::computerBlockEntityType), - COMPUTER.get() - ).build(null)); public static final DeferredHolder, BlockEntityType> QUEEN_TRAIL_SPAWN_MARKER_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("queen_trail_spawn_marker", () -> BlockEntityType.Builder.of( (pos, state) -> new QueenTrailSpawnMarkerBlockEntity(pos, state, AntarchyNeoforgeBlocks::queenTrailSpawnMarkerBlockEntityType), @@ -1054,10 +1052,6 @@ public final class AntarchyNeoforgeBlocks { return UPPER_BLOCK_ENTITY.get(); } - private static BlockEntityType computerBlockEntityType() { - return COMPUTER_BLOCK_ENTITY.get(); - } - private static BlockEntityType vortexLensBlockEntityType() { return VORTEX_LENS_BLOCK_ENTITY.get(); } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeItems.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeItems.java index cfbc793b..71df7403 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeItems.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeItems.java @@ -92,7 +92,6 @@ public final class AntarchyNeoforgeItems { () -> Ingredient.of(AntarchyNeoforgeItems.QUEEN_SCALE.get()) ); - public static final DeferredItem FLOPPY_DISK = ITEMS.register("floppy_disk", () -> new com.craisinlord.antarchy.content.item.FloppyDiskItem(new Item.Properties())); public static final DeferredItem NIGHTMARE_SCALE = ITEMS.registerSimpleItem("nightmare_scale", new Item.Properties().rarity(Rarity.RARE).fireResistant()); @@ -206,8 +205,6 @@ public final class AntarchyNeoforgeItems { // Block items public static final DeferredItem DUPLICATOR_LOG_ITEM = ITEMS.registerSimpleBlockItem(AntarchyNeoforgeBlocks.DUPLICATOR_LOG); - public static final DeferredItem COMPUTER_ITEM = ITEMS.register("computer", - () -> new ComputerItem(AntarchyNeoforgeBlocks.COMPUTER.get(), new Item.Properties())); public static final DeferredItem OURANWOOD_LOG_ITEM = ITEMS.registerSimpleBlockItem(AntarchyNeoforgeBlocks.OURANWOOD_LOG); public static final DeferredItem OURANWOOD_WOOD_ITEM = ITEMS.registerSimpleBlockItem(AntarchyNeoforgeBlocks.OURANWOOD_WOOD); public static final DeferredItem MOSSY_OURANWOOD_LOG_ITEM = ITEMS.registerSimpleBlockItem(AntarchyNeoforgeBlocks.MOSSY_OURANWOOD_LOG); @@ -396,6 +393,11 @@ public final class AntarchyNeoforgeItems { public static final DeferredItem BROWN_ANT_NEST_ITEM = ITEMS.registerSimpleBlockItem(AntarchyNeoforgeBlocks.BROWN_ANT_NEST); public static final DeferredItem RAINBOW_ANT_NEST_ITEM = ITEMS.registerSimpleBlockItem(AntarchyNeoforgeBlocks.RAINBOW_ANT_NEST); public static final DeferredItem TERMITE_NEST_ITEM = ITEMS.registerSimpleBlockItem(AntarchyNeoforgeBlocks.TERMITE_NEST); + + public static final DeferredItem ANT_TRAP_ITEM = ITEMS.registerSimpleBlockItem(AntarchyNeoforgeBlocks.ANT_TRAP); + + public static final DeferredItem BUG_SPRAY = ITEMS.register("bug_spray", + () -> new com.craisinlord.antarchy.content.item.BugSprayItem(new Item.Properties().stacksTo(16))); public static final DeferredItem URANIUM_ORE_ITEM = ITEMS.register("uranium_ore", () -> new net.minecraft.world.item.BlockItem(AntarchyNeoforgeBlocks.URANIUM_ORE.get(), new Item.Properties().rarity(Rarity.UNCOMMON))); public static final DeferredItem DEEPSLATE_URANIUM_ORE_ITEM = ITEMS.register("deepslate_uranium_ore", diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java index ebcf9680..e64287f0 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java @@ -71,7 +71,7 @@ public final class AntarchyNeoforgeMisc { public static final DeferredHolder COMPUTER_POI = POI_TYPES.register("computer", () -> new PoiType( - java.util.Set.copyOf(AntarchyNeoforgeBlocks.COMPUTER.get().getStateDefinition().getPossibleStates()), + java.util.Set.copyOf(net.minecraft.world.level.block.Block.byItem(com.craisinlord.antos.content.AntOSObjects.COMPUTER_ITEM.get()).getStateDefinition().getPossibleStates()), 1, 1)); @@ -83,6 +83,13 @@ public final class AntarchyNeoforgeMisc { com.google.common.collect.ImmutableSet.of(), com.google.common.collect.ImmutableSet.of(), net.minecraft.sounds.SoundEvents.VILLAGER_WORK_LIBRARIAN)); + + public static final DeferredHolder PEST_CONTROL_POI = POI_TYPES.register("pest_control", + () -> new PoiType(java.util.Set.copyOf(AntarchyNeoforgeBlocks.ANT_TRAP.get().getStateDefinition().getPossibleStates()), 1, 1)); + + public static final DeferredHolder PEST_CONTROL = VILLAGER_PROFESSIONS.register("pest_control", + () -> new VillagerProfession("pest_control", holder -> holder.value() == PEST_CONTROL_POI.get(), + holder -> holder.value() == PEST_CONTROL_POI.get(), com.google.common.collect.ImmutableSet.of(), com.google.common.collect.ImmutableSet.of(), net.minecraft.sounds.SoundEvents.VILLAGER_WORK_FLETCHER)); private static final DeferredRegister MOB_EFFECTS = DeferredRegister.create(Registries.MOB_EFFECT, Antarchy.MODID); private static final DeferredRegister POTIONS = DeferredRegister.create(Registries.POTION, Antarchy.MODID); private static final DeferredRegister FLUID_TYPES = DeferredRegister.create(NeoForgeRegistries.Keys.FLUID_TYPES, Antarchy.MODID); @@ -105,12 +112,6 @@ public final class AntarchyNeoforgeMisc { .persistent(com.craisinlord.antarchy.content.entity.glimmer.GlimmerVariant.CODEC) .networkSynchronized(com.craisinlord.antarchy.content.entity.glimmer.GlimmerVariant.STREAM_CODEC) .build()); - public static final DeferredHolder, net.minecraft.core.component.DataComponentType> FLOPPY_DISK_COMPONENT = - DATA_COMPONENT_TYPES.register("floppy_disk", - () -> net.minecraft.core.component.DataComponentType.builder() - .persistent(net.minecraft.resources.ResourceLocation.CODEC) - .networkSynchronized(net.minecraft.resources.ResourceLocation.STREAM_CODEC) - .build()); public static final DeferredHolder, net.minecraft.core.component.DataComponentType> AMERICAN = DATA_COMPONENT_TYPES.register("american", () -> net.minecraft.core.component.DataComponentType.builder() @@ -234,6 +235,8 @@ public final class AntarchyNeoforgeMisc { // Particle types public static final DeferredHolder, SimpleParticleType> DREAM_FIRE_FLAME = PARTICLE_TYPES.register("dream_fire_flame", () -> new SimpleParticleType(true)); + public static final DeferredHolder, SimpleParticleType> BUG_SPRAY_PARTICLE = PARTICLE_TYPES.register("bug_spray", + () -> new SimpleParticleType(true)); public static final DeferredHolder, SimpleParticleType> NIGHTMARE_FIRE_FLAME = PARTICLE_TYPES.register("nightmare_fire_flame", () -> new SimpleParticleType(true)); public static final DeferredHolder, SimpleParticleType> STINKY_GAS = PARTICLE_TYPES.register("stinky_gas", diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgePayloadHandlers.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgePayloadHandlers.java index 746159c8..b424a17c 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgePayloadHandlers.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgePayloadHandlers.java @@ -1,21 +1,10 @@ package com.craisinlord.antarchy.neoforge.registry; import com.craisinlord.antarchy.content.network.BloodCrystalKatanaTrailPayload; -import com.craisinlord.antarchy.content.network.ComputerAccessHandler; -import com.craisinlord.antarchy.content.network.ComputerAccessPayload; -import com.craisinlord.antarchy.content.network.ComputerAccessResultPayload; +import com.craisinlord.antarchy.content.network.AntarchyGameNetworkHandler; +import com.craisinlord.antarchy.content.network.AntarchyGamePayload; +import com.craisinlord.antarchy.content.network.AntarchyGameResultPayload; import com.craisinlord.antarchy.content.network.ToggleTigerEyeCamouflagePayload; -import com.craisinlord.antarchy.content.network.AntmailPayloadTypes; -import com.craisinlord.antarchy.content.network.AntmailSetupPayload; -import com.craisinlord.antarchy.content.network.AntmailStateRequestPayload; -import com.craisinlord.antarchy.content.network.AntmailMessageRequestPayload; -import com.craisinlord.antarchy.content.network.AntmailSendPayload; -import com.craisinlord.antarchy.content.network.AntmailReadPayload; -import com.craisinlord.antarchy.content.network.AntmailDeletePayload; -import com.craisinlord.antarchy.content.network.AntmailDraftPayload; -import com.craisinlord.antarchy.content.network.AntmailRetryPayload; -import com.craisinlord.antarchy.content.network.AntmailResultPayload; -import com.craisinlord.antarchy.content.network.AntmailServerHandler; import com.craisinlord.antarchy.content.item.TigerEyeArmorUtil; import com.craisinlord.antarchy.content.tigereye.TigerEyeCamouflageController; import com.craisinlord.antarchy.neoforge.network.AntarchyGravityNetworking; @@ -38,52 +27,18 @@ public class AntarchyNeoforgePayloadHandlers { static void registerPayloadHandlers(RegisterPayloadHandlersEvent event) { var registrar = event.registrar("1"); registrar.playToServer( - ComputerAccessPayload.TYPE, - ComputerAccessPayload.STREAM_CODEC, + AntarchyGamePayload.TYPE, + AntarchyGamePayload.STREAM_CODEC, (payload, ctx) -> ctx.enqueueWork(() -> { if (ctx.player() instanceof net.minecraft.server.level.ServerPlayer player) { - ComputerAccessHandler.handle(player, payload); + AntarchyGameNetworkHandler.handle(player, payload); } }) ); - registrar.playToServer(AntmailSetupPayload.TYPE, AntmailSetupPayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> { - if (ctx.player() instanceof net.minecraft.server.level.ServerPlayer player) AntmailServerHandler.handle(player, payload); - })); - registrar.playToServer(AntmailStateRequestPayload.TYPE, AntmailStateRequestPayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> { - if (ctx.player() instanceof net.minecraft.server.level.ServerPlayer player) AntmailServerHandler.handle(player, payload); - })); - registrar.playToServer(AntmailMessageRequestPayload.TYPE, AntmailMessageRequestPayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> { - if (ctx.player() instanceof net.minecraft.server.level.ServerPlayer player) AntmailServerHandler.handle(player, payload); - })); - registrar.playToServer(AntmailSendPayload.TYPE, AntmailSendPayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> { - if (ctx.player() instanceof net.minecraft.server.level.ServerPlayer player) AntmailServerHandler.handle(player, payload); - })); - registrar.playToServer(AntmailReadPayload.TYPE, AntmailReadPayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> { - if (ctx.player() instanceof net.minecraft.server.level.ServerPlayer player) AntmailServerHandler.handle(player, payload); - })); - registrar.playToServer(AntmailDeletePayload.TYPE, AntmailDeletePayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> { - if (ctx.player() instanceof net.minecraft.server.level.ServerPlayer player) AntmailServerHandler.handle(player, payload); - })); - registrar.playToServer(AntmailDraftPayload.TYPE, AntmailDraftPayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> { - if (ctx.player() instanceof net.minecraft.server.level.ServerPlayer player) AntmailServerHandler.handle(player, payload); - })); - registrar.playToServer(AntmailRetryPayload.TYPE, AntmailRetryPayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> { - if (ctx.player() instanceof net.minecraft.server.level.ServerPlayer player) AntmailServerHandler.handle(player, payload); - })); - registrar.playToClient(AntmailResultPayload.TYPE, AntmailResultPayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> com.craisinlord.antarchy.content.client.AntmailClientState.update(payload))); registrar.playToClient( - ComputerAccessResultPayload.TYPE, - ComputerAccessResultPayload.STREAM_CODEC, - (payload, ctx) -> ctx.enqueueWork(() -> com.craisinlord.antarchy.content.client.ComputerAccessClientState.update(payload)) + AntarchyGameResultPayload.TYPE, + AntarchyGameResultPayload.STREAM_CODEC, + (payload, ctx) -> ctx.enqueueWork(() -> com.craisinlord.antarchy.content.client.AntarchyGameClientState.update(payload)) ); AntarchyGravityNetworking.register(registrar); AntarchyTimeDilationNetworking.register(registrar); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java index 8e265d8d..c0a128b3 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java @@ -259,6 +259,9 @@ public final class AntarchyNeoforgeSounds { public static final DeferredHolder PRINCESS_SHOOT = registerSoundEvent("princess_shoot"); public static final DeferredHolder PRINCESS_FLY = registerSoundEvent("princess_fly"); public static final DeferredHolder PRINCESS_STEP = registerSoundEvent("princess_step"); + public static final DeferredHolder HOVERBOARD_MOUNT = registerSoundEvent("hoverboard_mount"); + public static final DeferredHolder HOVERBOARD_DISMOUNT = registerSoundEvent("hoverboard_dismount"); + public static final DeferredHolder HOVERBOARD_IDLE = registerSoundEvent("hoverboard_idle"); private AntarchyNeoforgeSounds() {} diff --git a/neoforge-1.21.1/src/main/resources/META-INF/neoforge.mods.toml b/neoforge-1.21.1/src/main/resources/META-INF/neoforge.mods.toml index 9939e998..4367d003 100644 --- a/neoforge-1.21.1/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge-1.21.1/src/main/resources/META-INF/neoforge.mods.toml @@ -25,6 +25,13 @@ displayURL="https://www.curseforge.com/minecraft/mc-mods/antarchy" type="required" versionRange="[1.21.1,)" +[[dependencies.antarchy]] + modId="antos" + type="required" + versionRange="*" + ordering="AFTER" + side="BOTH" + [[dependencies.antarchy]] modId="integrated_api" type="required" @@ -59,4 +66,4 @@ displayURL="https://www.curseforge.com/minecraft/mc-mods/antarchy" type="optional" versionRange="*" ordering="NONE" - reason="Enables an in-game config screen for Antarchy's settings; Antarchy works fully without it." \ No newline at end of file + reason="Enables an in-game config screen for Antarchy's settings; Antarchy works fully without it." diff --git a/neoforge-1.21.1/src/main/resources/data/antarchy/neoforge/biome_modifier/octopus_bomb_spawns.json b/neoforge-1.21.1/src/main/resources/data/antarchy/neoforge/biome_modifier/octopus_bomb_spawns.json deleted file mode 100644 index 8faeead3..00000000 --- a/neoforge-1.21.1/src/main/resources/data/antarchy/neoforge/biome_modifier/octopus_bomb_spawns.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "neoforge:add_spawns", - "biomes": [ - "minecraft:deep_ocean", - "minecraft:deep_cold_ocean", - "minecraft:deep_lukewarm_ocean", - "minecraft:deep_frozen_ocean" - ], - "spawners": [ - { - "type": "antarchy:octopus_bomb", - "weight": 2, - "minCount": 1, - "maxCount": 2 - } - ] -}