├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── build.yml │ └── tag.yml ├── .gitignore ├── FPVersionless └── src │ └── main │ └── java │ └── dev │ └── tr7zw │ └── firstperson │ └── versionless │ ├── Constants.java │ ├── FirstPersonBase.java │ ├── config │ ├── ConfigUpgrader.java │ ├── FirstPersonSettings.java │ └── VanillaHands.java │ └── mixinbase │ └── ModelPartBase.java ├── LICENSE ├── README.md ├── curseforge.md ├── gradle-compose.yml ├── gradle └── gradle-compose.jar ├── gradlecw ├── gradlecw.bat ├── settings.json ├── src ├── main │ ├── java │ │ └── dev │ │ │ └── tr7zw │ │ │ └── firstperson │ │ │ ├── FirstPersonBootstrap.java │ │ │ ├── FirstPersonModelCore.java │ │ │ ├── FirstPersonModelMod.java │ │ │ ├── InventoryUtil.java │ │ │ ├── LogicHandler.java │ │ │ ├── access │ │ │ ├── AgeableListModelAccess.java │ │ │ └── PlayerModelAccess.java │ │ │ ├── api │ │ │ ├── ActivationHandler.java │ │ │ ├── FirstPersonAPI.java │ │ │ └── PlayerOffsetHandler.java │ │ │ ├── config │ │ │ └── ConfigScreenProvider.java │ │ │ ├── fabric │ │ │ └── FPModMenuProvider.java │ │ │ ├── forge │ │ │ └── RenderHandEventListener.java │ │ │ ├── mixins │ │ │ ├── AgeableListModelMixin.java │ │ │ ├── ArmorFeatureRendererMixin.java │ │ │ ├── CustomHeadLayerMixin.java │ │ │ ├── ElytraLayerMixin.java │ │ │ ├── FeatureRendererMixin.java │ │ │ ├── FishingBobberRendererMixin.java │ │ │ ├── HeldItemFeatureRendererMixin.java │ │ │ ├── HeldItemRendererMixin.java │ │ │ ├── LivingEntityRendererMixin.java │ │ │ ├── ModelPartMixin.java │ │ │ ├── PlayerMixin.java │ │ │ ├── PlayerModelMixin.java │ │ │ ├── PlayerRendererMixin.java │ │ │ ├── RenderDispatcherMixin.java │ │ │ ├── StuckInBodyLayerMixin.java │ │ │ └── WorldRendererMixin.java │ │ │ └── modsupport │ │ │ ├── FreecamSupport.java │ │ │ ├── ModSupportLoader.java │ │ │ └── PlayerAnimatorSupport.java │ └── resources │ │ ├── assets │ │ └── firstperson │ │ │ └── lang │ │ │ ├── be_by.json │ │ │ ├── en_us.json │ │ │ ├── ko_kr.json │ │ │ ├── pt_br.json │ │ │ ├── pt_pt.json │ │ │ ├── ru_ru.json │ │ │ ├── tr_tr.json │ │ │ ├── zh_cn.json │ │ │ └── zh_tw.json │ │ ├── firstperson.mixins.json │ │ └── icon.png └── test │ └── java │ └── dev │ └── tr7zw │ └── tests │ └── MixinTests.java └── versions └── mainProject /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | ko_fi: tr7zw -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: net.fabricmc:fabric-loom 10 | versions: 11 | - 0.6.32 12 | - 0.6.35 13 | - 0.6.36 14 | - 0.6.37 15 | - 0.6.41 16 | - 0.6.48 17 | - 0.6.50 18 | - 0.7.10 19 | - 0.7.11 20 | - 0.7.12 21 | - 0.7.14 22 | - 0.7.17 23 | - 0.7.18 24 | - 0.7.19 25 | - 0.7.2 26 | - 0.7.21 27 | - 0.7.24 28 | - 0.7.26 29 | - 0.7.27 30 | - 0.7.29 31 | - 0.7.3 32 | - 0.7.9 33 | - dependency-name: net.minecraftforge.gradle:ForgeGradle 34 | versions: 35 | - 4.1.7 36 | - dependency-name: me.shedaniel.cloth:cloth-config-forge 37 | versions: 38 | - 4.1.3 39 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | name: build 6 | on: [pull_request, push] 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: checkout repository 12 | uses: actions/checkout@v2 13 | - name: setup jdk 21 14 | uses: actions/setup-java@v4 15 | with: 16 | distribution: temurin 17 | java-version: 21 18 | # to cache gradle files 19 | - name: Setup Gradle 20 | uses: gradle/actions/setup-gradle@v4 21 | with: 22 | gradle-home-cache-cleanup: true 23 | dependency-graph: generate-and-submit 24 | - name: build 25 | run: ./gradlecw build --stacktrace 26 | - name: capture build artifacts 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: Artifacts 30 | path: 'versions/**/build/libs/*.jar' 31 | 32 | # - name: Test Report 33 | # uses: dorny/test-reporter@v1 34 | # if: success() || failure() 35 | # with: 36 | # name: JUnit Tests 37 | # path: '**/build/test-results/test/TEST-*.xml' 38 | # reporter: java-junit -------------------------------------------------------------------------------- /.github/workflows/tag.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | on: 3 | release: 4 | types: [published] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: checkout repository 10 | uses: actions/checkout@v2 11 | - name: setup jdk 21 12 | uses: actions/setup-java@v1 13 | with: 14 | java-version: 21 15 | - name: Get Release Changelog 16 | run: | 17 | # Read the event payload to get the changelog and save it to a file 18 | changelog=$(jq -r '.release.body' $GITHUB_EVENT_PATH) 19 | echo "$changelog" > changelog.md 20 | echo "Changelog saved to changelog.md" 21 | - name: make gradle wrapper executable 22 | run: chmod +x ./gradlecw 23 | - name: build 24 | run: ./gradlecw build publishMods -Pbuild.release=true --info 25 | env: 26 | CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} 27 | MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} 28 | - name: Release 29 | uses: softprops/action-gh-release@v1 30 | with: 31 | files: 'versions/**/build/libs/!(*-@(dev|sources|javadoc|all)).jar' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | logs/ 8 | 9 | # eclipse 10 | 11 | *.launch 12 | 13 | # idea 14 | 15 | .idea/ 16 | *.iml 17 | *.ipr 18 | *.iws 19 | 20 | # vscode 21 | 22 | .settings/ 23 | .vscode/ 24 | bin/ 25 | .classpath 26 | .project 27 | 28 | # macos 29 | 30 | *.DS_Store 31 | 32 | # fabric 33 | 34 | run/ 35 | logs/ 36 | 37 | # compose 38 | 39 | *.gradle 40 | *.properties 41 | gradle/wrapper 42 | VersionShared 43 | UtilityCode 44 | formatter.xml 45 | src/main/fabric-resources/fabric.mod.json 46 | src/main/forge-resources/META-INF/mods.toml 47 | src/main/neoforge-resources/META-INF/mods.toml 48 | **/src/main/java/dev/tr7zw/config/CustomConfigScreen.java 49 | **/src/main/java/dev/tr7zw/util/ComponentProvider.java 50 | src/main/resources/pack.mcmeta 51 | src/main/java/dev/tr7zw/util/NMSHelper.java 52 | src/main/java/dev/tr7zw/util/ModLoaderUtil.java 53 | *.log 54 | src/main/neoforge-resources/META-INF/neoforge.mods.toml 55 | -------------------------------------------------------------------------------- /FPVersionless/src/main/java/dev/tr7zw/firstperson/versionless/Constants.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.versionless; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | @UtilityClass 6 | public class Constants { 7 | 8 | public static final float SNEAK_BODY_OFFSET = 0.27f; 9 | public static final float SWIM_UP_BODY_OFFSET = 0.60f; 10 | public static final float SWIM_DOWN_BODY_OFFSET = 0.50f; 11 | public static final float IN_VEHICLE_BODY_OFFSET = 0.20f; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /FPVersionless/src/main/java/dev/tr7zw/firstperson/versionless/FirstPersonBase.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.versionless; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | import java.nio.file.Files; 7 | 8 | import org.apache.logging.log4j.LogManager; 9 | import org.apache.logging.log4j.Logger; 10 | 11 | import com.google.gson.Gson; 12 | import com.google.gson.GsonBuilder; 13 | 14 | import dev.tr7zw.firstperson.versionless.config.ConfigUpgrader; 15 | import dev.tr7zw.firstperson.versionless.config.FirstPersonSettings; 16 | import lombok.Getter; 17 | import lombok.Setter; 18 | 19 | public class FirstPersonBase { 20 | 21 | public static final Logger LOGGER = LogManager.getLogger("FirstPersonModel"); 22 | /** 23 | * Stays true till the first entity is rendered when the player is supposed to 24 | * be rendered 25 | */ 26 | @Getter 27 | @Setter 28 | private boolean isRenderingPlayer = false; 29 | /** 30 | * Stays true for the entire render time of the entity, even after the entity 31 | * itself 32 | */ 33 | @Getter 34 | @Setter 35 | private boolean isRenderingPlayerPost = false; 36 | @Getter 37 | @Setter 38 | private boolean enabled = true; 39 | @Getter 40 | private FirstPersonSettings config = null; 41 | private File settingsFile = new File("config", "firstperson.json"); 42 | 43 | public void loadConfig() { 44 | if (settingsFile.exists()) { 45 | try { 46 | config = new Gson().fromJson( 47 | new String(Files.readAllBytes(settingsFile.toPath()), StandardCharsets.UTF_8), 48 | FirstPersonSettings.class); 49 | } catch (Exception ex) { 50 | LOGGER.warn("Unable to load the config, creating a new one.", ex); 51 | } 52 | } 53 | if (config == null) { 54 | config = new FirstPersonSettings(); 55 | } 56 | ConfigUpgrader.upgradeConfig(config); 57 | writeSettings(); 58 | setEnabled(config.enabledByDefault); 59 | } 60 | 61 | public void writeSettings() { 62 | String json = new GsonBuilder().setPrettyPrinting().create().toJson(config); 63 | try { 64 | Files.write(settingsFile.toPath(), json.getBytes(StandardCharsets.UTF_8)); 65 | } catch (IOException e) { 66 | LOGGER.warn("Error while saving the config!", e); 67 | } 68 | } 69 | 70 | public void resetSettings() { 71 | config = new FirstPersonSettings(); 72 | } 73 | 74 | /** 75 | * Checks if a class exists or not 76 | * 77 | * @param name 78 | * @return 79 | */ 80 | protected static boolean isValidClass(String name) { 81 | try { 82 | if (Class.forName(name) != null) { 83 | return true; 84 | } 85 | } catch (ClassNotFoundException e) { 86 | // ignored 87 | } 88 | return false; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /FPVersionless/src/main/java/dev/tr7zw/firstperson/versionless/config/ConfigUpgrader.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.versionless.config; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | 6 | import lombok.experimental.UtilityClass; 7 | 8 | @UtilityClass 9 | public class ConfigUpgrader { 10 | 11 | public static void upgradeConfig(FirstPersonSettings conf) { 12 | if (conf.configVersion < 1) { 13 | // re-init the auto vanilla hands set 14 | conf.autoVanillaHands 15 | .addAll(new HashSet<>(Arrays.asList("antiqueatlas:antique_atlas", "twilightforest:filled_magic_map", 16 | "twilightforest:filled_maze_map", "twilightforest:filled_ore_map", "create:potato_cannon", 17 | "create:extendo_grip", "create:handheld_worldshaper", "map_atlases:atlas"))); 18 | conf.configVersion = 1; 19 | } 20 | if (conf.configVersion < 2) { 21 | conf.autoToggleModItems.add("exposure:camera"); 22 | conf.configVersion = 2; 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /FPVersionless/src/main/java/dev/tr7zw/firstperson/versionless/config/FirstPersonSettings.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.versionless.config; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | public class FirstPersonSettings { 8 | 9 | public int configVersion = 2; 10 | public boolean enabledByDefault = true; 11 | public int xOffset = 0; 12 | public int sneakXOffset = 0; 13 | 14 | public int sitXOffset = 0; 15 | 16 | public boolean renderStuckFeatures = true; 17 | public VanillaHands vanillaHandsMode = VanillaHands.OFF; 18 | public boolean dynamicMode = true; 19 | public boolean vanillaHandsSkipSwimming = true; 20 | 21 | public Set autoVanillaHands = new HashSet<>(Arrays.asList("antiqueatlas:antique_atlas", 22 | "twilightforest:filled_magic_map", "twilightforest:filled_maze_map", "twilightforest:filled_ore_map", 23 | "create:potato_cannon", "create:extendo_grip", "create:handheld_worldshaper", "map_atlases:atlas")); 24 | 25 | public Set autoToggleModItems = new HashSet<>(Arrays.asList("exposure:camera")); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /FPVersionless/src/main/java/dev/tr7zw/firstperson/versionless/config/VanillaHands.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.versionless.config; 2 | 3 | public enum VanillaHands { 4 | OFF, ALL, ALL_DOUBLE, ITEMS; 5 | } 6 | -------------------------------------------------------------------------------- /FPVersionless/src/main/java/dev/tr7zw/firstperson/versionless/mixinbase/ModelPartBase.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.versionless.mixinbase; 2 | 3 | public interface ModelPartBase { 4 | 5 | void setHidden(); 6 | 7 | void showAgain(); 8 | 9 | boolean isHidden(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 tr7zw 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # First Person Models 2 | 3 | [![Downloads](http://cf.way2muchnoise.eu/full_333287_downloads.svg)](https://www.curseforge.com/minecraft/mc-mods/first-person-model/) 4 | [![Versions](http://cf.way2muchnoise.eu/versions/333287.svg)](https://www.curseforge.com/minecraft/mc-mods/first-person-model/) 5 | [![Discord](https://img.shields.io/discord/342814924310970398?color=%237289DA&label=Discord&logo=discord&logoColor=white)](https://discordapp.com/invite/yk4caxM) 6 | 7 | Enables the third-person Model in first-person, so you can experience Minecraft from Steve's real perspective. This mod doesn't change any animations, so you also get to see Mojangs questionable animation descitions(looking at you, shield + bow). However this mod adds so few and simple mixin hooks into the game, it should work with other mods that come out(MoBends, a real eating/crawling animation, custom 3d armor, etc). 8 | 9 | Since this mod is purely visual, it will also work on vanilla servers and everything 10 | -------------------------------------------------------------------------------- /curseforge.md: -------------------------------------------------------------------------------- 1 | ![](https://trsha.re/1679269573.png) 2 | 3 | [![Discord](https://tr7zw.dev/curse/Discord-long.png)](https://discord.gg/55zrqwHA9C) 4 | 5 | # First-person Model 6 | 7 | Enables the third-person Model in first-person, so you can experience Minecraft from Steve's real perspective. This mod doesn't change any animations, so you also get to see Mojangs "questionable" animation decisions(looking at you, shield + bow). However this mod adds so few and simple mixin hooks into the game, it should work with other mods that come out(MoBends, a real eating/crawling animation, custom 3d armor, etc). 8 | 9 | Since this mod is purely visual, it will also work on vanilla servers. 10 | 11 | Requires ModMenu(used to change settings). 12 | 13 | ### Dependencies 14 | 15 | - [Not Enough Animations (Forge&Fabric)](https://www.curseforge.com/minecraft/mc-mods/not-enough-animations) 16 | - [Fabric API (Fabric)](https://www.curseforge.com/minecraft/mc-mods/fabric-api) 17 | - [ModMenu (Fabric)](https://www.curseforge.com/minecraft/mc-mods/modmenu) 18 | 19 | ### Incompatible 20 | 21 | - Epic Fight mod 22 | - Optifine emissive textures 23 | - Sitting on modded chairs/mounts doesn't look right 24 | - Any mods replacing the player model might not work correctly 25 | 26 | ### Help, where did feature X go? 27 | 28 | Since this mod became too large, I started cutting it into smaller, more manageable parts. All the smaller mods are available for Fabric and Forge + back-ported to some older versions! 29 | 30 | - [3d Skin Layers](https://www.curseforge.com/minecraft/mc-mods/skin-layers-3d) Turns the skin layers from a flat box into a 3d model. 31 | - [PaperDoll](https://www.curseforge.com/minecraft/mc-mods/paperdoll) adds a highly configurable Bedrock/PocketEdition like player preview. 32 | - [Not Enough Animations](https://www.curseforge.com/minecraft/mc-mods/not-enough-animations) brings first-person animations to the third-person, like eating, drinking and holding maps. 33 | - [Wavey Capes](https://www.curseforge.com/minecraft/mc-mods/waveycapes) turns the slab of a cape into something more dynamic and pleasant to look at. 34 | 35 | Support via [![Discord](https://tr7zw.dev/curse/Discord.png)](https://discord.gg/55zrqwHA9C) or [Github](https://github.com/tr7zw/FirstPersonModel-Fabric)! 36 | -------------------------------------------------------------------------------- /gradle-compose.yml: -------------------------------------------------------------------------------- 1 | version: '0.0.2' 2 | source: "https://github.com/tr7zw/ProcessedModTemplate/tree/master" 3 | replacements: 4 | name: "FirstPerson" 5 | id: "firstperson" 6 | version: "2.4.9" 7 | description: "Enables the third person Model in firstperson" 8 | homepageUrl: "https://modrinth.com/mod/first-person-model" 9 | sourcesUrl: "https://github.com/tr7zw/FirstPersonModel" 10 | issuesUrl: "https://github.com/tr7zw/FirstPersonModel/issues" 11 | fabric_entrypoint: "dev.tr7zw.firstperson.FirstPersonModelMod" 12 | fabric_modmenu_entrypoint: "dev.tr7zw.firstperson.fabric.FPModMenuProvider" 13 | relocationpackage: "dev.tr7zw.firstperson" 14 | curseforgeid: "333287" 15 | modrinthid: "H5XMjpHi" 16 | versionlessname: "FPVersionless" 17 | breaks: ', "FastAnim": "*"' 18 | fabric_dependencies: " 19 | modCompileOnly 'maven.modrinth:playeranimator:1.0.2-rc1+1.20-fabric'\n 20 | modCompileOnly 'maven.modrinth:freecam:Cg10zLnR'\n 21 | " 22 | forge_dependencies: " 23 | modCompileOnly 'maven.modrinth:playeranimator:1.0.2-rc1+1.20-forge'\n 24 | modCompileOnly 'maven.modrinth:freecam:EMcbl7eP'\n 25 | " 26 | neoforge_dependencies: " 27 | modCompileOnly 'maven.modrinth:playeranimator:1.0.2-rc1+1.20-forge'\n 28 | modCompileOnly 'maven.modrinth:freecam:ZgduhqT3'\n 29 | " 30 | modrinth_dependencies: " 31 | requires('not-enough-animations')\n 32 | " 33 | curseforge_dependencies: " 34 | requires('not-enough-animations')\n 35 | " 36 | enabledFlags: 37 | - autopublish 38 | - publishFabric 39 | - publishForge 40 | - modrinth 41 | - curseforge 42 | - versionless 43 | rootProject: 44 | template: "." 45 | subProjects: 46 | FPVersionless: 47 | template: "Versionless" 48 | replacements: 49 | dependencies: ' 50 | compileOnly "com.google.code.gson:gson:2.10.1" 51 | 52 | compileOnly "org.apache.logging.log4j:log4j-core:2.20.0" 53 | ' 54 | # FPFabric: 55 | # template: "Fabric" 56 | # replacements: 57 | # dependencies: " 58 | # modCompileOnly 'maven.modrinth:playeranimator:1.0.2-rc1+1.20-fabric'\n 59 | # modCompileOnly 'maven.modrinth:freecam:1.1.6-mc1.19'\n 60 | # " 61 | # FPForge: 62 | # template: "Forge" 63 | # replacements: 64 | # dependencies: ' 65 | # modCompileOnly "maven.modrinth:playeranimator:1.0.2-rc1+1.20-forge" 66 | # ' -------------------------------------------------------------------------------- /gradle/gradle-compose.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tr7zw/FirstPersonModel/71ca9868dbb1551b6daa06922bfe96d6ad0e70ef/gradle/gradle-compose.jar -------------------------------------------------------------------------------- /gradlecw: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | COMPOSE=$APP_HOME/gradle/gradle-compose.jar 86 | 87 | # Determine the Java command to use to start the JVM. 88 | if [ -n "$JAVA_HOME" ] ; then 89 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 90 | # IBM's JDK on AIX uses strange locations for the executables 91 | JAVACMD="$JAVA_HOME/jre/sh/java" 92 | else 93 | JAVACMD="$JAVA_HOME/bin/java" 94 | fi 95 | if [ ! -x "$JAVACMD" ] ; then 96 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 97 | 98 | Please set the JAVA_HOME variable in your environment to match the 99 | location of your Java installation." 100 | fi 101 | else 102 | JAVACMD="java" 103 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 104 | 105 | Please set the JAVA_HOME variable in your environment to match the 106 | location of your Java installation." 107 | fi 108 | 109 | # Increase the maximum file descriptors if we can. 110 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 111 | MAX_FD_LIMIT=`ulimit -H -n` 112 | if [ $? -eq 0 ] ; then 113 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 114 | MAX_FD="$MAX_FD_LIMIT" 115 | fi 116 | ulimit -n $MAX_FD 117 | if [ $? -ne 0 ] ; then 118 | warn "Could not set maximum file descriptor limit: $MAX_FD" 119 | fi 120 | else 121 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 122 | fi 123 | fi 124 | 125 | # For Darwin, add options to specify how the application appears in the dock 126 | if $darwin; then 127 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 128 | fi 129 | 130 | # For Cygwin or MSYS, switch paths to Windows format before running java 131 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 132 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 133 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 134 | 135 | JAVACMD=`cygpath --unix "$JAVACMD"` 136 | 137 | # We build the pattern for arguments to be converted via cygpath 138 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 139 | SEP="" 140 | for dir in $ROOTDIRSRAW ; do 141 | ROOTDIRS="$ROOTDIRS$SEP$dir" 142 | SEP="|" 143 | done 144 | OURCYGPATTERN="(^($ROOTDIRS))" 145 | # Add a user-defined pattern to the cygpath arguments 146 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 147 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 148 | fi 149 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 150 | i=0 151 | for arg in "$@" ; do 152 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 153 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 154 | 155 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 156 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 157 | else 158 | eval `echo args$i`="\"$arg\"" 159 | fi 160 | i=`expr $i + 1` 161 | done 162 | case $i in 163 | 0) set -- ;; 164 | 1) set -- "$args0" ;; 165 | 2) set -- "$args0" "$args1" ;; 166 | 3) set -- "$args0" "$args1" "$args2" ;; 167 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 168 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 169 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 170 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 171 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 172 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 173 | esac 174 | fi 175 | 176 | 177 | 178 | "$JAVACMD" -jar $COMPOSE 179 | 180 | # Escape application args 181 | save () { 182 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 183 | echo " " 184 | } 185 | APP_ARGS=`save "$@"` 186 | 187 | # Collect all arguments for the java command, following the shell quoting and substitution rules 188 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 189 | 190 | exec "$JAVACMD" "$@" 191 | -------------------------------------------------------------------------------- /gradlecw.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | set COMPOSE=%APP_HOME%\gradle\gradle-compose.jar 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" -jar "%COMPOSE%" %* 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if "%ERRORLEVEL%"=="0" goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 87 | exit /b 1 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "versions": [ 3 | "1.16.5-forge", 4 | "1.16.5-fabric", 5 | "1.18.2-forge", 6 | "1.18.2-fabric", 7 | "1.19.2-forge", 8 | "1.19.2-fabric", 9 | "1.19.4-forge", 10 | "1.19.4-fabric", 11 | "1.20.1-forge", 12 | "1.20.1-fabric", 13 | "1.20.2-forge", 14 | "1.20.2-neoforge", 15 | "1.20.2-fabric", 16 | "1.20.4-forge", 17 | "1.20.4-neoforge", 18 | "1.20.4-fabric", 19 | "1.20.6-forge", 20 | "1.20.6-neoforge", 21 | "1.20.6-fabric", 22 | "1.21-forge", 23 | "1.21-neoforge", 24 | "1.21-fabric", 25 | "1.21.3-forge", 26 | "1.21.3-neoforge", 27 | "1.21.3-fabric", 28 | "1.21.4-forge", 29 | "1.21.4-neoforge", 30 | "1.21.4-fabric", 31 | "1.21.5-forge", 32 | "1.21.5-neoforge", 33 | "1.21.5-fabric" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/FirstPersonBootstrap.java: -------------------------------------------------------------------------------- 1 | //#if FORGE 2 | //$$package dev.tr7zw.firstperson; 3 | //$$ 4 | //$$import net.minecraftforge.api.distmarker.Dist; 5 | //$$import net.minecraftforge.fml.DistExecutor; 6 | //$$import net.minecraftforge.fml.common.Mod; 7 | //$$ 8 | //$$@Mod("firstperson") 9 | //$$public class FirstPersonBootstrap { 10 | //$$ 11 | //$$ public FirstPersonBootstrap() { 12 | //$$ DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> FirstPersonModelMod::new); 13 | //$$ } 14 | //$$ 15 | //$$} 16 | //#elseif NEOFORGE 17 | //$$package dev.tr7zw.firstperson; 18 | //$$ 19 | //$$import net.neoforged.api.distmarker.Dist; 20 | //$$import net.neoforged.fml.loading.FMLEnvironment; 21 | //$$import net.neoforged.fml.common.Mod; 22 | //$$ 23 | //$$@Mod("firstperson") 24 | //$$public class FirstPersonBootstrap { 25 | //$$ 26 | //$$ public FirstPersonBootstrap() { 27 | //$$ if(FMLEnvironment.dist == Dist.CLIENT) { 28 | //$$ new FirstPersonModelMod(); 29 | //$$ } 30 | //$$ } 31 | //$$ 32 | //$$} 33 | //#endif -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/FirstPersonModelCore.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson; 2 | 3 | import dev.tr7zw.firstperson.api.FirstPersonAPI; 4 | import dev.tr7zw.firstperson.config.ConfigScreenProvider; 5 | import dev.tr7zw.firstperson.modsupport.ModSupportLoader; 6 | import dev.tr7zw.firstperson.modsupport.PlayerAnimatorSupport; 7 | import dev.tr7zw.firstperson.versionless.FirstPersonBase; 8 | import dev.tr7zw.util.ModLoaderUtil; 9 | import lombok.Getter; 10 | import net.minecraft.client.KeyMapping; 11 | import net.minecraft.client.Minecraft; 12 | 13 | public abstract class FirstPersonModelCore extends FirstPersonBase { 14 | 15 | @Getter 16 | private LogicHandler logicHandler; 17 | public static FirstPersonModelCore instance; 18 | private boolean isHeld = false; 19 | private KeyMapping keyBinding = new KeyMapping("key.firstperson.toggle", 295, "firstperson.keybind"); 20 | private boolean lateInit = true; 21 | @Deprecated 22 | public static boolean enabled = true; 23 | @Deprecated 24 | public static boolean isRenderingPlayer = false; 25 | 26 | protected FirstPersonModelCore() { 27 | instance = this; 28 | ModLoaderUtil.disableDisplayTest(); 29 | ModLoaderUtil.registerConfigScreen(ConfigScreenProvider::createConfigScreen); 30 | ModLoaderUtil.registerClientSetupListener(this::sharedSetup); 31 | } 32 | 33 | public void sharedSetup() { 34 | LOGGER.info("Loading FirstPerson Mod"); 35 | logicHandler = new LogicHandler(Minecraft.getInstance(), this); 36 | 37 | super.loadConfig(); 38 | 39 | ModLoaderUtil.registerKeybind(keyBinding); 40 | ModLoaderUtil.registerClientTickListener(this::onTick); 41 | 42 | ModSupportLoader.loadSupport(); 43 | } 44 | 45 | private void lateInit() { 46 | try { 47 | if (isValidClass("dev.kosmx.playerAnim.core.impl.AnimationProcessor")) { 48 | LOGGER.info("Loading PlayerAnimator support!"); 49 | FirstPersonAPI.registerPlayerHandler(new PlayerAnimatorSupport()); 50 | } else { 51 | LOGGER.info("PlayerAnimator not found!"); 52 | } 53 | } catch (Throwable ex) { 54 | LOGGER.warn("Error during initialization of mod support.", ex); 55 | } 56 | 57 | logicHandler.registerDefaultHandlers(); 58 | logicHandler.reloadAutoVanillaHandsSettings(); 59 | } 60 | 61 | public void onTick() { 62 | if (lateInit) { 63 | lateInit = false; 64 | lateInit(); 65 | } 66 | if (keyBinding.isDown()) { 67 | if (isHeld) { 68 | return; 69 | } 70 | isHeld = true; 71 | setEnabled(!isEnabled()); 72 | } else { 73 | isHeld = false; 74 | } 75 | } 76 | 77 | @Override 78 | public void setRenderingPlayer(boolean isRenderingPlayer) { 79 | super.setRenderingPlayer(isRenderingPlayer); 80 | FirstPersonModelCore.isRenderingPlayer = isRenderingPlayer; 81 | } 82 | 83 | @Override 84 | public void setEnabled(boolean enabled) { 85 | super.setEnabled(enabled); 86 | FirstPersonModelCore.enabled = enabled; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/FirstPersonModelMod.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson; 2 | 3 | //#if FABRIC 4 | import net.fabricmc.api.ClientModInitializer; 5 | 6 | public class FirstPersonModelMod extends FirstPersonModelCore implements ClientModInitializer { 7 | 8 | @Override 9 | public void onInitializeClient() { 10 | sharedSetup(); 11 | } 12 | //#else 13 | //$$ import dev.tr7zw.util.ModLoaderUtil; 14 | //$$ import dev.tr7zw.firstperson.forge.RenderHandEventListener; 15 | //$$ public class FirstPersonModelMod extends FirstPersonModelCore { 16 | //$$ public FirstPersonModelMod() { 17 | //$$ ModLoaderUtil.registerForgeEvent(new RenderHandEventListener()::onRender); 18 | //$$ } 19 | //#endif 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/InventoryUtil.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson; 2 | 3 | import java.util.List; 4 | 5 | import net.minecraft.world.entity.player.Inventory; 6 | import net.minecraft.world.entity.player.Player; 7 | import net.minecraft.world.item.ItemStack; 8 | 9 | public class InventoryUtil { 10 | 11 | public static Inventory getInventory(Player player) { 12 | //#if MC >= 11700 13 | return player.getInventory(); 14 | //#else 15 | //$$ return player.inventory; 16 | //#endif 17 | } 18 | 19 | public static ItemStack getSelected(Inventory inventory) { 20 | //#if MC >= 12105 21 | return inventory.getSelectedItem(); 22 | //#else 23 | //$$ return inventory.getSelected(); 24 | //#endif 25 | } 26 | 27 | public static ItemStack getOffhand(Inventory inventory) { 28 | //#if MC >= 12105 29 | return inventory.getItem(Inventory.SLOT_OFFHAND); 30 | //#else 31 | //$$ return inventory.offhand.get(0); 32 | //#endif 33 | } 34 | 35 | public static int getSelectedId(Inventory inventory) { 36 | //#if MC >= 12105 37 | return inventory.getSelectedSlot(); 38 | //#else 39 | //$$ return inventory.selected; 40 | //#endif 41 | } 42 | 43 | public static List getNonEquipmentItems(Inventory inventory) { 44 | //#if MC >= 12105 45 | return inventory.getNonEquipmentItems(); 46 | //#else 47 | //$$ return inventory.items; 48 | //#endif 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/LogicHandler.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import dev.tr7zw.firstperson.api.ActivationHandler; 7 | import dev.tr7zw.firstperson.api.FirstPersonAPI; 8 | import dev.tr7zw.firstperson.versionless.Constants; 9 | import dev.tr7zw.firstperson.versionless.FirstPersonBase; 10 | import dev.tr7zw.firstperson.versionless.config.VanillaHands; 11 | import dev.tr7zw.util.NMSHelper; 12 | import lombok.Getter; 13 | import lombok.RequiredArgsConstructor; 14 | import net.minecraft.client.CameraType; 15 | import net.minecraft.client.Minecraft; 16 | import net.minecraft.client.player.AbstractClientPlayer; 17 | import net.minecraft.util.Mth; 18 | import net.minecraft.world.entity.Entity; 19 | import net.minecraft.world.entity.LivingEntity; 20 | import net.minecraft.world.entity.Pose; 21 | import net.minecraft.world.entity.player.Inventory; 22 | import net.minecraft.world.entity.player.Player; 23 | import net.minecraft.world.entity.vehicle.Boat; 24 | import net.minecraft.world.entity.vehicle.Minecart; 25 | import net.minecraft.world.item.Item; 26 | import net.minecraft.world.item.ItemStack; 27 | import net.minecraft.world.phys.Vec3; 28 | 29 | @RequiredArgsConstructor 30 | public class LogicHandler { 31 | 32 | private final Minecraft client; 33 | private final FirstPersonModelCore fpm; 34 | @Getter 35 | private Vec3 offset = Vec3.ZERO; // Current offset used for rendering 36 | private Set autoVanillaHandItems = new HashSet<>(); 37 | private Set autoDisableItems = new HashSet<>(); 38 | private long timeout = 0; 39 | 40 | void registerDefaultHandlers() { 41 | FirstPersonAPI.registerPlayerHandler((ActivationHandler) () -> { 42 | if (client.player.isSleeping() || client.player.isAutoSpinAttack() || client.player.isFallFlying() 43 | || (client.player.getSwimAmount(1f) != 0 && !isCrawlingOrSwimming(client.player))) { 44 | timeout = System.currentTimeMillis() + 100; 45 | return true; 46 | } 47 | // FIXME: Evil hack to fix weird 1 frame-ish issues when landing with an elytra 48 | if (System.currentTimeMillis() < timeout) { 49 | return true; 50 | } 51 | if (autoDisableItems.contains(client.player.getMainHandItem().getItem()) 52 | || autoDisableItems.contains(client.player.getOffhandItem().getItem())) { 53 | return true; 54 | } 55 | //#if MC >= 11700 56 | if (client.player.isScoping()) { 57 | return true; 58 | } 59 | //#endif 60 | return false; 61 | }); 62 | } 63 | 64 | /** 65 | * Checks rather the mod should render at all. 66 | * 67 | * @param thirdPerson 68 | * @return 69 | */ 70 | public boolean shouldApplyThirdPerson(boolean thirdPerson) { 71 | if (!fpm.isEnabled() || thirdPerson) { 72 | return false; 73 | } 74 | for (ActivationHandler handler : FirstPersonAPI.getActivationHandlers()) { 75 | if (handler.preventFirstperson()) { 76 | return false; 77 | } 78 | } 79 | return true; 80 | } 81 | 82 | /** 83 | * Calculates the X/Z offset applied to the player model to get it relative to 84 | * the vanilla camera position 85 | * 86 | * @param entity 87 | * @param defValue 88 | * @param delta 89 | */ 90 | public void updatePositionOffset(Entity entity, float delta) { 91 | offset = Vec3.ZERO; 92 | // handle sleeping 93 | if (entity == client.getCameraEntity() && client.player.isSleeping()) { 94 | return; 95 | } 96 | double x = 0; 97 | double y = 0; 98 | double z = 0; 99 | AbstractClientPlayer player; 100 | double realYaw; 101 | if ((entity != client.player) || (client.options.getCameraType() != CameraType.FIRST_PERSON) 102 | || !fpm.isRenderingPlayer()) { 103 | return; 104 | } 105 | player = (AbstractClientPlayer) entity; 106 | realYaw = Mth.rotLerp(delta, player.yBodyRotO, player.yBodyRot); 107 | if (!player.isLocalPlayer() || client.getCameraEntity() == player) { 108 | float bodyOffset; 109 | if (isCrawlingOrSwimming(client.player)) { 110 | player.yBodyRot = player.yHeadRot; 111 | if (player.xRotO > 0) { 112 | bodyOffset = Constants.SWIM_UP_BODY_OFFSET; 113 | } else { 114 | bodyOffset = Constants.SWIM_DOWN_BODY_OFFSET; 115 | } 116 | // some mods seem to break the isCrouching method 117 | } else if (player.isCrouching() || player.getPose() == Pose.CROUCHING) { 118 | bodyOffset = Constants.SNEAK_BODY_OFFSET + fpm.getConfig().sneakXOffset / 100f; 119 | } else if (player.isPassenger()) { 120 | if (player.getVehicle() instanceof Boat || player.getVehicle() instanceof Minecart) { 121 | realYaw = Mth.rotLerp(delta, player.yBodyRotO, player.yBodyRot); 122 | } else if (player.getVehicle() instanceof LivingEntity living) { 123 | realYaw = calculateBodyRot(Mth.rotLerp(delta, living.yBodyRotO, living.yBodyRot), 124 | NMSHelper.getYRot(player)); 125 | } else { 126 | // Non living entities don't use any custom rotation 127 | // realYaw = Mth.rotLerp(client.getFrameTime(), player.getVehicle().yRotO, 128 | // NMSHelper.getYRot(player.getVehicle())); 129 | } 130 | bodyOffset = Constants.IN_VEHICLE_BODY_OFFSET + fpm.getConfig().sitXOffset / 100f; 131 | } else { 132 | bodyOffset = 0.25f + fpm.getConfig().xOffset / 100f; 133 | } 134 | x += bodyOffset * Math.sin(Math.toRadians(realYaw)); 135 | z -= bodyOffset * Math.cos(Math.toRadians(realYaw)); 136 | if (isCrawlingOrSwimming(client.player)) { 137 | if (player.xRotO > 0 && player.isUnderWater()) { 138 | y += 0.6f * Math.sin(Math.toRadians(player.xRotO)); 139 | } else { 140 | y += 0.01f * -Math.sin(Math.toRadians(player.xRotO)); 141 | } 142 | } 143 | 144 | } 145 | offset = new Vec3(x, y, z); 146 | } 147 | 148 | private static float calculateBodyRot(float entityBodyRot, float riderHeadRot) { 149 | // Wrap the head rotation to the range [-180, 180] 150 | float wrappedHeadRot = Mth.wrapDegrees(riderHeadRot); 151 | 152 | // Calculate the difference between the head and body rotation 153 | float rotDiff = Mth.wrapDegrees(wrappedHeadRot - entityBodyRot); 154 | 155 | // If the difference is more than 50 degrees, adjust the body rotation 156 | if (Mth.abs(rotDiff) > 50.0F) { 157 | // Pull the body along with the head 158 | entityBodyRot = wrappedHeadRot - 50.0F * Math.signum(rotDiff); 159 | } 160 | 161 | // Ensure the body rotation is wrapped to [-180, 180] 162 | entityBodyRot = Mth.wrapDegrees(entityBodyRot); 163 | 164 | return entityBodyRot; 165 | } 166 | 167 | /** 168 | * Util method to quicker find where swimming is referenced 169 | * 170 | * @param player 171 | * @return 172 | */ 173 | public boolean isSwimming(Player player) { 174 | return player.isSwimming(); 175 | } 176 | 177 | /** 178 | * Util method to quicker find where the crawling/swimming animation is 179 | * referenced 180 | * 181 | * @param player 182 | * @return 183 | */ 184 | public boolean isCrawlingOrSwimming(Player player) { 185 | return player.isVisuallySwimming(); 186 | } 187 | 188 | public boolean showVanillaHands() { 189 | return showVanillaHands(client.player); 190 | } 191 | 192 | public boolean showVanillaHands(LivingEntity livingEntity) { 193 | if (livingEntity instanceof Player player) { 194 | return showVanillaHands(InventoryUtil.getSelected(InventoryUtil.getInventory(player)), 195 | InventoryUtil.getOffhand(InventoryUtil.getInventory(player))); 196 | } 197 | return false; 198 | } 199 | 200 | /** 201 | * Don't skip the vanilla first person arm rendering 202 | * 203 | * @param mainhand 204 | * @param offhand 205 | * @return 206 | */ 207 | public boolean showVanillaHands(ItemStack mainhand, ItemStack offhand) { 208 | return fpm.getConfig().vanillaHandsMode == VanillaHands.ALL 209 | || fpm.getConfig().vanillaHandsMode == VanillaHands.ALL_DOUBLE 210 | || (fpm.getConfig().vanillaHandsMode == VanillaHands.ITEMS 211 | && (!mainhand.isEmpty() || !offhand.isEmpty())) 212 | || autoVanillaHandItems.contains(mainhand.getItem()) || autoVanillaHandItems.contains(offhand.getItem()) 213 | || autoDisableItems.contains(mainhand.getItem()) || autoDisableItems.contains(offhand.getItem()); 214 | } 215 | 216 | public boolean hideArmsAndItems() { 217 | return hideArmsAndItems(client.player); 218 | } 219 | 220 | public boolean hideArmsAndItems(LivingEntity livingEntity) { 221 | if (livingEntity instanceof Player player) { 222 | return hideArmsAndItems(livingEntity, InventoryUtil.getSelected(InventoryUtil.getInventory(player)), 223 | InventoryUtil.getOffhand(InventoryUtil.getInventory(player))); 224 | } 225 | return false; 226 | } 227 | 228 | /** 229 | * Should the models arms and items not be rendered? 230 | * 231 | * @param mainhand 232 | * @param offhand 233 | * @return 234 | */ 235 | public boolean hideArmsAndItems(LivingEntity livingEntity, ItemStack mainhand, ItemStack offhand) { 236 | if (FirstPersonModelCore.instance.getConfig().vanillaHandsSkipSwimming && livingEntity instanceof Player player 237 | && isSwimming(player)) { 238 | return false; 239 | } 240 | if (lookingDown()) { 241 | return false; 242 | } 243 | return fpm.getConfig().vanillaHandsMode != VanillaHands.OFF || autoVanillaHandItems.contains(mainhand.getItem()) 244 | || autoVanillaHandItems.contains(offhand.getItem()) || autoDisableItems.contains(mainhand.getItem()) 245 | || autoDisableItems.contains(offhand.getItem()); 246 | } 247 | 248 | public boolean dynamicHandsEnabled() { 249 | return dynamicHandsEnabled(client.player); 250 | } 251 | 252 | public boolean dynamicHandsEnabled(LivingEntity livingEntity) { 253 | if (livingEntity instanceof Player player) { 254 | return dynamicHandsEnabled(livingEntity, InventoryUtil.getSelected(InventoryUtil.getInventory(player)), 255 | InventoryUtil.getOffhand(InventoryUtil.getInventory(player))); 256 | } 257 | return false; 258 | } 259 | 260 | /** 261 | * True is dynamic hands is enabled and could apply at this moment 262 | * 263 | * @param mainhand 264 | * @param offhand 265 | * @return 266 | */ 267 | public boolean dynamicHandsEnabled(LivingEntity livingEntity, ItemStack mainhand, ItemStack offhand) { 268 | if (FirstPersonModelCore.instance.getConfig().vanillaHandsSkipSwimming && livingEntity instanceof Player player 269 | && isSwimming(player)) { 270 | return false; 271 | } 272 | return fpm.getConfig().dynamicMode && fpm.getConfig().vanillaHandsMode != VanillaHands.OFF 273 | && !(autoVanillaHandItems.contains(mainhand.getItem()) 274 | || autoVanillaHandItems.contains(offhand.getItem()) 275 | || autoDisableItems.contains(mainhand.getItem()) 276 | || autoDisableItems.contains(offhand.getItem())); 277 | } 278 | 279 | /** 280 | * Is Dynamic hands enabled and the player looking down? 281 | * 282 | * @return 283 | */ 284 | public boolean lookingDown() { 285 | return dynamicHandsEnabled() && NMSHelper.getXRot(Minecraft.getInstance().player) > 30; 286 | } 287 | 288 | public void addAutoVanillaHandsItem(Item item) { 289 | autoVanillaHandItems.add(item); 290 | } 291 | 292 | public void addAutoDisableItem(Item item) { 293 | autoDisableItems.add(item); 294 | } 295 | 296 | public void reloadAutoVanillaHandsSettings() { 297 | autoVanillaHandItems.clear(); 298 | autoDisableItems.clear(); 299 | Item invalid = NMSHelper.getItem(NMSHelper.getResourceLocation("minecraft", "air")); 300 | for (String itemId : fpm.getConfig().autoVanillaHands) { 301 | try { 302 | Item item = NMSHelper 303 | .getItem(NMSHelper.getResourceLocation(itemId.split(":")[0], itemId.split(":")[1])); 304 | if (invalid != item) { 305 | addAutoVanillaHandsItem(item); 306 | } 307 | } catch (Exception ex) { 308 | FirstPersonBase.LOGGER.info("Unknown item to add to the auto vanilla hold list: {}", itemId); 309 | } 310 | } 311 | FirstPersonBase.LOGGER.info("Loaded Vanilla Hands items: {}", autoVanillaHandItems); 312 | for (String itemId : fpm.getConfig().autoToggleModItems) { 313 | try { 314 | Item item = NMSHelper 315 | .getItem(NMSHelper.getResourceLocation(itemId.split(":")[0], itemId.split(":")[1])); 316 | if (invalid != item) { 317 | addAutoDisableItem(item); 318 | } 319 | } catch (Exception ex) { 320 | FirstPersonBase.LOGGER.info("Unknown item to add to the auto disable list: {}", itemId); 321 | } 322 | } 323 | FirstPersonBase.LOGGER.info("Loaded Auto Disable items: {}", autoDisableItems); 324 | } 325 | 326 | } 327 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/access/AgeableListModelAccess.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.access; 2 | 3 | import net.minecraft.client.model.geom.ModelPart; 4 | 5 | public interface AgeableListModelAccess { 6 | 7 | Iterable firstPersonHeadPartsGetter(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/access/PlayerModelAccess.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.access; 2 | 3 | //#if MC < 12103 4 | //$$import net.minecraft.client.model.geom.ModelPart; 5 | //#endif 6 | 7 | public interface PlayerModelAccess { 8 | 9 | //#if MC < 12103 10 | //$$ ModelPart getCloak(); 11 | //#endif 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/api/ActivationHandler.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.api; 2 | 3 | public interface ActivationHandler { 4 | 5 | boolean preventFirstperson(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/api/FirstPersonAPI.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.api; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import dev.tr7zw.firstperson.FirstPersonModelCore; 7 | 8 | public final class FirstPersonAPI { 9 | 10 | private static List playerOffsetHandlers = new ArrayList<>(); 11 | private static List activationHandlers = new ArrayList<>(); 12 | 13 | private FirstPersonAPI() { 14 | // private 15 | } 16 | 17 | /** 18 | * The current status of the FirstPerson Mod. If true, the mod will attempt to 19 | * apply the required modifications during rendering. 20 | * 21 | * @return true if the mod is intended to be running right now 22 | */ 23 | public static boolean isEnabled() { 24 | return FirstPersonModelCore.instance.isEnabled(); 25 | } 26 | 27 | /** 28 | * Change the enabled status. Simulates pressing the (by default) F6 hotkey. 29 | * 30 | * @param enabled 31 | */ 32 | public static void setEnabled(boolean enabled) { 33 | FirstPersonModelCore.instance.setEnabled(enabled); 34 | } 35 | 36 | /** 37 | * Returns true when called during the first person player rendering. While this 38 | * is true, no heads/helmets/hats should be rendered! 39 | * 40 | * @return 41 | */ 42 | public static boolean isRenderingPlayer() { 43 | return FirstPersonModelCore.instance.isRenderingPlayer(); 44 | } 45 | 46 | /** 47 | * Adds a new handler. Supports: - {@link PlayerOffsetHandler} - 48 | * {@link ActivationHandler} 49 | * 50 | * @param handler 51 | */ 52 | public static void registerPlayerHandler(Object handler) { 53 | if (handler instanceof PlayerOffsetHandler offset) { 54 | playerOffsetHandlers.add(offset); 55 | } 56 | if (handler instanceof ActivationHandler activation) { 57 | activationHandlers.add(activation); 58 | } 59 | } 60 | 61 | /** 62 | * Get all registered {@link PlayerOffsetHandler}. The list is mutable, this 63 | * might be used by mods to resolve mod conflicts where one mod includes a fix 64 | * for another mod. 65 | * 66 | * @return 67 | */ 68 | public static List getPlayerOffsetHandlers() { 69 | return playerOffsetHandlers; 70 | } 71 | 72 | /** 73 | * Get all registered {@link ActivationHandler}. The list is mutable, this might 74 | * be used by mods to resolve mod conflicts where one mod includes a fix for 75 | * another mod. 76 | * 77 | * @return 78 | */ 79 | public static List getActivationHandlers() { 80 | return activationHandlers; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/api/PlayerOffsetHandler.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.api; 2 | 3 | import net.minecraft.client.player.AbstractClientPlayer; 4 | import net.minecraft.world.phys.Vec3; 5 | 6 | public interface PlayerOffsetHandler { 7 | 8 | /** 9 | * Allows custom model offsets during rendering. The current value is the value 10 | * pre-calculated by the mod(or handlers running before). If the handler doesn't 11 | * apply to the current situation, the expected behavior is to just return 12 | * "current". 13 | * 14 | * @param entity 15 | * @param delta 16 | * @param original 17 | * @param current 18 | * @return 19 | */ 20 | Vec3 applyOffset(AbstractClientPlayer entity, float delta, Vec3 original, Vec3 current); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/config/ConfigScreenProvider.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import dev.tr7zw.config.CustomConfigScreen; 7 | import dev.tr7zw.firstperson.FirstPersonModelCore; 8 | import dev.tr7zw.firstperson.versionless.config.VanillaHands; 9 | import lombok.experimental.UtilityClass; 10 | //#if MC >= 11900 11 | import net.minecraft.client.OptionInstance; 12 | //#else 13 | //$$ import net.minecraft.client.Option; 14 | //#endif 15 | import net.minecraft.client.gui.screens.Screen; 16 | 17 | @UtilityClass 18 | public class ConfigScreenProvider { 19 | 20 | public static Screen createConfigScreen(Screen parent) { 21 | return new CustomConfigScreen(parent, "text.firstperson.title") { 22 | 23 | @Override 24 | public void initialize() { 25 | FirstPersonModelCore fpm = FirstPersonModelCore.instance; 26 | getOptions().addBig(getOnOffOption("text.firstperson.option.firstperson.enabledByDefault", 27 | () -> fpm.getConfig().enabledByDefault, b -> fpm.getConfig().enabledByDefault = b)); 28 | 29 | List options = new ArrayList<>(); 30 | options.add(getIntOption("text.firstperson.option.firstperson.xOffset", -40, 40, 31 | () -> fpm.getConfig().xOffset, i -> fpm.getConfig().xOffset = i)); 32 | options.add(getIntOption("text.firstperson.option.firstperson.sneakXOffset", -40, 40, 33 | () -> fpm.getConfig().sneakXOffset, i -> fpm.getConfig().sneakXOffset = i)); 34 | options.add(getIntOption("text.firstperson.option.firstperson.sitXOffset", -40, 40, 35 | () -> fpm.getConfig().sitXOffset, i -> fpm.getConfig().sitXOffset = i)); 36 | options.add(getOnOffOption("text.firstperson.option.firstperson.renderStuckFeatures", 37 | () -> fpm.getConfig().renderStuckFeatures, b -> fpm.getConfig().renderStuckFeatures = b)); 38 | options.add(getEnumOption("text.firstperson.option.firstperson.vanillaHandMode", VanillaHands.class, 39 | () -> fpm.getConfig().vanillaHandsMode, b -> fpm.getConfig().vanillaHandsMode = b)); 40 | options.add(getOnOffOption("text.firstperson.option.firstperson.dynamicMode", 41 | () -> fpm.getConfig().dynamicMode, b -> fpm.getConfig().dynamicMode = b)); 42 | options.add(getOnOffOption("text.firstperson.option.firstperson.vanillaHandsSkipSwimming", 43 | () -> fpm.getConfig().vanillaHandsSkipSwimming, 44 | b -> fpm.getConfig().vanillaHandsSkipSwimming = b)); 45 | 46 | //#if MC >= 11900 47 | getOptions().addSmall(options.toArray(new OptionInstance[0])); 48 | //#else 49 | //$$getOptions().addSmall(options.toArray(new Option[0])); 50 | //#endif 51 | 52 | } 53 | 54 | @Override 55 | public void save() { 56 | FirstPersonModelCore.instance.writeSettings(); 57 | } 58 | 59 | @Override 60 | public void reset() { 61 | FirstPersonModelCore.instance.resetSettings(); 62 | FirstPersonModelCore.instance.writeSettings(); 63 | } 64 | 65 | }; 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/fabric/FPModMenuProvider.java: -------------------------------------------------------------------------------- 1 | //#if FABRIC 2 | package dev.tr7zw.firstperson.fabric; 3 | 4 | import com.terraformersmc.modmenu.api.ConfigScreenFactory; 5 | import com.terraformersmc.modmenu.api.ModMenuApi; 6 | 7 | import dev.tr7zw.firstperson.config.ConfigScreenProvider; 8 | 9 | public class FPModMenuProvider implements ModMenuApi { 10 | 11 | @Override 12 | public ConfigScreenFactory getModConfigScreenFactory() { 13 | return ConfigScreenProvider::createConfigScreen; 14 | } 15 | 16 | } 17 | //#endif -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/forge/RenderHandEventListener.java: -------------------------------------------------------------------------------- 1 | //#if FORGE || NEOFORGE 2 | //$$ package dev.tr7zw.firstperson.forge; 3 | //$$ 4 | //$$ import dev.tr7zw.firstperson.FirstPersonModelCore; 5 | //#if FORGE 6 | //$$ import net.minecraftforge.client.event.RenderHandEvent; 7 | //$$ import net.minecraftforge.eventbus.api.SubscribeEvent; 8 | //#else 9 | //$$ import net.neoforged.bus.api.SubscribeEvent; 10 | //$$ import net.neoforged.neoforge.client.event.RenderHandEvent; 11 | //#endif 12 | //$$ 13 | //$$ public class RenderHandEventListener { 14 | //$$ 15 | //$$ @SubscribeEvent 16 | //$$ public void onRender(RenderHandEvent e) { 17 | //$$ if(FirstPersonModelCore.instance.isEnabled() && !FirstPersonModelCore.instance.getLogicHandler().showVanillaHands()) { 18 | //$$ e.setCanceled(true); 19 | //$$ } 20 | //$$ } 21 | //$$ 22 | //$$ } 23 | //#endif -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/AgeableListModelMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | 5 | //#if MC < 12103 6 | //$$import org.spongepowered.asm.mixin.Shadow; 7 | //$$import dev.tr7zw.firstperson.access.AgeableListModelAccess; 8 | //$$import net.minecraft.client.model.AgeableListModel; 9 | //#endif 10 | import net.minecraft.client.model.geom.ModelPart; 11 | 12 | //#if MC >= 12103 13 | @Mixin(ModelPart.class) 14 | public abstract class AgeableListModelMixin { 15 | } 16 | //#else 17 | //$$@Mixin(AgeableListModel.class) 18 | //$$public abstract class AgeableListModelMixin implements AgeableListModelAccess { 19 | //$$ 20 | //$$ @Override 21 | //$$ public Iterable firstPersonHeadPartsGetter() { 22 | //$$ return headParts(); 23 | //$$ } 24 | //$$ 25 | //$$ @Shadow 26 | //$$ public abstract Iterable headParts(); 27 | //$$ 28 | //$$} 29 | //#endif -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/ArmorFeatureRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 8 | 9 | import com.mojang.blaze3d.vertex.PoseStack; 10 | 11 | import dev.tr7zw.firstperson.FirstPersonModelCore; 12 | import dev.tr7zw.firstperson.versionless.mixinbase.ModelPartBase; 13 | import net.minecraft.client.Minecraft; 14 | import net.minecraft.client.model.HumanoidModel; 15 | import net.minecraft.client.player.LocalPlayer; 16 | import net.minecraft.client.renderer.MultiBufferSource; 17 | import net.minecraft.client.renderer.entity.RenderLayerParent; 18 | import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; 19 | import net.minecraft.client.renderer.entity.layers.RenderLayer; 20 | import net.minecraft.world.entity.EquipmentSlot; 21 | import net.minecraft.world.entity.LivingEntity; 22 | //#if MC >= 12103 23 | import net.minecraft.world.item.equipment.Equippable; 24 | //#endif 25 | 26 | // lower prio to run before fabric api 27 | @SuppressWarnings("rawtypes") 28 | @Mixin(value = HumanoidArmorLayer.class, priority = 100) 29 | public abstract class ArmorFeatureRendererMixin 30 | extends RenderLayer { 31 | 32 | private static Minecraft fpmMcInstance = Minecraft.getInstance(); 33 | private static boolean hideLeftArm = false; 34 | private static boolean hideRightArm = false; 35 | 36 | protected ArmorFeatureRendererMixin(RenderLayerParent context) { 37 | super(context); 38 | } 39 | 40 | //#if MC >= 12103 41 | @Inject(method = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;shouldRender(Lnet/minecraft/world/item/equipment/Equippable;Lnet/minecraft/world/entity/EquipmentSlot;)Z", at = @At("HEAD"), cancellable = true) 42 | private static void shouldRender(Equippable equippable, EquipmentSlot equipmentSlot, 43 | CallbackInfoReturnable ci) { 44 | hideLeftArm = false; 45 | hideRightArm = false; 46 | if (FirstPersonModelCore.instance.isRenderingPlayer()) { 47 | if (equipmentSlot == EquipmentSlot.HEAD) { 48 | ci.setReturnValue(false); 49 | } 50 | if (equipmentSlot == EquipmentSlot.CHEST) { 51 | if (FirstPersonModelCore.instance.getLogicHandler().isSwimming(Minecraft.getInstance().player)) { 52 | ci.setReturnValue(false); 53 | } 54 | if (FirstPersonModelCore.instance.getLogicHandler().hideArmsAndItems()) { 55 | hideLeftArm = true; 56 | hideRightArm = true; 57 | } else if (FirstPersonModelCore.instance.getLogicHandler().dynamicHandsEnabled()) {// TODO DYNAMIC HAND 58 | 59 | if (!Minecraft.getInstance().player.getOffhandItem().isEmpty()) 60 | hideLeftArm = true; 61 | if (!Minecraft.getInstance().player.getMainHandItem().isEmpty()) 62 | hideRightArm = true; 63 | } 64 | } 65 | } 66 | } 67 | 68 | @Inject(method = "setPartVisibility", at = @At("TAIL")) 69 | protected void setPartVisibility(A model, EquipmentSlot slot, CallbackInfo ci) { 70 | if (hideLeftArm) { 71 | ((ModelPartBase) (Object) model.leftArm).setHidden(); 72 | } else { 73 | ((ModelPartBase) (Object) model.leftArm).showAgain(); 74 | } 75 | if (hideRightArm) { 76 | ((ModelPartBase) (Object) model.rightArm).setHidden(); 77 | } else { 78 | ((ModelPartBase) (Object) model.rightArm).showAgain(); 79 | } 80 | } 81 | 82 | //#else 83 | //$$@Inject(method = "renderArmorPiece", at = @At("HEAD"), cancellable = true) 84 | //#endif 85 | private void renderArmor(PoseStack matrices, MultiBufferSource vertexConsumers, T livingEntity, 86 | EquipmentSlot equipmentSlot, int i, A bipedEntityModel, CallbackInfo info) { 87 | if (livingEntity != fpmMcInstance.cameraEntity) { 88 | return; 89 | } 90 | if (equipmentSlot == EquipmentSlot.HEAD && FirstPersonModelCore.instance.isRenderingPlayer()) { 91 | info.cancel(); 92 | } 93 | if (equipmentSlot == EquipmentSlot.CHEST && FirstPersonModelCore.instance.isRenderingPlayer() 94 | && livingEntity instanceof LocalPlayer player 95 | && FirstPersonModelCore.instance.getLogicHandler().isSwimming(player)) { 96 | info.cancel(); 97 | } 98 | if (equipmentSlot == EquipmentSlot.CHEST && FirstPersonModelCore.instance.isRenderingPlayer()) { 99 | if (FirstPersonModelCore.instance.getLogicHandler().hideArmsAndItems()) { 100 | ((ModelPartBase) (Object) bipedEntityModel.leftArm).setHidden(); 101 | ((ModelPartBase) (Object) bipedEntityModel.rightArm).setHidden(); 102 | } else if (FirstPersonModelCore.instance.getLogicHandler().dynamicHandsEnabled()) {// TODO DYNAMIC HAND 103 | if (!livingEntity.getOffhandItem().isEmpty()) 104 | ((ModelPartBase) (Object) bipedEntityModel.leftArm).setHidden(); 105 | if (!livingEntity.getMainHandItem().isEmpty()) 106 | ((ModelPartBase) (Object) bipedEntityModel.rightArm).setHidden(); 107 | } else { 108 | ((ModelPartBase) (Object) bipedEntityModel.leftArm).showAgain(); 109 | ((ModelPartBase) (Object) bipedEntityModel.rightArm).showAgain(); 110 | } 111 | } else { 112 | ((ModelPartBase) (Object) bipedEntityModel.leftArm).showAgain(); 113 | ((ModelPartBase) (Object) bipedEntityModel.rightArm).showAgain(); 114 | } 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/CustomHeadLayerMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | 10 | import dev.tr7zw.firstperson.FirstPersonModelCore; 11 | import net.minecraft.client.renderer.MultiBufferSource; 12 | import net.minecraft.client.renderer.entity.layers.CustomHeadLayer; 13 | //#if MC >= 12103 14 | import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; 15 | //#else 16 | //$$import net.minecraft.world.entity.LivingEntity; 17 | //#endif 18 | 19 | //lower prio to run before other mods 20 | @Mixin(value = CustomHeadLayer.class, priority = 100) 21 | public class CustomHeadLayerMixin { 22 | 23 | @Inject(method = "render", at = @At("HEAD"), cancellable = true) 24 | //#if MC >= 12103 25 | public void render(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, 26 | LivingEntityRenderState livingEntityRenderState, float f, float g, CallbackInfo info) { 27 | //#else 28 | //$$public void render(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, LivingEntity livingEntity, 29 | //$$ float f, float g, float h, float j, float k, float l, CallbackInfo info) { 30 | //#endif 31 | if (FirstPersonModelCore.instance.isRenderingPlayer()) { 32 | info.cancel(); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/ElytraLayerMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | 10 | import dev.tr7zw.firstperson.FirstPersonModelCore; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.player.AbstractClientPlayer; 13 | import net.minecraft.client.renderer.MultiBufferSource; 14 | //#if MC >= 12103 15 | import net.minecraft.client.renderer.entity.layers.WingsLayer; 16 | import net.minecraft.client.renderer.entity.state.HumanoidRenderState; 17 | //#else 18 | //$$import net.minecraft.client.renderer.entity.layers.ElytraLayer; 19 | //#endif 20 | import net.minecraft.world.entity.LivingEntity; 21 | 22 | /** 23 | * @author KxmischesDomi | https://github.com/kxmischesdomi 24 | */ 25 | //#if MC >= 12103 26 | @Mixin(WingsLayer.class) 27 | //#else 28 | //$$@Mixin(ElytraLayer.class) 29 | //#endif 30 | public class ElytraLayerMixin { 31 | 32 | //#if MC >= 12103 33 | @Inject(method = "render", at = @At("HEAD"), cancellable = true) 34 | public void render(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, 35 | HumanoidRenderState humanoidRenderState, float f, float g, CallbackInfo ci) { 36 | //#else 37 | //$$ @Inject(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/entity/LivingEntity;FFFFFF)V", at = @At("HEAD"), cancellable = true) 38 | //$$ public void render(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, T livingEntity, float f, 39 | //$$ float g, float h, float j, float k, float l, CallbackInfo ci) { 40 | //#endif 41 | if (FirstPersonModelCore.instance.isRenderingPlayer() 42 | && Minecraft.getInstance().getCameraEntity() instanceof AbstractClientPlayer player 43 | && FirstPersonModelCore.instance.getLogicHandler().isSwimming(player)) { 44 | ci.cancel(); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/FeatureRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | 10 | import dev.tr7zw.firstperson.FirstPersonModelCore; 11 | import dev.tr7zw.firstperson.versionless.mixinbase.ModelPartBase; 12 | import lombok.AccessLevel; 13 | import lombok.NoArgsConstructor; 14 | import net.minecraft.client.model.EntityModel; 15 | import net.minecraft.client.model.HeadedModel; 16 | import net.minecraft.client.renderer.MultiBufferSource; 17 | import net.minecraft.client.renderer.entity.layers.RenderLayer; 18 | import net.minecraft.resources.ResourceLocation; 19 | import net.minecraft.world.entity.LivingEntity; 20 | //#if MC >= 12103 21 | import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; 22 | //#endif 23 | //#if MC < 12104 24 | //$$import net.minecraft.client.model.VillagerHeadModel; 25 | //#endif 26 | 27 | @Mixin(RenderLayer.class) 28 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 29 | public abstract class FeatureRendererMixin { 30 | @Inject(method = "renderColoredCutoutModel", at = @At("HEAD"), cancellable = true) 31 | //#if MC >= 12103 32 | private static void renderColoredCutoutModel(EntityModel model, ResourceLocation resourceLocation, 33 | PoseStack poseStack, MultiBufferSource multiBufferSource, int i, 34 | LivingEntityRenderState livingEntityRenderState, int j, CallbackInfo ci) { 35 | //#elseif MC >= 12100 36 | //$$private static void removeHead(EntityModel model, ResourceLocation texture, 37 | //$$ PoseStack matrices, MultiBufferSource vertexConsumers, int light, T entity, int color, CallbackInfo ci) { 38 | //#else 39 | //$$private static void removeHead(EntityModel model, ResourceLocation texture, 40 | //$$ PoseStack matrices, MultiBufferSource vertexConsumers, int light, T entity, float red, float green, 41 | //$$ float blue, CallbackInfo ci) { 42 | //#endif 43 | if (FirstPersonModelCore.instance.isRenderingPlayer()) { 44 | if (!(model instanceof HeadedModel)) { 45 | ci.cancel(); 46 | return; 47 | } 48 | ((ModelPartBase) (Object) ((HeadedModel) model).getHead()).setHidden(); 49 | //#if MC < 12104 50 | //$$if (model instanceof VillagerHeadModel villager) { 51 | //$$ villager.hatVisible(false); 52 | //$$} 53 | //#endif 54 | } 55 | } 56 | 57 | @Inject(method = "renderColoredCutoutModel", at = @At("RETURN"), cancellable = true) 58 | //#if MC >= 12103 59 | private static void removeReturn(EntityModel model, ResourceLocation resourceLocation, PoseStack poseStack, 60 | MultiBufferSource multiBufferSource, int i, LivingEntityRenderState livingEntityRenderState, int j, 61 | CallbackInfo ci) { 62 | //#elseif MC >= 12100 63 | //$$private static void removeReturn(EntityModel model, ResourceLocation texture, 64 | //$$ PoseStack matrices, MultiBufferSource vertexConsumers, int light, T entity, int color, CallbackInfo ci) { 65 | //#else 66 | //$$private static void removeReturn(EntityModel model, ResourceLocation texture, 67 | //$$ PoseStack matrices, MultiBufferSource vertexConsumers, int light, T entity, float red, float green, 68 | //$$ float blue, CallbackInfo ci) { 69 | //#endif 70 | if (model instanceof HeadedModel) { 71 | ((ModelPartBase) (Object) ((HeadedModel) model).getHead()).showAgain(); 72 | //#if MC < 12104 73 | //$$ if (model instanceof VillagerHeadModel villager) { 74 | //$$ villager.hatVisible(true); 75 | //$$ } 76 | //#endif 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/FishingBobberRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 9 | 10 | import com.mojang.blaze3d.vertex.PoseStack; 11 | 12 | import dev.tr7zw.firstperson.FirstPersonModelCore; 13 | import net.minecraft.client.CameraType; 14 | import net.minecraft.client.Minecraft; 15 | import net.minecraft.client.Options; 16 | import net.minecraft.client.renderer.MultiBufferSource; 17 | import net.minecraft.client.renderer.entity.FishingHookRenderer; 18 | import net.minecraft.world.entity.player.Player; 19 | import net.minecraft.world.phys.Vec3; 20 | //#if MC >= 12103 21 | import net.minecraft.client.renderer.entity.state.FishingHookRenderState; 22 | //#else 23 | //$$ import net.minecraft.world.entity.projectile.FishingHook; 24 | //#endif 25 | 26 | @Mixin(FishingHookRenderer.class) 27 | public class FishingBobberRendererMixin { 28 | 29 | private Vec3 offsetvec3d = Vec3.ZERO; // to not create @Nullable 30 | 31 | private boolean doCorrect() { 32 | return FirstPersonModelCore.instance.isEnabled() 33 | && Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON 34 | && !FirstPersonModelCore.instance.getLogicHandler().hideArmsAndItems(); 35 | } 36 | 37 | //#if MC <= 12004 38 | //$$ @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Options;getCameraType()Lnet/minecraft/client/CameraType;")) 39 | //#else 40 | @Redirect(method = "getPlayerHandPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Options;getCameraType()Lnet/minecraft/client/CameraType;")) 41 | //#endif 42 | private CameraType redirect(Options gameOptions) { 43 | return doCorrect() ? CameraType.THIRD_PERSON_BACK : gameOptions.getCameraType(); 44 | } 45 | 46 | @Inject(method = "render", at = @At("HEAD")) 47 | //#if MC >= 12103 48 | public void render(FishingHookRenderState fishingHookRenderState, PoseStack poseStack, 49 | MultiBufferSource multiBufferSource, int i, CallbackInfo info) { 50 | //#else 51 | //$$ private void calcOffset(FishingHook fishingBobberEntity, float f, float g, PoseStack matrixStack, 52 | //$$ MultiBufferSource vertexConsumerProvider, int i, CallbackInfo info) { 53 | //#endif 54 | if (FirstPersonModelCore.instance.isRenderingPlayer()) { 55 | offsetvec3d = FirstPersonModelCore.instance.getLogicHandler().getOffset();// getPositionOffset((Player) 56 | // fishingBobberEntity.getOwner(), 57 | // matrixStack); 58 | } else { 59 | offsetvec3d = new Vec3(0, 0, 0); 60 | } 61 | } 62 | 63 | //#if MC <= 12004 64 | //$$ @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getX()D")) 65 | //$$ private double offsetX(Player playerEntity) { 66 | //$$ return playerEntity.getX() + offsetvec3d.x(); 67 | //$$ } 68 | //$$ 69 | //$$ @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getZ()D")) 70 | //$$ private double offsetZ(Player playerEntity) { 71 | //$$ return playerEntity.getZ() + offsetvec3d.z(); 72 | //$$ } 73 | //$$ 74 | //$$ @Redirect(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/player/Player;xo:D")) 75 | //$$ private double prevOffsetX(Player playerEntity) { 76 | //$$ return playerEntity.xo + offsetvec3d.x(); 77 | //$$ } 78 | //$$ 79 | //$$ @Redirect(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/player/Player;zo:D")) 80 | //$$ private double prevOffsetZ(Player playerEntity) { 81 | //$$ return playerEntity.zo + offsetvec3d.z(); 82 | //$$ } 83 | //#else 84 | @Inject(method = "getPlayerHandPos", at = @At("RETURN"), cancellable = true) 85 | private void getPlayerHandPosOffset(Player player, float f, float g, CallbackInfoReturnable ci) { 86 | ci.setReturnValue(ci.getReturnValue().add(offsetvec3d)); 87 | } 88 | //#endif 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/HeldItemFeatureRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | 10 | import dev.tr7zw.firstperson.FirstPersonModelCore; 11 | import net.minecraft.client.Minecraft; 12 | 13 | import net.minecraft.client.renderer.MultiBufferSource; 14 | import net.minecraft.client.renderer.entity.layers.ItemInHandLayer; 15 | import net.minecraft.world.entity.HumanoidArm; 16 | //#if MC < 12104 17 | //$$import net.minecraft.world.item.ItemStack; 18 | //#endif 19 | //#if MC == 12103 20 | //$$import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; 21 | //#endif 22 | //#if MC >= 12103 && MC < 12104 23 | //$$import net.minecraft.client.resources.model.BakedModel; 24 | //#else 25 | //$$import net.minecraft.client.player.LocalPlayer; 26 | //$$import net.minecraft.world.entity.LivingEntity; 27 | //#endif 28 | //#if MC >= 11904 29 | //#if MC < 12104 30 | //$$import net.minecraft.world.item.ItemDisplayContext; 31 | //#endif 32 | //#else 33 | //$$ import net.minecraft.client.renderer.block.model.ItemTransforms.TransformType; 34 | //#endif 35 | //#if MC >= 12104 36 | import net.minecraft.client.renderer.entity.state.ArmedEntityRenderState; 37 | import net.minecraft.client.renderer.item.ItemStackRenderState; 38 | //#endif 39 | 40 | /** 41 | * Stops items in the hand from rendering while in first person. 42 | * 43 | */ 44 | @Mixin(ItemInHandLayer.class) 45 | public class HeldItemFeatureRendererMixin { 46 | 47 | @Inject(at = @At("HEAD"), method = "renderArmWithItem", cancellable = true) 48 | //#if MC >= 12104 49 | private void renderArmWithItem(ArmedEntityRenderState livingEntityRenderState, 50 | ItemStackRenderState itemStackRenderState, HumanoidArm humanoidArm, PoseStack poseStack, 51 | MultiBufferSource multiBufferSource, int i, CallbackInfo ci) { 52 | //#elseif MC >= 12103 53 | //$$private void renderArmWithItem(LivingEntityRenderState livingEntityRenderState, BakedModel bakedModel, 54 | //$$ ItemStack itemStack, ItemDisplayContext itemDisplayContext, HumanoidArm humanoidArm, PoseStack poseStack, 55 | //$$ MultiBufferSource multiBufferSource, int i, CallbackInfo ci) { 56 | //#elseif MC >= 11904 57 | //$$private void renderArmWithItem(LivingEntity livingEntity, ItemStack itemStack, ItemDisplayContext itemDisplayContext, 58 | //$$ HumanoidArm humanoidArm, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) { 59 | //#else 60 | //$$ private void renderArmWithItem(LivingEntity livingEntity, ItemStack itemStack, TransformType transformType, 61 | //$$ HumanoidArm humanoidArm, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) { 62 | //#endif 63 | if (FirstPersonModelCore.instance.isRenderingPlayer()) { 64 | if (FirstPersonModelCore.instance.getLogicHandler().hideArmsAndItems(Minecraft.getInstance().player) 65 | && !FirstPersonModelCore.instance.getLogicHandler().lookingDown()) { 66 | ci.cancel(); 67 | } 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/HeldItemRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Shadow; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | 9 | import com.mojang.blaze3d.vertex.PoseStack; 10 | 11 | import dev.tr7zw.firstperson.FirstPersonModelCore; 12 | import dev.tr7zw.firstperson.versionless.config.VanillaHands; 13 | import dev.tr7zw.util.NMSHelper; 14 | import net.minecraft.client.Minecraft; 15 | import net.minecraft.client.player.AbstractClientPlayer; 16 | import net.minecraft.client.player.LocalPlayer; 17 | import net.minecraft.client.renderer.ItemInHandRenderer; 18 | import net.minecraft.client.renderer.MultiBufferSource; 19 | import net.minecraft.client.renderer.entity.EntityRenderDispatcher; 20 | import net.minecraft.world.InteractionHand; 21 | import net.minecraft.world.entity.HumanoidArm; 22 | import net.minecraft.world.item.ItemStack; 23 | import net.minecraft.world.item.Items; 24 | 25 | /** 26 | * Hides the normal first person hands 27 | * 28 | */ 29 | @Mixin(ItemInHandRenderer.class) 30 | public abstract class HeldItemRendererMixin { 31 | 32 | @Shadow 33 | private EntityRenderDispatcher entityRenderDispatcher; 34 | @Shadow 35 | private float mainHandHeight; 36 | @Shadow 37 | private float offHandHeight; 38 | @Shadow 39 | private ItemStack mainHandItem; 40 | @Shadow 41 | private ItemStack offHandItem; 42 | 43 | @Inject(at = @At("HEAD"), method = "renderArmWithItem", cancellable = true) 44 | public void renderFirstPersonItem(AbstractClientPlayer player, float tickDelta, float pitch, InteractionHand hand, 45 | float swingProgress, ItemStack item, float equipProgress, PoseStack matrices, 46 | MultiBufferSource vertexConsumers, int light, CallbackInfo info) { 47 | 48 | if (!FirstPersonModelCore.instance.isEnabled()) { 49 | return; 50 | } 51 | if (!FirstPersonModelCore.instance.getLogicHandler().showVanillaHands()) { 52 | info.cancel(); 53 | return; 54 | } 55 | if (FirstPersonModelCore.instance.getConfig().vanillaHandsSkipSwimming 56 | && FirstPersonModelCore.instance.getLogicHandler().isSwimming(player)) { 57 | // while actively swimming, dont show the offhand item 58 | info.cancel(); 59 | return; 60 | } 61 | // filter out vanilla hands with no item 62 | if (FirstPersonModelCore.instance.getLogicHandler().dynamicHandsEnabled() && pitch > 35) { 63 | // item held too low, hide 64 | info.cancel(); 65 | return; 66 | } 67 | // double hands 68 | if (FirstPersonModelCore.instance.getConfig().vanillaHandsMode != VanillaHands.ALL_DOUBLE 69 | || player.getMainHandItem().getItem() == Items.FILLED_MAP 70 | //#if MC >= 11700 71 | || player.isScoping()) { 72 | //#else 73 | //$$|| false) { 74 | //#endif 75 | return; 76 | } 77 | boolean bl = hand == InteractionHand.MAIN_HAND; 78 | HumanoidArm arm = bl ? player.getMainArm() : player.getMainArm().getOpposite(); 79 | matrices.pushPose(); 80 | if (item.isEmpty() && !bl && !player.isInvisible()) { 81 | renderPlayerArm(matrices, vertexConsumers, light, equipProgress, swingProgress, arm); 82 | } 83 | matrices.popPose(); 84 | } 85 | 86 | @Shadow 87 | public abstract void renderPlayerArm(PoseStack matrices, MultiBufferSource vertexConsumers, int light, 88 | float equipProgress, float swingProgress, HumanoidArm arm); 89 | 90 | /* 91 | * public boolean skip() {//TODO NO NEED? return 92 | * !FirstPersonModelCore.instance.isEnabled() || 93 | * FirstPersonModelCore.instance.getLogicHandler().showVanillaHands(); } 94 | */ 95 | 96 | @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getAttackStrengthScale(F)F", shift = At.Shift.BEFORE), method = "tick", cancellable = true) 97 | public void tick(CallbackInfo ci) {// TODO DYNAMIC HAND 98 | if (FirstPersonModelCore.instance.isEnabled() 99 | && FirstPersonModelCore.instance.getLogicHandler().showVanillaHands() 100 | && FirstPersonModelCore.instance.getLogicHandler().dynamicHandsEnabled()) { 101 | LocalPlayer localPlayer = Minecraft.getInstance().player; 102 | float f = NMSHelper.getXRot(localPlayer); 103 | if (f > 15) { 104 | if (f < 30) { 105 | this.mainHandHeight = 15 / f; 106 | this.offHandHeight = 15 / f; 107 | } else { 108 | this.mainHandHeight -= this.mainHandHeight > -0.1f ? 0.15f : 0; 109 | this.offHandHeight -= this.offHandHeight > -0.1f ? 0.15f : 0; 110 | } 111 | ci.cancel(); 112 | this.mainHandItem = localPlayer.getMainHandItem(); 113 | this.offHandItem = localPlayer.getOffhandItem(); 114 | } 115 | } 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/LivingEntityRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.util.Mth; 5 | import net.minecraft.world.item.Items; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.At.Shift; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | import com.mojang.blaze3d.vertex.PoseStack; 18 | 19 | import dev.tr7zw.firstperson.FirstPersonModelCore; 20 | import dev.tr7zw.firstperson.InventoryUtil; 21 | import dev.tr7zw.firstperson.access.AgeableListModelAccess; 22 | //#if MC < 12103 23 | //$$ import dev.tr7zw.firstperson.access.PlayerModelAccess; 24 | //#endif 25 | import dev.tr7zw.firstperson.versionless.mixinbase.ModelPartBase; 26 | import dev.tr7zw.util.NMSHelper; 27 | import net.minecraft.client.model.EntityModel; 28 | import net.minecraft.client.model.HeadedModel; 29 | import net.minecraft.client.model.HumanoidModel; 30 | import net.minecraft.client.model.Model; 31 | import net.minecraft.client.model.PlayerModel; 32 | 33 | import net.minecraft.client.player.AbstractClientPlayer; 34 | import net.minecraft.client.renderer.MultiBufferSource; 35 | import net.minecraft.client.renderer.entity.LivingEntityRenderer; 36 | import net.minecraft.world.entity.Entity; 37 | import net.minecraft.world.entity.LivingEntity; 38 | import net.minecraft.world.entity.monster.Shulker; 39 | import net.minecraft.world.entity.player.Player; 40 | //#if MC >= 12103 41 | import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; 42 | import net.minecraft.client.renderer.entity.state.PlayerRenderState; 43 | //#endif 44 | //#if MC < 12104 45 | //$$import net.minecraft.client.model.VillagerHeadModel; 46 | //#endif 47 | 48 | @Mixin(LivingEntityRenderer.class) 49 | public abstract class LivingEntityRendererMixin { 50 | 51 | private static List revert = new ArrayList(); 52 | 53 | // pull all registers to try to get rid of the head or other bodyparts 54 | //#if MC >= 12103 55 | @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/EntityModel;setupAnim(Lnet/minecraft/client/renderer/entity/state/EntityRenderState;)V", shift = Shift.AFTER), cancellable = true) 56 | public void render(LivingEntityRenderState livingEntityRenderState, PoseStack matrixStack, 57 | MultiBufferSource multiBufferSource, int i, CallbackInfo info) { 58 | if (!FirstPersonModelCore.instance.isRenderingPlayer()) 59 | return; 60 | Entity entity = Minecraft.getInstance().cameraEntity; 61 | if (!(entity instanceof LivingEntity)) { 62 | return; 63 | } 64 | LivingEntity livingEntity = (LivingEntity) entity; 65 | //#else 66 | //$$@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/EntityModel;setupAnim(Lnet/minecraft/world/entity/Entity;FFFFF)V", shift = Shift.AFTER), cancellable = true) 67 | //$$public void renderPostAnim(LivingEntity livingEntity, float f, float g, PoseStack matrixStack, 68 | //$$ MultiBufferSource vertexConsumerProvider, int i, CallbackInfo info) { 69 | //#endif 70 | if (!revert.isEmpty()) { 71 | for (Runnable r : revert) { 72 | r.run(); 73 | } 74 | revert.clear(); 75 | } 76 | if (!FirstPersonModelCore.instance.isRenderingPlayer()) 77 | return; 78 | if (livingEntity instanceof Shulker) { 79 | return;// No need to mess with 80 | } 81 | Model model = getModel(); 82 | boolean headShouldBeHidden = false; 83 | if (model instanceof AgeableListModelAccess agable) { 84 | agable.firstPersonHeadPartsGetter().forEach(part -> { 85 | ((ModelPartBase) (Object) part).setHidden(); 86 | revert.add(() -> ((ModelPartBase) (Object) part).showAgain()); 87 | }); 88 | headShouldBeHidden = true; 89 | } 90 | if (model instanceof HeadedModel headed) { 91 | ((ModelPartBase) (Object) headed.getHead()).setHidden(); 92 | revert.add(() -> ((ModelPartBase) (Object) headed.getHead()).showAgain()); 93 | headShouldBeHidden = true; 94 | } 95 | if (model instanceof HumanoidModel humanModel && livingEntity instanceof Player player) { 96 | if (FirstPersonModelCore.instance.getLogicHandler().hideArmsAndItems(livingEntity)) { 97 | ((ModelPartBase) (Object) humanModel.leftArm).setHidden(); 98 | ((ModelPartBase) (Object) humanModel.rightArm).setHidden(); 99 | revert.add(() -> { 100 | ((ModelPartBase) (Object) humanModel.leftArm).showAgain(); 101 | ((ModelPartBase) (Object) humanModel.rightArm).showAgain(); 102 | }); 103 | } else if (FirstPersonModelCore.instance.getLogicHandler().dynamicHandsEnabled()) {// TODO VANILLA HANDS 104 | // ITEM 105 | float offset = Mth.clamp(-NMSHelper.getXRot(Minecraft.getInstance().player) / 20 + 2, -0.0f, 0.7f); 106 | humanModel.rightArm.xRot += offset; 107 | humanModel.leftArm.xRot += offset; 108 | // humanModel.rightArm.offsetRotation(new Vector3f(offset, 0, 0)); 109 | // humanModel.leftArm.offsetRotation(new Vector3f(offset, 0, 0)); 110 | 111 | if (!FirstPersonModelCore.instance.getLogicHandler().lookingDown()) {// TODO DYNAMIC HAND 112 | if (!InventoryUtil.getOffhand(InventoryUtil.getInventory(player)).isEmpty() 113 | || livingEntity.getMainHandItem().getItem().equals(Items.FILLED_MAP)) { 114 | ((ModelPartBase) (Object) humanModel.leftArm).setHidden(); 115 | revert.add(() -> { 116 | ((ModelPartBase) (Object) humanModel.leftArm).showAgain(); 117 | }); 118 | } 119 | if (!InventoryUtil.getSelected(InventoryUtil.getInventory(player)).isEmpty()) { 120 | ((ModelPartBase) (Object) humanModel.rightArm).setHidden(); 121 | revert.add(() -> { 122 | ((ModelPartBase) (Object) humanModel.rightArm).showAgain(); 123 | }); 124 | } 125 | } 126 | } 127 | } 128 | //#if MC < 12104 129 | //$$if (model instanceof VillagerHeadModel villaterHead) { 130 | //$$ villaterHead.hatVisible(false); 131 | //$$ revert.add(() -> { 132 | //$$ villaterHead.hatVisible(true); 133 | //$$ }); 134 | //$$} 135 | //#endif 136 | if (model instanceof PlayerModel playerModel) { 137 | headShouldBeHidden = true; 138 | ((ModelPartBase) (Object) playerModel.hat).setHidden(); 139 | revert.add(() -> ((ModelPartBase) (Object) playerModel.hat).showAgain()); 140 | if (livingEntity instanceof Player player) { 141 | if (FirstPersonModelCore.instance.getLogicHandler().hideArmsAndItems(livingEntity)) { 142 | ((ModelPartBase) (Object) playerModel.leftSleeve).setHidden(); 143 | ((ModelPartBase) (Object) playerModel.rightSleeve).setHidden(); 144 | revert.add(() -> { 145 | ((ModelPartBase) (Object) playerModel.leftSleeve).showAgain(); 146 | ((ModelPartBase) (Object) playerModel.rightSleeve).showAgain(); 147 | }); 148 | } else if (FirstPersonModelCore.instance.getLogicHandler().dynamicHandsEnabled()) {// TODO VANILLA HANDS 149 | // ITEM 150 | float offset = Mth.clamp(-NMSHelper.getXRot(Minecraft.getInstance().player) / 20 + 2, -0.0f, 0.7f); 151 | playerModel.rightSleeve.xRot += offset; 152 | playerModel.leftSleeve.xRot += offset; 153 | // playerModel.rightSleeve.offsetRotation(new Vector3f(offset, 0, 0)); 154 | // playerModel.leftSleeve.offsetRotation(new Vector3f(offset, 0, 0)); 155 | 156 | if (!FirstPersonModelCore.instance.getLogicHandler().lookingDown()) {// TODO DYNAMIC HAND 157 | if (!InventoryUtil.getOffhand(InventoryUtil.getInventory(player)).isEmpty() 158 | || livingEntity.getMainHandItem().getItem().equals(Items.FILLED_MAP)) { 159 | ((ModelPartBase) (Object) playerModel.leftSleeve).setHidden(); 160 | revert.add(() -> ((ModelPartBase) (Object) playerModel.leftSleeve).showAgain()); 161 | } 162 | if (!InventoryUtil.getSelected(InventoryUtil.getInventory(player)).isEmpty()) { 163 | ((ModelPartBase) (Object) playerModel.rightSleeve).setHidden(); 164 | revert.add(() -> ((ModelPartBase) (Object) playerModel.rightSleeve).showAgain()); 165 | } 166 | } 167 | } 168 | } 169 | } 170 | if (livingEntity instanceof AbstractClientPlayer player && (Object) model instanceof PlayerModel playerModel 171 | && FirstPersonModelCore.instance.getLogicHandler().isSwimming(player)) { 172 | ((ModelPartBase) (Object) playerModel.body).setHidden(); 173 | //#if MC >= 12103 174 | if (livingEntityRenderState instanceof PlayerRenderState prs) { 175 | prs.showCape = false; 176 | } 177 | //#else 178 | //$$((ModelPartBase) (Object) ((PlayerModelAccess) model).getCloak()).setHidden(); 179 | //#endif 180 | revert.add(() -> { 181 | ((ModelPartBase) (Object) playerModel.body).showAgain(); 182 | //#if MC < 12103 183 | //$$ ((ModelPartBase) (Object) ((PlayerModelAccess) model).getCloak()).showAgain(); 184 | //#endif 185 | }); 186 | } 187 | if (!headShouldBeHidden) { 188 | // we failed to hide the head. So either its a mob without one, or something is 189 | // going really wrong. Cancel the render for the firstperson mode 190 | matrixStack.popPose(); 191 | info.cancel(); 192 | } 193 | } 194 | 195 | @Inject(method = "render", at = @At("RETURN")) 196 | //#if MC >= 12103 197 | public void renderEnd(LivingEntityRenderState livingEntityRenderState, PoseStack poseStack, 198 | MultiBufferSource multiBufferSource, int i, CallbackInfo info) { 199 | //#else 200 | //$$ public void renderReturn(LivingEntity livingEntity, float f, float g, PoseStack matrixStack, 201 | //$$ MultiBufferSource vertexConsumerProvider, int i, CallbackInfo info) { 202 | //#endifs 203 | if (!revert.isEmpty()) { 204 | for (Runnable r : revert) { 205 | r.run(); 206 | } 207 | revert.clear(); 208 | } 209 | FirstPersonModelCore.instance.setRenderingPlayer(false); 210 | } 211 | 212 | @Shadow 213 | public abstract EntityModel getModel(); 214 | 215 | } 216 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/ModelPartMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Shadow; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | 9 | import dev.tr7zw.firstperson.versionless.mixinbase.ModelPartBase; 10 | import net.minecraft.client.model.geom.ModelPart; 11 | 12 | @Mixin(ModelPart.class) 13 | public class ModelPartMixin implements ModelPartBase { 14 | 15 | @Shadow 16 | public float z; 17 | @Shadow 18 | public boolean visible; 19 | 20 | private float zCopy = 0; 21 | private boolean moved = false; 22 | 23 | @Override 24 | public void setHidden() { 25 | if (!moved) { 26 | zCopy = z; 27 | } 28 | z = 5000; 29 | moved = true; 30 | visible = false; 31 | } 32 | 33 | @Override 34 | public void showAgain() { 35 | if (moved) { 36 | z = zCopy; 37 | moved = false; 38 | visible = true; 39 | } 40 | } 41 | 42 | @Inject(method = "setPos", at = @At("RETURN")) 43 | public void setPivot(float x, float y, float z, CallbackInfo info) { 44 | if (moved) { 45 | zCopy = z; 46 | this.z = 5000; 47 | } 48 | } 49 | 50 | @Inject(method = "copyFrom", at = @At("RETURN")) 51 | public void copyTransform(ModelPart modelPart, CallbackInfo info) { 52 | if (moved) { 53 | zCopy = z; 54 | z = 5000; 55 | } 56 | } 57 | 58 | @Override 59 | public boolean isHidden() { 60 | return moved; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/PlayerMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import dev.tr7zw.firstperson.FirstPersonModelCore; 9 | import dev.tr7zw.firstperson.InventoryUtil; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.world.entity.EquipmentSlot; 12 | import net.minecraft.world.entity.player.Player; 13 | import net.minecraft.world.item.ItemStack; 14 | 15 | //#if MC >= 12105 16 | import net.minecraft.world.entity.LivingEntity; 17 | 18 | @Mixin(LivingEntity.class) 19 | //#else 20 | //$$ @Mixin(Player.class) 21 | //#endif 22 | public class PlayerMixin { 23 | 24 | @Inject(method = "getItemBySlot", at = @At("HEAD"), cancellable = true) 25 | public void getItemBySlot(EquipmentSlot slot, CallbackInfoReturnable ci) { 26 | if (FirstPersonModelCore.instance.isRenderingPlayer() && Minecraft.getInstance().isSameThread() 27 | && (Object) this instanceof Player player) { 28 | if (slot == EquipmentSlot.HEAD) { 29 | ci.setReturnValue(ItemStack.EMPTY); 30 | return; 31 | } 32 | if ((slot == EquipmentSlot.MAINHAND || slot == EquipmentSlot.OFFHAND) 33 | && FirstPersonModelCore.instance.getLogicHandler().hideArmsAndItems(Minecraft.getInstance().player, 34 | InventoryUtil.getSelected(InventoryUtil.getInventory(player)), 35 | InventoryUtil.getOffhand(InventoryUtil.getInventory(player)))) { 36 | ci.setReturnValue(ItemStack.EMPTY); 37 | return; 38 | } 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/PlayerModelMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | 5 | import dev.tr7zw.firstperson.access.PlayerModelAccess; 6 | import net.minecraft.client.model.PlayerModel; 7 | import net.minecraft.world.entity.LivingEntity; 8 | //#if MC < 12103 9 | //$$ import org.spongepowered.asm.mixin.Final; 10 | //$$ import org.spongepowered.asm.mixin.Shadow; 11 | //$$ import net.minecraft.client.model.geom.ModelPart; 12 | //#endif 13 | 14 | @Mixin(value = PlayerModel.class) 15 | public class PlayerModelMixin implements PlayerModelAccess { 16 | 17 | //#if MC < 12103 18 | //$$@Shadow 19 | //$$@Final 20 | //$$private ModelPart cloak; 21 | //$$ 22 | //$$@Override 23 | //$$public ModelPart getCloak() { 24 | //$$ return cloak; 25 | //$$} 26 | //#endif 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/PlayerRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 7 | 8 | import dev.tr7zw.firstperson.FirstPersonModelCore; 9 | import dev.tr7zw.firstperson.api.FirstPersonAPI; 10 | import dev.tr7zw.firstperson.api.PlayerOffsetHandler; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.player.AbstractClientPlayer; 13 | import net.minecraft.client.renderer.entity.player.PlayerRenderer; 14 | import net.minecraft.world.phys.Vec3; 15 | //#if MC >= 12103 16 | import net.minecraft.client.renderer.entity.state.PlayerRenderState; 17 | //#endif 18 | 19 | /** 20 | * Offset the player behind the camera 21 | * 22 | * @author tr7zw 23 | * 24 | */ 25 | @Mixin(value = PlayerRenderer.class, priority = 500) 26 | public class PlayerRendererMixin { 27 | 28 | private static Minecraft fpmMcInstance = Minecraft.getInstance(); 29 | 30 | @Inject(method = "getRenderOffset", at = @At("RETURN"), cancellable = true) 31 | //#if MC >= 12103 32 | public void getRenderOffset(PlayerRenderState playerRenderState, CallbackInfoReturnable ci) { 33 | AbstractClientPlayer entity = Minecraft.getInstance().player; 34 | float delta = Minecraft.getInstance().getDeltaTracker().getGameTimeDeltaPartialTick(false); 35 | //#else 36 | //$$public void getRenderOffset(AbstractClientPlayer entity, float delta, CallbackInfoReturnable ci) { 37 | //#endif 38 | if (entity == fpmMcInstance.cameraEntity && FirstPersonModelCore.instance.isRenderingPlayer()) { 39 | FirstPersonModelCore.instance.getLogicHandler().updatePositionOffset(entity, delta); 40 | 41 | Vec3 offset = ci.getReturnValue().add(FirstPersonModelCore.instance.getLogicHandler().getOffset()); 42 | for (PlayerOffsetHandler handler : FirstPersonAPI.getPlayerOffsetHandlers()) { 43 | offset = handler.applyOffset(entity, delta, ci.getReturnValue(), offset); 44 | } 45 | 46 | ci.setReturnValue(offset); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/RenderDispatcherMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | import com.mojang.blaze3d.vertex.VertexConsumer; 10 | 11 | import dev.tr7zw.firstperson.FirstPersonModelCore; 12 | import lombok.AccessLevel; 13 | import lombok.NoArgsConstructor; 14 | import net.minecraft.client.Minecraft; 15 | import net.minecraft.client.renderer.MultiBufferSource; 16 | import net.minecraft.client.renderer.entity.EntityRenderDispatcher; 17 | import net.minecraft.world.entity.Entity; 18 | import net.minecraft.world.level.LevelReader; 19 | 20 | //#if MC >= 12105 21 | import net.minecraft.client.renderer.entity.state.HitboxRenderState; 22 | //#endif 23 | //#if MC >= 12103 24 | import net.minecraft.client.renderer.entity.state.EntityRenderState; 25 | //#else 26 | //$$import org.spongepowered.asm.mixin.injection.At; 27 | //$$import org.spongepowered.asm.mixin.injection.At.Shift; 28 | //$$import org.spongepowered.asm.mixin.injection.Redirect; 29 | //$$import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 30 | //$$import com.mojang.blaze3d.vertex.VertexConsumer; 31 | //$$import net.minecraft.client.renderer.MultiBufferSource; 32 | //$$import net.minecraft.util.Mth; 33 | //$$import net.minecraft.world.entity.Entity; 34 | //$$import net.minecraft.world.level.LevelReader; 35 | //$$import net.minecraft.world.phys.Vec3; 36 | //#endif 37 | 38 | /** 39 | * Move the first person shadow to be at the correct location 40 | * 41 | */ 42 | @Mixin(EntityRenderDispatcher.class) 43 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 44 | public abstract class RenderDispatcherMixin { 45 | 46 | private static Minecraft fpmMcInstance = Minecraft.getInstance(); 47 | 48 | //#if MC >= 12103 49 | private static double tmpX; 50 | private static double tmpZ; 51 | 52 | @Inject(method = "renderShadow", at = @At("HEAD")) 53 | private static void renderShadow(PoseStack poseStack, MultiBufferSource multiBufferSource, 54 | EntityRenderState entityRenderState, float f, 55 | //#if MC < 12105 56 | //$$float g, 57 | //#endif 58 | LevelReader levelReader, float h, CallbackInfo ci) { 59 | if (FirstPersonModelCore.instance.isRenderingPlayerPost()) { 60 | poseStack.pushPose(); 61 | poseStack.translate(FirstPersonModelCore.instance.getLogicHandler().getOffset()); 62 | tmpX = entityRenderState.x; 63 | tmpZ = entityRenderState.z; 64 | entityRenderState.x += FirstPersonModelCore.instance.getLogicHandler().getOffset().x; 65 | entityRenderState.z += FirstPersonModelCore.instance.getLogicHandler().getOffset().z; 66 | } 67 | } 68 | 69 | @Inject(method = "renderShadow", at = @At("RETURN")) 70 | private static void renderShadowEnd(PoseStack poseStack, MultiBufferSource multiBufferSource, 71 | EntityRenderState entityRenderState, float f, 72 | //#if MC < 12105 73 | //$$float g, 74 | //#endif 75 | LevelReader levelReader, float h, CallbackInfo ci) { 76 | if (FirstPersonModelCore.instance.isRenderingPlayerPost()) { 77 | entityRenderState.x = tmpX; 78 | entityRenderState.z = tmpZ; 79 | poseStack.popPose(); 80 | } 81 | } 82 | 83 | @Inject(method = "renderHitbox", at = @At(value = "HEAD"), cancellable = true) 84 | private static void renderHitbox(PoseStack poseStack, VertexConsumer buffer, 85 | //#if MC >= 12105 86 | HitboxRenderState hitboxRenderState, 87 | //#else 88 | //$$ Entity entity, float red, float green, float blue, float alpha, 89 | //#endif 90 | CallbackInfo ci) { 91 | if (FirstPersonModelCore.instance.isRenderingPlayerPost()) { 92 | ci.cancel(); 93 | } 94 | } 95 | 96 | //#else 97 | //$$ @Redirect(method = "renderShadow", at = @At(value = "invoke", target = "Lnet/minecraft/util/Mth;lerp(DDD)D", ordinal = 0)) 98 | //$$ private static double shadowOffsetX(double delta, double old, double cur, PoseStack poseStack, 99 | //$$ MultiBufferSource multiBufferSource, Entity entity, float f, float g, LevelReader levelReader, float h) { 100 | //$$ if (FirstPersonModelCore.instance.isRenderingPlayerPost()) { 101 | //$$ return Mth.lerp(delta, old, cur) + FirstPersonModelCore.instance.getLogicHandler().getOffset().x; 102 | //$$ } 103 | //$$ return Mth.lerp(delta, old, cur); 104 | //$$ } 105 | //$$ 106 | //$$ @Redirect(method = "renderShadow", at = @At(value = "invoke", target = "Lnet/minecraft/util/Mth;lerp(DDD)D", ordinal = 2)) 107 | //$$ private static double shadowOffsetZ(double delta, double old, double cur, PoseStack poseStack, 108 | //$$ MultiBufferSource multiBufferSource, Entity entity, float f, float g, LevelReader levelReader, float h) { 109 | //$$ if (FirstPersonModelCore.instance.isRenderingPlayerPost()) { 110 | //$$ return Mth.lerp(delta, old, cur) + FirstPersonModelCore.instance.getLogicHandler().getOffset().z; 111 | //$$ } 112 | //$$ return Mth.lerp(delta, old, cur); 113 | //$$ } 114 | //$$ 115 | //$$ @Inject(method = "renderShadow", at = @At(value = "invoke", target = "Lcom/mojang/blaze3d/vertex/PoseStack;last()Lcom/mojang/blaze3d/vertex/PoseStack$Pose;", shift = Shift.BEFORE)) 116 | //$$ private static void shadowMove(PoseStack matrices, MultiBufferSource vertexConsumers, Entity entity, float opacity, 117 | //$$ float tickDelta, LevelReader world, float radius, CallbackInfo ci) { 118 | //$$ if (!FirstPersonModelCore.instance.isRenderingPlayerPost()) { 119 | //$$ return; 120 | //$$ } 121 | //$$ Vec3 offset = FirstPersonModelCore.instance.getLogicHandler().getOffset(); 122 | //$$ matrices.translate(offset.x, offset.y, offset.z); 123 | //$$ } 124 | //$$ 125 | //$$@Inject(method = "renderHitbox", at = @At(value = "HEAD"), cancellable = true) 126 | //#if MC < 11700 127 | //$$ private void renderHitbox(PoseStack poseStack, VertexConsumer vertexConsumer, Entity entity, float f, CallbackInfo ci) { 128 | //#elseif MC < 12100 129 | //$$ private static void renderHitbox(PoseStack poseStack, VertexConsumer vertexConsumer, Entity entity, float f, 130 | //$$ CallbackInfo ci) { 131 | //#else 132 | //$$private static void renderHitbox(PoseStack poseStack, VertexConsumer vertexConsumer, Entity entity, float f, 133 | //$$ float g, float h, float i, 134 | //$$ CallbackInfo ci) { 135 | //#endif 136 | //$$ if (FirstPersonModelCore.instance.isRenderingPlayerPost()) { 137 | //$$ ci.cancel(); 138 | //$$ } 139 | //$$} 140 | //#endif 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/StuckInBodyLayerMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | 10 | import dev.tr7zw.firstperson.FirstPersonModelCore; 11 | import net.minecraft.client.renderer.MultiBufferSource; 12 | import net.minecraft.client.renderer.entity.layers.StuckInBodyLayer; 13 | import net.minecraft.world.entity.LivingEntity; 14 | //#if MC >= 12103 15 | import net.minecraft.client.renderer.entity.state.PlayerRenderState; 16 | //#endif 17 | 18 | /** 19 | * @author KxmischesDomi | https://github.com/kxmischesdomi 20 | */ 21 | @Mixin(StuckInBodyLayer.class) 22 | public class StuckInBodyLayerMixin { 23 | 24 | //#if MC >= 12103 25 | @Inject(method = "render", at = @At("HEAD"), cancellable = true) 26 | public void disableStuckFeatureLayer(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, 27 | PlayerRenderState playerRenderState, float f, float g, CallbackInfo ci) { 28 | //#else 29 | //$$@Inject(method = "render(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/entity/LivingEntity;FFFFFF)V", at = @At("HEAD"), cancellable = true) 30 | //$$public void disableStuckFeatureLayer(PoseStack poseStack, MultiBufferSource multiBufferSource, int i, 31 | //$$ T livingEntity, float f, float g, float h, float j, float k, float l, CallbackInfo ci) { 32 | //#endif 33 | if (FirstPersonModelCore.instance.isRenderingPlayer() 34 | && !FirstPersonModelCore.instance.getConfig().renderStuckFeatures) { 35 | ci.cancel(); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/mixins/WorldRendererMixin.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.mixins; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Shadow; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.At.Shift; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | import com.mojang.blaze3d.vertex.PoseStack; 11 | 12 | import dev.tr7zw.firstperson.FirstPersonModelCore; 13 | import net.minecraft.client.Camera; 14 | import net.minecraft.client.renderer.GameRenderer; 15 | import net.minecraft.client.renderer.LevelRenderer; 16 | import net.minecraft.client.renderer.LightTexture; 17 | import net.minecraft.client.renderer.MultiBufferSource; 18 | import net.minecraft.client.renderer.MultiBufferSource.BufferSource; 19 | import net.minecraft.client.renderer.RenderBuffers; 20 | import net.minecraft.world.entity.Entity; 21 | import net.minecraft.world.phys.Vec3; 22 | //#if MC >= 12103 23 | import com.mojang.blaze3d.resource.GraphicsResourceAllocator; 24 | //#endif 25 | //#if MC >= 12100 26 | import net.minecraft.client.DeltaTracker; 27 | 28 | import java.util.List; 29 | 30 | //#endif 31 | //#if MC >= 11903 32 | import org.joml.Matrix4f; 33 | //#else 34 | //$$ import com.mojang.math.Matrix4f; 35 | //#endif 36 | 37 | /** 38 | * Detects when the player is rendered and triggers the correct changes 39 | * 40 | */ 41 | @Mixin(LevelRenderer.class) 42 | public class WorldRendererMixin { 43 | 44 | @Shadow 45 | private RenderBuffers renderBuffers; 46 | 47 | //#if MC <= 12004 48 | //$$ @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;checkPoseStack(Lcom/mojang/blaze3d/vertex/PoseStack;)V", ordinal = 0)) 49 | //$$ public void render(PoseStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, 50 | //$$ GameRenderer gameRenderer, LightTexture lightmapTextureManager, Matrix4f matrix4f, CallbackInfo info) { 51 | //#elseif MC < 12100 52 | //$$ @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;checkPoseStack(Lcom/mojang/blaze3d/vertex/PoseStack;)V", ordinal = 0)) 53 | //$$ public void render(float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, 54 | //$$ GameRenderer gameRenderer, LightTexture lightmapTextureManager, Matrix4f matrix4f, Matrix4f matrix4f2, CallbackInfo info) { 55 | //$$ PoseStack matrices = new PoseStack(); 56 | //#elseif MC < 12103 57 | //$$ @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;checkPoseStack(Lcom/mojang/blaze3d/vertex/PoseStack;)V", ordinal = 0)) 58 | //$$ public void render(DeltaTracker deltaTracker, boolean bl, Camera camera, GameRenderer gameRenderer, 59 | //$$ LightTexture lightTexture, Matrix4f matrix4f, Matrix4f matrix4f2, CallbackInfo info) { 60 | //$$ PoseStack matrices = new PoseStack(); 61 | //#else 62 | @Inject(method = "renderEntities", at = @At(value = "HEAD")) 63 | private void renderEntities(PoseStack poseStack, BufferSource bufferSource, Camera camera, 64 | DeltaTracker deltaTracker, List list, CallbackInfo ci) { 65 | PoseStack matrices = new PoseStack(); 66 | //#endif 67 | if (camera.isDetached() || !FirstPersonModelCore.instance.getLogicHandler().shouldApplyThirdPerson(false)) { 68 | return; 69 | } 70 | Vec3 vec3d = camera.getPosition(); 71 | MultiBufferSource.BufferSource immediate = renderBuffers.bufferSource(); 72 | FirstPersonModelCore.instance.setRenderingPlayer(true); 73 | FirstPersonModelCore.instance.setRenderingPlayerPost(true); 74 | //#if MC < 12100 75 | //$$ renderEntity(camera.getEntity(), vec3d.x(), vec3d.y(), vec3d.z(), tickDelta, matrices, immediate); 76 | //#else 77 | renderEntity(camera.getEntity(), vec3d.x(), vec3d.y(), vec3d.z(), 78 | deltaTracker.getGameTimeDeltaPartialTick(false), matrices, immediate); 79 | //#endif 80 | FirstPersonModelCore.instance.setRenderingPlayer(false); 81 | FirstPersonModelCore.instance.setRenderingPlayerPost(false); 82 | } 83 | 84 | @Shadow 85 | private void renderEntity(Entity entity, double cameraX, double cameraY, double cameraZ, float tickDelta, 86 | PoseStack matrices, MultiBufferSource vertexConsumers) { 87 | // shadow 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/modsupport/FreecamSupport.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.modsupport; 2 | 3 | import dev.tr7zw.firstperson.api.ActivationHandler; 4 | import net.xolt.freecam.Freecam; 5 | 6 | public class FreecamSupport implements ActivationHandler { 7 | 8 | public FreecamSupport() { 9 | // making sure the constructor throws an exception, in case something is wrong. 10 | Freecam.isEnabled(); 11 | } 12 | 13 | @Override 14 | public boolean preventFirstperson() { 15 | return Freecam.isEnabled(); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/modsupport/ModSupportLoader.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.modsupport; 2 | 3 | import dev.tr7zw.firstperson.api.FirstPersonAPI; 4 | import dev.tr7zw.firstperson.versionless.FirstPersonBase; 5 | import dev.tr7zw.util.ModLoaderUtil; 6 | import lombok.experimental.UtilityClass; 7 | 8 | @UtilityClass 9 | public class ModSupportLoader { 10 | 11 | public static void loadSupport() { 12 | try { 13 | if (ModLoaderUtil.isModLoaded("freecam")) { 14 | FirstPersonAPI.registerPlayerHandler(new FreecamSupport()); 15 | FirstPersonBase.LOGGER.info("Freecam support loaded."); 16 | } 17 | } catch (Throwable ex) { 18 | FirstPersonBase.LOGGER.warn("Error during initialization of mod support.", ex); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/tr7zw/firstperson/modsupport/PlayerAnimatorSupport.java: -------------------------------------------------------------------------------- 1 | package dev.tr7zw.firstperson.modsupport; 2 | 3 | import dev.kosmx.playerAnim.api.TransformType; 4 | import dev.kosmx.playerAnim.core.impl.AnimationProcessor; 5 | import dev.kosmx.playerAnim.core.util.Vec3f; 6 | import dev.kosmx.playerAnim.impl.IAnimatedPlayer; 7 | import dev.tr7zw.firstperson.api.PlayerOffsetHandler; 8 | import dev.tr7zw.util.NMSHelper; 9 | import net.minecraft.client.Minecraft; 10 | import net.minecraft.client.player.AbstractClientPlayer; 11 | import net.minecraft.util.Mth; 12 | import net.minecraft.world.phys.Vec3; 13 | //#if MC >= 11903 14 | import org.joml.Matrix4f; 15 | import org.joml.Vector4f; 16 | //#else 17 | //$$ import com.mojang.math.Matrix4f; 18 | //$$ import com.mojang.math.Vector4f; 19 | //#endif 20 | 21 | public class PlayerAnimatorSupport implements PlayerOffsetHandler { 22 | 23 | private Minecraft minecraft = Minecraft.getInstance(); 24 | 25 | @Override 26 | public Vec3 applyOffset(AbstractClientPlayer entity, float delta, Vec3 original, Vec3 current) { 27 | if (entity instanceof IAnimatedPlayer player && player.playerAnimator_getAnimation().isActive()) { 28 | AnimationProcessor anim = player.playerAnimator_getAnimation(); 29 | anim.setTickDelta(delta); // Probably the tick is done, set tick delta. 30 | 31 | // get required data 32 | Vec3f rot = anim.get3DTransform("body", TransformType.ROTATION, Vec3f.ZERO); 33 | Vec3f pos = anim.get3DTransform("body", TransformType.POSITION, Vec3f.ZERO); // .scale(1 / 16f); It has been 34 | // pre-scaled 35 | Vec3f headPos = anim.get3DTransform("head", TransformType.POSITION, Vec3f.ZERO).scale(1 / 16f); 36 | 37 | //#if MC >= 11903 38 | // Matrix4f#translate is an offset applied from LEFT (or absolute) 39 | Matrix4f relativeTranslation = new Matrix4f(); 40 | relativeTranslation.identity(); 41 | relativeTranslation.scale(-1f); // 0.935 scaling is not even needed :D 42 | final float bodyOffset = 0.8f; // Distance from the base origin to the head pivot 43 | relativeTranslation.translate(0, -bodyOffset, 0); // Shift matrix down 44 | 45 | relativeTranslation.translate(pos.getX(), pos.getY(), pos.getZ()); // Apply torso 46 | // transformation 47 | 48 | relativeTranslation.rotate(NMSHelper.ZP.rotation(rot.getZ())); 49 | relativeTranslation.rotate(NMSHelper.YP.rotation(rot.getY())); 50 | relativeTranslation.rotate(NMSHelper.XP.rotation(rot.getX())); 51 | // relativeTranslation.multiply(Quaternion.fromXYZ(rot.getX(), rot.getY(), 52 | // rot.getZ())); 53 | 54 | relativeTranslation.translate(-headPos.getX(), -headPos.getY(), headPos.getZ()); 55 | 56 | relativeTranslation.translate(0, bodyOffset, 0); // Roll back the first [0,1,0] translation. 57 | 58 | // calculate the actual rotations and 59 | float realYaw = Mth.rotLerp(delta, entity.yBodyRotO, entity.yBodyRot); 60 | 61 | Matrix4f matrix = new Matrix4f(); // To multiply from LEFT, I have to create a new instance?! 62 | matrix.mul(new Matrix4f().scale(-1, 1, 1)); // What is going on with this?! 63 | 64 | matrix.mul(new Matrix4f().rotate(NMSHelper.YP.rotationDegrees(realYaw))); 65 | 66 | matrix.mul(new Matrix4f().scale(1, 1, -1)); 67 | matrix.mul(relativeTranslation); 68 | 69 | Vector4f offset = new Vector4f(0, 0, 0, 1); 70 | offset.mul(matrix); 71 | //#elseif MC > 11700 72 | //$$ Matrix4f relativeTranslation = new Matrix4f(); 73 | //$$ relativeTranslation.setIdentity(); 74 | //$$ relativeTranslation.multiply(-1f); // 0.935 scaling is not even needed :D 75 | //$$ final float bodyOffset = 0.8f; // Distance from the base origin to the head pivot 76 | //$$ relativeTranslation.multiplyWithTranslation(0, -bodyOffset, 0); // Shift matrix down 77 | //$$ 78 | //$$ relativeTranslation.multiplyWithTranslation(pos.getX(), pos.getY(), pos.getZ()); // Apply torso 79 | //$$ // transformation 80 | //$$ 81 | //$$ relativeTranslation.multiply(NMSHelper.ZP.rotation(rot.getZ())); 82 | //$$ relativeTranslation.multiply(NMSHelper.YP.rotation(rot.getY())); 83 | //$$ relativeTranslation.multiply(NMSHelper.XP.rotation(rot.getX())); 84 | //$$ // relativeTranslation.multiply(Quaternion.fromXYZ(rot.getX(), rot.getY(), 85 | //$$ // rot.getZ())); 86 | //$$ 87 | //$$ relativeTranslation.multiplyWithTranslation(-headPos.getX(), -headPos.getY(), headPos.getZ()); 88 | //$$ 89 | //$$ relativeTranslation.multiplyWithTranslation(0, bodyOffset, 0); // Roll back the first [0,1,0] translation. 90 | //$$ 91 | //$$ // calculate the actual rotations and 92 | //$$ float realYaw = Mth.rotLerp(minecraft.getFrameTime(), entity.yBodyRotO, entity.yBodyRot); 93 | //$$ 94 | //$$ Matrix4f matrix = new Matrix4f(); // To multiply from LEFT, I have to create a new instance?! 95 | //$$ matrix.setIdentity(); 96 | //$$ matrix.multiply(Matrix4f.createScaleMatrix(-1, 1, 1)); // What is going on with this?! 97 | //$$ 98 | //$$ matrix.multiply(new Matrix4f(NMSHelper.YP.rotationDegrees(realYaw))); 99 | //$$ 100 | //$$ matrix.multiply(Matrix4f.createScaleMatrix(1, 1, -1)); 101 | //$$ matrix.multiply(relativeTranslation); 102 | //$$ 103 | //$$ Vector4f offset = new Vector4f(0, 0, 0, 1); 104 | //$$ offset.transform(matrix); 105 | //#else 106 | //$$ Matrix4f relativeTranslation = new Matrix4f(); 107 | //$$ relativeTranslation.setIdentity(); 108 | //$$ relativeTranslation.multiply(-1f); // 0.935 scaling is not even needed :D 109 | //$$ final float bodyOffset = 0.8f; // Distance from the base origin to the head pivot 110 | //$$ relativeTranslation.multiply(Matrix4f.createTranslateMatrix(0, -bodyOffset, 0)); // Shift matrix down 111 | //$$ 112 | //$$ relativeTranslation.multiply(Matrix4f.createTranslateMatrix(pos.getX(), pos.getY(), pos.getZ())); // Apply torso 113 | //$$ // transformation 114 | //$$ 115 | //$$ relativeTranslation.multiply(NMSHelper.ZP.rotation(rot.getZ())); 116 | //$$ relativeTranslation.multiply(NMSHelper.YP.rotation(rot.getY())); 117 | //$$ relativeTranslation.multiply(NMSHelper.XP.rotation(rot.getX())); 118 | //$$ // relativeTranslation.multiply(Quaternion.fromXYZ(rot.getX(), rot.getY(), 119 | //$$ // rot.getZ())); 120 | //$$ 121 | //$$ relativeTranslation.multiply(Matrix4f.createTranslateMatrix(-headPos.getX(), -headPos.getY(), headPos.getZ())); 122 | //$$ 123 | //$$ relativeTranslation.multiply(Matrix4f.createTranslateMatrix(0, bodyOffset, 0)); // Roll back the first [0,1,0] translation. 124 | //$$ 125 | //$$ // calculate the actual rotations and 126 | //$$ float realYaw = Mth.rotLerp(minecraft.getFrameTime(), entity.yBodyRotO, entity.yBodyRot); 127 | //$$ 128 | //$$ Matrix4f matrix = new Matrix4f(); // To multiply from LEFT, I have to create a new instance?! 129 | //$$ matrix.setIdentity(); 130 | //$$ matrix.multiply(Matrix4f.createScaleMatrix(-1, 1, 1)); // What is going on with this?! 131 | //$$ 132 | //$$ matrix.multiply(new Matrix4f(NMSHelper.YP.rotationDegrees(realYaw))); 133 | //$$ 134 | //$$ matrix.multiply(Matrix4f.createScaleMatrix(1, 1, -1)); 135 | //$$ matrix.multiply(relativeTranslation); 136 | //$$ 137 | //$$ Vector4f offset = new Vector4f(0, 0, 0, 1); 138 | //$$ offset.transform(matrix); 139 | //#endif 140 | 141 | // You may use the Y offset too. new Vector, since it already cancels out all 142 | // deltas 143 | return current.add(offset.x(), /* offset.y() */ 0, offset.z()); 144 | } 145 | return current; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/main/resources/assets/firstperson/lang/be_by.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.firstperson.title": "Налады FirstPerson", 3 | "key.firstperson.toggle": "Уключыць Firstperson", 4 | "category.firstperson.firstperson": "FirstPerson", 5 | "text.firstperson.option.firstperson.xOffset": "Змяшчэнне стойкі", 6 | "text.firstperson.option.firstperson.xOffset.tooltip": "Перамяшчае цела адносна камеры назад ці ўперад", 7 | "text.firstperson.option.firstperson.sneakXOffset": "Змяшчэнне употайкі", 8 | "text.firstperson.option.firstperson.sneakXOffset.tooltip": "Перамяшчае цела адносна камеры назад ці ўперад, калі вы крадзёцеся", 9 | "text.firstperson.option.firstperson.enabledByDefault": "Па змаўчанні", 10 | "text.firstperson.option.firstperson.enabledByDefault.tooltip": "Стан па-змаўчанні мода FirstPerson пасля запуску гульні", 11 | "text.firstperson.option.firstperson.sitXOffset": "Змяшчэнне седзячы", 12 | "text.firstperson.option.firstperson.sitXOffset.tooltip": "Перамяшчае цела адносна камеры назад ці ўперад while sitting", 13 | "text.firstperson.option.firstperson.renderStuckFeatures": "Рэндэр стрэл і нітак", 14 | "text.firstperson.option.firstperson.renderStuckFeatures.tooltip": "Паказвае стрэлы, ніткі ці іншыя мадыфіцыраваныя функцыі ад першай асобы", 15 | "text.firstperson.option.firstperson.vanillaHands": "Ванільныя рукі", 16 | "text.firstperson.option.firstperson.vanillaHands.tooltip": "Паказвае ванільныя рукі ад першай асобы, але і мадэль гульца (але без рук)", 17 | "text.firstperson.option.firstperson.doubleHands": "Двайныя рукі", 18 | "text.firstperson.option.firstperson.doubleHands.tooltip": "Калі вы выкарыстоўваеце Ванільныя рукі, заўсёды паказваюцца дзве рукі", 19 | "text.firstperson.option.firstperson.forceActive": "Прымушальная сумяшчальнасць", 20 | "text.firstperson.option.firstperson.forceActive.tooltip": "Прымушае FirstPerson працаваць з іншымі модамі, якія змяняюць рэндэр, але гэта выклікае візуальныя памылкі(напрыклад пачнуць прападаць голавы гульцоў)" 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/assets/firstperson/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.firstperson.title": "FirstPerson Settings", 3 | "firstperson.keybind": "Firstperson", 4 | "key.firstperson.toggle": "Toggle Firstperson", 5 | "category.firstperson.firstperson": "FirstPerson", 6 | "text.firstperson.option.firstperson.xOffset": "Standing x Offset", 7 | "text.firstperson.option.firstperson.xOffset.tooltip": "Moves the body relative to the camera back and forth", 8 | "text.firstperson.option.firstperson.sneakXOffset": "Sneaking x Offset", 9 | "text.firstperson.option.firstperson.sneakXOffset.tooltip": "Moves the body relative to the camera back and forth while sneaking", 10 | "text.firstperson.option.firstperson.enabledByDefault": "Enabled by default", 11 | "text.firstperson.option.firstperson.enabledByDefault.tooltip": "The default state of the FirstPerson mod after starting the game", 12 | "text.firstperson.option.firstperson.sitXOffset": "Sit X Offset", 13 | "text.firstperson.option.firstperson.sitXOffset.tooltip": "Moves the body relative to the camera back and forth while sitting", 14 | "text.firstperson.option.firstperson.renderStuckFeatures": "Render Arrows & Stings", 15 | "text.firstperson.option.firstperson.renderStuckFeatures.tooltip": "Shows arrows, bee stings or other modded features in first person", 16 | "text.firstperson.option.firstperson.vanillaHandMode": "Vanilla Hands Mode", 17 | "text.firstperson.option.firstperson.vanillaHandMode.OFF": "Off", 18 | "text.firstperson.option.firstperson.vanillaHandMode.ALL": "Always", 19 | "text.firstperson.option.firstperson.vanillaHandMode.ALL_DOUBLE": "Always Both Hands", 20 | "text.firstperson.option.firstperson.vanillaHandMode.ITEMS": "Item In Hand", 21 | "text.firstperson.option.firstperson.vanillaHandMode.tooltip": "Shows the vanilla first person hands", 22 | "text.firstperson.option.firstperson.dynamicMode": "Dynamic Mode", 23 | "text.firstperson.option.firstperson.dynamicMode.tooltip": "Changes the Vanilla Hands view based on the current view", 24 | "modmenu.summaryTranslation.firstperson": "Enables the third-person Model in first-person", 25 | "modmenu.descriptionTranslation.firstperson": "Enables the third-person Model in first-person", 26 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming": "Disable Vanilla Hands while Swimming", 27 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming.tooltip": "Disables the Vanilla Hands while swimming(also ignoring the dynamic mode)" 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/assets/firstperson/lang/ko_kr.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.firstperson.title": "1인칭 설정", 3 | "firstperson.keybind": "1인칭", 4 | "key.firstperson.toggle": "1인칭 토글", 5 | "category.firstperson.firstperson": "1인칭", 6 | "text.firstperson.option.firstperson.xOffset": "서있을 때 X 오브셋", 7 | "text.firstperson.option.firstperson.xOffset.tooltip": "카메라에 맞춰 몸을 앞뒤로 이동시킵니다.", 8 | "text.firstperson.option.firstperson.sneakXOffset": "웅크릴 떄 X 오브셋", 9 | "text.firstperson.option.firstperson.sneakXOffset.tooltip": "웅크리고 있을 떄 카메라에 맞춰 몸을 앞뒤로 이동시킵니다.", 10 | "text.firstperson.option.firstperson.enabledByDefault": "기본으로 켜짐", 11 | "text.firstperson.option.firstperson.enabledByDefault.tooltip": "게임이 시작될 떄 1인칭 모드가 자동으로 켜질지 결정합니다.", 12 | "text.firstperson.option.firstperson.sitXOffset": "앉을 떄 X 오브셋", 13 | "text.firstperson.option.firstperson.sitXOffset.tooltip": "앉아 있을 떄 카메라에 맞춰 몸을 앞뒤로 이동시킵니다.", 14 | "text.firstperson.option.firstperson.renderStuckFeatures": "화살, 끈 표시", 15 | "text.firstperson.option.firstperson.renderStuckFeatures.tooltip": "화살, 벌침, 그리고 다른 모딩된 기능을 1인칭 시점에서 보여줍니다.", 16 | "text.firstperson.option.firstperson.vanillaHandMode": "바닐라 손 모드", 17 | "text.firstperson.option.firstperson.vanillaHandMode.OFF": "숨기기", 18 | "text.firstperson.option.firstperson.vanillaHandMode.ALL": "항상", 19 | "text.firstperson.option.firstperson.vanillaHandMode.ALL_DOUBLE": "항상 양쪽 손", 20 | "text.firstperson.option.firstperson.vanillaHandMode.ITEMS": "아이템을 들고 있을 떄만", 21 | "text.firstperson.option.firstperson.vanillaHandMode.tooltip": "바닐라 1인칭 손을 보여줍니다.", 22 | "text.firstperson.option.firstperson.dynamicMode": "자동 모드", 23 | "text.firstperson.option.firstperson.dynamicMode.tooltip": "상황에 따라 바닐라 손을 보이거나 숨깁니다", 24 | "modmenu.summaryTranslation.firstperson": "3인칭 모델을 1인칭 시점에서 보여줍니다.", 25 | "modmenu.descriptionTranslation.firstperson": "3인칭 모델을 1인칭 시점에서 보여줍니다.", 26 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming": "수영중 바닐라 손 숨기기", 27 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming.tooltip": "수영중 바닐라 손을 숨깁니다(자동 모드 무시)" 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/assets/firstperson/lang/pt_br.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.firstperson.title": "Configurações - FirstPerson", 3 | "key.firstperson.toggle": "Alternar Modelo Em Primeira Pessoa", 4 | "category.firstperson.firstperson": "FirstPerson", 5 | "text.firstperson.option.firstperson.xOffset": "Posição X Em Pé", 6 | "text.firstperson.option.firstperson.xOffset.tooltip": "Movimenta o corpo em relação a câmera para frente e para trás quando em pé", 7 | "text.firstperson.option.firstperson.sneakXOffset": "Posição X Agachado", 8 | "text.firstperson.option.firstperson.sneakXOffset.tooltip": "Movimenta o corpo em relação a câmera para frente e para trás quando agachado", 9 | "text.firstperson.option.firstperson.enabledByDefault": "Ativado por Padrão", 10 | "text.firstperson.option.firstperson.enabledByDefault.tooltip": "O modo padrão do FirstPerson mod após o jogo iniciar", 11 | "text.firstperson.option.firstperson.sitXOffset": "Posição X Sentado", 12 | "text.firstperson.option.firstperson.sitXOffset.tooltip": "Movimenta o corpo em relação a câmera para frente e para trás quando sentado", 13 | "text.firstperson.option.firstperson.renderStuckFeatures": "Renderizar Flechas e Ferrões de Abelhas", 14 | "text.firstperson.option.firstperson.renderStuckFeatures.tooltip": "Mostra flechas, ferrões de abelhas e adiçÕes de outros mods em Primeira Pessoa", 15 | "text.firstperson.option.firstperson.vanillaHandMode": "Mostrar Mãos do Vanilla", 16 | "text.firstperson.option.firstperson.vanillaHandMode.OFF": "Desligado", 17 | "text.firstperson.option.firstperson.vanillaHandMode.ALL": "Sempre", 18 | "text.firstperson.option.firstperson.vanillaHandMode.ALL_DOUBLE": "Sempre Duas Mãos", 19 | "text.firstperson.option.firstperson.vanillaHandMode.ITEMS": "Com Item na Mão", 20 | "text.firstperson.option.firstperson.vanillaHandMode.tooltip": "Mostra em primeira pessoa as mãos do Vanilla", 21 | "text.firstperson.option.firstperson.dynamicMode": "Mãos Dinâmicas", 22 | "text.firstperson.option.firstperson.dynamicMode.tooltip": "Alterna as mãos do vanilla com base na altura que você está olhando", 23 | "modmenu.summaryTranslation.firstperson": "Ativa o modelo de jogador de terceira pessoa em primeira pessoa.", 24 | "modmenu.descriptionTranslation.firstperson": "Ativa o modelo de jogador de terceira pessoa em primeira pessoa.", 25 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming": "Ocultar Mãos do Vanilla ao Nadar", 26 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming.tooltip": "Oculta as mãos do vanilla enquanto está nadando (ignorando as Mãos Dinâmicas)" 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/assets/firstperson/lang/pt_pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.firstperson.title": "Configurações - FirstPerson", 3 | "key.firstperson.toggle": "Alternar Modelo em Primeira Pessoa", 4 | "category.firstperson.firstperson": "FirstPerson", 5 | "text.firstperson.option.firstperson.xOffset": "Posição X de Pé", 6 | "text.firstperson.option.firstperson.xOffset.tooltip": "Move o corpo em relação à câmara para a frente e para trás quando de pé", 7 | "text.firstperson.option.firstperson.sneakXOffset": "Posição X Agachado", 8 | "text.firstperson.option.firstperson.sneakXOffset.tooltip": "Move o corpo em relação à câmara para a frente e para trás quando agachado", 9 | "text.firstperson.option.firstperson.enabledByDefault": "Ativado por Defeito", 10 | "text.firstperson.option.firstperson.enabledByDefault.tooltip": "O modo por defeito do mod FirstPerson após o jogo iniciar", 11 | "text.firstperson.option.firstperson.sitXOffset": "Posição X Sentado", 12 | "text.firstperson.option.firstperson.sitXOffset.tooltip": "Move o corpo em relação à câmara para a frente e para trás quando sentado", 13 | "text.firstperson.option.firstperson.renderStuckFeatures": "Renderizar Setas e Ferrões de Abelhas", 14 | "text.firstperson.option.firstperson.renderStuckFeatures.tooltip": "Mostra setas, ferrões de abelhas e adições de outros mods em Primeira Pessoa", 15 | "text.firstperson.option.firstperson.vanillaHandMode": "Mostrar Mãos do Vanilla", 16 | "text.firstperson.option.firstperson.vanillaHandMode.OFF": "Desligado", 17 | "text.firstperson.option.firstperson.vanillaHandMode.ALL": "Sempre", 18 | "text.firstperson.option.firstperson.vanillaHandMode.ALL_DOUBLE": "Sempre Duas Mãos", 19 | "text.firstperson.option.firstperson.vanillaHandMode.ITEMS": "Com Item na Mão", 20 | "text.firstperson.option.firstperson.vanillaHandMode.tooltip": "Mostra em primeira pessoa as mãos do Vanilla", 21 | "text.firstperson.option.firstperson.dynamicMode": "Mãos Dinâmicas", 22 | "text.firstperson.option.firstperson.dynamicMode.tooltip": "Alterna as mãos do vanilla com base na altura que está a olhar", 23 | "modmenu.summaryTranslation.firstperson": "Ativa o modelo de jogador de terceira pessoa em primeira pessoa.", 24 | "modmenu.descriptionTranslation.firstperson": "Ativa o modelo de jogador de terceira pessoa em primeira pessoa.", 25 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming": "Ocultar Mãos do Vanilla ao Nadar", 26 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming.tooltip": "Oculta as mãos do vanilla enquanto está a nadar (ignorando as Mãos Dinâmicas)" 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/assets/firstperson/lang/ru_ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.firstperson.title": "Настройки FirstPerson", 3 | "key.firstperson.toggle": "Переключение вида от первого лица", 4 | "category.firstperson.firstperson": "FirstPerson", 5 | "text.firstperson.option.firstperson.xOffset": "Стояние: смещение по X", 6 | "text.firstperson.option.firstperson.xOffset.tooltip": "Смещение тела относительно камеры вперед-назад", 7 | "text.firstperson.option.firstperson.sneakXOffset": "Подкрадывание: смещение по X", 8 | "text.firstperson.option.firstperson.sneakXOffset.tooltip": "Смещение тела относительно камеры вперед и назад во время подкрадывания", 9 | "text.firstperson.option.firstperson.enabledByDefault": "Включено по умолчанию", 10 | "text.firstperson.option.firstperson.enabledByDefault.tooltip": "Состояние мода по умолчанию после запуска игры", 11 | "text.firstperson.option.firstperson.sitXOffset": "Сидение: смещение по X", 12 | "text.firstperson.option.firstperson.sitXOffset.tooltip": "Смещение относительно камеры вперед-назад в положении сидя", 13 | "text.firstperson.option.firstperson.renderStuckFeatures": "Отображение стрел и жал", 14 | "text.firstperson.option.firstperson.renderStuckFeatures.tooltip": "Показывает стрелы, пчелиные жала или другие модифицированные функции от первого лица", 15 | "text.firstperson.option.firstperson.vanillaHands": "Ванильные руки", 16 | "text.firstperson.option.firstperson.vanillaHands.tooltip": "Показывает ванильные руки от первого лица, а также модель игрока (но без рук)", 17 | "text.firstperson.option.firstperson.doubleHands": "Двойные руки", 18 | "text.firstperson.option.firstperson.doubleHands.tooltip": "При использовании ванильных рук всегда будут показаны обе руки", 19 | "text.firstperson.option.firstperson.vanillaHandsItem": "Ванильные с предметом", 20 | "text.firstperson.option.firstperson.vanillaHandsItem.tooltip": "Показывает ванильные руки от первого лица, если есть предмет", 21 | "text.firstperson.option.firstperson.dynamicHands": "Динамические руки", 22 | "text.firstperson.option.firstperson.dynamicHands.tooltip": "Динамическая смена рук при изменении угла обзора (нужны Ванильные с предметом)", 23 | "text.firstperson.option.firstperson.forceActive": "Принудительная активация", 24 | "text.firstperson.option.firstperson.forceActive.tooltip": "Принудительное использование мода в тех случаях, когда другие моды изменяют рендеринг, но при этом возникают визуальные проблемы (например, пропадает голова игрока)", 25 | "modmenu.summaryTranslation.firstperson": "Включает модель третьего лица в первом лице", 26 | "modmenu.descriptionTranslation.firstperson": "Включает модель третьего лица в первом лице" 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/assets/firstperson/lang/tr_tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.firstperson.title": "FirstPerson (BirinciŞahıs) Ayarları", 3 | "text.firstperson.option.firstperson.xOffset": "Dik Dururkenki X Göreli Konumu", 4 | "text.firstperson.option.firstperson.xOffset.tooltip": "Kameraya göre vücudu ileri geriye hareket ettirir", 5 | "text.firstperson.option.firstperson.sneakXOffset": "Eğilirkenki X Göreli Konumu", 6 | "text.firstperson.option.firstperson.sneakXOffset.tooltip": "Kameraya göre eğilirken vücudu ileri geriye hareket ettirir", 7 | "key.firstperson.toggle": "Birinci Şahsı Aç/Kapa", 8 | "text.firstperson.option.firstperson.improvedCompatibility": "Gelişmiş Mod Uyumluluğu", 9 | "text.firstperson.option.firstperson.improvedCompatibility.tooltip": "Immersive Portals, Mirrors gibi modlardaki ofset sorununu çözer", 10 | "text.firstperson.option.firstperson.enabledByDefault": "Varsayılan olarak etkin", 11 | "text.firstperson.option.firstperson.enabledByDefault.tooltip": "Oyunu başlattıktan sonraki FirstPerson modunun varsayılan durumu" 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/assets/firstperson/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.firstperson.title": "第一人称模型设置 §7(First Person Model)", 3 | "key.firstperson.toggle": "切换第一人称", 4 | "category.firstperson.firstperson": "第一人称", 5 | "text.firstperson.option.firstperson.xOffset": "站立时的 X 轴补偿", 6 | "text.firstperson.option.firstperson.xOffset.tooltip": "前后调节站立时身体相对于镜头的位置", 7 | "text.firstperson.option.firstperson.sneakXOffset": "潜行时的 X 轴补偿", 8 | "text.firstperson.option.firstperson.sneakXOffset.tooltip": "前后调节潜行时身体相对于镜头的位置", 9 | "text.firstperson.option.firstperson.enabledByDefault": "默认启用", 10 | "text.firstperson.option.firstperson.enabledByDefault.tooltip": "开始游戏后是否默认启用第一人称模型", 11 | "text.firstperson.option.firstperson.lockBodyOnItems": "拿着某些物品时锁定身体旋转", 12 | "text.firstperson.option.firstperson.lockBodyOnItems.tooltip": "拿着地图或指南针时将身体旋转同步为头部旋转,以便更好使用物品", 13 | "text.firstperson.option.firstperson.sitXOffset": "坐下时的 X 轴补偿", 14 | "text.firstperson.option.firstperson.sitXOffset.tooltip": "前后调节坐下时身体相对于镜头的位置", 15 | "text.firstperson.option.firstperson.vanillaHands": "原版的手部显示效果", 16 | "text.firstperson.option.firstperson.vanillaHandMode.OFF": "关闭", 17 | "text.firstperson.option.firstperson.vanillaHandMode.ALL": "总是", 18 | "text.firstperson.option.firstperson.vanillaHandMode.ALL_DOUBLE": "总是显示双手", 19 | "text.firstperson.option.firstperson.vanillaHandMode.ITEMS": "手上的物品", 20 | "text.firstperson.option.firstperson.vanillaHands.tooltip": "显示原版第一人称手的显示效果,但同时也显示玩家模型(没有手臂)", 21 | "text.firstperson.option.firstperson.dynamicMode": "动态模式", 22 | "text.firstperson.option.firstperson.dynamicMode.tooltip": "基于当前的显示改变原版的手部显示", 23 | "text.firstperson.option.firstperson.forceActive": "强制使用", 24 | "text.firstperson.option.firstperson.forceActive.tooltip": "当其他模组修改渲染时强制使用第一人称模型,但会导致视觉问题(例如玩家头部缺失)", 25 | "modmenu.summaryTranslation.firstperson": "在第一人称的视角启用第三人称的模型", 26 | "modmenu.descriptionTranslation.firstperson": "在第一人称的视角启用第三人称的模型", 27 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming": "在游泳时禁用原版手部显示", 28 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming.tooltip": "在游泳时禁用原版手部显示(当然也会忽略动态模式)" 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/assets/firstperson/lang/zh_tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "text.firstperson.title": "第一人稱模型設定 (First-person Model)", 3 | "key.firstperson.toggle": "切換第一人稱", 4 | "category.firstperson.firstperson": "第一人稱", 5 | "text.firstperson.option.firstperson.xOffset": "站立 x 偏移", 6 | "text.firstperson.option.firstperson.xOffset.tooltip": "前後移動身體相對於攝影機的位置", 7 | "text.firstperson.option.firstperson.sneakXOffset": "潛行 x 偏移", 8 | "text.firstperson.option.firstperson.sneakXOffset.tooltip": "潛行時前後移動身體相對於攝影機的位置", 9 | "text.firstperson.option.firstperson.enabledByDefault": "預設啟用", 10 | "text.firstperson.option.firstperson.enabledByDefault.tooltip": "開始遊戲後 FirstPerson 模組的預設狀態", 11 | "text.firstperson.option.firstperson.sitXOffset": "坐下 x 偏移", 12 | "text.firstperson.option.firstperson.sitXOffset.tooltip": "坐下時前後移動身體相對於攝影機的位置", 13 | "text.firstperson.option.firstperson.renderStuckFeatures": "繪製箭矢和毒刺", 14 | "text.firstperson.option.firstperson.renderStuckFeatures.tooltip": "以第一人稱顯示箭矢、蜜蜂毒刺或其他模組功能", 15 | "text.firstperson.option.firstperson.vanillaHandMode": "原版手臂模式", 16 | "text.firstperson.option.firstperson.vanillaHandMode.OFF": "關閉", 17 | "text.firstperson.option.firstperson.vanillaHandMode.ALL": "總是顯示", 18 | "text.firstperson.option.firstperson.vanillaHandMode.ALL_DOUBLE": "總是顯示雙手", 19 | "text.firstperson.option.firstperson.vanillaHandMode.ITEMS": "手持物品時顯示", 20 | "text.firstperson.option.firstperson.vanillaHandMode.tooltip": "顯示原版第一人稱手臂", 21 | "text.firstperson.option.firstperson.dynamicMode": "動態模式", 22 | "text.firstperson.option.firstperson.dynamicMode.tooltip": "根據當前視角變更原版手臂的顯示方式", 23 | "modmenu.summaryTranslation.firstperson": "在第一人稱中啟用第三人稱模型", 24 | "modmenu.descriptionTranslation.firstperson": "在第一人稱中啟用第三人稱模型", 25 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming": "游泳時停用原版手臂", 26 | "text.firstperson.option.firstperson.vanillaHandsSkipSwimming.tooltip": "游泳時停用原版手臂(同時忽略動態模式)" 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/firstperson.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "dev.tr7zw.firstperson.mixins", 4 | "compatibilityLevel": "JAVA_8", 5 | "mixins": [], 6 | "client": [ 7 | "HeldItemRendererMixin", 8 | "WorldRendererMixin", 9 | "FishingBobberRendererMixin", 10 | "RenderDispatcherMixin", 11 | "HeldItemFeatureRendererMixin", 12 | "ModelPartMixin", 13 | "LivingEntityRendererMixin", 14 | "FeatureRendererMixin", 15 | "ArmorFeatureRendererMixin", 16 | "StuckInBodyLayerMixin", 17 | "ElytraLayerMixin", 18 | "PlayerRendererMixin", 19 | "CustomHeadLayerMixin", 20 | "PlayerModelMixin", 21 | "PlayerMixin", 22 | "AgeableListModelMixin" 23 | ], 24 | "injectors": { 25 | "defaultRequire": 1 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tr7zw/FirstPersonModel/71ca9868dbb1551b6daa06922bfe96d6ad0e70ef/src/main/resources/icon.png -------------------------------------------------------------------------------- /src/test/java/dev/tr7zw/tests/MixinTests.java: -------------------------------------------------------------------------------- 1 | //#if MC >= 11800 2 | package dev.tr7zw.tests; 3 | 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import org.junit.jupiter.api.BeforeAll; 7 | import org.junit.jupiter.api.Test; 8 | import org.objenesis.Objenesis; 9 | import org.objenesis.ObjenesisStd; 10 | 11 | import dev.tr7zw.firstperson.versionless.mixinbase.ModelPartBase; 12 | import net.minecraft.SharedConstants; 13 | import net.minecraft.client.model.HumanoidModel; 14 | import net.minecraft.client.model.geom.ModelPart; 15 | import net.minecraft.client.renderer.ItemInHandRenderer; 16 | import net.minecraft.client.renderer.LevelRenderer; 17 | import net.minecraft.client.renderer.entity.EntityRenderDispatcher; 18 | import net.minecraft.client.renderer.entity.FishingHookRenderer; 19 | import net.minecraft.client.renderer.entity.layers.ArrowLayer; 20 | import net.minecraft.client.renderer.entity.layers.CapeLayer; 21 | import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; 22 | import net.minecraft.client.renderer.entity.layers.ItemInHandLayer; 23 | import net.minecraft.client.renderer.entity.player.PlayerRenderer; 24 | import net.minecraft.server.Bootstrap; 25 | 26 | public class MixinTests { 27 | 28 | @BeforeAll 29 | public static void setup() { 30 | SharedConstants.tryDetectVersion(); 31 | Bootstrap.bootStrap(); 32 | } 33 | 34 | @Test 35 | void testInjectedInterfaces() { 36 | Objenesis objenesis = new ObjenesisStd(); 37 | assertTrue((Object) objenesis.newInstance(ModelPart.class) instanceof ModelPartBase); 38 | } 39 | 40 | @Test 41 | void testMixins() { 42 | Objenesis objenesis = new ObjenesisStd(); 43 | objenesis.newInstance(HumanoidArmorLayer.class); 44 | // objenesis.newInstance(ElytraLayer.class); 45 | objenesis.newInstance(CapeLayer.class); 46 | objenesis.newInstance(FishingHookRenderer.class); 47 | objenesis.newInstance(ItemInHandLayer.class); 48 | objenesis.newInstance(ItemInHandRenderer.class); 49 | objenesis.newInstance(HumanoidModel.class); 50 | objenesis.newInstance(PlayerRenderer.class); 51 | objenesis.newInstance(EntityRenderDispatcher.class); 52 | objenesis.newInstance(ArrowLayer.class); 53 | objenesis.newInstance(LevelRenderer.class); 54 | } 55 | 56 | } 57 | //#endif -------------------------------------------------------------------------------- /versions/mainProject: -------------------------------------------------------------------------------- 1 | 1.21.5-fabric --------------------------------------------------------------------------------