├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── controller_support.md │ ├── feature_request.md │ └── mod_support.md └── workflows │ ├── build.yml │ └── publish.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── HEADER ├── LICENSE ├── README.md ├── build.gradle ├── common ├── build.gradle └── src │ └── main │ ├── java │ └── eu │ │ └── midnightdust │ │ └── midnightcontrols │ │ ├── ControlsMode.java │ │ ├── MidnightControls.java │ │ ├── MidnightControlsConstants.java │ │ ├── MidnightControlsFeature.java │ │ ├── client │ │ ├── MidnightControlsClient.java │ │ ├── MidnightControlsConfig.java │ │ ├── MidnightControlsModMenu.java │ │ ├── MidnightControlsReloadListener.java │ │ ├── MidnightInput.java │ │ ├── MidnightReacharound.java │ │ ├── compat │ │ │ ├── BedrockifyCompat.java │ │ │ ├── CompatHandler.java │ │ │ ├── EMICompat.java │ │ │ ├── EmotecraftCompat.java │ │ │ ├── HQMCompat.java │ │ │ ├── InventoryTabsCompat.java │ │ │ ├── LibGuiCompat.java │ │ │ ├── MidnightControlsCompat.java │ │ │ ├── MidnightControlsMixinPlugin.java │ │ │ ├── SodiumCompat.java │ │ │ ├── YACLCompat.java │ │ │ └── mixin │ │ │ │ └── sodium │ │ │ │ └── SodiumOptionsGUIAccessor.java │ │ ├── controller │ │ │ ├── ButtonBinding.java │ │ │ ├── ButtonCategory.java │ │ │ ├── Controller.java │ │ │ ├── InputHandlers.java │ │ │ ├── InputManager.java │ │ │ ├── MovementHandler.java │ │ │ └── PressAction.java │ │ ├── enums │ │ │ ├── ButtonState.java │ │ │ ├── CameraMode.java │ │ │ ├── ControllerType.java │ │ │ ├── HudSide.java │ │ │ ├── TouchMode.java │ │ │ └── VirtualMouseSkin.java │ │ ├── gui │ │ │ ├── MappingsStringInputWidget.java │ │ │ ├── MidnightControlsHud.java │ │ │ ├── MidnightControlsRenderer.java │ │ │ ├── MidnightControlsSettingsScreen.java │ │ │ ├── ReloadControllerMappingsOption.java │ │ │ ├── RingScreen.java │ │ │ └── widget │ │ │ │ ├── ControllerButtonWidget.java │ │ │ │ ├── ControllerControlsWidget.java │ │ │ │ └── ControlsListWidget.java │ │ ├── mixin │ │ │ ├── AbstractBlockAccessor.java │ │ │ ├── AbstractSignEditScreenMixin.java │ │ │ ├── AdvancementsScreenAccessor.java │ │ │ ├── BookEditScreenAccessor.java │ │ │ ├── ChatScreenMixin.java │ │ │ ├── ClickableWidgetAccessor.java │ │ │ ├── ClientPlayerEntityMixin.java │ │ │ ├── CreativeInventoryScreenAccessor.java │ │ │ ├── DrawContextAccessor.java │ │ │ ├── GameOptionsScreenMixin.java │ │ │ ├── GameRendererMixin.java │ │ │ ├── HandledScreenMixin.java │ │ │ ├── InputAccessor.java │ │ │ ├── InputUtilMixin.java │ │ │ ├── KeyBindingIDAccessor.java │ │ │ ├── KeyBindingMixin.java │ │ │ ├── KeyboardMixin.java │ │ │ ├── MinecraftClientMixin.java │ │ │ ├── MouseAccessor.java │ │ │ ├── MouseMixin.java │ │ │ ├── RecipeBookScreenAccessor.java │ │ │ ├── RecipeBookWidgetAccessor.java │ │ │ ├── ScreenMixin.java │ │ │ ├── TabNavigationWidgetAccessor.java │ │ │ └── WorldRendererMixin.java │ │ ├── mouse │ │ │ └── EyeTrackerHandler.java │ │ ├── ring │ │ │ ├── ButtonBindingRingAction.java │ │ │ ├── MidnightRing.java │ │ │ ├── RingAction.java │ │ │ ├── RingButtonMode.java │ │ │ └── RingPage.java │ │ ├── touch │ │ │ ├── TouchInput.java │ │ │ ├── TouchUtils.java │ │ │ └── gui │ │ │ │ ├── ItemUseButtonWidget.java │ │ │ │ ├── SilentTexturedButtonWidget.java │ │ │ │ └── TouchscreenOverlay.java │ │ ├── util │ │ │ ├── AbstractSignEditScreenAccessor.java │ │ │ ├── HandledScreenAccessor.java │ │ │ ├── InventoryUtil.java │ │ │ ├── KeyBindingAccessor.java │ │ │ ├── MathUtil.java │ │ │ ├── RainbowColor.java │ │ │ ├── ToggleSneakSprintUtil.java │ │ │ ├── platform │ │ │ │ ├── ItemGroupUtil.java │ │ │ │ └── NetworkUtil.java │ │ │ └── storage │ │ │ │ ├── AxisStorage.java │ │ │ │ └── ButtonStorage.java │ │ └── virtualkeyboard │ │ │ ├── KeyboardLayout.java │ │ │ ├── KeyboardLayoutManager.java │ │ │ ├── MouseClickInterceptor.java │ │ │ ├── clickhandler │ │ │ ├── AbstractScreenClickHandler.java │ │ │ ├── BookEditScreenClickHandler.java │ │ │ ├── DefaultScreenClickHandler.java │ │ │ ├── SignEditScreenClickHandler.java │ │ │ └── TextFieldWrapper.java │ │ │ └── gui │ │ │ └── VirtualKeyboardScreen.java │ │ └── packet │ │ ├── ControlsModePayload.java │ │ ├── FeaturePayload.java │ │ └── HelloPayload.java │ └── resources │ ├── architectury.midnightcontrols.json │ ├── assets │ └── midnightcontrols │ │ ├── icon.png │ │ ├── keyboard_layouts │ │ ├── de_quertz.json │ │ └── en_querty.json │ │ ├── lang │ │ ├── cs_cz.json │ │ ├── de_de.json │ │ ├── en_us.json │ │ ├── es_mx.json │ │ ├── et_ee.json │ │ ├── fr_ca.json │ │ ├── fr_fr.json │ │ ├── it_it.json │ │ ├── ko_kr.json │ │ ├── pt_br.json │ │ ├── ru_ru.json │ │ ├── tr_tr.json │ │ ├── uk_ua.json │ │ ├── vi_vn.json │ │ ├── zh_cn.json │ │ └── zh_tw.json │ │ └── textures │ │ └── gui │ │ ├── controller_axis.png │ │ ├── controller_buttons.png │ │ ├── controller_expanded.png │ │ ├── sprites │ │ ├── binding │ │ │ └── debug_screen.png │ │ ├── cursor │ │ │ ├── dark │ │ │ │ ├── default.png │ │ │ │ ├── default_slot.png │ │ │ │ ├── mouse_pointer.png │ │ │ │ ├── secondary.png │ │ │ │ └── secondary_slot.png │ │ │ └── light │ │ │ │ ├── default.png │ │ │ │ ├── default_slot.png │ │ │ │ ├── mouse_pointer.png │ │ │ │ ├── secondary.png │ │ │ │ └── secondary_slot.png │ │ ├── icon │ │ │ └── controller.png │ │ └── touch │ │ │ ├── chat.png │ │ │ ├── emote.png │ │ │ ├── empty.png │ │ │ └── pause.png │ │ └── widgets.png │ ├── midnightcontrols.accesswidener │ ├── midnightcontrols.mixins.json │ ├── midnightcontrols_compat.mixins.json │ └── resourcepacks │ ├── bedrock │ ├── CREDITS │ ├── assets │ │ └── midnightcontrols │ │ │ └── textures │ │ │ └── gui │ │ │ ├── controller_axis.png │ │ │ └── controller_buttons.png │ ├── pack.mcmeta │ └── pack.png │ └── legacy │ ├── CREDITS │ ├── assets │ └── midnightcontrols │ │ └── textures │ │ └── gui │ │ ├── controller_axis.png │ │ └── controller_buttons.png │ ├── pack.mcmeta │ └── pack.png ├── controller.svg ├── controller_banner.svg ├── fabric ├── build.gradle └── src │ └── main │ ├── java │ └── eu │ │ └── midnightdust │ │ └── midnightcontrols │ │ ├── client │ │ └── util │ │ │ └── platform │ │ │ └── fabric │ │ │ ├── ItemGroupUtilImpl.java │ │ │ └── NetworkUtilImpl.java │ │ └── fabric │ │ ├── MidnightControlsClientFabric.java │ │ ├── MidnightControlsFabric.java │ │ └── event │ │ ├── MouseClickListener.java │ │ └── PlayerChangeControlsModeCallback.java │ └── resources │ └── fabric.mod.json ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── icon.png ├── images ├── controller_controls.png └── controller_options.png ├── neoforge ├── build.gradle ├── gradle.properties └── src │ └── main │ ├── java │ └── eu │ │ └── midnightdust │ │ └── midnightcontrols │ │ ├── client │ │ └── util │ │ │ └── platform │ │ │ └── neoforge │ │ │ ├── ItemGroupUtilImpl.java │ │ │ └── NetworkUtilImpl.java │ │ └── neoforge │ │ ├── MidnightControlsClientNeoforge.java │ │ ├── MidnightControlsNeoforge.java │ │ └── event │ │ └── PlayerChangeControlsModeEvent.java │ └── resources │ ├── META-INF │ └── neoforge.mods.toml │ └── icon.png └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: 'Bug: TITLE HERE' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots or videos to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Windows / Linux / MacOS] 28 | - Minecraft [e.g. 1.20.4] 29 | - Modloader [e.g. Fabric Loader 0.15.6] 30 | - Fabric/Quilt Libraries [e.g. Fabric Api 0.96.1+1.20.4] 31 | - Mods [e.g. Puzzle v1.6.1, LilTaterReloaded v1.1.15] 32 | - Version [e.g. 1.0.0] 33 | - Remove this line if you actually completed it 34 | 35 | **Additional context** 36 | Add any other context about the problem here. 37 | In case of a crash, please provide the crash log. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/controller_support.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Controller support 3 | about: Report a problem related to a specific controller 4 | title: 'Controller Issues: CONTROLLER NAME HERE' 5 | labels: controller 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the current state** 11 | A clear and concise description of current state of support for the controller and the issues. 12 | 13 | **To Reproduce** 14 | If needed, specify steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots or videos to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - Have you checked #263 for your controller? [e.g. Yes/No] 28 | - Connection method [e.g. Wired / Bluetooth] 29 | 30 | - OS: [e.g. Windows / Linux / MacOS] 31 | - Minecraft [e.g. 1.20.4] 32 | - Modloader [e.g. Fabric Loader 0.15.6] 33 | - Fabric/Quilt Libraries [e.g. Fabric Api 0.96.1+1.20.4] 34 | - Mods [e.g. Puzzle v1.6.1, LilTaterReloaded v1.1.15] 35 | - Version [e.g. 1.0.0] 36 | - Remove this line if you actually completed it 37 | 38 | **Additional context** 39 | Add any other context about the problem here. 40 | In case of a crash, please provide the crash log. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: 'Feature: TITLE HERE' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots/videos about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/mod_support.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Mod Support 3 | about: Compatibility improvements with a specific mod (not for crashes) 4 | title: 'Mod Support: MOD NAME HERE' 5 | labels: mod compatibility 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your compatibility request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I've always wanted to [...] 12 | 13 | **Describe the way of compatibility you'd imagine** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative mods or workarounds you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots/videos about the compatibility request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build with Gradle 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-24.04 8 | steps: 9 | - name: Checkout Repository 10 | uses: actions/checkout@v4 11 | with: 12 | persist-credentials: false 13 | - name: Set up Gradle 14 | uses: gradle/actions/setup-gradle@v4 15 | - name: Set up JDK 21 16 | uses: actions/setup-java@v4 17 | with: 18 | distribution: 'temurin' 19 | java-version: 21 20 | check-latest: true 21 | - name: Build with Gradle 22 | run: ./gradlew build 23 | - name: Upload artifacts to GitHub 24 | uses: actions/upload-artifact@v4 25 | with: 26 | name: MidnightControls-Artifacts 27 | path: | 28 | fabric/build/libs/ 29 | neoforge/build/libs/ 30 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Modrinth 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-24.04 11 | steps: 12 | - name: Checkout Repository 13 | uses: actions/checkout@v4 14 | with: 15 | persist-credentials: false 16 | - name: Set up Gradle 17 | uses: gradle/actions/setup-gradle@v4 18 | - name: Set up JDK 21 19 | uses: actions/setup-java@v4 20 | with: 21 | distribution: 'temurin' 22 | java-version: 21 23 | check-latest: true 24 | # The USERNAME and PASSWORD need to correspond to the credentials environment variables used in 25 | # the publishing section of your build.gradle 26 | - name: Publish to GitHub Packages and other Mavens 27 | run: ./gradlew publish 28 | env: 29 | BRANCH_NAME: ${{ github.ref }} 30 | RUN_COUNT: ${{ github.run_number }} 31 | REPO_NAME: ${{ github.repository }} 32 | USERNAME: ${{ github.actor }} 33 | TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | midnightcontrols_MAVEN: ${{ secrets.MAVEN_URL }} 35 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 36 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 37 | - name: Publish to Modrinth 38 | env: 39 | MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} 40 | run: ./gradlew publishModrinth 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # LambdAurora's ignore file 3 | # 4 | # v0.15 5 | 6 | # JetBrains 7 | .idea/ 8 | *.iml 9 | *.ipr 10 | *.iws 11 | ## Intellij IDEA 12 | out/ 13 | ## CLion 14 | cmake-build-debug*/ 15 | cmake-build-release*/ 16 | ## Eclipse 17 | eclipse 18 | *.launch 19 | .settings 20 | .metadata 21 | .classpath 22 | .project 23 | ## Visual Studio 24 | .vs/ 25 | CMakeSettings.json 26 | 27 | # Build system 28 | ## Cargo 29 | Cargo.lock 30 | ## CMake 31 | CMakeCache.txt 32 | CMakeFiles/ 33 | ## Gradle 34 | .gradle/ 35 | ## Node.JS 36 | node_modules/ 37 | 38 | # Editors 39 | ## VSCode 40 | .vscode/ 41 | 42 | # Logging 43 | logs/ 44 | 45 | # Languages 46 | ## Java 47 | classes/ 48 | ## Python 49 | __pycache__/ 50 | ## Rust 51 | **/*.rs.bk 52 | 53 | # OS 54 | ## Windows 55 | desktop.ini 56 | # MacOS 57 | .DS_Store 58 | 59 | # File types 60 | *.dll 61 | *.so 62 | *.dylib 63 | *.lib 64 | lib*.a 65 | *.png~ 66 | *.tar.?z 67 | 68 | # Common 69 | bin/ 70 | build/ 71 | dist/ 72 | lib/ 73 | obj/ 74 | run/ 75 | target/ 76 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at aurora42lambda@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to midnightcontrols 2 | 3 | :tada: First of all, thanks for taking time to contribute! :tada: 4 | 5 | The following is a set of guidelines for contributing to MidnightControls. 6 | Feel free to propose changes to this document in a pull request. 7 | 8 | **Table of Contents** 9 | 10 | [Code of Conduct](#code-of-conduct) 11 | 12 | [What should I know before I get started?](#what-should-i-know-before-i-get-started) 13 | 14 | [How can I contribute?](#how-can-i-contribute) 15 | 16 | [Styleguides](#styleguides) 17 | 18 | ## Code of Conduct 19 | 20 | This project and everyone participating in it is governed by the [Code of Conduct](https://github.com/LambdAurora/midnightcontrols/blob/master/CODE_OF_CONDUCT.md). 21 | By participating, you are expected to uphold this code. Please report unacceptable behavior at [motschen@midnightdust.eu](mailto:motschen@midnightdust.eu). 22 | 23 | ## What should I know before I get started? 24 | 25 | ### Fabric 26 | 27 | [Fabric](https://fabricmc.net/) is the mod loader and the software which allows Gradle to setup the workspace. 28 | 29 | ### Java 17 30 | 31 | Java is the main language used to make MidnightControls alive. 32 | Knowing how to code in Java is necessary if you contribute to the code. 33 | 34 | ### Minecraft 35 | 36 | As it is a Minecraft mod you should know a bit how Minecraft works and how modding works. 37 | 38 | ### Mixins 39 | 40 | [Mixins](https://github.com/SpongePowered/Mixin/wiki) are a main part in this mod, they allow the necessary modifications to the Minecraft Client. 41 | 42 | ### Gradle 43 | 44 | [Gradle](https://gradle.org/) is the build tool used for this project. 45 | 46 | ### Git 47 | 48 | Git is the control version software we use for midnightcontrols, please know how to use it if you consider contributing to the code. 49 | 50 | Git commits should be signed. 51 | 52 | ## How can I contribute? 53 | 54 | ### Reporting Bugs 55 | 56 | #### Before submitting a bug report 57 | 58 | - Check if you can reproduce it on other platforms. 59 | - Perform a search to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. 60 | 61 | #### How do I submit a bug report? 62 | 63 | Go in the issues tab in GitHub and read the [bug report guide](https://github.com/TeamMidnightDust/MidnightControls/blob/1.18/.github/ISSUE_TEMPLATE/bug_report.md) 64 | 65 | ### Suggesting enhancements 66 | 67 | Enhancement suggestions are tracked as [GitHub issues](https://github.com/TeamMidnightDust/MidnightControls/issues). 68 | Check out the [feature request](https://github.com/TeamMidnightDust/MidnightControls/blob/1.17/.github/ISSUE_TEMPLATE/feature_request.md) guide. 69 | 70 | ### Do pull requests 71 | 72 | You can help midnightcontrols by writing code and submit it with pull requests. 73 | 74 | Pull requests will be accepted if they follow the [styleguide](#styleguides), if they are useful, etc... 75 | We can refuse a pull request if the commits are not signed, so don't forget to [sign them](https://help.github.com/en/articles/signing-commits)! 76 | 77 | Feel free to pull request! 78 | 79 | ## Styleguides 80 | 81 | ### Git commit messages 82 | 83 | * Use the imperative mood ("Move cursor to..." not "Moves cursor to...") 84 | * (Not for the message) Don't forget to sign the commit. 85 | 86 | ### Naming convention 87 | 88 | Names in the code should be explicit and always in `camelCase`, `snake_case` will not be allowed. 89 | `PascalCase` can be used for class name. 90 | 91 | ### Brace placement 92 | 93 | Every braces should be at the end of the line of function declaration, etc. 94 | 95 | ### Quick note for users of the Intellij IDEA IDE 96 | 97 | As a user of the Intellij IDEA IDE you have the format code shortcut which use a codestyle described by a file. 98 | You can import the codestyle file here: [LambdAurora's dotfiles](https://github.com/LambdAurora/dotfiles/blob/master/jetbrains/lambdacodestyle2.xml). 99 | -------------------------------------------------------------------------------- /HEADER: -------------------------------------------------------------------------------- 1 | Copyright © 2021 LambdAurora 2 | Copyright © 2022 Motschen 3 | 4 | This file is part of MidnightControls. 5 | 6 | Licensed under the MIT license. For more information, 7 | see the LICENSE file. 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2021 LambdAurora 4 | Copyright © 2022 Motschen 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "architectury-plugin" version "3.4-SNAPSHOT" 3 | id "dev.architectury.loom" version "1.10-SNAPSHOT" apply false 4 | id "me.shedaniel.unified-publishing" version "0.1.+" apply false 5 | id 'com.github.johnrengelman.shadow' version '8.1.1' apply false 6 | } 7 | 8 | architectury { 9 | minecraft = rootProject.minecraft_version 10 | } 11 | 12 | repositories { 13 | maven { 14 | url = "https://api.modrinth.com/maven" 15 | } 16 | flatDir { 17 | dirs("localMaven") 18 | } 19 | } 20 | 21 | subprojects { 22 | apply plugin: "dev.architectury.loom" 23 | repositories { 24 | maven { 25 | url = "https://api.modrinth.com/maven" 26 | } 27 | maven { url 'https://jitpack.io' } 28 | flatDir { 29 | dirs("../localMaven") 30 | } 31 | } 32 | 33 | dependencies { 34 | minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" 35 | // The following line declares the mojmap mappings, you may use other mappings as well 36 | //mappings loom.officialMojangMappings() 37 | // The following line declares the yarn mappings you may select this one as well. 38 | mappings loom.layered { 39 | it.mappings("net.fabricmc:yarn:$rootProject.yarn_mappings:v2") 40 | it.mappings("dev.architectury:yarn-mappings-patch-neoforge:$rootProject.yarn_mappings_patch_neoforge_version") 41 | } 42 | } 43 | } 44 | 45 | 46 | allprojects { 47 | apply plugin: "java" 48 | apply plugin: "architectury-plugin" 49 | apply plugin: "maven-publish" 50 | 51 | archivesBaseName = rootProject.archives_base_name 52 | version = rootProject.mod_version 53 | group = rootProject.maven_group 54 | 55 | repositories { 56 | maven { url 'https://aperlambda.github.io/maven' } 57 | } 58 | dependencies { 59 | implementation('org.aperlambda:lambdajcommon:1.8.1') { 60 | exclude group: 'com.google.code.gson' 61 | exclude group: 'com.google.guava' 62 | } 63 | } 64 | 65 | tasks.withType(JavaCompile) { 66 | options.encoding = "UTF-8" 67 | options.release = 21 68 | } 69 | ext { 70 | releaseChangelog = { 71 | def changes = new StringBuilder() 72 | changes << "## MidnightControls v$project.version for $project.minecraft_version\n[View the changelog](https://www.github.com/TeamMidnightDust/MidnightControls/commits/)" 73 | def proc = "git log --max-count=1 --pretty=format:%s".execute() 74 | proc.in.eachLine { line -> 75 | def processedLine = line.toString() 76 | if (!processedLine.contains("New translations") && !processedLine.contains("Merge") && !processedLine.contains("branch")) { 77 | changes << "\n- ${processedLine.capitalize()}" 78 | } 79 | } 80 | proc.waitFor() 81 | return changes.toString() 82 | } 83 | } 84 | 85 | java { 86 | withSourcesJar() 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | architectury { 2 | common(rootProject.enabled_platforms.split(",")) 3 | } 4 | loom { 5 | accessWidenerPath = file("src/main/resources/midnightcontrols.accesswidener") 6 | } 7 | repositories { 8 | maven { 9 | name 'Gegy' 10 | url 'https://maven.gegy.dev' 11 | } 12 | maven { 13 | name = "CottonMC" 14 | url = "https://server.bbkr.space/artifactory/libs-release" 15 | } 16 | maven { url "https://maven.terraformersmc.com/releases/" } 17 | maven { url 'https://maven.kosmx.dev' } 18 | maven { url 'https://maven.isxander.dev/releases' } 19 | maven { url 'https://maven.shedaniel.me/' } 20 | maven { url 'https://jitpack.io' } 21 | maven { url "https://api.modrinth.com/maven" } 22 | maven { url 'https://maven.quiltmc.org/repository/release'} 23 | } 24 | 25 | dependencies { 26 | // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies 27 | // Do NOT use other classes from fabric loader 28 | modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" 29 | // Using the Fabric version of midnightlib here to create a common config and get useful utilities 30 | // Just make sure NOT to use classes from the .fabric classpath 31 | modCompileOnlyApi "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric" 32 | modCompileOnlyApi "maven.modrinth:obsidianui:${rootProject.obsidianui_version}-fabric" 33 | modCompileOnlyApi ("com.terraformersmc:modmenu:${project.modmenu_version}") { 34 | exclude(group: "net.fabricmc.fabric-api") 35 | } 36 | 37 | // Compatibility mods 38 | modCompileOnlyApi "io.github.cottonmc:LibGui:${project.libgui_version}" 39 | modCompileOnlyApi "org.quiltmc:quilt-json5:1.0.0" 40 | modImplementation "maven.modrinth:sodium:${project.sodium_version}-fabric" 41 | modCompileOnlyApi "maven.modrinth:emi:${project.emi_version}" 42 | modImplementation "maven.modrinth:emotecraft:${project.emotecraft_version}" 43 | modCompileOnlyApi "io.github.kosmx:bendy-lib:${project.bendylib_version}" 44 | modCompileOnlyApi "dev.isxander:yet-another-config-lib:${project.yacl_version}" 45 | modCompileOnlyApi "maven.modrinth:inventory-tabs-updated:${project.inventorytabs_version}" 46 | modCompileOnlyApi "maven.modrinth:bedrockify:${project.bedrockify_version}" 47 | // Required for Inventory Tabs 48 | modCompileOnlyApi("me.shedaniel.cloth:cloth-config-fabric:${project.clothconfig_version}") { 49 | exclude(group: "net.fabricmc.fabric-api") 50 | } 51 | } 52 | 53 | publishing { 54 | publications { 55 | mavenCommon(MavenPublication) { 56 | artifactId = rootProject.archives_base_name 57 | from components.java 58 | } 59 | } 60 | 61 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. 62 | repositories { 63 | // Add repositories to publish to here. 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/ControlsMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols; 11 | 12 | import org.jetbrains.annotations.NotNull; 13 | import org.thinkingstudio.obsidianui.util.Nameable; 14 | 15 | import java.util.Arrays; 16 | import java.util.Optional; 17 | 18 | /** 19 | * Represents the controls mode. 20 | * 21 | * @author LambdAurora 22 | * @version 1.7.0 23 | * @since 1.0.0 24 | */ 25 | public enum ControlsMode { 26 | DEFAULT, 27 | CONTROLLER, 28 | TOUCHSCREEN; 29 | 30 | /** 31 | * Returns the next controls mode available. 32 | * 33 | * @return the next available controls mode 34 | */ 35 | public ControlsMode next() { 36 | var v = values(); 37 | if (v.length == this.ordinal() + 1) 38 | return v[0]; 39 | return v[this.ordinal() + 1]; 40 | } 41 | 42 | /** 43 | * Gets the translation key of this controls mode. 44 | * 45 | * @return the translated key of this controls mode 46 | * @since 1.1.0 47 | */ 48 | public String getTranslationKey() { 49 | return "midnightcontrols.controls_mode." + this.getName(); 50 | } 51 | 52 | public @NotNull String getName() { 53 | return this.name().toLowerCase(); 54 | } 55 | 56 | /** 57 | * Gets the controls mode from its identifier. 58 | * 59 | * @param id the identifier of the controls mode 60 | * @return the controls mode if found, else empty 61 | */ 62 | public static Optional byId(@NotNull String id) { 63 | return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/MidnightControls.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols; 11 | 12 | import eu.midnightdust.lib.util.PlatformFunctions; 13 | import net.minecraft.util.Identifier; 14 | import org.apache.logging.log4j.LogManager; 15 | import org.apache.logging.log4j.Logger; 16 | 17 | /** 18 | * Represents the MidnightControls mod. 19 | * 20 | * @author LambdAurora & Motschen 21 | * @version 1.8.0 22 | * @since 1.0.0 23 | */ 24 | public class MidnightControls { 25 | public static boolean isExtrasLoaded; 26 | 27 | public static final Logger logger = LogManager.getLogger("MidnightControls"); 28 | 29 | public static void init() { 30 | isExtrasLoaded = PlatformFunctions.isModLoaded("midnightcontrols-extra"); 31 | log("Initializing MidnightControls..."); 32 | } 33 | public static Identifier id(String path) { 34 | return Identifier.of(MidnightControlsConstants.NAMESPACE, path); 35 | } 36 | 37 | /** 38 | * Prints a message to the terminal. 39 | * 40 | * @param info the message to print 41 | */ 42 | public static void log(String info) { 43 | logger.info("[MidnightControls] {}", info); 44 | } 45 | 46 | /** 47 | * Prints a warning to the terminal. 48 | * 49 | * @param warning the warning to print 50 | */ 51 | public static void warn(String warning) { 52 | logger.warn("[MidnightControls] {}", warning); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/MidnightControlsConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols; 11 | 12 | 13 | import net.minecraft.util.Identifier; 14 | 15 | import static eu.midnightdust.midnightcontrols.MidnightControls.id; 16 | 17 | /** 18 | * Represents the constants used by MidnightControls. 19 | * 20 | * @author LambdAurora 21 | * @version 1.1.0 22 | * @since 1.1.0 23 | */ 24 | public class MidnightControlsConstants { 25 | public static final String NAMESPACE = "midnightcontrols"; 26 | public static final Identifier CONTROLS_MODE_CHANNEL = id("controls_mode"); 27 | public static final Identifier FEATURE_CHANNEL = id("feature"); 28 | public static final Identifier HELLO_CHANNEL = id("hello"); 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/MidnightControlsModMenu.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client; 11 | 12 | import com.terraformersmc.modmenu.api.ConfigScreenFactory; 13 | import com.terraformersmc.modmenu.api.ModMenuApi; 14 | import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen; 15 | 16 | /** 17 | * Represents the API implementation of ModMenu for midnightcontrols. 18 | * 19 | * @author LambdAurora 20 | * @version 1.7.0 21 | * @since 1.1.0 22 | */ 23 | public class MidnightControlsModMenu implements ModMenuApi { 24 | @Override 25 | public ConfigScreenFactory getModConfigScreenFactory() { 26 | return parent -> new MidnightControlsSettingsScreen(parent, false); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/MidnightControlsReloadListener.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client; 2 | 3 | import eu.midnightdust.midnightcontrols.client.virtualkeyboard.KeyboardLayoutManager; 4 | import net.minecraft.resource.ResourceManager; 5 | import net.minecraft.resource.SynchronousResourceReloader; 6 | 7 | public class MidnightControlsReloadListener implements SynchronousResourceReloader { 8 | public static final MidnightControlsReloadListener INSTANCE = new MidnightControlsReloadListener(); 9 | 10 | private MidnightControlsReloadListener() {} 11 | 12 | @Override 13 | public void reload(ResourceManager manager) { 14 | manager.findResources("keyboard_layouts", path -> path.toString().startsWith("midnightcontrols") && path.toString().endsWith(".json")).forEach(KeyboardLayoutManager::loadLayout); 15 | } 16 | } -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/BedrockifyCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2022 Motschen 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.compat; 11 | 12 | import me.juancarloscp52.bedrockify.client.BedrockifyClient; 13 | 14 | /** 15 | * Represents HQM compatibility handler. 16 | * 17 | * @author Motschen 18 | * @version 1.7.0 19 | * @since 1.7.0 20 | */ 21 | public class BedrockifyCompat implements CompatHandler { 22 | 23 | @Override 24 | public void handle() { 25 | BedrockifyClient.getInstance().settings.disableFlyingMomentum = false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/EMICompat.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.compat; 2 | 3 | import dev.emi.emi.api.EmiApi; 4 | import dev.emi.emi.config.EmiConfig; 5 | import dev.emi.emi.screen.EmiScreenManager; 6 | import eu.midnightdust.midnightcontrols.client.MidnightControlsClient; 7 | import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; 8 | import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory; 9 | import eu.midnightdust.midnightcontrols.client.controller.InputManager; 10 | import org.lwjgl.glfw.GLFW; 11 | 12 | import static eu.midnightdust.midnightcontrols.MidnightControls.id; 13 | 14 | public class EMICompat implements CompatHandler { 15 | public static boolean handleEmiPages(boolean direction) { 16 | if (isEMIEnabled() && MidnightControlsClient.input.actionGuiCooldown == 0 && EmiScreenManager.getSearchPanel() != null && EmiScreenManager.getSearchPanel().pageLeft != null && EmiScreenManager.getSearchPanel().pageRight != null) { 17 | if (direction) EmiScreenManager.getSearchPanel().pageRight.onPress(); 18 | else EmiScreenManager.getSearchPanel().pageLeft.onPress(); 19 | MidnightControlsClient.input.actionGuiCooldown = 5; 20 | return true; 21 | } 22 | return false; 23 | } 24 | @Override 25 | public void handle() { 26 | ButtonCategory category = new ButtonCategory(id("category.emi")); 27 | InputManager.registerCategory(category); 28 | new ButtonBinding.Builder("emi_page_left") 29 | .buttons(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true)) 30 | .category(category) 31 | .action((client,action,value,buttonState)->handleEmiPages(false)).cooldown() 32 | .filter(((buttonBinding) -> EmiApi.getHandledScreen() != null)) 33 | .register(); 34 | new ButtonBinding.Builder("emi_page_right") 35 | .buttons(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true)) 36 | .category(category) 37 | .action((client,action,value,buttonState)->handleEmiPages(true)).cooldown() 38 | .filter(((buttonBinding) -> EmiApi.getHandledScreen() != null)) 39 | .register(); 40 | } 41 | public static boolean isEMIEnabled() { 42 | return EmiConfig.enabled; 43 | } 44 | public static boolean isSearchBarCentered() { 45 | return EmiConfig.centerSearchBar; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/EmotecraftCompat.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.compat; 2 | 3 | import eu.midnightdust.midnightcontrols.client.controller.InputManager; 4 | import eu.midnightdust.midnightcontrols.client.mixin.MouseAccessor; 5 | import io.github.kosmx.emotes.arch.screen.ingame.FastMenuScreen; 6 | import net.minecraft.client.MinecraftClient; 7 | import net.minecraft.client.gui.screen.Screen; 8 | import org.joml.Vector2i; 9 | import org.lwjgl.glfw.GLFW; 10 | 11 | public class EmotecraftCompat { 12 | private static final MinecraftClient client = MinecraftClient.getInstance(); 13 | 14 | public static void openEmotecraftScreen(Screen parent) { 15 | client.setScreen(new FastMenuScreen(parent)); 16 | } 17 | public static boolean isEmotecraftScreen(Screen screen) { 18 | return screen instanceof FastMenuScreen; 19 | } 20 | 21 | static int prevIndex = -1; 22 | public static void handleEmoteSelector(int index) { 23 | try { 24 | if (client.currentScreen instanceof FastMenuScreen) { 25 | boolean stickReleased = index == -1 && prevIndex != -1; 26 | var pos = calcMousePos(stickReleased ? prevIndex : index); 27 | InputManager.queueMousePosition(pos.x, pos.y); 28 | InputManager.INPUT_MANAGER.updateMousePosition(client); 29 | 30 | if (stickReleased) { 31 | ((MouseAccessor) client.mouse).midnightcontrols$onMouseButton(client.getWindow().getHandle(), GLFW.GLFW_MOUSE_BUTTON_LEFT, GLFW.GLFW_PRESS, 0); 32 | prevIndex = -1; 33 | } 34 | else prevIndex = index; 35 | } else prevIndex = -1; 36 | } catch (Exception ignored) {} 37 | } 38 | public static Vector2i calcMousePos(int index) { 39 | int x = client.getWindow().getWidth() / 2; 40 | int y = client.getWindow().getHeight() / 2; 41 | switch (index) { 42 | case 0, 3, 5 -> x -= 275; 43 | case 2, 4, 7 -> x += 275; 44 | } 45 | switch (index) { 46 | case 0, 1, 2 -> y -= 275; 47 | case 5, 6, 7 -> y += 275; 48 | } 49 | return new Vector2i(x, y); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/HQMCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.compat; 11 | 12 | import net.minecraft.client.gui.screen.Screen; 13 | import org.aperlambda.lambdacommon.utils.LambdaReflection; 14 | 15 | import java.util.Optional; 16 | 17 | /** 18 | * Represents HQM compatibility handler. 19 | *

20 | * This is bad. 21 | * 22 | * @author LambdAurora 23 | * @version 1.3.2 24 | * @since 1.3.2 25 | */ 26 | public class HQMCompat implements CompatHandler { 27 | public static final String GUI_BASE_CLASS_PATH = "hardcorequesting.client.interfaces.GuiBase"; 28 | private Optional> guiBaseClass; 29 | 30 | @Override 31 | public void handle() { 32 | this.guiBaseClass = LambdaReflection.getClass(GUI_BASE_CLASS_PATH); 33 | } 34 | 35 | @Override 36 | public boolean requireMouseOnScreen(Screen screen) { 37 | return this.guiBaseClass.map(clazz -> clazz.isInstance(screen)).orElse(false); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/InventoryTabsCompat.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.compat; 2 | 3 | import com.kqp.inventorytabs.tabs.TabManager; 4 | import net.minecraft.client.gui.screen.Screen; 5 | import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; 6 | import net.minecraft.client.gui.screen.ingame.HandledScreen; 7 | 8 | public class InventoryTabsCompat implements CompatHandler { 9 | @Override 10 | public boolean handleTabs(Screen screen, boolean next) { 11 | if (screen instanceof HandledScreen && !(screen instanceof CreativeInventoryScreen)) { 12 | TabManager tabManager = TabManager.getInstance(); 13 | int tabIndex = tabManager.tabs.indexOf(tabManager.currentTab); 14 | if (next) { 15 | if (tabIndex < tabManager.tabs.size() - 1) tabManager.onTabClick(tabManager.tabs.get(tabIndex + 1)); 16 | else tabManager.onTabClick(tabManager.tabs.getFirst()); 17 | } else { 18 | if (tabIndex > 0) tabManager.onTabClick(tabManager.tabs.get(tabIndex - 1)); 19 | else tabManager.onTabClick(tabManager.tabs.getLast()); 20 | } 21 | return true; 22 | } 23 | return false; 24 | } 25 | @Override 26 | public boolean handlePages(Screen screen, boolean next) { 27 | if (screen instanceof HandledScreen && !(screen instanceof CreativeInventoryScreen)) { 28 | TabManager tabManager = TabManager.getInstance(); 29 | if (next) { 30 | if (tabManager.canGoForwardAPage()) { 31 | tabManager.setCurrentPage(tabManager.currentPage + 1); 32 | return true; 33 | } 34 | } else { 35 | if (tabManager.canGoBackAPage()) { 36 | tabManager.setCurrentPage(tabManager.currentPage - 1); 37 | return true; 38 | } 39 | } 40 | } 41 | return false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/LibGuiCompat.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.compat; 2 | 3 | import io.github.cottonmc.cotton.gui.impl.client.CottonScreenImpl; 4 | import io.github.cottonmc.cotton.gui.widget.WButton; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.gui.screen.Screen; 7 | import net.minecraft.client.sound.PositionedSoundInstance; 8 | import net.minecraft.sound.SoundEvents; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | @SuppressWarnings("UnstableApiUsage") 12 | public class LibGuiCompat { 13 | public static boolean handlePress(@NotNull Screen screen) { 14 | if (screen instanceof CottonScreenImpl cottonScreen) { 15 | if (cottonScreen.getDescription() != null && cottonScreen.getDescription().getFocus() != null) { 16 | if (cottonScreen.getDescription().getFocus() instanceof WButton button && button.getOnClick() != null) { 17 | button.getOnClick().run(); 18 | MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F)); 19 | return true; 20 | } 21 | } 22 | } 23 | return false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/MidnightControlsMixinPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.compat; 11 | 12 | import eu.midnightdust.lib.util.PlatformFunctions; 13 | import org.objectweb.asm.tree.ClassNode; 14 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; 15 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 16 | 17 | import java.util.List; 18 | import java.util.Set; 19 | 20 | /** 21 | * This plugin is only present for the conditional mixins. 22 | * 23 | * @author LambdAurora & Motschen 24 | * @version 1.6.0 25 | * @since 1.2.0 26 | */ 27 | public class MidnightControlsMixinPlugin implements IMixinConfigPlugin { 28 | private String mixinPackage; 29 | 30 | @Override 31 | public void onLoad(String mixinPackage) { 32 | this.mixinPackage = mixinPackage + "."; 33 | } 34 | 35 | @Override 36 | public String getRefMapperConfig() { 37 | return null; 38 | } 39 | 40 | @Override 41 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { 42 | final String mixinName = mixinClassName.substring(this.mixinPackage.length()); 43 | final String packageName = mixinName.substring(0, mixinName.lastIndexOf('.')); 44 | 45 | if (packageName.startsWith("sodium") && !PlatformFunctions.isModLoaded("sodium")) 46 | return false; 47 | 48 | return true; 49 | } 50 | 51 | @Override 52 | public void acceptTargets(Set myTargets, Set otherTargets) { 53 | } 54 | 55 | @Override 56 | public List getMixins() { 57 | return null; 58 | } 59 | 60 | @Override 61 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 62 | } 63 | 64 | @Override 65 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/SodiumCompat.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.compat; 2 | 3 | import eu.midnightdust.midnightcontrols.client.compat.mixin.sodium.SodiumOptionsGUIAccessor; 4 | import net.caffeinemc.mods.sodium.client.gui.SodiumOptionsGUI; 5 | import net.minecraft.client.gui.screen.Screen; 6 | 7 | public class SodiumCompat implements CompatHandler { 8 | @Override 9 | public boolean handleTabs(Screen screen, boolean direction) { 10 | if (screen instanceof SodiumOptionsGUI optionsGUI) { 11 | SodiumOptionsGUIAccessor accessor = (SodiumOptionsGUIAccessor) optionsGUI; 12 | final int max = accessor.getPages().size()-1; 13 | int i = accessor.getPages().indexOf(accessor.getCurrentPage()); 14 | i = (direction ? ((max > i) ? ++i : 0) : (i > 0 ? --i : max)); 15 | optionsGUI.setPage(accessor.getPages().get(i)); 16 | return true; 17 | } 18 | return false; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/YACLCompat.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.compat; 2 | 3 | import dev.isxander.yacl.gui.AbstractWidget; 4 | import dev.isxander.yacl.gui.OptionListWidget; 5 | import dev.isxander.yacl.gui.YACLScreen; 6 | import dev.isxander.yacl.gui.controllers.ControllerWidget; 7 | import dev.isxander.yacl.gui.controllers.slider.SliderControllerElement; 8 | import net.minecraft.client.gui.Element; 9 | import net.minecraft.client.gui.screen.Screen; 10 | import org.lwjgl.glfw.GLFW; 11 | 12 | public class YACLCompat implements CompatHandler { 13 | public static boolean handleAButton(Screen screen, Element element) { 14 | if (element instanceof AbstractWidget abstractWidget) { 15 | // imitate enter key press 16 | return abstractWidget.keyPressed(GLFW.GLFW_KEY_ENTER, 0, 0); 17 | } 18 | return false; 19 | } 20 | 21 | public static boolean handleLeftRight(Screen screen, boolean direction) { 22 | if (screen instanceof YACLScreen yaclScreen) { 23 | SliderControllerElement focusedSlider = yaclScreen.optionList.children().stream() 24 | .filter(OptionListWidget.OptionEntry.class::isInstance) 25 | .map(entry -> ((OptionListWidget.OptionEntry) entry).widget) 26 | .filter(ControllerWidget.class::isInstance) 27 | .map(ControllerWidget.class::cast) 28 | .filter(SliderControllerElement.class::isInstance) 29 | .map(SliderControllerElement.class::cast) 30 | .filter(ControllerWidget::isHovered) 31 | .findFirst() 32 | .orElse(null); 33 | 34 | if (focusedSlider == null) 35 | return false; 36 | 37 | focusedSlider.incrementValue(direction ? 1 : -1); 38 | return true; 39 | } 40 | 41 | return false; 42 | } 43 | 44 | @Override 45 | public boolean handleTabs(Screen screen, boolean direction) { 46 | if (screen instanceof YACLScreen yaclScreen) { 47 | int categoryIdx = yaclScreen.getCurrentCategoryIdx(); 48 | if (direction) categoryIdx++; else categoryIdx--; 49 | if (categoryIdx < 0) categoryIdx = yaclScreen.config.categories().size() - 1; 50 | if (categoryIdx >= yaclScreen.config.categories().size()) categoryIdx = 0; 51 | 52 | yaclScreen.changeCategory(categoryIdx); 53 | return true; 54 | } 55 | return false; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/compat/mixin/sodium/SodiumOptionsGUIAccessor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.compat.mixin.sodium; 2 | 3 | import net.caffeinemc.mods.sodium.client.gui.SodiumOptionsGUI; 4 | import net.caffeinemc.mods.sodium.client.gui.options.OptionPage; 5 | import net.caffeinemc.mods.sodium.client.gui.options.control.ControlElement; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | import java.util.List; 10 | 11 | @Mixin(value = SodiumOptionsGUI.class, remap = false) 12 | public interface SodiumOptionsGUIAccessor { 13 | @Accessor 14 | List> getControls(); 15 | @Accessor 16 | List getPages(); 17 | @Accessor 18 | OptionPage getCurrentPage(); 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/controller/ButtonCategory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.controller; 11 | 12 | import net.minecraft.client.resource.language.I18n; 13 | import net.minecraft.util.Identifier; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.Collections; 19 | import java.util.List; 20 | 21 | /** 22 | * Represents a button binding category 23 | * 24 | * @author LambdAurora 25 | * @version 1.1.0 26 | * @since 1.1.0 27 | */ 28 | public class ButtonCategory { 29 | private final List bindings = new ArrayList<>(); 30 | private final Identifier id; 31 | private final int priority; 32 | 33 | public ButtonCategory(@NotNull Identifier id, int priority) { 34 | this.id = id; 35 | this.priority = priority; 36 | } 37 | 38 | public ButtonCategory(@NotNull Identifier id) { 39 | this(id, 100); 40 | } 41 | @Deprecated 42 | public ButtonCategory(@NotNull org.aperlambda.lambdacommon.Identifier id, int priority) { 43 | this(Identifier.of(id.getNamespace(), id.getName()), priority); 44 | } 45 | 46 | @Deprecated 47 | public ButtonCategory(@NotNull org.aperlambda.lambdacommon.Identifier id) { 48 | this(id, 100); 49 | } 50 | 51 | public void registerBinding(@NotNull ButtonBinding binding) { 52 | if (this.bindings.contains(binding)) 53 | throw new IllegalStateException("Cannot register twice a button binding in the same category."); 54 | this.bindings.add(binding); 55 | } 56 | 57 | public void registerAllBindings(@NotNull ButtonBinding... bindings) { 58 | this.registerAllBindings(Arrays.asList(bindings)); 59 | } 60 | 61 | public void registerAllBindings(@NotNull List bindings) { 62 | bindings.forEach(this::registerBinding); 63 | } 64 | 65 | /** 66 | * Gets the bindings assigned to this category. 67 | * 68 | * @return the bindings assigned to this category 69 | */ 70 | public @NotNull List getBindings() { 71 | return Collections.unmodifiableList(this.bindings); 72 | } 73 | 74 | /** 75 | * Gets the translated name of this category. 76 | *

77 | * The translation key should be `modid.identifier_name`. 78 | * 79 | * @return the translated name 80 | */ 81 | public @NotNull String getTranslatedName() { 82 | if (this.id.getNamespace().equals("minecraft")) 83 | return I18n.translate(this.id.getPath()); 84 | else 85 | return I18n.translate(this.id.getNamespace() + "." + this.id.getPath()); 86 | } 87 | 88 | /** 89 | * Gets the priority display of this category. 90 | * It will defines in which order the categories will display on the controls screen. 91 | * 92 | * @return the priority of this category 93 | */ 94 | public int getPriority() { 95 | return this.priority; 96 | } 97 | 98 | public @NotNull Identifier getIdentifier() { 99 | return this.id; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/controller/MovementHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.controller; 11 | 12 | import eu.midnightdust.midnightcontrols.client.enums.ButtonState; 13 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 14 | import eu.midnightdust.midnightcontrols.client.mixin.InputAccessor; 15 | import eu.midnightdust.midnightcontrols.client.util.MathUtil; 16 | import net.minecraft.client.MinecraftClient; 17 | import net.minecraft.client.network.ClientPlayerEntity; 18 | import net.minecraft.entity.attribute.EntityAttributes; 19 | import net.minecraft.util.PlayerInput; 20 | import net.minecraft.util.math.MathHelper; 21 | import net.minecraft.util.math.Vec2f; 22 | import org.jetbrains.annotations.NotNull; 23 | 24 | /** 25 | * Represents the movement handler. 26 | * 27 | * @author LambdAurora 28 | * @version 1.6.0 29 | * @since 1.4.0 30 | */ 31 | public final class MovementHandler implements PressAction { 32 | public static final MovementHandler HANDLER = new MovementHandler(); 33 | private boolean shouldOverrideMovement = false; 34 | private boolean pressingForward = false; 35 | private boolean pressingBack = false; 36 | private boolean pressingLeft = false; 37 | private boolean pressingRight = false; 38 | private float slowdownFactor = 1.f; 39 | private float movementForward = 0.f; 40 | private float movementSideways = 0.f; 41 | private final MathUtil.PolarUtil polarUtil = new MathUtil.PolarUtil(); 42 | 43 | private MovementHandler() { 44 | } 45 | 46 | /** 47 | * Applies movement input of this handler to the player's input. 48 | * 49 | * @param player The client player. 50 | */ 51 | public void applyMovement(@NotNull ClientPlayerEntity player) { 52 | if (!this.shouldOverrideMovement) 53 | return; 54 | player.input.playerInput = new PlayerInput(this.pressingForward, this.pressingBack, this.pressingLeft, this.pressingRight, 55 | player.input.playerInput.jump(), player.input.playerInput.sneak(), player.input.playerInput.sprint()); 56 | 57 | polarUtil.calculate(this.movementSideways, this.movementForward, this.slowdownFactor); 58 | Vec2f inputVector = new Vec2f(polarUtil.polarX, polarUtil.polarY); 59 | ((InputAccessor)player.input).setMovementVector(inputVector); 60 | 61 | this.shouldOverrideMovement = false; 62 | } 63 | 64 | @Override 65 | public boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action) { 66 | if (client.currentScreen != null || client.player == null) 67 | return this.shouldOverrideMovement = false; 68 | 69 | int direction = 0; 70 | if (button == ButtonBinding.FORWARD || button == ButtonBinding.LEFT) 71 | direction = 1; 72 | else if (button == ButtonBinding.BACK || button == ButtonBinding.RIGHT) 73 | direction = -1; 74 | 75 | if (action.isUnpressed()) 76 | direction = 0; 77 | 78 | this.shouldOverrideMovement = direction != 0; 79 | 80 | if (!MidnightControlsConfig.analogMovement) { 81 | value = 1.f; 82 | } 83 | 84 | this.slowdownFactor = client.player.shouldSlowDown() ? (MathHelper.clamp( 85 | 0.3F + (float) client.player.getAttributeValue(EntityAttributes.SNEAKING_SPEED), 86 | 0.0F, 87 | 1.0F 88 | )) : 1.f; 89 | 90 | if (button == ButtonBinding.FORWARD || button == ButtonBinding.BACK) { 91 | // Handle forward movement. 92 | this.pressingForward = direction > 0; 93 | this.pressingBack = direction < 0; 94 | this.movementForward = direction * value; 95 | } else { 96 | // Handle sideways movement. 97 | this.pressingLeft = direction > 0; 98 | this.pressingRight = direction < 0; 99 | this.movementSideways = direction * value; 100 | } 101 | 102 | return this.shouldOverrideMovement; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/controller/PressAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.controller; 11 | 12 | import eu.midnightdust.midnightcontrols.client.enums.ButtonState; 13 | import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor; 14 | import net.minecraft.client.MinecraftClient; 15 | import net.minecraft.client.option.StickyKeyBinding; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | /** 19 | * Represents a press action callback. 20 | * 21 | * @author LambdAurora 22 | * @version 1.7.0 23 | * @since 1.0.0 24 | */ 25 | @FunctionalInterface 26 | public interface PressAction { 27 | PressAction DEFAULT_ACTION = (client, button, value, action) -> { 28 | if (action == ButtonState.REPEAT || client.currentScreen != null) 29 | return false; 30 | button.asKeyBinding().ifPresent(binding -> { 31 | if (binding instanceof StickyKeyBinding) 32 | binding.setPressed(button.isPressed()); 33 | else 34 | ((KeyBindingAccessor) binding).midnightcontrols$handlePressState(button.isPressed()); 35 | }); 36 | return true; 37 | }; 38 | 39 | /** 40 | * Handles when there is a press action. 41 | * 42 | * @param client the client instance 43 | * @param action the action done 44 | */ 45 | boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action); 46 | } 47 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/enums/ButtonState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.enums; 11 | 12 | /** 13 | * Represents a button state. 14 | * 15 | * @author LambdAurora 16 | * @version 1.1.0 17 | * @since 1.1.0 18 | */ 19 | public enum ButtonState { 20 | NONE(0), 21 | PRESS(1), 22 | RELEASE(2), 23 | REPEAT(3); 24 | 25 | public final int id; 26 | 27 | ButtonState(int id) { 28 | this.id = id; 29 | } 30 | 31 | /** 32 | * Returns whether this state is a pressed state. 33 | * 34 | * @return true if this state is a pressed state, else false 35 | */ 36 | public boolean isPressed() { 37 | return this == PRESS || this == REPEAT; 38 | } 39 | 40 | /** 41 | * Returns whether this state is an unpressed state. 42 | * 43 | * @return true if this state is an unpressed state, else false 44 | */ 45 | public boolean isUnpressed() { 46 | return this == RELEASE || this == NONE; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/enums/CameraMode.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.enums; 2 | 3 | import net.minecraft.text.Text; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public enum CameraMode { 7 | FLAT, ADAPTIVE; 8 | public Text getTranslatedText() { 9 | return Text.translatable("midnightcontrols.midnightconfig.enum."+this.getClass().getSimpleName()+"."+this.name()); 10 | } 11 | public @NotNull CameraMode next() { 12 | var v = values(); 13 | if (v.length == this.ordinal() + 1) 14 | return v[0]; 15 | return v[this.ordinal() + 1]; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/enums/ControllerType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.enums; 11 | 12 | import net.minecraft.text.Text; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.Arrays; 16 | import java.util.Optional; 17 | 18 | /** 19 | * Represents a controller type. 20 | * 21 | * @author LambdAurora 22 | * @version 1.4.3 23 | * @since 1.0.0 24 | */ 25 | public enum ControllerType { 26 | DEFAULT(0), 27 | DUALSHOCK(1), 28 | DUALSENSE(2), 29 | SWITCH(3), 30 | XBOX_360(4), 31 | XBOX(5), 32 | STEAM_DECK(6), 33 | STEAM_CONTROLLER(7), 34 | OUYA(8), 35 | NUMBERED(9); 36 | 37 | private final int id; 38 | private final Text text; 39 | 40 | ControllerType(int id) { 41 | this.id = id; 42 | this.text = Text.translatable("midnightcontrols.controller_type." + this.getName()); 43 | } 44 | 45 | ControllerType(int id, @NotNull Text text) { 46 | this.id = id; 47 | this.text = text; 48 | } 49 | 50 | /** 51 | * Returns the controller type's identifier. 52 | * 53 | * @return the controller type's identifier 54 | */ 55 | public int getId() { 56 | return this.id; 57 | } 58 | 59 | /** 60 | * Returns the next controller type available. 61 | * 62 | * @return the next available controller type 63 | */ 64 | public @NotNull ControllerType next() { 65 | var v = values(); 66 | if (v.length == this.ordinal() + 1) 67 | return v[0]; 68 | return v[this.ordinal() + 1]; 69 | } 70 | 71 | /** 72 | * Gets the translated text of this controller type. 73 | * 74 | * @return the translated text of this controller type 75 | */ 76 | public @NotNull Text getTranslatedText() { 77 | return this.text; 78 | } 79 | 80 | public @NotNull String getName() { 81 | return this.name().toLowerCase(); 82 | } 83 | 84 | /** 85 | * Gets the controller type from its identifier. 86 | * 87 | * @param id the identifier of the controller type 88 | * @return the controller type if found, else empty 89 | */ 90 | public static @NotNull Optional byId(@NotNull String id) { 91 | return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/enums/HudSide.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.enums; 11 | 12 | import net.minecraft.text.Text; 13 | import org.aperlambda.lambdacommon.utils.Nameable; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.util.Arrays; 17 | import java.util.Optional; 18 | 19 | /** 20 | * Represents the hud side which is the side where the movements buttons are. 21 | * 22 | * @author LambdAurora 23 | * @version 1.4.0 24 | * @since 1.0.0 25 | */ 26 | public enum HudSide { 27 | LEFT, 28 | RIGHT; 29 | 30 | private final Text text; 31 | 32 | HudSide() { 33 | this.text = Text.translatable(this.getTranslationKey()); 34 | } 35 | 36 | /** 37 | * Returns the next side available. 38 | * 39 | * @return the next available side 40 | */ 41 | public @NotNull HudSide next() { 42 | var v = values(); 43 | if (v.length == this.ordinal() + 1) 44 | return v[0]; 45 | return v[this.ordinal() + 1]; 46 | } 47 | 48 | /** 49 | * Returns the translation key of this hud side. 50 | * 51 | * @return the translation key of this hude side 52 | */ 53 | public @NotNull String getTranslationKey() { 54 | return "midnightcontrols.hud_side." + this.getName(); 55 | } 56 | 57 | /** 58 | * Gets the translated text of this hud side. 59 | * 60 | * @return the translated text of this hud side 61 | */ 62 | public @NotNull Text getTranslatedText() { 63 | return this.text; 64 | } 65 | 66 | public @NotNull String getName() { 67 | return this.name(); 68 | } 69 | 70 | /** 71 | * Gets the hud side from its identifier. 72 | * 73 | * @param id the identifier of the hud side 74 | * @return the hud side if found, else empty 75 | */ 76 | @Deprecated 77 | public static @NotNull Optional byId(@NotNull String id) { 78 | return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/enums/TouchMode.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.enums; 2 | 3 | import net.minecraft.text.Text; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public enum TouchMode { 7 | CROSSHAIR, FINGER_POS; 8 | public Text getTranslatedText() { 9 | return Text.translatable("midnightcontrols.midnightconfig.enum."+this.getClass().getSimpleName()+"."+this.name()); 10 | } 11 | public @NotNull TouchMode next() { 12 | var v = values(); 13 | if (v.length == this.ordinal() + 1) 14 | return v[0]; 15 | return v[this.ordinal() + 1]; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/enums/VirtualMouseSkin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.enums; 11 | 12 | import net.minecraft.text.Text; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.Arrays; 16 | import java.util.Optional; 17 | 18 | /** 19 | * Represents the virtual mouse skins. 20 | * 21 | * @version 1.7.0 22 | * @since 1.2.0 23 | */ 24 | public enum VirtualMouseSkin { 25 | DEFAULT_LIGHT("default_light"), 26 | DEFAULT_DARK("default_dark"), 27 | SECOND_LIGHT("second_light"), 28 | SECOND_DARK("second_dark"); 29 | 30 | private final String name; 31 | private final Text text; 32 | 33 | VirtualMouseSkin(String name) { 34 | this.name = name; 35 | this.text = Text.translatable(this.getTranslationKey()); 36 | } 37 | 38 | /** 39 | * Returns the next virtual mouse skin available. 40 | * 41 | * @return the next available virtual mouse skin 42 | */ 43 | public @NotNull VirtualMouseSkin next() { 44 | var v = values(); 45 | if (v.length == this.ordinal() + 1) 46 | return v[0]; 47 | return v[this.ordinal() + 1]; 48 | } 49 | 50 | /** 51 | * Returns the translation key of this virtual mouse skin. 52 | * 53 | * @return the virtual mouse skin's translation key 54 | */ 55 | public @NotNull String getTranslationKey() { 56 | return "midnightcontrols.virtual_mouse.skin." + this.getName(); 57 | } 58 | 59 | /** 60 | * Gets the translated text of this virtual mouse skin. 61 | * 62 | * @return the translated text of this virtual mouse skin 63 | */ 64 | public @NotNull Text getTranslatedText() { 65 | return this.text; 66 | } 67 | 68 | public @NotNull String getName() { 69 | return this.name; 70 | } 71 | 72 | /** 73 | * Gets the virtual mouse skin from its identifier. 74 | * 75 | * @param id the identifier of the virtual mouse skin 76 | * @return the virtual mouse skin if found, else empty 77 | */ 78 | @Deprecated 79 | public static @NotNull Optional byId(@NotNull String id) { 80 | return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst(); 81 | } 82 | public String getSpritePath() { 83 | return switch (this) { 84 | case DEFAULT_LIGHT -> "cursor/light/default"; 85 | case DEFAULT_DARK -> "cursor/dark/default"; 86 | case SECOND_LIGHT -> "cursor/light/secondary"; 87 | case SECOND_DARK -> "cursor/dark/secondary"; 88 | }; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/gui/ReloadControllerMappingsOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.gui; 11 | 12 | import eu.midnightdust.midnightcontrols.client.controller.Controller; 13 | import org.thinkingstudio.obsidianui.option.SpruceSimpleActionOption; 14 | import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget; 15 | import net.minecraft.client.MinecraftClient; 16 | import net.minecraft.client.toast.SystemToast; 17 | import net.minecraft.text.Text; 18 | import org.jetbrains.annotations.Nullable; 19 | 20 | import java.util.function.Consumer; 21 | 22 | /** 23 | * Represents the option to reload the controller mappings. 24 | */ 25 | public class ReloadControllerMappingsOption { 26 | private static final String KEY = "midnightcontrols.menu.reload_controller_mappings"; 27 | 28 | public static SpruceSimpleActionOption newOption(@Nullable Consumer before) { 29 | return SpruceSimpleActionOption.of(KEY, btn -> { 30 | var client = MinecraftClient.getInstance(); 31 | if (before != null) 32 | before.accept(btn); 33 | Controller.updateMappings(); 34 | if (client.currentScreen instanceof MidnightControlsSettingsScreen) 35 | client.currentScreen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight()); 36 | client.getToastManager().add(SystemToast.create(client, SystemToast.Type.PERIODIC_NOTIFICATION, 37 | Text.translatable("midnightcontrols.controller.mappings.updated"), Text.empty())); 38 | }, Text.translatable("midnightcontrols.tooltip.reload_controller_mappings")); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/gui/RingScreen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.gui; 11 | 12 | import eu.midnightdust.midnightcontrols.client.MidnightControlsClient; 13 | import eu.midnightdust.midnightcontrols.client.ring.RingButtonMode; 14 | import eu.midnightdust.midnightcontrols.client.ring.RingPage; 15 | import net.minecraft.client.gui.DrawContext; 16 | import net.minecraft.client.gui.screen.Screen; 17 | import net.minecraft.client.gui.widget.ButtonWidget; 18 | import net.minecraft.text.Text; 19 | 20 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.ring; 21 | 22 | /** 23 | * Represents the controls ring screen. 24 | * 25 | * @author LambdAurora 26 | * @version 1.4.3 27 | * @since 1.4.3 28 | */ 29 | public class RingScreen extends Screen { 30 | 31 | public RingScreen() { 32 | super(Text.literal("midnightcontrols.menu.title.ring")); 33 | } 34 | 35 | @Override 36 | protected void init() { 37 | super.init(); 38 | if (ring.getMaxPages() > 1) { 39 | this.addDrawableChild(ButtonWidget.builder(Text.of("◀"), button -> ring.cyclePage(false)).dimensions(5, 5, 20, 20).build()); 40 | this.addDrawableChild(ButtonWidget.builder(Text.of("▶"), button -> ring.cyclePage(true)).dimensions(width - 25, 5, 20, 20).build()); 41 | } 42 | } 43 | 44 | @Override 45 | public boolean shouldPause() { 46 | return false; 47 | } 48 | 49 | @Override 50 | public void render(DrawContext context, int mouseX, int mouseY, float delta) { 51 | super.render(context, mouseX, mouseY, delta); 52 | 53 | RingPage page = ring.getCurrentPage(); 54 | page.render(context, this.textRenderer, this.width, this.height, mouseX, mouseY, delta); 55 | } 56 | 57 | @Override 58 | public void close() { 59 | super.close(); 60 | assert client != null; 61 | client.currentScreen = null; 62 | RingPage page = ring.getCurrentPage(); 63 | if (RingPage.selected >= 0 && page.actions[RingPage.selected] != null) 64 | page.actions[RingPage.selected].activate(RingButtonMode.PRESS); 65 | RingPage.selected = -1; 66 | this.removed(); 67 | } 68 | // @Override 69 | // public boolean changeFocus(boolean lookForwards) { 70 | // if (lookForwards) { 71 | // if (RingPage.selected < 7) ++RingPage.selected; 72 | // else RingPage.selected = -1; 73 | // } 74 | // else { 75 | // if (RingPage.selected > -1) --RingPage.selected; 76 | // else RingPage.selected = 7; 77 | // } 78 | // return true; 79 | // } 80 | 81 | @Override 82 | public boolean mouseReleased(double mouseX, double mouseY, int button) { 83 | if (ring.getCurrentPage().onClick(width, height, (int) mouseX, (int) mouseY)) { 84 | this.close(); 85 | return true; 86 | } 87 | return false; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/gui/widget/ControllerButtonWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.gui.widget; 11 | 12 | import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; 13 | import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer; 14 | import org.thinkingstudio.obsidianui.Position; 15 | import org.thinkingstudio.obsidianui.SpruceTexts; 16 | import org.thinkingstudio.obsidianui.widget.AbstractSpruceIconButtonWidget; 17 | import net.minecraft.client.MinecraftClient; 18 | import net.minecraft.client.gui.DrawContext; 19 | import net.minecraft.text.Text; 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | /** 23 | * Represents a controller button widget. 24 | */ 25 | public class ControllerButtonWidget extends AbstractSpruceIconButtonWidget { 26 | private ButtonBinding binding; 27 | private int iconWidth; 28 | 29 | public ControllerButtonWidget(Position position, int width, @NotNull ButtonBinding binding, @NotNull PressAction action) { 30 | super(position, width, 20, ButtonBinding.getLocalizedButtonName(binding.getButton()[0]), action); 31 | this.binding = binding; 32 | } 33 | 34 | public void update() { 35 | int length = binding.getButton().length; 36 | this.setMessage(this.binding.isNotBound() ? SpruceTexts.NOT_BOUND.copy() : 37 | (length > 0 ? ButtonBinding.getLocalizedButtonName(binding.getButton()[0]) : Text.literal("<>"))); 38 | } 39 | 40 | @Override 41 | public Text getMessage() { 42 | if (this.binding.getButton().length > 1) 43 | return Text.empty(); 44 | return super.getMessage(); 45 | } 46 | 47 | @Override 48 | protected int renderIcon(DrawContext context, int mouseX, int mouseY, float delta) { 49 | int x = this.getX(); 50 | if (this.binding.getButton().length > 1) { 51 | x += (this.width / 2 - this.iconWidth / 2) - 4; 52 | } 53 | var size = MidnightControlsRenderer.drawButton(context, x, this.getY(), this.binding, MinecraftClient.getInstance()); 54 | this.iconWidth = size.length(); 55 | return size.height(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/gui/widget/ControllerControlsWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.gui.widget; 11 | 12 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 13 | import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; 14 | import eu.midnightdust.midnightcontrols.client.controller.InputManager; 15 | import org.thinkingstudio.obsidianui.Position; 16 | import org.thinkingstudio.obsidianui.SpruceTexts; 17 | import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget; 18 | import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget; 19 | import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen; 20 | import net.minecraft.client.gui.DrawContext; 21 | import net.minecraft.client.gui.screen.option.ControlsOptionsScreen; 22 | import net.minecraft.text.Text; 23 | import org.aperlambda.lambdacommon.utils.function.Predicates; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.stream.Collectors; 28 | 29 | /** 30 | * Represents the controls screen. 31 | */ 32 | public class ControllerControlsWidget extends SpruceContainerWidget { 33 | private SpruceButtonWidget resetButton; 34 | public ButtonBinding focusedBinding; 35 | public boolean waiting = false; 36 | public List currentButtons = new ArrayList<>(); 37 | 38 | public ControllerControlsWidget(Position position, int width, int height) { 39 | super(position, width, height); 40 | 41 | this.init(); 42 | } 43 | 44 | protected void init() { 45 | this.addChild(new SpruceButtonWidget(Position.of(this, this.width / 2 - 155, 18), 310, 20, 46 | Text.translatable("midnightcontrols.menu.keyboard_controls"), 47 | btn -> this.client.setScreen(new ControlsOptionsScreen(null, this.client.options)))); 48 | ControlsListWidget bindingsListWidget = new ControlsListWidget(Position.of(this, 0, 43), this.width, this.height - 43 - 35, this); 49 | bindingsListWidget.setBackground(new MidnightControlsSettingsScreen.MidnightControlsBackground(130)); 50 | this.addChild(bindingsListWidget); 51 | this.addChild(this.resetButton = new SpruceButtonWidget(Position.of(this, this.width / 2 - 155, this.height - 29), 150, 20, 52 | SpruceTexts.CONTROLS_RESET_ALL, 53 | btn -> InputManager.streamBindings().collect(Collectors.toSet()).forEach(binding -> MidnightControlsConfig.setButtonBinding(binding, binding.getDefaultButton())))); 54 | } 55 | 56 | @Override 57 | public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { 58 | context.drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.title.controller_controls"), 59 | this.getX() + this.width / 2, this.getY() + 4, 16777215); 60 | this.resetButton.setActive(InputManager.streamBindings().anyMatch(Predicates.not(ButtonBinding::isDefault))); 61 | super.renderWidget(context, mouseX, mouseY, delta); 62 | } 63 | 64 | public void finishBindingEdit(int... buttons) { 65 | if (this.focusedBinding == null) return; 66 | MidnightControlsConfig.setButtonBinding(this.focusedBinding, buttons); 67 | this.focusedBinding = null; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/AbstractBlockAccessor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import net.minecraft.block.AbstractBlock; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.util.math.BlockPos; 6 | import net.minecraft.world.WorldView; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.gen.Invoker; 9 | 10 | @Mixin(AbstractBlock.class) 11 | public interface AbstractBlockAccessor { 12 | @Invoker("canPlaceAt") 13 | boolean midnightcontrols$canPlaceAt(BlockState state, WorldView world, BlockPos pos); 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/AbstractSignEditScreenMixin.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import eu.midnightdust.midnightcontrols.client.util.AbstractSignEditScreenAccessor; 4 | import net.minecraft.block.entity.SignBlockEntity; 5 | import net.minecraft.block.entity.SignText; 6 | import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen; 7 | import net.minecraft.text.Text; 8 | import org.spongepowered.asm.mixin.Final; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | 12 | @Mixin(AbstractSignEditScreen.class) 13 | public class AbstractSignEditScreenMixin implements AbstractSignEditScreenAccessor { 14 | @Shadow @Final private String[] messages; 15 | @Shadow private SignText text; 16 | @Shadow @Final protected SignBlockEntity blockEntity; 17 | @Shadow @Final private boolean front; 18 | 19 | @Override 20 | public String[] midnightcontrols$getMessages() { 21 | return messages; 22 | } 23 | 24 | @Override 25 | public void midnightcontrols$setMessage(int line, String text) { 26 | this.messages[line] = text; 27 | this.text = this.text.withMessage(line, Text.literal(text)); 28 | } 29 | 30 | @Override 31 | public void midnightcontrols$writeToBlockEntity() { 32 | this.blockEntity.setText(this.text, this.front); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/AdvancementsScreenAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import net.minecraft.advancement.Advancement; 13 | import net.minecraft.client.gui.screen.advancement.AdvancementTab; 14 | import net.minecraft.client.gui.screen.advancement.AdvancementsScreen; 15 | import net.minecraft.client.network.ClientAdvancementManager; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.gen.Accessor; 18 | 19 | import java.util.Map; 20 | 21 | /** 22 | * Represents an accessor of {@link AdvancementsScreen}. 23 | */ 24 | @Mixin(AdvancementsScreen.class) 25 | public interface AdvancementsScreenAccessor { 26 | @Accessor("advancementHandler") 27 | ClientAdvancementManager getAdvancementManager(); 28 | 29 | @Accessor("tabs") 30 | Map getTabs(); 31 | 32 | @Accessor("selectedTab") 33 | AdvancementTab getSelectedTab(); 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/BookEditScreenAccessor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import net.minecraft.client.gui.screen.ingame.BookEditScreen; 4 | import net.minecraft.client.util.SelectionManager; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | import org.spongepowered.asm.mixin.gen.Invoker; 8 | 9 | @Mixin(BookEditScreen.class) 10 | public interface BookEditScreenAccessor { 11 | @Accessor("signing") 12 | boolean midnightcontrols$isSigning(); 13 | 14 | @Accessor("title") 15 | String midnightcontrols$getTitle(); 16 | 17 | @Accessor("title") 18 | void midnightcontrols$setTitle(String title); 19 | 20 | @Accessor("currentPageSelectionManager") 21 | SelectionManager midnightcontrols$getCurrentPageSelectionManager(); 22 | 23 | @Invoker("getCurrentPageContent") 24 | String midnightcontrols$getCurrentPageContent(); 25 | 26 | @Invoker("setPageContent") 27 | void midnightcontrols$setPageContent(String newContent); 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/ChatScreenMixin.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 4 | import net.minecraft.client.gui.DrawContext; 5 | import net.minecraft.client.gui.screen.ChatScreen; 6 | import net.minecraft.client.gui.screen.Screen; 7 | import net.minecraft.client.gui.widget.TextFieldWidget; 8 | import net.minecraft.text.Text; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(ChatScreen.class) 16 | public abstract class ChatScreenMixin extends Screen { 17 | @Shadow protected TextFieldWidget chatField; 18 | 19 | protected ChatScreenMixin(Text title) { 20 | super(title); 21 | } 22 | 23 | @Inject(at = @At("TAIL"), method = "init") 24 | private void midnightcontrols$moveInputField(CallbackInfo ci) { 25 | if (MidnightControlsConfig.moveChat) chatField.setY(4); 26 | } 27 | @Inject(method = "render", at = @At("HEAD")) 28 | private void midnightcontrols$moveInputFieldBackground(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { 29 | if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, -this.height + 16, 0f); 30 | } 31 | @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/TextFieldWidget;render(Lnet/minecraft/client/gui/DrawContext;IIF)V", shift = At.Shift.BEFORE)) 32 | private void midnightcontrols$dontMoveOtherStuff(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { 33 | if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, this.height - 16, 0f); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/ClickableWidgetAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import net.minecraft.client.gui.widget.ClickableWidget; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.gen.Accessor; 15 | 16 | @Mixin(ClickableWidget.class) 17 | public interface ClickableWidgetAccessor { 18 | @Accessor("height") 19 | int getHeight(); 20 | @Accessor("focused") 21 | void setFocused(boolean value); 22 | } 23 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/ClientPlayerEntityMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import com.mojang.authlib.GameProfile; 13 | import eu.midnightdust.midnightcontrols.MidnightControls; 14 | import eu.midnightdust.midnightcontrols.client.MidnightControlsClient; 15 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 16 | import eu.midnightdust.midnightcontrols.client.controller.MovementHandler; 17 | import net.minecraft.client.MinecraftClient; 18 | import net.minecraft.client.input.Input; 19 | import net.minecraft.client.network.AbstractClientPlayerEntity; 20 | import net.minecraft.client.network.ClientPlayerEntity; 21 | import net.minecraft.client.world.ClientWorld; 22 | import net.minecraft.entity.MovementType; 23 | import net.minecraft.network.encryption.PlayerPublicKey; 24 | import net.minecraft.util.math.Vec3d; 25 | import org.jetbrains.annotations.Nullable; 26 | import org.spongepowered.asm.mixin.Final; 27 | import org.spongepowered.asm.mixin.Mixin; 28 | import org.spongepowered.asm.mixin.Shadow; 29 | import org.spongepowered.asm.mixin.Unique; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Inject; 32 | import org.spongepowered.asm.mixin.injection.Redirect; 33 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 34 | 35 | /** 36 | * Injects the anti fly drifting feature. 37 | */ 38 | @Mixin(ClientPlayerEntity.class) 39 | public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity { 40 | @Unique private boolean midnightcontrols$driftingPrevented = false; 41 | 42 | public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile) { 43 | super(world, profile); 44 | } 45 | 46 | @Shadow 47 | protected abstract boolean hasMovementInput(); 48 | 49 | @Shadow 50 | @Final 51 | protected MinecraftClient client; 52 | 53 | @Shadow 54 | public Input input; 55 | 56 | @Shadow 57 | protected abstract boolean isCamera(); 58 | 59 | @Shadow protected int ticksLeftToDoubleTapSprint; 60 | 61 | 62 | @Inject(method = "move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V")) 63 | public void onMove(MovementType type, Vec3d movement, CallbackInfo ci) { 64 | if (!MidnightControlsConfig.doubleTapToSprint) ticksLeftToDoubleTapSprint = 0; 65 | if (!MidnightControls.isExtrasLoaded) return; 66 | if (type == MovementType.SELF) { 67 | if (this.getAbilities().flying && (!MidnightControlsConfig.flyDrifting || !MidnightControlsConfig.verticalFlyDrifting)) { 68 | if (!this.hasMovementInput()) { 69 | if (!this.midnightcontrols$driftingPrevented) { 70 | if (!MidnightControlsConfig.flyDrifting) 71 | this.setVelocity(this.getVelocity().multiply(0, 1.0, 0)); 72 | } 73 | this.midnightcontrols$driftingPrevented = true; 74 | } else 75 | this.midnightcontrols$driftingPrevented = false; 76 | } 77 | } 78 | } 79 | 80 | @Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick()V", shift = At.Shift.AFTER)) 81 | public void onInputUpdate(CallbackInfo ci) { 82 | MovementHandler.HANDLER.applyMovement((ClientPlayerEntity) (Object) this); 83 | } 84 | 85 | @Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isCamera()Z")) 86 | public void onTickMovement(CallbackInfo ci) { 87 | if (this.getAbilities().flying && this.isCamera()) { 88 | if (MidnightControlsConfig.verticalFlyDrifting || !MidnightControls.isExtrasLoaded) 89 | return; 90 | int moving = 0; 91 | if (this.input.playerInput.sneak()) { 92 | --moving; 93 | } 94 | 95 | if (this.input.playerInput.jump()) { 96 | ++moving; 97 | } 98 | 99 | if (moving == 0) { 100 | this.setVelocity(this.getVelocity().multiply(1.0, 0.0, 1.0)); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/CreativeInventoryScreenAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; 13 | import net.minecraft.item.ItemGroup; 14 | import net.minecraft.item.ItemGroups; 15 | import net.minecraft.screen.slot.Slot; 16 | import org.jetbrains.annotations.NotNull; 17 | import org.jetbrains.annotations.Nullable; 18 | import org.spongepowered.asm.mixin.Mixin; 19 | import org.spongepowered.asm.mixin.gen.Accessor; 20 | import org.spongepowered.asm.mixin.gen.Invoker; 21 | 22 | /** 23 | * Represents an accessor to CreativeInventoryScreen. 24 | */ 25 | @Mixin(CreativeInventoryScreen.class) 26 | public interface CreativeInventoryScreenAccessor { 27 | /** 28 | * Gets the selected tab. 29 | * 30 | * @return the selected tab index 31 | */ 32 | @Accessor("selectedTab") 33 | static ItemGroup getSelectedTab() { 34 | return ItemGroups.getDefaultTab(); 35 | } 36 | 37 | /** 38 | * Sets the selected tab. 39 | * 40 | * @param group the tab's item group 41 | */ 42 | @Invoker("setSelectedTab") 43 | void midnightcontrols$setSelectedTab(@NotNull ItemGroup group); 44 | 45 | /** 46 | * Returns whether the slot belongs to the creative inventory or not. 47 | * 48 | * @param slot the slot to check 49 | * @return true if the slot is from the creative inventory, else false 50 | */ 51 | @Invoker("isCreativeInventorySlot") 52 | boolean midnightcontrols$isCreativeInventorySlot(@Nullable Slot slot); 53 | 54 | /** 55 | * Returns whether the current tab has a scrollbar or not. 56 | * 57 | * @return true if the current tab has a scrollbar, else false 58 | */ 59 | @Invoker("hasScrollbar") 60 | boolean midnightcontrols$hasScrollbar(); 61 | 62 | /** 63 | * Triggers searching the creative inventory from the current value of the internal {@link net.minecraft.client.gui.widget.TextFieldWidget} 64 | */ 65 | @Invoker("search") 66 | void midnightcontrols$search(); 67 | } 68 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/DrawContextAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import net.minecraft.client.gui.DrawContext; 13 | import net.minecraft.client.render.VertexConsumerProvider; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.gen.Accessor; 16 | 17 | @Mixin(DrawContext.class) 18 | public interface DrawContextAccessor { 19 | @Accessor("vertexConsumers") 20 | VertexConsumerProvider.Immediate getVertexConsumers(); 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/GameOptionsScreenMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen; 13 | import net.minecraft.client.MinecraftClient; 14 | import net.minecraft.client.gui.screen.Screen; 15 | import net.minecraft.client.gui.screen.option.ControlsOptionsScreen; 16 | import net.minecraft.client.gui.screen.option.GameOptionsScreen; 17 | import net.minecraft.client.gui.widget.OptionListWidget; 18 | import net.minecraft.client.gui.widget.TextIconButtonWidget; 19 | import net.minecraft.text.Text; 20 | import org.jetbrains.annotations.Nullable; 21 | import org.spongepowered.asm.mixin.Mixin; 22 | import org.spongepowered.asm.mixin.Shadow; 23 | import org.spongepowered.asm.mixin.Unique; 24 | import org.spongepowered.asm.mixin.injection.At; 25 | import org.spongepowered.asm.mixin.injection.Inject; 26 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 27 | 28 | import static eu.midnightdust.midnightcontrols.MidnightControls.id; 29 | 30 | /** 31 | * Injects the new controls settings button. 32 | */ 33 | @Mixin(GameOptionsScreen.class) 34 | public abstract class GameOptionsScreenMixin extends Screen { 35 | @Shadow @Nullable protected OptionListWidget body; 36 | @Unique TextIconButtonWidget midnightcontrols$button = TextIconButtonWidget.builder(Text.translatable("midnightcontrols.menu.title.controller"), 37 | (button -> this.client.setScreen(new MidnightControlsSettingsScreen(this, false))), true) 38 | .dimension(20,20).texture(id("icon/controller"), 20, 20).build(); 39 | 40 | protected GameOptionsScreenMixin(Text title) { 41 | super(title); 42 | } 43 | 44 | @Inject(method = "initBody", at = @At("TAIL")) 45 | public void midnightcontrols$addMCButton(CallbackInfo ci) { 46 | if (this.getClass().toString().equals(ControlsOptionsScreen.class.toString())) { 47 | this.midnightcontrols$setButtonPos(); 48 | this.addSelectableChild(midnightcontrols$button); 49 | } 50 | } 51 | @Inject(method = "init", at = @At("TAIL")) 52 | public void midnightcontrols$drawMCButton(CallbackInfo ci) { 53 | if (this.getClass().toString().equals(ControlsOptionsScreen.class.toString())) { 54 | this.addDrawableChild(midnightcontrols$button); 55 | } 56 | } 57 | 58 | @Inject(method = "refreshWidgetPositions", at = @At("TAIL")) 59 | public void midnightcontrols$onResize(CallbackInfo ci) { 60 | this.midnightcontrols$setButtonPos(); 61 | } 62 | @Unique 63 | public void midnightcontrols$setButtonPos() { 64 | if (body != null) { 65 | midnightcontrols$button.setPosition(body.getWidth() / 2 + 158, body.getY() + 4); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/GameRendererMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import com.llamalad7.mixinextras.sugar.Local; 13 | import com.mojang.blaze3d.systems.RenderSystem; 14 | import eu.midnightdust.midnightcontrols.ControlsMode; 15 | import eu.midnightdust.midnightcontrols.client.MidnightControlsClient; 16 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 17 | import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer; 18 | import eu.midnightdust.midnightcontrols.client.touch.TouchUtils; 19 | import net.minecraft.client.MinecraftClient; 20 | import net.minecraft.client.gui.DrawContext; 21 | import net.minecraft.client.render.GameRenderer; 22 | import net.minecraft.client.render.RenderTickCounter; 23 | import org.joml.Matrix4f; 24 | import org.spongepowered.asm.mixin.Final; 25 | import org.spongepowered.asm.mixin.Mixin; 26 | import org.spongepowered.asm.mixin.Shadow; 27 | import org.spongepowered.asm.mixin.injection.At; 28 | import org.spongepowered.asm.mixin.injection.Inject; 29 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 30 | 31 | @Mixin(GameRenderer.class) 32 | public abstract class GameRendererMixin { 33 | @Shadow @Final private MinecraftClient client; 34 | 35 | @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Mouse;getScaledX(Lnet/minecraft/client/util/Window;)D", shift = At.Shift.BEFORE)) 36 | private void midnightcontrols$onRender(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci) { 37 | if (this.client.currentScreen != null && MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER) 38 | MidnightControlsClient.input.onPreRenderScreen(this.client.currentScreen); 39 | } 40 | @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;draw()V", shift = At.Shift.BEFORE)) 41 | private void midnightcontrols$renderVirtualCursor(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci, @Local DrawContext drawContext) { 42 | MidnightControlsRenderer.renderVirtualCursor(drawContext, client); 43 | if (MidnightControlsClient.isWayland) MidnightControlsRenderer.renderWaylandCursor(drawContext, client); 44 | drawContext.draw(); 45 | } 46 | @Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/GameRenderer;renderHand:Z"), method = "renderWorld") 47 | private void midnigtcontrols$captureMatrices(RenderTickCounter tickCounter, CallbackInfo ci, @Local(ordinal = 2) Matrix4f matrices) { 48 | TouchUtils.lastProjMat.set(RenderSystem.getProjectionMatrix()); 49 | TouchUtils.lastModMat.set(RenderSystem.getModelViewMatrix()); 50 | TouchUtils.lastWorldSpaceMatrix.set(matrices); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/HandledScreenMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import eu.midnightdust.lib.util.PlatformFunctions; 13 | import eu.midnightdust.midnightcontrols.ControlsMode; 14 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 15 | import eu.midnightdust.midnightcontrols.client.compat.EMICompat; 16 | import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat; 17 | import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; 18 | import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer; 19 | import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor; 20 | import net.minecraft.client.MinecraftClient; 21 | import net.minecraft.client.gui.DrawContext; 22 | import net.minecraft.client.gui.screen.ingame.HandledScreen; 23 | import net.minecraft.screen.slot.Slot; 24 | import net.minecraft.screen.slot.SlotActionType; 25 | import net.minecraft.text.Text; 26 | import org.jetbrains.annotations.Nullable; 27 | import org.spongepowered.asm.mixin.Mixin; 28 | import org.spongepowered.asm.mixin.gen.Accessor; 29 | import org.spongepowered.asm.mixin.gen.Invoker; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Inject; 32 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 33 | 34 | /** 35 | * Represents the mixin for the class ContainerScreen. 36 | */ 37 | @Mixin(HandledScreen.class) 38 | public abstract class HandledScreenMixin implements HandledScreenAccessor { 39 | @Accessor("x") 40 | public abstract int getX(); 41 | 42 | @Accessor("y") 43 | public abstract int getY(); 44 | 45 | @Invoker("getSlotAt") 46 | public abstract Slot midnightcontrols$getSlotAt(double posX, double posY); 47 | 48 | @Invoker("isClickOutsideBounds") 49 | public abstract boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int x, int y, int button); 50 | 51 | 52 | @Invoker("onMouseClick") 53 | public abstract void midnightcontrols$onMouseClick(@Nullable Slot slot, int slotId, int clickData, SlotActionType actionType); 54 | 55 | @Inject(method = "render", at = @At("RETURN")) 56 | public void onRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { 57 | if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.hudEnable) { 58 | var client = MinecraftClient.getInstance(); 59 | int x = 2, y = client.getWindow().getScaledHeight() - 2 - MidnightControlsRenderer.ICON_SIZE; 60 | if (PlatformFunctions.isModLoaded("emi") && EMICompat.isEMIEnabled()) { 61 | x += 42; 62 | } 63 | if (!ButtonBinding.TAKE_ALL.isNotBound()) x = MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.TAKE_ALL,true, client) + 2; 64 | if (!ButtonBinding.EXIT.isNotBound()) x = MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.EXIT, true, client) + 2; 65 | if (PlatformFunctions.isModLoaded("roughlyenoughitems")) { 66 | x = 2; 67 | y -= 24; 68 | } 69 | if (PlatformFunctions.isModLoaded("emi") && EMICompat.isEMIEnabled() && EMICompat.isSearchBarCentered()) { 70 | x = client.getWindow().getScaledWidth() - 4 - client.textRenderer.getWidth(Text.translatable("midnightcontrols.action.pickup")) 71 | - client.textRenderer.getWidth(Text.translatable("midnightcontrols.action.quick_move")) 72 | - 2 * MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.TAKE) - MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.QUICK_MOVE); 73 | y += 2; 74 | } 75 | if (!ButtonBinding.TAKE.isNotBound()) x = MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.TAKE, true, client); 76 | if (!ButtonBinding.QUICK_MOVE.isNotBound()) MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.QUICK_MOVE, true, client); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/InputAccessor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import net.minecraft.client.input.Input; 4 | import net.minecraft.util.math.Vec2f; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(Input.class) 9 | public interface InputAccessor { 10 | @Accessor 11 | void setMovementVector(Vec2f input); 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/InputUtilMixin.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import net.minecraft.client.util.InputUtil; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 9 | 10 | @Mixin(InputUtil.class) 11 | public abstract class InputUtilMixin { 12 | 13 | /** 14 | * @author kabliz 15 | * @reason This method is static, and there is a terrible UX issue if raw input is turned on at the same time as 16 | * eye tracking. Raw input only tracks literal mice and not other devices, leading to the game appearing to be 17 | * unresponsive and the player not understanding why. This overwrite preserves the user's mouse preferences, 18 | * while not interfering with eye tracking, and the two modes can be switched between during a play session. 19 | */ 20 | @Inject(method = "isRawMouseMotionSupported", at = @At("HEAD"), cancellable = true) 21 | private static void setRawMouseMotionSupported(CallbackInfoReturnable cir) { 22 | if (MidnightControlsConfig.eyeTrackerAsMouse) cir.setReturnValue(false); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/KeyBindingIDAccessor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import net.minecraft.client.option.KeyBinding; 4 | import org.spongepowered.asm.mixin.Final; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import java.util.Map; 9 | 10 | @Mixin(KeyBinding.class) 11 | public interface KeyBindingIDAccessor { 12 | @Accessor @Final 13 | static Map getKEYS_BY_ID() {return null;}; 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/KeyBindingMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor; 13 | import net.minecraft.client.option.KeyBinding; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.Shadow; 16 | 17 | @Mixin(KeyBinding.class) 18 | public abstract class KeyBindingMixin implements KeyBindingAccessor { 19 | @Shadow 20 | private int timesPressed; 21 | 22 | @Shadow 23 | private boolean pressed; 24 | 25 | @Override 26 | public boolean midnightcontrols$press() { 27 | boolean oldPressed = this.pressed; 28 | if (!this.pressed) 29 | this.pressed = true; 30 | ++this.timesPressed; 31 | return !oldPressed; 32 | } 33 | 34 | @Override 35 | public boolean midnightcontrols$unpress() { 36 | if (this.pressed) { 37 | this.pressed = false; 38 | return true; 39 | } 40 | return false; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/KeyboardMixin.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay; 4 | import net.minecraft.client.Keyboard; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.gui.screen.Screen; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Redirect; 10 | 11 | @Mixin(Keyboard.class) 12 | public class KeyboardMixin { 13 | @Redirect(method = "onKey", at = @At(value = "FIELD", target = "Lnet/minecraft/client/MinecraftClient;currentScreen:Lnet/minecraft/client/gui/screen/Screen;")) 14 | private Screen midnightcontrols$ignoreTouchOverlay(MinecraftClient instance) { 15 | if (instance.currentScreen instanceof TouchscreenOverlay) return null; 16 | return instance.currentScreen; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/MouseAccessor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import net.minecraft.client.Mouse; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(Mouse.class) 8 | public interface MouseAccessor { 9 | @Invoker("onCursorPos") 10 | void midnightcontrols$onCursorPos(long window, double x, double y); 11 | @Invoker("onMouseButton") 12 | void midnightcontrols$onMouseButton(long window, int button, int action, int mods); 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/RecipeBookScreenAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import net.minecraft.client.gui.screen.ingame.RecipeBookScreen; 13 | import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.gen.Accessor; 16 | 17 | @Mixin(RecipeBookScreen.class) 18 | public interface RecipeBookScreenAccessor { 19 | @Accessor("recipeBook") 20 | RecipeBookWidget getRecipeBook(); 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/RecipeBookWidgetAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.mixin; 11 | 12 | import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; 13 | import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.gen.Accessor; 16 | import org.spongepowered.asm.mixin.gen.Invoker; 17 | 18 | import java.util.List; 19 | 20 | @Mixin(RecipeBookWidget.class) 21 | public interface RecipeBookWidgetAccessor { 22 | @Accessor("tabButtons") 23 | List getTabButtons(); 24 | 25 | @Accessor("currentTab") 26 | RecipeGroupButtonWidget getCurrentTab(); 27 | 28 | @Accessor("currentTab") 29 | void setCurrentTab(RecipeGroupButtonWidget currentTab); 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/ScreenMixin.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import org.thinkingstudio.obsidianui.Position; 4 | import eu.midnightdust.midnightcontrols.ControlsMode; 5 | import eu.midnightdust.midnightcontrols.client.enums.ButtonState; 6 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 7 | import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; 8 | import eu.midnightdust.midnightcontrols.client.controller.InputHandlers; 9 | import eu.midnightdust.midnightcontrols.client.touch.gui.SilentTexturedButtonWidget; 10 | import net.minecraft.client.MinecraftClient; 11 | import net.minecraft.client.gui.Drawable; 12 | import net.minecraft.client.gui.Element; 13 | import net.minecraft.client.gui.Selectable; 14 | import net.minecraft.client.gui.screen.Screen; 15 | import net.minecraft.client.gui.screen.ingame.HandledScreen; 16 | import net.minecraft.text.Text; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.Shadow; 19 | import org.spongepowered.asm.mixin.injection.At; 20 | import org.spongepowered.asm.mixin.injection.Inject; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 22 | 23 | import static eu.midnightdust.midnightcontrols.client.touch.gui.TouchscreenOverlay.WIDGETS_LOCATION; 24 | 25 | @Mixin(Screen.class) 26 | public abstract class ScreenMixin { 27 | @Shadow protected abstract T addDrawableChild(T drawableElement); 28 | 29 | @Shadow public int width; 30 | 31 | @Inject(method = "init(Lnet/minecraft/client/MinecraftClient;II)V", at = @At("TAIL")) 32 | public void midnightcontrols$addCloseButton(MinecraftClient client, int width, int height, CallbackInfo ci) { 33 | if (MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN && (MidnightControlsConfig.closeButtonScreens.stream().anyMatch(s -> this.getClass().getName().startsWith(s) || ((Object)this) instanceof HandledScreen))) { 34 | this.addDrawableChild(new SilentTexturedButtonWidget(Position.of(this.width - 30, 10), 20, 20, Text.empty(), btn -> 35 | InputHandlers.handleExit().press(client, ButtonBinding.BACK, 0f, ButtonState.PRESS), 20, 160, 20, WIDGETS_LOCATION)); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mixin/TabNavigationWidgetAccessor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mixin; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.minecraft.client.gui.tab.Tab; 5 | import net.minecraft.client.gui.tab.TabManager; 6 | import net.minecraft.client.gui.widget.TabNavigationWidget; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.gen.Accessor; 9 | 10 | @Mixin(TabNavigationWidget.class) 11 | public interface TabNavigationWidgetAccessor { 12 | @Accessor 13 | TabManager getTabManager(); 14 | @Accessor 15 | ImmutableList getTabs(); 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/mouse/EyeTrackerHandler.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.mouse; 2 | 3 | import net.minecraft.client.MinecraftClient; 4 | import net.minecraft.client.util.GlfwUtil; 5 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 6 | import net.minecraft.util.math.Smoother; 7 | 8 | public class EyeTrackerHandler { 9 | 10 | /** 11 | * Based on the updateMouse method in the Mouse.class, this changes the mouse algorithm to suit eye tracking. 12 | * This requires the cursor to not be locked, and the raw input setting to be turned off. 13 | */ 14 | public static void updateMouseWithEyeTracking(double mouseX, double mouseY, MinecraftClient client, double lastMouseUpdateTime, boolean holdingLeftMouseButton, boolean usingLongRangedTool, Smoother smoothX, Smoother smoothY) { 15 | if (client.player == null) return; 16 | /* The player wants objects of interest to be moved under the crosshair that is always center of screen. 17 | * Normal mouse controls operate with the delta values from the direction of mouse movement, 18 | * but in eye tracking we want to use the cursor's actual x,y values (their point of gaze), relative to 19 | * the screen center (where the crosshair is). This new eye tracking delta creates a vector that points 20 | * from the crosshair to the gaze point. As the player keeps their eyes on the object of interest, we pull 21 | * that object into the center until the object is underneath the crosshair. 22 | */ 23 | double deltaTime = GlfwUtil.getTime() - lastMouseUpdateTime; 24 | 25 | // The center of screen is the new (0,0) 26 | double centerX = client.getWindow().getWidth() / 2.0; 27 | double centerY = client.getWindow().getHeight() / 2.0; 28 | double gazeRawX = mouseX - centerX; 29 | double gazeRawY = mouseY - centerY; 30 | 31 | //This part follows the original mouse.java somewhat closely, with different constants 32 | double feeling = 2.5; 33 | double sensitivity = client.options.getMouseSensitivity().getValue() * feeling; 34 | double spyglass = sensitivity * sensitivity * sensitivity; 35 | double moveScalar = spyglass * 8.0; 36 | 37 | double frameScalar; 38 | if(client.options.getPerspective().isFirstPerson() && client.player.isUsingSpyglass()) { 39 | frameScalar = spyglass; 40 | } else { 41 | frameScalar = moveScalar; 42 | } 43 | if(holdingLeftMouseButton && !usingLongRangedTool) { 44 | frameScalar *= 0.5; //Don't move the camera so much while mining. It's annoying. 45 | } 46 | 47 | // The longest vector connects the center to the corner of the screen, so that is our maximum magnitude for 48 | // normalization. We use normalized screen size vector for resolution independent control 49 | double magnitudeMax = Math.sqrt(centerX*centerX + centerY*centerY); 50 | double normalizedX = gazeRawX / magnitudeMax; 51 | double normalizedY = gazeRawY / magnitudeMax; 52 | 53 | double moveX = normalizedX * frameScalar; 54 | double moveY = normalizedY * frameScalar; 55 | if (client.options.smoothCameraEnabled) { 56 | moveX = smoothX.smooth(moveX, moveScalar*deltaTime); 57 | moveY = smoothY.smooth(moveY, moveScalar*deltaTime); 58 | } 59 | 60 | // The player entity's needs their facing rotated. 61 | double invertY = 1.0; 62 | double moveMagnitude = Math.sqrt(normalizedX*normalizedX + normalizedY*normalizedY); 63 | if (client.options.getInvertYMouse().getValue()) { 64 | invertY = -1.0; 65 | } 66 | boolean notInDeadzone = (moveMagnitude > MidnightControlsConfig.eyeTrackerDeadzone) && !usingLongRangedTool; 67 | if (client.player != null && notInDeadzone) { 68 | client.player.changeLookDirection(moveX, moveY * invertY); 69 | client.getTutorialManager().onUpdateMouse(moveX, moveY); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/ring/ButtonBindingRingAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.ring; 11 | 12 | import com.google.gson.Gson; 13 | import eu.midnightdust.midnightcontrols.client.enums.ButtonState; 14 | import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; 15 | import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor; 16 | import net.minecraft.client.MinecraftClient; 17 | import net.minecraft.client.font.TextRenderer; 18 | import net.minecraft.client.gui.DrawContext; 19 | import net.minecraft.client.gui.screen.Screen; 20 | import net.minecraft.text.OrderedText; 21 | import net.minecraft.text.Text; 22 | import org.jetbrains.annotations.NotNull; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | import java.util.List; 26 | import java.util.function.Supplier; 27 | 28 | public class ButtonBindingRingAction extends RingAction { 29 | public static final Factory FACTORY = new Factory(); 30 | public final ButtonBinding binding; 31 | 32 | public ButtonBindingRingAction(@NotNull ButtonBinding binding) { 33 | super(); 34 | this.binding = binding; 35 | } 36 | 37 | public @NotNull String getName() { 38 | return this.binding.getTranslationKey(); 39 | } 40 | 41 | @Override 42 | public void onAction(@NotNull RingButtonMode mode) { 43 | binding.handle(MinecraftClient.getInstance(), 1.0f, ButtonState.PRESS); 44 | if (binding.asKeyBinding().isPresent()) { 45 | binding.asKeyBinding().get().setPressed(true); 46 | ((KeyBindingAccessor)binding.asKeyBinding().get()).midnightcontrols$press(); 47 | } 48 | } 49 | 50 | @Override 51 | public void drawIcon(@NotNull DrawContext context, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered) { 52 | List lines = textRenderer.wrapLines(Text.translatable(this.getName()), MidnightRing.ELEMENT_SIZE); 53 | for (int i = 0; i < lines.size(); ++i) { 54 | context.drawCenteredTextWithShadow(textRenderer, lines.get(i), x + MidnightRing.ELEMENT_SIZE / 2, y + MidnightRing.ELEMENT_SIZE / 2 - textRenderer.fontHeight / 2 * (lines.size()-1) - textRenderer.fontHeight / 2 + textRenderer.fontHeight * i, 0xffffff); 55 | } 56 | } 57 | 58 | protected static class Factory implements RingAction.Factory { 59 | @Override 60 | public @NotNull Supplier newFromGui(@NotNull Screen screen) { 61 | return () -> null; 62 | } 63 | 64 | @Override 65 | public @Nullable RingAction parse(@NotNull Gson config) { 66 | return null; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/ring/MidnightRing.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.ring; 11 | 12 | import eu.midnightdust.midnightcontrols.MidnightControls; 13 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 14 | import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; 15 | import eu.midnightdust.midnightcontrols.client.controller.InputManager; 16 | import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; 17 | import net.minecraft.util.math.MathHelper; 18 | import org.jetbrains.annotations.NotNull; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | /** 26 | * Represents a key binding ring. 27 | * 28 | * @author LambdAurora 29 | * @version 1.7.0 30 | * @since 1.4.0 31 | */ 32 | public final class MidnightRing { 33 | public static final int ELEMENT_SIZE = 75; 34 | 35 | private final Map actionFactories = new Object2ObjectOpenHashMap<>(); 36 | private final List pages = new ArrayList<>(Collections.singletonList(RingPage.DEFAULT)); 37 | private int currentPage = 0; 38 | 39 | public MidnightRing() { 40 | } 41 | 42 | public void registerAction(@NotNull String name, @NotNull RingAction.Factory factory) { 43 | if (this.actionFactories.containsKey(name)) { 44 | MidnightControls.warn("Tried to register a ring action twice: \"" + name + "\"."); 45 | return; 46 | } 47 | this.actionFactories.put(name, factory); 48 | } 49 | 50 | /** 51 | * Loads the ring from configuration. 52 | */ 53 | public void loadFromConfig() { 54 | List configBindings = MidnightControlsConfig.ringBindings; 55 | if (configBindings != null) { 56 | this.pages.clear(); 57 | int bindingIndex = 0; 58 | for (int i = 0; i < MathHelper.ceil(configBindings.size() / 8f); ++i) { 59 | this.pages.add(new RingPage(i+1 + " / " + MathHelper.ceil(configBindings.size() / 8f))); 60 | } 61 | 62 | for (String binding : configBindings) { 63 | ButtonBinding buttonBinding = InputManager.getBinding(binding); 64 | if (buttonBinding != null) { 65 | RingPage page = this.pages.get(MathHelper.floor(bindingIndex / 8f)); 66 | page.actions[bindingIndex - 8 * (MathHelper.floor(bindingIndex / 8f))] = (new ButtonBindingRingAction(buttonBinding)); 67 | ++bindingIndex; 68 | } 69 | } 70 | } 71 | if (this.pages.isEmpty()) { 72 | this.pages.add(RingPage.DEFAULT); 73 | } 74 | } 75 | /** 76 | * Loads the ring from all unbound keys. 77 | */ 78 | public void loadFromUnbound() { 79 | List unboundBindings = InputManager.getUnboundBindings(); 80 | if (unboundBindings != null) { 81 | this.pages.clear(); 82 | int bindingIndex = 0; 83 | for (int i = 0; i < MathHelper.ceil(unboundBindings.size() / 8f); ++i) { 84 | this.pages.add(new RingPage(i+1 + " / " + MathHelper.ceil(unboundBindings.size() / 8f))); 85 | } 86 | 87 | for (ButtonBinding buttonBinding : unboundBindings) { 88 | if (buttonBinding != null) { 89 | RingPage page = this.pages.get(MathHelper.floor(bindingIndex / 8f)); 90 | page.actions[bindingIndex - 8 * (MathHelper.floor(bindingIndex / 8f))] = (new ButtonBindingRingAction(buttonBinding)); 91 | ++bindingIndex; 92 | } 93 | } 94 | } 95 | if (this.pages.isEmpty()) { 96 | this.pages.add(RingPage.DEFAULT); 97 | } 98 | } 99 | public int getMaxPages() { 100 | return this.pages.size(); 101 | } 102 | 103 | public @NotNull RingPage getCurrentPage() { 104 | if (this.currentPage >= this.pages.size()) 105 | this.currentPage = this.pages.size() - 1; 106 | else if (this.currentPage < 0) 107 | this.currentPage = 0; 108 | return this.pages.get(this.currentPage); 109 | } 110 | public void cyclePage(boolean forwards) { 111 | if (forwards) { 112 | if (currentPage < pages.size()-1) ++currentPage; 113 | else currentPage = 0; 114 | } else { 115 | if (currentPage > 0) --currentPage; 116 | else currentPage = pages.size(); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/ring/RingAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.ring; 11 | 12 | import com.google.gson.Gson; 13 | import net.minecraft.client.font.TextRenderer; 14 | import net.minecraft.client.gui.DrawContext; 15 | import net.minecraft.client.gui.screen.Screen; 16 | import net.minecraft.text.Text; 17 | import org.aperlambda.lambdacommon.utils.Nameable; 18 | import org.jetbrains.annotations.NotNull; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import java.util.function.Supplier; 22 | 23 | /** 24 | * Represents a ring action. 25 | * 26 | * @author LambdAurora 27 | * @version 1.5.0 28 | * @since 1.4.0 29 | */ 30 | public abstract class RingAction { 31 | protected boolean activated = false; 32 | 33 | public RingAction() { 34 | } 35 | 36 | /** 37 | * Returns whether the action is activated or not. 38 | * 39 | * @return true if the action is activated, else false 40 | */ 41 | public boolean isActivated() { 42 | return this.activated; 43 | } 44 | 45 | public void activate(@NotNull RingButtonMode mode) { 46 | this.activated = !this.activated; 47 | 48 | this.onAction(mode); 49 | } 50 | 51 | public abstract void onAction(@NotNull RingButtonMode mode); 52 | 53 | public void render(@NotNull DrawContext context, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered, int index) { 54 | context.fill(x, y, x + MidnightRing.ELEMENT_SIZE, y + MidnightRing.ELEMENT_SIZE, hovered || RingPage.selected == index ? 0xbb777777 : 0xbb000000); 55 | drawIcon(context, textRenderer, x, y, hovered); 56 | } 57 | 58 | public abstract void drawIcon(@NotNull DrawContext context, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered); 59 | 60 | /** 61 | * Represents a factory for {@link RingAction}. 62 | * 63 | * @version 1.4.3 64 | * @since 1.4.3 65 | */ 66 | public interface Factory { 67 | @NotNull Supplier newFromGui(@NotNull Screen screen); 68 | 69 | @Nullable RingAction parse(@NotNull Gson config); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/ring/RingButtonMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.ring; 11 | 12 | import net.minecraft.text.Text; 13 | import org.aperlambda.lambdacommon.utils.Nameable; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | /** 17 | * Represents the mode of a ring button. 18 | * 19 | * @author LambdAurora 20 | * @version 1.4.0 21 | * @since 1.4.0 22 | */ 23 | public enum RingButtonMode implements Nameable { 24 | PRESS("press"), 25 | HOLD("hold"), 26 | TOGGLE("toggle"); 27 | 28 | private final String name; 29 | private final Text text; 30 | 31 | RingButtonMode(@NotNull String name) { 32 | this.name = name; 33 | this.text = Text.translatable(this.getTranslationKey()); 34 | } 35 | 36 | /** 37 | * Returns the next ring button mode available. 38 | * 39 | * @return the next ring button mode 40 | */ 41 | public @NotNull RingButtonMode next() { 42 | var v = values(); 43 | if (v.length == this.ordinal() + 1) 44 | return v[0]; 45 | return v[this.ordinal() + 1]; 46 | } 47 | 48 | /** 49 | * Returns the translation key of this ring button mode. 50 | * 51 | * @return the translation key of this ring button mode 52 | */ 53 | public @NotNull String getTranslationKey() { 54 | return "midnightcontrols.ring.button_mode." + this.getName(); 55 | } 56 | 57 | /** 58 | * Gets the translated name of this ring button mode. 59 | * 60 | * @return the translated name of this ring button mode 61 | */ 62 | public @NotNull Text getTranslatedText() { 63 | return this.text; 64 | } 65 | 66 | @Override 67 | public @NotNull String getName() { 68 | return this.name; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/touch/TouchUtils.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.touch; 2 | 3 | import eu.midnightdust.lib.util.PlatformFunctions; 4 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 5 | import eu.midnightdust.midnightcontrols.client.enums.TouchMode; 6 | import net.minecraft.client.MinecraftClient; 7 | import net.minecraft.client.render.Camera; 8 | import net.minecraft.entity.projectile.ProjectileUtil; 9 | import net.minecraft.item.ItemStack; 10 | import net.minecraft.item.consume.UseAction; 11 | import net.minecraft.util.hit.BlockHitResult; 12 | import net.minecraft.util.hit.EntityHitResult; 13 | import net.minecraft.util.hit.HitResult; 14 | import net.minecraft.util.math.Box; 15 | import net.minecraft.util.math.Vec3d; 16 | import net.minecraft.world.RaycastContext; 17 | import org.joml.Matrix4f; 18 | import org.joml.Vector3f; 19 | import org.lwjgl.opengl.GL11; 20 | 21 | import static eu.midnightdust.midnightcontrols.client.MidnightReacharound.getPlayerRange; 22 | 23 | public class TouchUtils { 24 | private static final MinecraftClient client = MinecraftClient.getInstance(); 25 | public static final Matrix4f lastWorldSpaceMatrix = new Matrix4f(); 26 | public static final Matrix4f lastProjMat = new Matrix4f(); 27 | public static final Matrix4f lastModMat = new Matrix4f(); 28 | 29 | public static HitResult getTargetedObject(double mouseX, double mouseY) { 30 | if (client.player == null || client.world == null || MidnightControlsConfig.touchMode == TouchMode.CROSSHAIR || PlatformFunctions.isModLoaded("vulkanmod")) { 31 | return client.crosshairTarget; 32 | } 33 | Vec3d near = screenSpaceToWorldSpace(mouseX, mouseY, 0); 34 | Vec3d far = screenSpaceToWorldSpace(mouseX, mouseY, 1); 35 | 36 | float playerRange = getPlayerRange(client); 37 | EntityHitResult entityCast = ProjectileUtil.raycast(client.player, near, far, Box.from(client.player.getPos()).expand(playerRange), entity -> (!entity.isSpectator() && entity.isAttackable()), playerRange * playerRange); 38 | 39 | if (entityCast != null && entityCast.getType() == HitResult.Type.ENTITY) return entityCast; 40 | 41 | BlockHitResult result = client.world.raycast(new RaycastContext(near, far, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, client.player)); 42 | 43 | if (client.player.getPos().distanceTo(result.getPos()) > playerRange) return null; 44 | return result; 45 | } 46 | 47 | /* Taken from https://github.com/0x3C50/Renderer/blob/master/src/main/java/me/x150/renderer/util/RendererUtils.java#L270 48 | * Credits to 0x3C50 */ 49 | public static Vec3d screenSpaceToWorldSpace(double x, double y, double d) { 50 | Camera camera = client.getEntityRenderDispatcher().camera; 51 | int displayHeight = client.getWindow().getScaledHeight(); 52 | int displayWidth = client.getWindow().getScaledWidth(); 53 | int[] viewport = new int[4]; 54 | GL11.glGetIntegerv(GL11.GL_VIEWPORT, viewport); 55 | Vector3f target = new Vector3f(); 56 | 57 | Matrix4f matrixProj = new Matrix4f(lastProjMat); 58 | Matrix4f matrixModel = new Matrix4f(lastModMat); 59 | 60 | matrixProj.mul(matrixModel) 61 | .mul(lastWorldSpaceMatrix) 62 | .unproject((float) x / displayWidth * viewport[2], 63 | (float) (displayHeight - y) / displayHeight * viewport[3], (float) d, viewport, target); 64 | 65 | return new Vec3d(target.x, target.y, target.z).add(camera.getPos()); 66 | } 67 | 68 | public static boolean hasInWorldUseAction(ItemStack stack) { 69 | UseAction action = stack.getUseAction(); 70 | return action == UseAction.BOW || action == UseAction.BRUSH || action == UseAction.SPEAR; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/touch/gui/ItemUseButtonWidget.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.touch.gui; 2 | 3 | import net.minecraft.component.DataComponentTypes; 4 | import net.minecraft.item.consume.UseAction; 5 | import org.thinkingstudio.obsidianui.Position; 6 | import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget; 7 | import eu.midnightdust.midnightcontrols.MidnightControlsConstants; 8 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 9 | import net.minecraft.text.Text; 10 | 11 | public class ItemUseButtonWidget extends SpruceButtonWidget { 12 | 13 | public ItemUseButtonWidget(Position position, int width, int height, Text message, PressAction action) { 14 | super(position, width, height, message, action); 15 | } 16 | @Override 17 | protected void onRelease(double mouseX, double mouseY) { 18 | assert client.player != null; 19 | assert client.interactionManager != null; 20 | UseAction action = client.player.getMainHandStack().getUseAction(); 21 | if (action == UseAction.SPYGLASS || action == UseAction.TOOT_HORN) client.interactionManager.stopUsingItem(client.player); 22 | super.onRelease(mouseX, mouseY); 23 | } 24 | 25 | @Override 26 | public void setVisible(boolean visible) { 27 | if (visible && client.player != null && client.player.getMainHandStack() != null) { 28 | UseAction action = client.player.getMainHandStack().getUseAction(); 29 | if (action == UseAction.EAT) { 30 | this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.eat")); 31 | } else if (action == UseAction.DRINK) { 32 | this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.drink")); 33 | } else if (client.player.getMainHandStack().getComponents().contains(DataComponentTypes.EQUIPPABLE)) { 34 | this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.equip")); 35 | } else if (!action.equals(UseAction.NONE)) { 36 | this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.use")); 37 | } 38 | } 39 | this.setAlpha(MidnightControlsConfig.touchTransparency / 100f); 40 | super.setVisible(visible); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/touch/gui/SilentTexturedButtonWidget.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.touch.gui; 2 | 3 | import eu.midnightdust.midnightcontrols.client.touch.TouchInput; 4 | import org.thinkingstudio.obsidianui.Position; 5 | import org.thinkingstudio.obsidianui.widget.SpruceTexturedButtonWidget; 6 | import net.minecraft.text.Text; 7 | import net.minecraft.util.Identifier; 8 | 9 | public class SilentTexturedButtonWidget extends SpruceTexturedButtonWidget { 10 | public SilentTexturedButtonWidget(Position position, int width, int height, Text message, PressAction action, int u, int v, int hoveredVOffset, Identifier texture) { 11 | super(position, width, height, message, action, u, v, hoveredVOffset, texture); 12 | } 13 | 14 | public SilentTexturedButtonWidget(Position position, int width, int height, Text message, boolean showMessage, PressAction action, int u, int v, int hoveredVOffset, Identifier texture) { 15 | super(position, width, height, message, showMessage, action, u, v, hoveredVOffset, texture); 16 | } 17 | 18 | public SilentTexturedButtonWidget(Position position, int width, int height, Text message, PressAction action, int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight) { 19 | super(position, width, height, message, action, u, v, hoveredVOffset, texture, textureWidth, textureHeight); 20 | } 21 | 22 | public SilentTexturedButtonWidget(Position position, int width, int height, Text message, boolean showMessage, PressAction action, int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight) { 23 | super(position, width, height, message, showMessage, action, u, v, hoveredVOffset, texture, textureWidth, textureHeight); 24 | } 25 | @Override 26 | public void playDownSound() {} 27 | @Override 28 | protected void onRelease(double mouseX, double mouseY) { 29 | this.setActive(false); 30 | super.onClick(mouseX, mouseY); 31 | super.onRelease(mouseX, mouseY); 32 | this.setActive(true); 33 | } 34 | @Override 35 | public void onClick(double mouseX, double mouseY) { 36 | this.setActive(true); 37 | super.onClick(mouseX, mouseY); 38 | TouchInput.clickStartTime = -1; 39 | this.setActive(false); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/util/AbstractSignEditScreenAccessor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util; 2 | 3 | import org.spongepowered.asm.mixin.Unique; 4 | 5 | public interface AbstractSignEditScreenAccessor { 6 | @Unique 7 | String[] midnightcontrols$getMessages(); 8 | 9 | @Unique 10 | void midnightcontrols$setMessage(int line, String text); 11 | 12 | @Unique 13 | void midnightcontrols$writeToBlockEntity(); 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/util/HandledScreenAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.util; 11 | 12 | import net.minecraft.screen.slot.Slot; 13 | import net.minecraft.screen.slot.SlotActionType; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | /** 17 | * Represents an accessor to AbstractContainerScreen. 18 | */ 19 | public interface HandledScreenAccessor { 20 | /** 21 | * Gets the left coordinate of the GUI. 22 | * 23 | * @return the left coordinate of the GUI 24 | */ 25 | int getX(); 26 | 27 | /** 28 | * Gets the top coordinate of the GUI. 29 | * 30 | * @return the top coordinate of the GUI 31 | */ 32 | int getY(); 33 | 34 | /** 35 | * Gets the slot at position. 36 | * 37 | * @param posX the X position to check 38 | * @param posY the Y position to check 39 | * @return the slot at the specified position 40 | */ 41 | Slot midnightcontrols$getSlotAt(double posX, double posY); 42 | 43 | boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int x, int y, int button); 44 | 45 | /** 46 | * Handles a mouse click on the specified slot. 47 | * 48 | * @param slot the slot instance 49 | * @param slotId the slot id 50 | * @param clickData the click data 51 | * @param actionType the action type 52 | */ 53 | void midnightcontrols$onMouseClick(@Nullable Slot slot, int slotId, int clickData, SlotActionType actionType); 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/util/KeyBindingAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.client.util; 11 | 12 | /** 13 | * Represents a Minecraft keybinding with extra access. 14 | */ 15 | public interface KeyBindingAccessor { 16 | boolean midnightcontrols$press(); 17 | 18 | boolean midnightcontrols$unpress(); 19 | 20 | default boolean midnightcontrols$handlePressState(boolean pressed) { 21 | if (pressed) 22 | return this.midnightcontrols$press(); 23 | else 24 | return this.midnightcontrols$unpress(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/util/MathUtil.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util; 2 | 3 | import net.minecraft.util.math.MathHelper; 4 | 5 | public class MathUtil { 6 | public static class PolarUtil { 7 | public float polarX; 8 | public float polarY; 9 | public PolarUtil() {} 10 | 11 | public void calculate(float x, float y, float speedFactor) { 12 | calculate(x, y, speedFactor, 0); 13 | } 14 | public void calculate(float x, float y, float speedFactor, double deadZone) { 15 | double inputR = Math.pow(x, 2) + Math.pow(y, 2); 16 | inputR = (Math.abs(speedFactor * MathHelper.clamp(inputR,0.f,1.f))); 17 | inputR = inputR < deadZone ? 0f : (inputR-deadZone) / (1f-deadZone); 18 | double inputTheta = Math.atan2(y, x); 19 | polarX = (float) (inputR *Math.cos(inputTheta)); 20 | polarY = (float) (inputR *Math.sin(inputTheta)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/util/RainbowColor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util; 2 | 3 | import java.awt.*; 4 | 5 | public class RainbowColor { 6 | public static float hue; 7 | public static void tick() { 8 | if (hue > 1) hue = 0f; 9 | hue = hue + 0.01f; 10 | } 11 | 12 | public static Color radialRainbow(float saturation, float brightness) { 13 | return Color.getHSBColor(hue, saturation, brightness); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/util/ToggleSneakSprintUtil.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util; 2 | 3 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 4 | import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding; 5 | 6 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client; 7 | 8 | public class ToggleSneakSprintUtil { 9 | public static boolean toggleSneak(ButtonBinding button) { 10 | if (client.player == null) return false; 11 | boolean isFlying = client.player.getAbilities().flying; 12 | var option = client.options.getSneakToggled(); 13 | 14 | button.asKeyBinding().ifPresent(binding -> { 15 | boolean sneakToggled = option.getValue(); 16 | if (isFlying && sneakToggled) 17 | option.setValue(false); 18 | else if (MidnightControlsConfig.controllerToggleSneak != sneakToggled) 19 | option.setValue(!sneakToggled); 20 | binding.setPressed(button.isPressed()); 21 | if (isFlying && sneakToggled) 22 | option.setValue(true); 23 | else if (MidnightControlsConfig.controllerToggleSneak != sneakToggled) 24 | option.setValue(sneakToggled); 25 | }); 26 | return true; 27 | } 28 | public static boolean toggleSprint(ButtonBinding button) { 29 | if (client.player == null) return false; 30 | boolean isFlying = client.player.getAbilities().flying; 31 | var option = client.options.getSprintToggled(); 32 | 33 | button.asKeyBinding().ifPresent(binding -> { 34 | boolean sprintToggled = option.getValue(); 35 | if (isFlying && sprintToggled) 36 | option.setValue(false); 37 | else if (MidnightControlsConfig.controllerToggleSprint != sprintToggled) 38 | option.setValue(!sprintToggled); 39 | binding.setPressed(button.isPressed()); 40 | if (client.player.getAbilities().flying && sprintToggled) 41 | option.setValue(true); 42 | else if (MidnightControlsConfig.controllerToggleSprint != sprintToggled) 43 | option.setValue(sprintToggled); 44 | }); 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/util/platform/ItemGroupUtil.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util.platform; 2 | 3 | import dev.architectury.injectables.annotations.ExpectPlatform; 4 | import eu.midnightdust.midnightcontrols.client.mixin.CreativeInventoryScreenAccessor; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; 7 | import net.minecraft.client.gui.widget.PressableWidget; 8 | import net.minecraft.item.ItemGroup; 9 | import net.minecraft.item.ItemGroups; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.List; 13 | 14 | public class ItemGroupUtil { 15 | @ExpectPlatform 16 | public static List getVisibleGroups(CreativeInventoryScreen screen) { 17 | throw new AssertionError(); 18 | } 19 | 20 | public static boolean cyclePage(boolean next, CreativeInventoryScreen screen) { 21 | try { 22 | return screen.children().stream().filter(element -> element instanceof PressableWidget) 23 | .map(element -> (PressableWidget) element) 24 | .filter(element -> element.getMessage() != null && element.getMessage().getContent() != null) 25 | .anyMatch(element -> { 26 | if (next && element.getMessage().getString().equals(">")) { 27 | element.onPress(); 28 | return true; 29 | } else if (element.getMessage().getString().equals("<")) { 30 | element.onPress(); 31 | return true; 32 | } 33 | return false; 34 | }); 35 | } catch (Exception ignored) {} 36 | return false; 37 | } 38 | 39 | public static @NotNull ItemGroup cycleTab(boolean next, MinecraftClient client) { 40 | ItemGroup currentTab = CreativeInventoryScreenAccessor.getSelectedTab(); 41 | int currentColumn = currentTab.getColumn(); 42 | ItemGroup.Row currentRow = currentTab.getRow(); 43 | ItemGroup newTab = null; 44 | List visibleTabs = ItemGroupUtil.getVisibleGroups((CreativeInventoryScreen) client.currentScreen); 45 | for (ItemGroup tab : visibleTabs) { 46 | if (tab.getRow().equals(currentRow) && ((newTab == null && ((next && tab.getColumn() > currentColumn) || 47 | (!next && tab.getColumn() < currentColumn))) || (newTab != null && ((next && tab.getColumn() > currentColumn && tab.getColumn() < newTab.getColumn()) || 48 | (!next && tab.getColumn() < currentColumn && tab.getColumn() > newTab.getColumn()))))) 49 | newTab = tab; 50 | } 51 | if (newTab == null) 52 | for (ItemGroup tab : visibleTabs) { 53 | if ((tab.getRow().compareTo(currentRow)) != 0 && ((next && newTab == null || next && newTab.getColumn() > tab.getColumn()) || (!next && newTab == null) || (!next && newTab.getColumn() < tab.getColumn()))) 54 | newTab = tab; 55 | } 56 | if (newTab == null) { 57 | for (ItemGroup tab : visibleTabs) { 58 | if ((next && tab.getRow() == ItemGroup.Row.TOP && tab.getColumn() == 0) || 59 | !next && tab.getRow() == ItemGroup.Row.BOTTOM && (newTab == null || tab.getColumn() > newTab.getColumn())) 60 | newTab = tab; 61 | } 62 | } 63 | if (newTab == null || newTab.equals(currentTab)) newTab = ItemGroups.getDefaultTab(); 64 | return newTab; 65 | } 66 | } -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/util/platform/NetworkUtil.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util.platform; 2 | 3 | import dev.architectury.injectables.annotations.ExpectPlatform; 4 | import net.minecraft.network.packet.CustomPayload; 5 | import net.minecraft.network.packet.Packet; 6 | 7 | public class NetworkUtil { 8 | @ExpectPlatform 9 | public static void sendPacketC2S(Packet packet) { 10 | throw new AssertionError(); 11 | } 12 | @ExpectPlatform 13 | public static void sendPayloadC2S(CustomPayload payload) { 14 | throw new AssertionError(); 15 | } 16 | } -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/util/storage/ButtonStorage.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util.storage; 2 | 3 | import eu.midnightdust.midnightcontrols.client.enums.ButtonState; 4 | 5 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT; 6 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP; 7 | 8 | public class ButtonStorage { 9 | public final int button; 10 | public final ButtonState state; 11 | 12 | public static ButtonStorage of(int button, ButtonState state) { 13 | return new ButtonStorage(button, state); 14 | } 15 | 16 | private ButtonStorage(int button, ButtonState state) { 17 | this.button = button; 18 | this.state = state; 19 | } 20 | public boolean isDpad() { 21 | return button >= GLFW_GAMEPAD_BUTTON_DPAD_UP && button <= GLFW_GAMEPAD_BUTTON_DPAD_LEFT; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/virtualkeyboard/KeyboardLayout.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.virtualkeyboard; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class KeyboardLayout { 10 | 11 | public static KeyboardLayout QWERTY = new KeyboardLayout("en_US:qwerty", createQwertyLetterLayout(), createSymbolLayout()); 12 | 13 | private final String id; 14 | private final List> letters; 15 | private final List> symbols; 16 | 17 | private KeyboardLayout(String id, List> letters, List> symbols) { 18 | this.id = id; 19 | this.letters = letters; 20 | this.symbols = symbols; 21 | } 22 | 23 | public static KeyboardLayout fromJson(JsonObject json) { 24 | try { 25 | return new KeyboardLayout(json.get("id").getAsString(), getFromJson(json, true), getFromJson(json, false)); 26 | } catch (Exception e) { 27 | throw new RuntimeException("Error loading keyboard definition: %s".formatted(e)); 28 | } 29 | } 30 | private static List> getFromJson(JsonObject json, boolean letters) { 31 | String type = letters ? "letters" : "symbols"; 32 | List> arr = new ArrayList<>(); 33 | if (json.has(type)) { 34 | JsonObject lettersJson = json.get(type).getAsJsonObject(); 35 | for (int i = 0; ; i++) { 36 | if (!lettersJson.has("row"+i)) break; 37 | var rowJson = lettersJson.get("row%s".formatted(i)).getAsJsonArray(); 38 | List row = new ArrayList<>(); 39 | for (int j = 0; j < rowJson.size(); j++) { 40 | row.add(rowJson.get(j).getAsString()); 41 | } 42 | arr.add(row); 43 | } 44 | return arr; 45 | } 46 | else { 47 | return letters ? createQwertyLetterLayout() : createSymbolLayout(); 48 | } 49 | } 50 | 51 | public String getId() { 52 | return id; 53 | } 54 | 55 | public String getTranslationKey() { 56 | String[] identifier = id.split(":"); 57 | if (identifier.length != 2) return "Invalid Keyboard ID: %s".formatted(id); 58 | return "midnightcontrols.virtual_keyboard.layout.%s.%s".formatted(identifier[0], identifier[1]); 59 | } 60 | 61 | public List> getLetters() { 62 | return letters; 63 | } 64 | 65 | public List> getSymbols() { 66 | return symbols; 67 | } 68 | 69 | private static List> createQwertyLetterLayout() { 70 | List> letters = new ArrayList<>(); 71 | letters.add(Arrays.asList( 72 | "q", "w", "e", "r", "t", 73 | "y", "u", "i", "o", "p" 74 | )); 75 | letters.add(Arrays.asList( 76 | "a", "s", "d", "f", "g", 77 | "h", "j", "k", "l" 78 | )); 79 | letters.add(Arrays.asList( 80 | "z", "x", "c", "v", 81 | "b", "n", "m" 82 | )); 83 | return letters; 84 | } 85 | 86 | private static List> createSymbolLayout() { 87 | List> symbols = new ArrayList<>(); 88 | symbols.add(Arrays.asList( 89 | "1", "2", "3", "4", "5", 90 | "6", "7", "8", "9", "0" 91 | )); 92 | symbols.add(Arrays.asList( 93 | "@", "#", "$", "%", "&", 94 | "*", "-", "+", "(", ")" 95 | )); 96 | symbols.add(Arrays.asList( 97 | "!", "\"", "'", ":", ";", 98 | ",", ".", "?", "/" 99 | )); 100 | return symbols; 101 | } 102 | } -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/virtualkeyboard/KeyboardLayoutManager.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.virtualkeyboard; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonParser; 5 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 6 | import net.minecraft.resource.Resource; 7 | import net.minecraft.util.Identifier; 8 | 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | public class KeyboardLayoutManager { 14 | private static final Map KEYBOARD_LAYOUTS = new HashMap<>(); 15 | 16 | public static void loadLayout(Identifier id, Resource resource) { 17 | try { 18 | JsonObject json = JsonParser.parseReader(resource.getReader()).getAsJsonObject(); 19 | KeyboardLayout layout = KeyboardLayout.fromJson(json); 20 | KEYBOARD_LAYOUTS.put(layout.getId(), layout); 21 | if (MidnightControlsConfig.debug) System.out.printf("Loaded keyboard layout: %s\n", layout.getId()); 22 | } catch (IOException e) { throw new RuntimeException(e); } 23 | } 24 | public static KeyboardLayout getById(String id) { 25 | return KEYBOARD_LAYOUTS.get(id) == null ? KeyboardLayout.QWERTY : KEYBOARD_LAYOUTS.get(id); 26 | } 27 | public static KeyboardLayout getNext(KeyboardLayout current) { 28 | KeyboardLayout[] layouts = KEYBOARD_LAYOUTS.values().toArray(KeyboardLayout[]::new); 29 | int currentIndex = -1; 30 | for (int i = 0; i < layouts.length; i++) { 31 | if (layouts[i] == current) currentIndex = i; 32 | } 33 | currentIndex = currentIndex+1 >= layouts.length ? 0 : currentIndex + 1; 34 | return layouts[currentIndex]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/virtualkeyboard/MouseClickInterceptor.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.virtualkeyboard; 2 | 3 | import eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler.AbstractScreenClickHandler; 4 | import eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler.BookEditScreenClickHandler; 5 | import eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler.DefaultScreenClickHandler; 6 | import eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler.SignEditScreenClickHandler; 7 | import net.minecraft.client.gui.screen.Screen; 8 | import net.minecraft.client.gui.screen.ingame.BookEditScreen; 9 | import net.minecraft.client.gui.screen.ingame.SignEditScreen; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | 15 | public class MouseClickInterceptor { 16 | 17 | private final Map, AbstractScreenClickHandler> clickHandlers; 18 | 19 | public MouseClickInterceptor() { 20 | this.clickHandlers = new HashMap<>(); 21 | this.clickHandlers.put(BookEditScreen.class, new BookEditScreenClickHandler()); 22 | this.clickHandlers.put(SignEditScreen.class, new SignEditScreenClickHandler()); 23 | this.clickHandlers.put(Screen.class, new DefaultScreenClickHandler()); 24 | } 25 | 26 | @SuppressWarnings("unchecked") 27 | public void intercept(T screen, double mouseX, double mouseY) { 28 | AbstractScreenClickHandler handler = (AbstractScreenClickHandler) clickHandlers.get(screen.getClass()); 29 | 30 | if (handler == null) { 31 | handler = (AbstractScreenClickHandler) clickHandlers.get(Screen.class); 32 | } 33 | 34 | handler.handle(screen, mouseX, mouseY); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/virtualkeyboard/clickhandler/AbstractScreenClickHandler.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler; 2 | 3 | import net.minecraft.client.gui.screen.Screen; 4 | 5 | public abstract class AbstractScreenClickHandler { 6 | public abstract void handle(T screen, double mouseX, double mouseY); 7 | } 8 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/virtualkeyboard/clickhandler/BookEditScreenClickHandler.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler; 2 | 3 | import eu.midnightdust.midnightcontrols.client.mixin.BookEditScreenAccessor; 4 | import eu.midnightdust.midnightcontrols.client.virtualkeyboard.gui.VirtualKeyboardScreen; 5 | import net.minecraft.client.gui.screen.ingame.BookEditScreen; 6 | 7 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client; 8 | 9 | public class BookEditScreenClickHandler extends AbstractScreenClickHandler { 10 | @Override 11 | public void handle(BookEditScreen screen, double mouseX, double mouseY) { 12 | // don't open the keyboard if a UI element was clicked 13 | if(screen.hoveredElement(mouseX, mouseY).isPresent()) { 14 | return; 15 | } 16 | 17 | var accessor = (BookEditScreenAccessor) screen; 18 | 19 | VirtualKeyboardScreen virtualKeyboardScreen; 20 | if(accessor.midnightcontrols$isSigning()) { 21 | virtualKeyboardScreen = new VirtualKeyboardScreen(accessor.midnightcontrols$getTitle(), (text) -> { 22 | client.setScreen(screen); 23 | accessor.midnightcontrols$setTitle(text); 24 | }, true); 25 | } 26 | else { 27 | virtualKeyboardScreen = new VirtualKeyboardScreen(accessor.midnightcontrols$getCurrentPageContent(), (text) -> { 28 | client.setScreen(screen); 29 | accessor.midnightcontrols$setPageContent(text); 30 | accessor.midnightcontrols$getCurrentPageSelectionManager().putCursorAtEnd(); 31 | }, true); 32 | } 33 | 34 | client.setScreen(virtualKeyboardScreen); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/virtualkeyboard/clickhandler/SignEditScreenClickHandler.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler; 2 | 3 | import eu.midnightdust.midnightcontrols.client.util.AbstractSignEditScreenAccessor; 4 | import eu.midnightdust.midnightcontrols.client.virtualkeyboard.gui.VirtualKeyboardScreen; 5 | import net.minecraft.client.gui.screen.ingame.SignEditScreen; 6 | 7 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client; 8 | 9 | public class SignEditScreenClickHandler extends AbstractScreenClickHandler { 10 | @Override 11 | public void handle(SignEditScreen screen, double mouseX, double mouseY) { 12 | // don't open the keyboard if a UI element was clicked 13 | if(screen.hoveredElement(mouseX, mouseY).isPresent()) { 14 | return; 15 | } 16 | 17 | var accessor = (AbstractSignEditScreenAccessor) screen; 18 | 19 | StringBuilder linesToString = new StringBuilder(); 20 | String[] messages = accessor.midnightcontrols$getMessages(); 21 | for (int i = 0; i < Math.min(4, messages.length); i++) { 22 | String line = messages[i]; 23 | linesToString.append(line); 24 | if (!line.isEmpty() && i < 3) linesToString.append("\n"); 25 | } 26 | 27 | VirtualKeyboardScreen virtualKeyboardScreen = new VirtualKeyboardScreen(linesToString.toString(), (text) -> { 28 | client.setScreen(screen); 29 | String[] lines = text.split("\n"); 30 | for (int i = 0; i < 4; i++) accessor.midnightcontrols$setMessage(i, lines.length > i ? lines[i] : ""); 31 | accessor.midnightcontrols$writeToBlockEntity(); 32 | }, true); 33 | 34 | client.setScreen(virtualKeyboardScreen); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/client/virtualkeyboard/clickhandler/TextFieldWrapper.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.virtualkeyboard.clickhandler; 2 | 3 | import net.minecraft.client.gui.Element; 4 | import net.minecraft.client.gui.widget.TextFieldWidget; 5 | import org.thinkingstudio.obsidianui.widget.text.SpruceTextFieldWidget; 6 | 7 | public record TextFieldWrapper(Object textField) { 8 | 9 | public TextFieldWrapper { 10 | if (!isValidTextField(textField)) { 11 | throw new IllegalArgumentException("Type " + textField.getClass() + " is not marked as a valid text field"); 12 | } 13 | } 14 | 15 | Element asElement() { 16 | return (Element) textField; 17 | } 18 | 19 | String getText() { 20 | switch (textField) { 21 | case SpruceTextFieldWidget spruceTextField -> { 22 | return spruceTextField.getText(); 23 | } 24 | case TextFieldWidget vanillaTextField -> { 25 | return vanillaTextField.getText(); 26 | } 27 | default -> { 28 | return null; 29 | } 30 | } 31 | } 32 | 33 | void setText(String text) { 34 | switch (textField) { 35 | case SpruceTextFieldWidget spruceTextField -> { 36 | spruceTextField.setText(text); 37 | } 38 | case TextFieldWidget vanillaTextField -> { 39 | vanillaTextField.setText(text); 40 | } 41 | default -> { 42 | } 43 | } 44 | } 45 | 46 | boolean isMouseOver(double mouseX, double mouseY) { 47 | switch (textField) { 48 | case SpruceTextFieldWidget spruceTextField -> { 49 | return spruceTextField.isMouseOver(mouseX, mouseY); 50 | } 51 | case TextFieldWidget vanillaTextField -> { 52 | return vanillaTextField.isMouseOver(mouseX, mouseY); 53 | } 54 | default -> { 55 | return false; 56 | } 57 | } 58 | } 59 | 60 | boolean isFocused() { 61 | switch (textField) { 62 | case SpruceTextFieldWidget spruceTextField -> { 63 | return spruceTextField.isFocused(); 64 | } 65 | case TextFieldWidget vanillaTextField -> { 66 | return vanillaTextField.isFocused(); 67 | } 68 | default -> { 69 | return false; 70 | } 71 | } 72 | } 73 | 74 | static boolean isValidTextField(Object textField) { 75 | return textField instanceof TextFieldWidget || textField instanceof SpruceTextFieldWidget; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/packet/ControlsModePayload.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.packet; 2 | 3 | import eu.midnightdust.midnightcontrols.MidnightControlsConstants; 4 | import net.minecraft.network.RegistryByteBuf; 5 | import net.minecraft.network.codec.PacketCodec; 6 | import net.minecraft.network.packet.CustomPayload; 7 | 8 | import java.util.Objects; 9 | 10 | public record ControlsModePayload(String controlsMode) implements CustomPayload { 11 | public static final Id PACKET_ID = new Id<>(MidnightControlsConstants.CONTROLS_MODE_CHANNEL); 12 | public static final PacketCodec codec = PacketCodec.of(ControlsModePayload::write, ControlsModePayload::read); 13 | 14 | public static ControlsModePayload read(RegistryByteBuf buf) { 15 | return new ControlsModePayload(buf.readString(32)); 16 | } 17 | 18 | public void write(RegistryByteBuf buf) { 19 | Objects.requireNonNull(controlsMode, "Controls mode cannot be null."); 20 | buf.writeString(controlsMode, 32); 21 | } 22 | 23 | @Override 24 | public Id getId() { 25 | return PACKET_ID; 26 | } 27 | } -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/packet/FeaturePayload.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.packet; 2 | 3 | import eu.midnightdust.midnightcontrols.MidnightControlsConstants; 4 | import eu.midnightdust.midnightcontrols.MidnightControlsFeature; 5 | import net.minecraft.network.RegistryByteBuf; 6 | import net.minecraft.network.codec.PacketCodec; 7 | import net.minecraft.network.packet.CustomPayload; 8 | 9 | public record FeaturePayload(MidnightControlsFeature... features) implements CustomPayload { 10 | public static final Id PACKET_ID = new Id<>(MidnightControlsConstants.FEATURE_CHANNEL); 11 | public static final PacketCodec codec = PacketCodec.of(FeaturePayload::write, FeaturePayload::read); 12 | 13 | public static FeaturePayload read(RegistryByteBuf buf) { 14 | int featureLength = buf.readVarInt(); 15 | MidnightControlsFeature[] receivedFeatures = new MidnightControlsFeature[featureLength]; 16 | for (int i = 0; i < featureLength; i++) { 17 | var name = buf.readString(64); 18 | boolean allowed = buf.readBoolean(); 19 | var feature = MidnightControlsFeature.fromName(name); 20 | if (feature.isPresent()) { 21 | feature.get().setAllowed(allowed); 22 | receivedFeatures[i] = feature.get(); 23 | } 24 | } 25 | return new FeaturePayload(receivedFeatures); 26 | } 27 | 28 | public void write(RegistryByteBuf buf) { 29 | if (features.length == 0) 30 | throw new IllegalArgumentException("At least one feature must be provided."); 31 | 32 | buf.writeVarInt(features.length); 33 | for (var feature : features) { 34 | buf.writeString(feature.getName(), 64); 35 | buf.writeBoolean(feature.isAllowed()); 36 | } 37 | } 38 | 39 | @Override 40 | public Id getId() { 41 | return PACKET_ID; 42 | } 43 | } -------------------------------------------------------------------------------- /common/src/main/java/eu/midnightdust/midnightcontrols/packet/HelloPayload.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.packet; 2 | 3 | import eu.midnightdust.midnightcontrols.MidnightControlsConstants; 4 | import net.minecraft.network.RegistryByteBuf; 5 | import net.minecraft.network.codec.PacketCodec; 6 | import net.minecraft.network.packet.CustomPayload; 7 | 8 | public record HelloPayload(String version, String controlsMode) implements CustomPayload { 9 | public static final CustomPayload.Id PACKET_ID = new CustomPayload.Id<>(MidnightControlsConstants.HELLO_CHANNEL); 10 | public static final PacketCodec codec = PacketCodec.of(HelloPayload::write, HelloPayload::read); 11 | 12 | public static HelloPayload read(RegistryByteBuf buf) { 13 | return new HelloPayload(buf.readString(32), buf.readString(32)); 14 | } 15 | 16 | public void write(RegistryByteBuf buf) { 17 | buf.writeString(version, 32).writeString(controlsMode, 32); 18 | } 19 | 20 | @Override 21 | public Id getId() { 22 | return PACKET_ID; 23 | } 24 | } -------------------------------------------------------------------------------- /common/src/main/resources/architectury.midnightcontrols.json: -------------------------------------------------------------------------------- 1 | { 2 | "accessWidener": "midnightcontrols.accesswidener" 3 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/icon.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/keyboard_layouts/de_quertz.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "de_DE:qwertz", 3 | 4 | "letters": { 5 | "row0": ["q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ü"], 6 | "row1": ["a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä"], 7 | "row2": ["y", "x", "c", "v", "b", "n", "m", "ß"] 8 | }, 9 | "symbols": { 10 | "row0": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], 11 | "row1": ["@", "#", "$", "%", "&", "*", "-", "+", "(", ")"], 12 | "row2": ["!", "\"", "'", ":", ";", ",", ".", "?", "/"] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/keyboard_layouts/en_querty.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "en_US:qwerty", 3 | 4 | "letters": { 5 | "row0": ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"], 6 | "row1": ["a", "s", "d", "f", "g", "h", "j", "k", "l"], 7 | "row2": ["z", "x", "c", "v", "b", "n", "m"] 8 | }, 9 | "symbols": { 10 | "row0": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], 11 | "row1": ["@", "#", "$", "%", "&", "*", "-", "+", "(", ")"], 12 | "row2": ["!", "\"", "'", ":", ";", ",", ".", "?", "/"] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/controller_axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/controller_axis.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/controller_buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/controller_buttons.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/controller_expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/controller_expanded.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/binding/debug_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/binding/debug_screen.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/default.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/default_slot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/default_slot.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/mouse_pointer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/mouse_pointer.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/secondary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/secondary.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/secondary_slot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/dark/secondary_slot.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/default.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/default_slot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/default_slot.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/mouse_pointer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/mouse_pointer.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/secondary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/secondary.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/secondary_slot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/cursor/light/secondary_slot.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/icon/controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/icon/controller.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/touch/chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/touch/chat.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/touch/emote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/touch/emote.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/touch/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/touch/empty.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/touch/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/sprites/touch/pause.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/midnightcontrols/textures/gui/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/assets/midnightcontrols/textures/gui/widgets.png -------------------------------------------------------------------------------- /common/src/main/resources/midnightcontrols.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v1 named 2 | 3 | #accessible class net/minecraft/client/gui/widget/EntryListWidget$MoveDirection -------------------------------------------------------------------------------- /common/src/main/resources/midnightcontrols.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "eu.midnightdust.midnightcontrols.client.mixin", 4 | "compatibilityLevel": "JAVA_21", 5 | "client": [ 6 | "AbstractBlockAccessor", 7 | "AbstractSignEditScreenMixin", 8 | "AdvancementsScreenAccessor", 9 | "BookEditScreenAccessor", 10 | "ChatScreenMixin", 11 | "ClickableWidgetAccessor", 12 | "ClientPlayerEntityMixin", 13 | "CreativeInventoryScreenAccessor", 14 | "DrawContextAccessor", 15 | "GameOptionsScreenMixin", 16 | "GameRendererMixin", 17 | "HandledScreenMixin", 18 | "InputAccessor", 19 | "InputUtilMixin", 20 | "KeyBindingIDAccessor", 21 | "KeyBindingMixin", 22 | "KeyboardMixin", 23 | "MinecraftClientMixin", 24 | "MouseAccessor", 25 | "MouseMixin", 26 | "RecipeBookScreenAccessor", 27 | "RecipeBookWidgetAccessor", 28 | "ScreenMixin", 29 | "TabNavigationWidgetAccessor", 30 | "WorldRendererMixin" 31 | ], 32 | "injectors": { 33 | "defaultRequire": 1 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /common/src/main/resources/midnightcontrols_compat.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "eu.midnightdust.midnightcontrols.client.compat.mixin", 4 | "plugin": "eu.midnightdust.midnightcontrols.client.compat.MidnightControlsMixinPlugin", 5 | "compatibilityLevel": "JAVA_21", 6 | "client": [ 7 | "sodium.SodiumOptionsGUIAccessor" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/bedrock/CREDITS: -------------------------------------------------------------------------------- 1 | Some of the textures in this Resourcepack are directly taken from the Bedrock assets and are therefore © Mojang Studios. 2 | Credits go to Ivanoks for redrawing the Bedrock XBox controller icons in 15x15, 3 | Motschen for creating the assets for the other controllers 4 | and LambdAurora for the original assets, part of which are also included. 5 | -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/bedrock/assets/midnightcontrols/textures/gui/controller_axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/resourcepacks/bedrock/assets/midnightcontrols/textures/gui/controller_axis.png -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/bedrock/assets/midnightcontrols/textures/gui/controller_buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/resourcepacks/bedrock/assets/midnightcontrols/textures/gui/controller_buttons.png -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/bedrock/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "pack_format": 15, 4 | "supported_formats": [15, 999], 5 | "description": "Makes controller tooltips use similar icons to Bedrock Edition" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/bedrock/pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/resourcepacks/bedrock/pack.png -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/legacy/CREDITS: -------------------------------------------------------------------------------- 1 | Some of the textures in this Resourcepack are directly taken from the Console Edition assets and are therefore © Mojang Studios. 2 | Credits go to Ivanoks for redrawing the Console Generic, Dualshock, DualSense, Switch, Xbox 360 and XBox One controller icons in 15x15, 3 | Motschen for creating the assets for the other controllers 4 | and LambdAurora for the original assets, part of which are also included. 5 | -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/legacy/assets/midnightcontrols/textures/gui/controller_axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/resourcepacks/legacy/assets/midnightcontrols/textures/gui/controller_axis.png -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/legacy/assets/midnightcontrols/textures/gui/controller_buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/resourcepacks/legacy/assets/midnightcontrols/textures/gui/controller_buttons.png -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/legacy/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "pack_format": 15, 4 | "supported_formats": [15, 999], 5 | "description": "Makes controller icons look similar to Legacy Console Edit." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /common/src/main/resources/resourcepacks/legacy/pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/common/src/main/resources/resourcepacks/legacy/pack.png -------------------------------------------------------------------------------- /fabric/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.github.johnrengelman.shadow' 3 | id "me.shedaniel.unified-publishing" 4 | } 5 | 6 | architectury { 7 | platformSetupLoomIde() 8 | fabric() 9 | } 10 | 11 | loom { 12 | } 13 | 14 | configurations { 15 | common 16 | shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files. 17 | compileClasspath.extendsFrom common 18 | runtimeClasspath.extendsFrom common 19 | developmentFabric.extendsFrom common 20 | archivesBaseName = rootProject.archives_base_name + "-fabric" 21 | } 22 | 23 | dependencies { 24 | modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" 25 | modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}" 26 | modImplementation include ("maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric") 27 | modImplementation include ("maven.modrinth:obsidianui:${rootProject.obsidianui_version}-fabric") {} 28 | include 'org.aperlambda:lambdajcommon:1.8.1' 29 | modCompileOnly "maven.modrinth:emi:${project.emi_version}" 30 | 31 | common(project(path: ":common", configuration: "namedElements")) { transitive false } 32 | shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false } 33 | } 34 | 35 | processResources { 36 | inputs.property "version", project.version 37 | 38 | filesMatching("fabric.mod.json") { 39 | expand "version": project.version 40 | } 41 | } 42 | 43 | shadowJar { 44 | exclude "architectury.common.json" 45 | 46 | configurations = [project.configurations.shadowCommon] 47 | archiveClassifier = "dev-shadow" 48 | } 49 | 50 | remapJar { 51 | input.set shadowJar.archiveFile 52 | dependsOn shadowJar 53 | } 54 | 55 | sourcesJar { 56 | def commonSources = project(":common").sourcesJar 57 | dependsOn commonSources 58 | from commonSources.archiveFile.map { zipTree(it) } 59 | } 60 | 61 | components.java { 62 | withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { 63 | skip() 64 | } 65 | } 66 | 67 | unifiedPublishing { 68 | project { 69 | displayName = "MidnightControls $project.version - Fabric $project.minecraft_version" 70 | releaseType = "$project.release_type" 71 | changelog = releaseChangelog() 72 | gameVersions = [] 73 | gameLoaders = ["fabric","quilt"] 74 | mainPublication remapJar 75 | relations { 76 | depends { 77 | curseforge = "fabric-api" 78 | modrinth = "fabric-api" 79 | } 80 | includes { 81 | curseforge = "midnightlib" 82 | modrinth = "midnightlib" 83 | } 84 | includes { 85 | curseforge = "obsidianui" 86 | modrinth = "obsidianui" 87 | } 88 | } 89 | 90 | var CURSEFORGE_TOKEN = project.findProperty("CURSEFORGE_TOKEN") ?: System.getenv("CURSEFORGE_TOKEN") 91 | if (CURSEFORGE_TOKEN != null) { 92 | curseforge { 93 | token = CURSEFORGE_TOKEN 94 | id = rootProject.curseforge_id 95 | gameVersions.addAll "Java 21", project.minecraft_version 96 | if (project.supported_versions != "") gameVersions.addAll project.supported_versions 97 | } 98 | } 99 | 100 | var MODRINTH_TOKEN = project.findProperty("MODRINTH_TOKEN") ?: System.getenv("MODRINTH_TOKEN") 101 | if (MODRINTH_TOKEN != null) { 102 | modrinth { 103 | token = MODRINTH_TOKEN 104 | id = rootProject.modrinth_id 105 | version = "$project.version-$project.name" 106 | gameVersions.addAll project.minecraft_version 107 | if (project.supported_versions != "") gameVersions.addAll project.supported_versions 108 | } 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /fabric/src/main/java/eu/midnightdust/midnightcontrols/client/util/platform/fabric/ItemGroupUtilImpl.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util.platform.fabric; 2 | 3 | import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; 4 | import net.minecraft.item.ItemGroup; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Implementation of fabric methods for 10 | * @see eu.midnightdust.midnightcontrols.client.util.platform.ItemGroupUtil 11 | */ 12 | public class ItemGroupUtilImpl { 13 | public static List getVisibleGroups(CreativeInventoryScreen screen) { 14 | return (screen.getItemGroupsOnPage(screen.getCurrentPage())); 15 | } 16 | } -------------------------------------------------------------------------------- /fabric/src/main/java/eu/midnightdust/midnightcontrols/client/util/platform/fabric/NetworkUtilImpl.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util.platform.fabric; 2 | 3 | import net.minecraft.client.network.ClientPlayNetworkHandler; 4 | import net.minecraft.network.packet.CustomPayload; 5 | import net.minecraft.network.packet.Packet; 6 | import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket; 7 | 8 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client; 9 | 10 | /** 11 | * Implementation of fabric methods for 12 | * @see eu.midnightdust.midnightcontrols.client.util.platform.NetworkUtil 13 | */ 14 | public class NetworkUtilImpl { 15 | private static final ClientPlayNetworkHandler handler = client.getNetworkHandler(); 16 | 17 | public static void sendPacketC2S(Packet packet) { 18 | if (handler != null) 19 | handler.sendPacket(packet); 20 | } 21 | public static void sendPayloadC2S(CustomPayload payload) { 22 | if (handler != null && client.world != null) 23 | handler.sendPacket(new CustomPayloadC2SPacket(payload)); 24 | } 25 | } -------------------------------------------------------------------------------- /fabric/src/main/java/eu/midnightdust/midnightcontrols/fabric/MidnightControlsClientFabric.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.fabric; 2 | 3 | import eu.midnightdust.midnightcontrols.MidnightControlsConstants; 4 | import eu.midnightdust.midnightcontrols.client.MidnightControlsClient; 5 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 6 | import eu.midnightdust.midnightcontrols.client.MidnightControlsReloadListener; 7 | import eu.midnightdust.midnightcontrols.fabric.event.MouseClickListener; 8 | import eu.midnightdust.midnightcontrols.packet.ControlsModePayload; 9 | import eu.midnightdust.midnightcontrols.packet.FeaturePayload; 10 | import eu.midnightdust.midnightcontrols.packet.HelloPayload; 11 | import net.fabricmc.api.ClientModInitializer; 12 | import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; 13 | import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; 14 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; 15 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 16 | import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; 17 | import net.fabricmc.fabric.api.client.screen.v1.ScreenMouseEvents; 18 | import net.fabricmc.fabric.api.resource.ResourceManagerHelper; 19 | import net.fabricmc.fabric.api.resource.ResourcePackActivationType; 20 | import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; 21 | import net.fabricmc.loader.api.FabricLoader; 22 | import net.fabricmc.loader.api.ModContainer; 23 | import net.minecraft.resource.ResourceManager; 24 | import net.minecraft.resource.ResourceType; 25 | import net.minecraft.util.Identifier; 26 | 27 | import java.util.Optional; 28 | 29 | import static eu.midnightdust.midnightcontrols.MidnightControls.id; 30 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.BINDING_LOOK_DOWN; 31 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.BINDING_LOOK_LEFT; 32 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.BINDING_LOOK_RIGHT; 33 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.BINDING_LOOK_UP; 34 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.BINDING_RING; 35 | 36 | public class MidnightControlsClientFabric implements ClientModInitializer { 37 | @Override 38 | public void onInitializeClient() { 39 | KeyBindingHelper.registerKeyBinding(BINDING_LOOK_UP); 40 | KeyBindingHelper.registerKeyBinding(BINDING_LOOK_RIGHT); 41 | KeyBindingHelper.registerKeyBinding(BINDING_LOOK_DOWN); 42 | KeyBindingHelper.registerKeyBinding(BINDING_LOOK_LEFT); 43 | KeyBindingHelper.registerKeyBinding(BINDING_RING); 44 | ClientPlayNetworking.registerGlobalReceiver(ControlsModePayload.PACKET_ID, (payload, context) -> 45 | context.responseSender().sendPacket(new ControlsModePayload(MidnightControlsConfig.controlsMode.getName()))); 46 | ClientPlayNetworking.registerGlobalReceiver(FeaturePayload.PACKET_ID, ((payload, context) -> {})); 47 | 48 | ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { 49 | var version = ""; 50 | Optional container; 51 | if ((container = FabricLoader.getInstance().getModContainer(MidnightControlsConstants.NAMESPACE)).isPresent()) { 52 | version = container.get().getMetadata().getVersion().getFriendlyString(); 53 | } 54 | var controlsMode = MidnightControlsConfig.controlsMode.getName(); 55 | sender.sendPacket(new HelloPayload(version, controlsMode)); 56 | sender.sendPacket(new ControlsModePayload(controlsMode)); 57 | }); 58 | ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> MidnightControlsClient.onLeave()); 59 | 60 | ClientTickEvents.START_CLIENT_TICK.register(MidnightControlsClient::onTick); 61 | ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { 62 | ScreenMouseEvents.allowMouseClick(screen).register(new MouseClickListener(screen)); 63 | }); 64 | 65 | FabricLoader.getInstance().getModContainer(MidnightControlsConstants.NAMESPACE).ifPresent(modContainer -> { 66 | ResourceManagerHelper.registerBuiltinResourcePack(id("bedrock"), modContainer, ResourcePackActivationType.NORMAL); 67 | ResourceManagerHelper.registerBuiltinResourcePack(id("legacy"), modContainer, ResourcePackActivationType.NORMAL); 68 | }); 69 | MidnightControlsClient.initClient(); 70 | 71 | ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(new SimpleSynchronousResourceReloadListener() { 72 | @Override 73 | public Identifier getFabricId() { 74 | return id("keyboard_layouts"); 75 | } 76 | @Override 77 | public void reload(ResourceManager manager) { 78 | MidnightControlsReloadListener.INSTANCE.reload(manager); 79 | } 80 | }); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /fabric/src/main/java/eu/midnightdust/midnightcontrols/fabric/MidnightControlsFabric.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.fabric; 2 | 3 | import eu.midnightdust.midnightcontrols.ControlsMode; 4 | import eu.midnightdust.midnightcontrols.MidnightControls; 5 | import eu.midnightdust.midnightcontrols.MidnightControlsFeature; 6 | import eu.midnightdust.midnightcontrols.fabric.event.PlayerChangeControlsModeCallback; 7 | import eu.midnightdust.midnightcontrols.packet.ControlsModePayload; 8 | import eu.midnightdust.midnightcontrols.packet.FeaturePayload; 9 | import eu.midnightdust.midnightcontrols.packet.HelloPayload; 10 | import net.fabricmc.api.ModInitializer; 11 | import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; 12 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 13 | 14 | public class MidnightControlsFabric implements ModInitializer { 15 | @Override 16 | public void onInitialize() { 17 | MidnightControls.init(); 18 | PayloadTypeRegistry.playC2S().register(HelloPayload.PACKET_ID, HelloPayload.codec); 19 | PayloadTypeRegistry.playC2S().register(ControlsModePayload.PACKET_ID, ControlsModePayload.codec); 20 | PayloadTypeRegistry.playS2C().register(ControlsModePayload.PACKET_ID, ControlsModePayload.codec); 21 | PayloadTypeRegistry.playS2C().register(FeaturePayload.PACKET_ID, FeaturePayload.codec); 22 | 23 | ServerPlayNetworking.registerGlobalReceiver(HelloPayload.PACKET_ID, (payload, context) -> { 24 | ControlsMode.byId(payload.controlsMode()).ifPresent(controlsMode -> PlayerChangeControlsModeCallback.EVENT.invoker().apply(context.player(), controlsMode)); 25 | context.responseSender().sendPacket(new FeaturePayload(MidnightControlsFeature.HORIZONTAL_REACHAROUND)); 26 | }); 27 | ServerPlayNetworking.registerGlobalReceiver(ControlsModePayload.PACKET_ID, (payload, context) -> { 28 | ControlsMode.byId(payload.controlsMode()).ifPresent(controlsMode -> PlayerChangeControlsModeCallback.EVENT.invoker().apply(context.player(), controlsMode)); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /fabric/src/main/java/eu/midnightdust/midnightcontrols/fabric/event/MouseClickListener.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.fabric.event; 2 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 3 | import net.fabricmc.fabric.api.client.screen.v1.ScreenMouseEvents; 4 | import net.minecraft.client.gui.screen.Screen; 5 | 6 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.clickInterceptor; 7 | 8 | public class MouseClickListener implements ScreenMouseEvents.AllowMouseClick { 9 | private final Screen screen; 10 | 11 | public MouseClickListener(Screen screen) { 12 | this.screen = screen; 13 | } 14 | 15 | @Override 16 | public boolean allowMouseClick(Screen screen, double mouseX, double mouseY, int button) { 17 | if(MidnightControlsConfig.virtualKeyboard) { 18 | clickInterceptor.intercept(screen, mouseX, mouseY); 19 | } 20 | return true; 21 | } 22 | 23 | // Add equals and hashCode to prevent duplicate registrations 24 | @Override 25 | public boolean equals(Object obj) { 26 | if (obj instanceof MouseClickListener) { 27 | return ((MouseClickListener) obj).screen == this.screen; 28 | } 29 | return false; 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | return screen.hashCode(); 35 | } 36 | 37 | 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /fabric/src/main/java/eu/midnightdust/midnightcontrols/fabric/event/PlayerChangeControlsModeCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 LambdAurora 3 | * 4 | * This file is part of midnightcontrols. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package eu.midnightdust.midnightcontrols.fabric.event; 11 | 12 | import eu.midnightdust.midnightcontrols.ControlsMode; 13 | import net.fabricmc.fabric.api.event.Event; 14 | import net.fabricmc.fabric.api.event.EventFactory; 15 | import net.minecraft.entity.player.PlayerEntity; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | /** 19 | * Represents an event callback which is fired when a player changes the controls mode. 20 | * 21 | * @author LambdAurora 22 | * @version 1.10.0 23 | * @since 1.1.0 24 | */ 25 | @FunctionalInterface 26 | public interface PlayerChangeControlsModeCallback { 27 | Event EVENT = EventFactory.createArrayBacked(PlayerChangeControlsModeCallback.class, listeners -> (player, controlsMode) -> { 28 | for (PlayerChangeControlsModeCallback event : listeners) { 29 | event.apply(player, controlsMode); 30 | } 31 | }); 32 | 33 | void apply(@NotNull PlayerEntity player, @NotNull ControlsMode controlsMode); 34 | } 35 | -------------------------------------------------------------------------------- /fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "midnightcontrols", 4 | "name": "MidnightControls", 5 | "version": "${version}", 6 | "description": "Adds controller support and enhanced controls overall.", 7 | "authors": [ 8 | "Motschen", 9 | "LambdAurora" 10 | ], 11 | "contributors": [ 12 | "akemin-dayo", 13 | "DioEgizio", 14 | "dogtopus", 15 | "egeesin", 16 | "EnnuiL", 17 | "FlashyReese", 18 | "gyular", 19 | "Hambaka", 20 | "Ivanoks", 21 | "joaoh1", 22 | "KiskaUWU", 23 | "Madis0", 24 | "RaptaG", 25 | "ronniedude", 26 | "spudpiggy", 27 | "TrueHorse" 28 | ], 29 | "contact": { 30 | "homepage": "https://modrinth.com/mod/midnightcontrols", 31 | "sources": "https://github.com/TeamMidnightDust/MidnightControls", 32 | "issues": "https://github.com/TeamMidnightDust/MidnightControls/issues" 33 | }, 34 | "license": "MIT", 35 | "icon": "assets/midnightcontrols/icon.png", 36 | "environment": "client", 37 | "entrypoints": { 38 | "main": [ 39 | "eu.midnightdust.midnightcontrols.fabric.MidnightControlsFabric" 40 | ], 41 | "client": [ 42 | "eu.midnightdust.midnightcontrols.fabric.MidnightControlsClientFabric" 43 | ], 44 | "modmenu": [ 45 | "eu.midnightdust.midnightcontrols.client.MidnightControlsModMenu" 46 | ] 47 | }, 48 | "mixins": [ 49 | "midnightcontrols.mixins.json", 50 | "midnightcontrols_compat.mixins.json" 51 | ], 52 | "depends": { 53 | "fabricloader": ">=0.11.3", 54 | "fabric": ">=0.71.0", 55 | "minecraft": ">=1.20.5", 56 | "obsidianui": ">=0.2.5", 57 | "java": ">=21" 58 | }, 59 | "suggests": { 60 | "kontrolo": "*" 61 | }, 62 | "breaks": { 63 | "lambdacontrols": "*", 64 | "modmenu": "<1.12.2", 65 | "sodium": "<=0.5.11" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Makes things faster 2 | org.gradle.parallel=true 3 | org.gradle.jvmargs=-Xmx2048M 4 | 5 | minecraft_version=1.21.5 6 | supported_versions= 7 | yarn_mappings=1.21.5+build.1 8 | enabled_platforms=fabric,neoforge 9 | 10 | archives_base_name=midnightcontrols 11 | mod_version=1.11.0 12 | maven_group=eu.midnightdust 13 | release_type=release 14 | modrinth_id = bXX9h73M 15 | curseforge_id = 621768 16 | # Configure the IDs here after creating the projects on the websites 17 | 18 | midnightlib_version=1.7.3+1.21.4 19 | 20 | fabric_loader_version=0.16.10 21 | fabric_api_version=0.119.5+1.21.5 22 | 23 | neoforge_version=21.5.2-beta 24 | yarn_mappings_patch_neoforge_version = 1.21+build.4 25 | 26 | quilt_loader_version=0.19.0-beta.18 27 | quilt_fabric_api_version=7.0.1+0.83.0-1.20 28 | 29 | sodium_version=mc1.21-0.6.0-beta.1 30 | obsidianui_version=0.2.11+mc1.21.5 31 | modmenu_version=10.0.0-beta.1 32 | emotecraft_version=2.5.5+1.21.4-fabric 33 | bendylib_version=2.0.+ 34 | emi_version=1.1.10+1.21+fabric 35 | libgui_version=6.0.0+1.19 36 | inventorytabs_version=inventorytabs-0.9.beta-1.19.x 37 | clothconfig_version=7.0.72 38 | yacl_version=2.2.0 39 | bedrockify_version=1.9+mc1.20 40 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.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 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS=-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 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% equ 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 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/icon.png -------------------------------------------------------------------------------- /images/controller_controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/images/controller_controls.png -------------------------------------------------------------------------------- /images/controller_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/images/controller_options.png -------------------------------------------------------------------------------- /neoforge/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.github.johnrengelman.shadow' 3 | id "me.shedaniel.unified-publishing" 4 | } 5 | 6 | repositories { 7 | maven { 8 | name = 'NeoForged' 9 | url = 'https://maven.neoforged.net/releases' 10 | } 11 | } 12 | 13 | 14 | architectury { 15 | platformSetupLoomIde() 16 | neoForge() 17 | } 18 | 19 | loom { 20 | accessWidenerPath = project(":common").loom.accessWidenerPath 21 | } 22 | 23 | configurations { 24 | common { 25 | canBeResolved = true 26 | canBeConsumed = false 27 | } 28 | compileClasspath.extendsFrom common 29 | runtimeClasspath.extendsFrom common 30 | developmentNeoForge.extendsFrom common 31 | 32 | // Files in this configuration will be bundled into your mod using the Shadow plugin. 33 | // Don't use the `shadow` configuration from the plugin itself as it's meant for excluding files. 34 | shadowBundle { 35 | canBeResolved = true 36 | canBeConsumed = false 37 | } 38 | archivesBaseName = rootProject.archives_base_name + "-neoforge" 39 | } 40 | 41 | dependencies { 42 | neoForge "net.neoforged:neoforge:$rootProject.neoforge_version" 43 | modImplementation include ("maven.modrinth:midnightlib:${rootProject.midnightlib_version}-neoforge") 44 | modImplementation include ("maven.modrinth:obsidianui:${rootProject.obsidianui_version}-neoforge") {} 45 | shadowBundle('org.aperlambda:lambdajcommon:1.8.1') { 46 | exclude group: 'com.google.code.gson' 47 | exclude group: 'com.google.guava' 48 | } 49 | 50 | common(project(path: ':common', configuration: 'namedElements')) { transitive false } 51 | shadowBundle project(path: ':common', configuration: 'transformProductionNeoForge') 52 | } 53 | 54 | processResources { 55 | inputs.property 'version', project.version 56 | 57 | filesMatching('META-INF/neoforge.mods.toml') { 58 | expand version: project.version 59 | } 60 | } 61 | 62 | shadowJar { 63 | configurations = [project.configurations.shadowBundle] 64 | archiveClassifier = 'dev-shadow' 65 | } 66 | 67 | remapJar { 68 | input.set shadowJar.archiveFile 69 | } 70 | 71 | sourcesJar { 72 | def commonSources = project(":common").sourcesJar 73 | dependsOn commonSources 74 | from commonSources.archiveFile.map { zipTree(it) } 75 | } 76 | 77 | components.java { 78 | withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { 79 | skip() 80 | } 81 | } 82 | 83 | unifiedPublishing { 84 | project { 85 | displayName = "MidnightControls $project.version - NeoForge $project.minecraft_version" 86 | releaseType = "$project.release_type" 87 | changelog = releaseChangelog() 88 | gameVersions = [] 89 | gameLoaders = ["neoforge"] 90 | mainPublication remapJar 91 | relations { 92 | includes { 93 | curseforge = "midnightlib" 94 | modrinth = "midnightlib" 95 | } 96 | includes { 97 | curseforge = "obsidianui" 98 | modrinth = "obsidianui" 99 | } 100 | } 101 | 102 | var CURSEFORGE_TOKEN = project.findProperty("CURSEFORGE_TOKEN") ?: System.getenv("CURSEFORGE_TOKEN") 103 | if (CURSEFORGE_TOKEN != null) { 104 | curseforge { 105 | token = CURSEFORGE_TOKEN 106 | id = rootProject.curseforge_id 107 | gameVersions.addAll "Java 21", project.minecraft_version 108 | if (project.supported_versions != "") gameVersions.addAll project.supported_versions 109 | } 110 | } 111 | 112 | var MODRINTH_TOKEN = project.findProperty("MODRINTH_TOKEN") ?: System.getenv("MODRINTH_TOKEN") 113 | if (MODRINTH_TOKEN != null) { 114 | modrinth { 115 | token = MODRINTH_TOKEN 116 | id = rootProject.modrinth_id 117 | version = "$project.version-$project.name" 118 | gameVersions.addAll project.minecraft_version 119 | if (project.supported_versions != "") gameVersions.addAll project.supported_versions 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /neoforge/gradle.properties: -------------------------------------------------------------------------------- 1 | loom.platform=neoforge -------------------------------------------------------------------------------- /neoforge/src/main/java/eu/midnightdust/midnightcontrols/client/util/platform/neoforge/ItemGroupUtilImpl.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util.platform.neoforge; 2 | 3 | import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; 4 | import net.minecraft.item.ItemGroup; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Implementation of neoforge methods for 10 | * @see eu.midnightdust.midnightcontrols.client.util.platform.ItemGroupUtil 11 | */ 12 | public class ItemGroupUtilImpl { 13 | public static List getVisibleGroups(CreativeInventoryScreen screen) { 14 | return (screen.getCurrentPage().getVisibleTabs()); 15 | } 16 | } -------------------------------------------------------------------------------- /neoforge/src/main/java/eu/midnightdust/midnightcontrols/client/util/platform/neoforge/NetworkUtilImpl.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.client.util.platform.neoforge; 2 | 3 | import net.minecraft.client.network.ClientPlayNetworkHandler; 4 | import net.minecraft.network.packet.CustomPayload; 5 | import net.minecraft.network.packet.Packet; 6 | import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket; 7 | 8 | import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client; 9 | 10 | /** 11 | * Implementation of neoforge methods for 12 | * @see eu.midnightdust.midnightcontrols.client.util.platform.NetworkUtil 13 | */ 14 | public class NetworkUtilImpl { 15 | private static final ClientPlayNetworkHandler handler = client.getNetworkHandler(); 16 | 17 | public static void sendPacketC2S(Packet packet) { 18 | if (handler != null) 19 | handler.send(packet); 20 | } 21 | public static void sendPayloadC2S(CustomPayload payload) { 22 | if (handler != null && client.world != null) { 23 | try { 24 | handler.send(new CustomPayloadC2SPacket(payload)); 25 | } catch (Exception e) { 26 | e.fillInStackTrace(); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /neoforge/src/main/java/eu/midnightdust/midnightcontrols/neoforge/MidnightControlsNeoforge.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.neoforge; 2 | 3 | import eu.midnightdust.midnightcontrols.ControlsMode; 4 | import eu.midnightdust.midnightcontrols.MidnightControls; 5 | import eu.midnightdust.midnightcontrols.MidnightControlsFeature; 6 | import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig; 7 | import eu.midnightdust.midnightcontrols.neoforge.event.PlayerChangeControlsModeEvent; 8 | import eu.midnightdust.midnightcontrols.packet.ControlsModePayload; 9 | import eu.midnightdust.midnightcontrols.packet.FeaturePayload; 10 | import eu.midnightdust.midnightcontrols.packet.HelloPayload; 11 | import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket; 12 | import net.minecraft.network.packet.s2c.common.CustomPayloadS2CPacket; 13 | import net.neoforged.bus.api.SubscribeEvent; 14 | import net.neoforged.fml.common.EventBusSubscriber; 15 | import net.neoforged.fml.common.Mod; 16 | import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; 17 | import net.neoforged.neoforge.network.registration.PayloadRegistrar; 18 | 19 | import static eu.midnightdust.midnightcontrols.MidnightControlsConstants.NAMESPACE; 20 | 21 | @Mod(value = NAMESPACE) 22 | public class MidnightControlsNeoforge { 23 | public MidnightControlsNeoforge() { 24 | MidnightControls.init(); 25 | } 26 | @EventBusSubscriber(modid = NAMESPACE, bus = EventBusSubscriber.Bus.MOD) 27 | public class CommonEvents { 28 | @SubscribeEvent 29 | public static void registerPayloads(RegisterPayloadHandlersEvent event) { 30 | PayloadRegistrar registrar = event.registrar("1").optional(); 31 | registrar.playToServer(HelloPayload.PACKET_ID, HelloPayload.codec, (payload, context) -> { 32 | ControlsMode.byId(payload.controlsMode()).ifPresent(controlsMode -> new PlayerChangeControlsModeEvent(context.player(), controlsMode)); 33 | context.connection().send(new CustomPayloadS2CPacket(new FeaturePayload(MidnightControlsFeature.HORIZONTAL_REACHAROUND))); 34 | }); 35 | registrar.playBidirectional(ControlsModePayload.PACKET_ID, ControlsModePayload.codec, (payload, context) -> { 36 | if (context.flow().isServerbound()) ControlsMode.byId(payload.controlsMode()).ifPresent(controlsMode -> new PlayerChangeControlsModeEvent(context.player(), controlsMode)); 37 | else context.connection().send(new CustomPayloadC2SPacket(new ControlsModePayload(MidnightControlsConfig.controlsMode.getName()))); 38 | }); 39 | registrar.playToClient(FeaturePayload.PACKET_ID, FeaturePayload.codec, (payload, context) -> {}); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /neoforge/src/main/java/eu/midnightdust/midnightcontrols/neoforge/event/PlayerChangeControlsModeEvent.java: -------------------------------------------------------------------------------- 1 | package eu.midnightdust.midnightcontrols.neoforge.event; 2 | 3 | import eu.midnightdust.midnightcontrols.ControlsMode; 4 | import net.minecraft.entity.player.PlayerEntity; 5 | import net.neoforged.bus.api.Event; 6 | import net.neoforged.fml.event.IModBusEvent; 7 | 8 | public class PlayerChangeControlsModeEvent extends Event implements IModBusEvent { 9 | public PlayerChangeControlsModeEvent(PlayerEntity player, ControlsMode controlsMode) { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[2,)" 3 | #issueTrackerURL = "" 4 | license = "MIT License" 5 | 6 | [[mods]] 7 | modId = "midnightcontrols" 8 | version = "${version}" 9 | displayName = "MidnightControls" 10 | logoFile = "icon.png" 11 | authors = "Motschen, LambdAurora" 12 | contributors = "akemin-dayo, DioEgizio, dogtopus, egeesin, EnnuiL, FlashyReese, gyular, Hambaka, Ivanoks, joaoh1, KiskaUWU, Madis0, RaptaG, ronniedude, spudpiggy, TrueHorse" 13 | description = ''' 14 | Adds controller support and enhanced controls overall. 15 | ''' 16 | 17 | [[mixins]] 18 | config = "midnightcontrols.mixins.json" 19 | 20 | [[dependencies.midnightcontrols]] 21 | modId = "neoforge" 22 | mandatory = true 23 | versionRange = "[21.0,)" 24 | ordering = "NONE" 25 | side = "BOTH" 26 | 27 | [[dependencies.midnightcontrols]] 28 | modId = "minecraft" 29 | mandatory = true 30 | versionRange = "[1.21,)" 31 | ordering = "NONE" 32 | side = "BOTH" 33 | 34 | [[dependencies.midnightcontrols]] 35 | modId = "midnightlib" 36 | mandatory = true 37 | versionRange = "[1.0,)" 38 | ordering = "AFTER" 39 | side = "BOTH" 40 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamMidnightDust/MidnightControls/54a43d41c0c0bbbe6191a6be6f956f326dce079d/neoforge/src/main/resources/icon.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url "https://maven.fabricmc.net/" } 4 | maven { url "https://maven.architectury.dev/" } 5 | maven { url "https://maven.neoforged.net/releases" } 6 | gradlePluginPortal() 7 | } 8 | } 9 | 10 | include("common") 11 | include("fabric") 12 | //include("quilt") // Native quilt support is disabled atm, as the Quilt libraries are currently in maintenance mode 13 | include("neoforge") 14 | 15 | rootProject.name = "midnightcontrols" 16 | --------------------------------------------------------------------------------