├── .github ├── renovate.json5 └── workflows │ ├── flex-build.yml │ ├── lifecycle.yml │ ├── publish-api.yml │ ├── run-gametests.yml │ └── test-local-action.yml ├── .gitignore ├── .release-please-manifest.json ├── 1_12 ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── mcmod.info │ └── main │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ ├── mixin │ │ └── MixinMinecraft.java │ │ └── tweaker │ │ └── McRuntimeTestTweaker.java │ └── resources │ ├── mc_runtime_test.mixins.json │ └── pack.mcmeta ├── 1_16 ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ └── main │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ ├── McGameTestRunner.java │ │ └── mixin │ │ ├── ICreateWorldScreen.java │ │ └── MixinMinecraft.java │ └── resources │ └── mc_runtime_test.mixins.json ├── 1_17 ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ └── main │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ ├── McGameTestRunner.java │ │ └── mixin │ │ ├── ICreateWorldScreen.java │ │ └── MixinMinecraft.java │ └── resources │ └── mc_runtime_test.mixins.json ├── 1_18 ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ └── main │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ ├── McGameTestRunner.java │ │ └── mixin │ │ ├── ICreateWorldScreen.java │ │ └── MixinMinecraft.java │ └── resources │ └── mc_runtime_test.mixins.json ├── 1_19 ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ └── main │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ ├── McGameTestRunner.java │ │ └── mixin │ │ ├── ICreateWorldScreen.java │ │ └── MixinMinecraft.java │ └── resources │ └── mc_runtime_test.mixins.json ├── 1_20 ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ ├── main │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ ├── McGameTestRunner.java │ │ │ └── mixin │ │ │ ├── ICreateWorldScreen.java │ │ │ └── MixinMinecraft.java │ └── resources │ │ └── mc_runtime_test.mixins.json │ └── neoforge │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ └── neoforge │ │ └── NeoForgeMod.java │ └── resources │ ├── META-INF │ └── mods.toml │ └── pack.mcmeta ├── 1_20_1 ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ └── main │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ ├── McGameTestRunner.java │ │ └── mixin │ │ ├── ICreateWorldScreen.java │ │ └── MixinMinecraft.java │ └── resources │ └── mc_runtime_test.mixins.json ├── 1_20_6 ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ ├── main │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ ├── McGameTestRunner.java │ │ │ └── mixin │ │ │ ├── ICreateWorldScreen.java │ │ │ └── MixinMinecraft.java │ └── resources │ │ └── mc_runtime_test.mixins.json │ └── neoforge │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ └── neoforge │ │ └── NeoForgeMod.java │ └── resources │ ├── META-INF │ └── neoforge.mods.toml │ └── pack.mcmeta ├── 1_21 ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ ├── main │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ ├── McGameTestRunner.java │ │ │ └── mixin │ │ │ ├── ICreateWorldScreen.java │ │ │ └── MixinMinecraft.java │ └── resources │ │ └── mc_runtime_test.mixins.json │ └── neoforge │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ └── neoforge │ │ └── NeoForgeMod.java │ └── resources │ ├── META-INF │ └── neoforge.mods.toml │ └── pack.mcmeta ├── 1_21_5 ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ ├── main │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ ├── McGameTestRunner.java │ │ │ └── mixin │ │ │ ├── ICreateWorldScreen.java │ │ │ └── MixinMinecraft.java │ └── resources │ │ └── mc_runtime_test.mixins.json │ └── neoforge │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ └── neoforge │ │ └── NeoForgeMod.java │ └── resources │ ├── META-INF │ └── neoforge.mods.toml │ └── pack.mcmeta ├── 1_7_10 ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── mcmod.info │ └── main │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ ├── WorldCreator.java │ │ ├── mixin │ │ └── MixinMinecraft.java │ │ └── tweaker │ │ └── McRuntimeTestTweaker.java │ └── resources │ ├── mc_runtime_test.mixins.json │ └── pack.mcmeta ├── 1_8_9 ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── mc_runtime_test │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── mcmod.info │ └── main │ ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ ├── mixin │ │ └── MixinMinecraft.java │ │ └── tweaker │ │ └── McRuntimeTestTweaker.java │ └── resources │ ├── mc_runtime_test.mixins.json │ └── pack.mcmeta ├── CHANGELOG.md ├── LICENSE ├── README.md ├── action.yml ├── api ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ └── main │ └── java │ └── me │ └── earth │ └── mc_runtime_test │ └── McRuntimeTest.java ├── gametest ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── fabric │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── clientgametest │ │ │ └── FabricEntryPoint.java │ └── resources │ │ └── fabric.mod.json │ ├── lexforge │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── clientgametest │ │ │ └── forge │ │ │ └── ForgeMod.java │ └── resources │ │ ├── META-INF │ │ └── mods.toml │ │ └── pack.mcmeta │ ├── main │ ├── java │ │ └── me │ │ │ └── earth │ │ │ └── clientgametest │ │ │ ├── GameTests.java │ │ │ └── mixin │ │ │ ├── MixinCommands.java │ │ │ └── MixinGameTestRegistry.java │ └── resources │ │ ├── assets │ │ └── clientgametest │ │ │ └── icon.png │ │ ├── clientgametest.mixins.json │ │ └── data │ │ └── clientgametest │ │ ├── gametest │ │ └── structures │ │ │ ├── empty.nbt │ │ │ └── empty.snbt │ │ └── structures │ │ ├── empty.nbt │ │ └── empty.snbt │ └── neoforge │ ├── java │ └── me │ │ └── earth │ │ └── clientgametest │ │ └── neoforge │ │ └── NeoForgeMod.java │ └── resources │ ├── META-INF │ └── mods.toml │ └── pack.mcmeta ├── release-please-config.json └── snapshots ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── fabric └── resources │ └── fabric.mod.json ├── lexforge ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ └── forge │ │ └── ForgeMod.java └── resources │ ├── META-INF │ └── mods.toml │ └── pack.mcmeta ├── main ├── java │ └── me │ │ └── earth │ │ └── mc_runtime_test │ │ ├── McGameTestRunner.java │ │ └── mixin │ │ ├── ICreateWorldScreen.java │ │ └── MixinMinecraft.java └── resources │ └── mc_runtime_test.mixins.json └── neoforge ├── java └── me │ └── earth │ └── mc_runtime_test │ └── neoforge │ └── NeoForgeMod.java └── resources ├── META-INF └── mods.toml └── pack.mcmeta /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | extends: [ 3 | ':enableRenovate', 4 | ':semanticCommits', 5 | ':enablePreCommit', 6 | 'config:best-practices', 7 | ], 8 | platform: 'github', 9 | onboarding: false, 10 | requireConfig: 'optional', 11 | timezone: 'UTC', 12 | dependencyDashboard: true, 13 | platformCommit: 'enabled', 14 | prCreation: 'not-pending', 15 | suppressNotifications: [ 16 | 'prIgnoreNotification', 17 | ], 18 | rebaseWhen: 'conflicted', 19 | packageRules: [ 20 | { 21 | matchUpdateTypes: [ 22 | 'major', 23 | ], 24 | addLabels: [ 25 | 'dependency/major', 26 | ], 27 | }, 28 | { 29 | matchUpdateTypes: [ 30 | 'minor', 31 | ], 32 | addLabels: [ 33 | 'dependency/minor', 34 | ], 35 | }, 36 | { 37 | matchUpdateTypes: [ 38 | 'patch', 39 | ], 40 | addLabels: [ 41 | 'dependency/patch', 42 | ], 43 | }, 44 | ], 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/flex-build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Flex Build 3 | 4 | "on": 5 | workflow_call: 6 | inputs: 7 | dir: 8 | description: Directory to build 9 | required: true 10 | type: string 11 | mc: 12 | description: Minecraft version 13 | type: string 14 | lex: 15 | description: LexForge version 16 | type: string 17 | neo: 18 | description: NeoForge version 19 | type: string 20 | java: 21 | description: Java version 22 | required: true 23 | type: string 24 | upload: 25 | description: Upload the build artifacts 26 | default: true 27 | type: boolean 28 | publish: 29 | description: Run Gradle publish 30 | default: false 31 | type: boolean 32 | 33 | jobs: 34 | build: # TODO: add build attestation and generate then combine gradle dependency graphs for SBOM 35 | name: Build ${{ inputs.dir }}/${{ inputs.mc }} 36 | runs-on: blacksmith-2vcpu-ubuntu-2204 37 | steps: 38 | - name: Checkout 39 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 40 | with: 41 | fetch-depth: 1 42 | sparse-checkout: | 43 | api 44 | ${{ inputs.dir }} 45 | 46 | - name: Cache build 47 | id: cache 48 | uses: useblacksmith/cache@c5fe29eb0efdf1cf4186b9f7fcbbcbc0cf025662 # v5 49 | with: 50 | path: ${{ inputs.dir }}/build 51 | key: build-${{ hashFiles(format('{0}/[a-z]**', inputs.dir), 'api/**') }} 52 | 53 | - if: steps.cache.outputs.cache-hit != 'true' 54 | name: Setup Java 55 | uses: useblacksmith/setup-java@4ef812391eff6e9737ba13bf0356d0f702877a64 # v5 56 | with: 57 | distribution: temurin 58 | java-version: ${{ inputs.java }} 59 | 60 | - if: steps.cache.outputs.cache-hit != 'true' 61 | name: Setup Gradle 62 | uses: useblacksmith/setup-gradle/setup-gradle@7f7b3552fe93f9f80c27f6fb113bb11b7c0fa8eb # v6 63 | with: 64 | workflow-job-context: '{}' # FIXME: avoid this cache duplication workaround 65 | 66 | - if: | 67 | steps.cache.outputs.cache-hit != 'true' 68 | && !inputs.release 69 | name: Gradle build 70 | run: > 71 | ./gradlew build --stacktrace 72 | -Pminecraft_version=${{ inputs.mc }} 73 | -Plexforge_version=${{ inputs.lex }} 74 | -Pneoforge_version=${{ inputs.neo }} 75 | working-directory: ${{ inputs.dir }} 76 | 77 | - if: inputs.release 78 | name: Gradle publish 79 | env: 80 | IS_MAVEN_PUB: true 81 | DEPLOY_TO_GITHUB_PACKAGES_URL: https://maven.pkg.github.com/${{ github.repository }} 82 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 83 | run: ./gradlew publish 84 | working-directory: ${{ inputs.dir }} 85 | 86 | - if: inputs.upload 87 | name: Upload artifacts 88 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 89 | with: 90 | name: jars-${{ inputs.mc || inputs.dir }}${{ inputs.dir == 'gametest' && '-gametest' || '' }} 91 | path: ${{ inputs.dir }}/build/libs/*.jar 92 | -------------------------------------------------------------------------------- /.github/workflows/publish-api.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Publish API 3 | 4 | "on": 5 | release: 6 | types: [created] 7 | 8 | jobs: 9 | publish-api: 10 | name: Publish API 11 | uses: ./.github/workflows/flex-build.yml 12 | with: 13 | dir: api 14 | java: 8 15 | publish: true 16 | upload: false 17 | -------------------------------------------------------------------------------- /.github/workflows/run-gametests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Run gametests 3 | 4 | "on": 5 | workflow_dispatch: # TODO: add relevant inputs 6 | 7 | jobs: # TODO: add cleanup job 8 | build: 9 | name: Build 10 | strategy: 11 | matrix: 12 | include: 13 | - { dir: gametest, mc: 1.20.4, lex: 49.0.38, neo: 219, java: 17 } 14 | - { dir: 1_20, mc: 1.20.4, lex: 49.0.38, neo: 219, java: 17 } 15 | uses: ./.github/workflows/flex-build.yml 16 | with: 17 | dir: ${{ matrix.dir }} 18 | mc: ${{ matrix.mc }} 19 | lex: ${{ matrix.lex }} 20 | neo: ${{ matrix.neo }} 21 | java: ${{ matrix.java }} 22 | 23 | merge: # TODO: simplify away this job 24 | name: Merge 25 | needs: [build] 26 | runs-on: blacksmith-2vcpu-ubuntu-2204 27 | steps: 28 | - name: Merge artifacts 29 | uses: actions/upload-artifact/merge@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 30 | with: 31 | delete-merged: true 32 | 33 | run: 34 | name: Run tests 35 | needs: [merge] 36 | strategy: 37 | matrix: 38 | include: 39 | - { mc: 1.20.4, type: lexforge, modloader: forge, regex: .*forge.*, java: 17 } 40 | - { mc: 1.20.4, type: neoforge, modloader: neoforge, regex: .*neoforge.*, java: 17 } 41 | - { mc: 1.20.4, type: fabric, modloader: fabric, regex: .*fabric.*, java: 17 } 42 | runs-on: blacksmith-2vcpu-ubuntu-2204 43 | steps: 44 | - name: Download artifacts 45 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 46 | 47 | - name: Stage artifacts 48 | run: | 49 | mkdir -p run/mods 50 | cp merged-artifacts/clientgametest-${{ matrix.mc }}-*-${{ matrix.type }}.jar run/mods 51 | cp merged-artifacts/mc-runtime-test-${{ matrix.mc }}-*-${{ matrix.type }}-release.jar run/mods 52 | 53 | - name: Setup temurin-${{ matrix.java }} 54 | uses: useblacksmith/setup-java@4ef812391eff6e9737ba13bf0356d0f702877a64 # v5 55 | with: 56 | java-version: ${{ matrix.java }} 57 | distribution: temurin 58 | 59 | - name: Run game 60 | timeout-minutes: 3 61 | uses: ./ 62 | with: 63 | mc: ${{ matrix.mc }} 64 | modloader: ${{ matrix.modloader }} 65 | regex: ${{ matrix.regex }} 66 | java: ${{ matrix.java }} 67 | mc-runtime-test: none 68 | headlessmc-command: --jvm "-Djava.awt.headless=true -DMcRuntimeGameTestMinExpectedGameTests=1" 69 | fabric-api: 0.97.0 70 | fabric-gametest-api: 1.3.5+85d85a934f 71 | -------------------------------------------------------------------------------- /.github/workflows/test-local-action.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test local action 3 | 4 | "on": 5 | workflow_dispatch: 6 | inputs: 7 | ref: 8 | description: Git ref to checkout before build (i.e. my-feature-branch ) 9 | default: "main" 10 | mc: 11 | description: Minecraft version to run 12 | default: "1.20.4" 13 | modloader: 14 | description: Modloader to install (forge, neoforge, fabric) 15 | default: fabric 16 | regex: 17 | description: Regex to match the modloader jar 18 | default: .*fabric.* 19 | mc-runtime-test: 20 | description: MC-Runtime-Test jar to download (none, lexforge, neoforge, fabric) 21 | default: fabric 22 | java: 23 | description: Java version to use 24 | default: "17" 25 | xvfb: 26 | description: Runs the game with Xvfb (true/false) 27 | default: true 28 | fabric-api: 29 | description: Fabric API version to download (e.g. 0.97.0) or none 30 | default: "0.97.0" 31 | fabric-gametest-api: 32 | description: Fabric GameTest API version (e.g. 1.3.5+85d85a934f) or none 33 | default: "1.3.5+85d85a934f" 34 | hmc-version: 35 | description: HeadlessMC version 36 | default: "2.5.1" 37 | 38 | env: 39 | java_version: ${{ github.event.inputs.java }} 40 | 41 | jobs: 42 | run: 43 | name: Run test 44 | runs-on: blacksmith-2vcpu-ubuntu-2204 45 | steps: 46 | - name: Checkout 47 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 48 | with: 49 | fetch-depth: 1 50 | ref: ${{ github.event.inputs.ref }} 51 | 52 | - name: Setup temurin-${{ env.java_version }} 53 | uses: useblacksmith/setup-java@4ef812391eff6e9737ba13bf0356d0f702877a64 # v5 54 | with: 55 | java-version: ${{ env.java_version }} 56 | distribution: temurin 57 | 58 | - name: Run game 59 | timeout-minutes: 3 60 | uses: ./ 61 | with: 62 | mc: ${{ github.event.inputs.mc }} 63 | modloader: ${{ github.event.inputs.modloader }} 64 | regex: ${{ github.event.inputs.regex }} 65 | mc-runtime-test: ${{ github.event.inputs.mc-runtime-test }} 66 | java: ${{ github.event.inputs.java }} 67 | xvfb: ${{ github.event.inputs.xvfb }} 68 | headlessmc-command: ${{ !inputs.xvfb && '-lwjgl' || '' }} --retries 3 --jvm -Djava.awt.headless=true 69 | fabric-api: ${{ github.event.inputs.fabric-api }} 70 | fabric-gametest-api: ${{ github.event.inputs.fabric-gametest-api }} 71 | hmc-version: ${{ github.event.inputs.hmc-version }} 72 | cache-mc: false 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "3.1.1" 3 | } 4 | -------------------------------------------------------------------------------- /1_12/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.12.2 4 | lexforge_version = 14.23.5.2860 5 | fabric_version = 0.14.20 6 | -------------------------------------------------------------------------------- /1_12/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_12/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_12/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /1_12/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_12/settings.gradle: -------------------------------------------------------------------------------- 1 | 2 | pluginManagement { 3 | repositories { 4 | mavenCentral() 5 | maven { 6 | url = "https://maven.neoforged.net/releases" 7 | } 8 | maven { 9 | url = "https://maven.minecraftforge.net/" 10 | } 11 | maven { 12 | url = "https://maven.fabricmc.net/" 13 | } 14 | maven { 15 | url = "https://maven.wagyourtail.xyz/releases" 16 | } 17 | maven { 18 | url 'https://3arthqu4ke.github.io/maven' 19 | } 20 | maven { 21 | url = "https://maven.wagyourtail.xyz/snapshots" 22 | } 23 | gradlePluginPortal() { 24 | content { 25 | excludeGroup("org.apache.logging.log4j") 26 | } 27 | } 28 | } 29 | } 30 | 31 | include 'api' 32 | project(':api').projectDir = file('../api') 33 | 34 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_12/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.12.2", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_12/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod(modid = "mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_12/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.12.2]" 19 | ordering = "NONE" 20 | side = "BOTH" -------------------------------------------------------------------------------- /1_12/src/lexforge/resources/mcmod.info: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modid": "mcruntimetest", 4 | "name": "MC-Runtime-Test", 5 | "description": "Allows developers to test the client inside their CLI", 6 | "version": "${version}", 7 | "mcversion": "1.12.2", 8 | "url": "https://github.com/headlesshq/mc-runtime-test", 9 | "updateUrl": "", 10 | "authorList": ["3arthqu4ke"], 11 | "credits": "", 12 | "logoFile": "/assets/modid/icon.png", 13 | "screenshots": [], 14 | "dependencies": [] 15 | } 16 | ] -------------------------------------------------------------------------------- /1_12/src/main/java/me/earth/mc_runtime_test/mixin/MixinMinecraft.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import me.earth.mc_runtime_test.McRuntimeTest; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.client.entity.EntityPlayerSP; 6 | import net.minecraft.client.gui.GuiErrorScreen; 7 | import net.minecraft.client.gui.GuiGameOver; 8 | import net.minecraft.client.gui.GuiMainMenu; 9 | import net.minecraft.client.gui.GuiScreen; 10 | import net.minecraft.client.multiplayer.WorldClient; 11 | import net.minecraft.server.integrated.IntegratedServer; 12 | import net.minecraft.world.GameType; 13 | import net.minecraft.world.WorldSettings; 14 | import net.minecraft.world.WorldType; 15 | import org.apache.logging.log4j.Logger; 16 | import org.spongepowered.asm.mixin.Final; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.Shadow; 19 | import org.spongepowered.asm.mixin.Unique; 20 | import org.spongepowered.asm.mixin.injection.At; 21 | import org.spongepowered.asm.mixin.injection.Inject; 22 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 23 | 24 | import java.util.Random; 25 | 26 | @Mixin(Minecraft.class) 27 | public abstract class MixinMinecraft { 28 | @Shadow @Final private static Logger LOGGER; 29 | 30 | @Shadow private IntegratedServer integratedServer; 31 | @Shadow public GuiScreen currentScreen; 32 | @Shadow public EntityPlayerSP player; 33 | @Shadow public WorldClient world; 34 | 35 | @Shadow 36 | volatile boolean running; 37 | @Unique 38 | private boolean mcRuntimeTest$startedLoadingSPWorld = false; 39 | 40 | @Shadow public abstract void launchIntegratedServer(String par1, String par2, WorldSettings par3); 41 | 42 | @Shadow public abstract void displayGuiScreen(GuiScreen par1); 43 | 44 | @Inject(method = "displayGuiScreen", at = @At("HEAD")) 45 | private void displayGuiScreenHook(GuiScreen guiScreenIn, CallbackInfo ci) { 46 | if (!McRuntimeTest.screenHook()) { 47 | return; 48 | } 49 | 50 | if (guiScreenIn instanceof GuiErrorScreen) { 51 | running = false; 52 | throw new RuntimeException("Error Screen " + guiScreenIn); 53 | } else if (guiScreenIn instanceof GuiGameOver && player != null) { 54 | player.respawnPlayer(); 55 | } 56 | } 57 | 58 | @Inject(method = "runTick", at = @At("HEAD")) 59 | private void tickHook(CallbackInfo ci) { 60 | if (!McRuntimeTest.tickHook()) { 61 | return; 62 | } 63 | 64 | if (currentScreen instanceof GuiMainMenu) { 65 | if (!mcRuntimeTest$startedLoadingSPWorld) { 66 | mc_runtime_test$loadSinglePlayerWorld(); 67 | mcRuntimeTest$startedLoadingSPWorld = true; 68 | } 69 | } else { 70 | LOGGER.info("Waiting for overlay to disappear..."); 71 | } 72 | 73 | if (player != null && world != null) { 74 | if (currentScreen == null) { 75 | if (!world.getChunk(((int) player.posX) >> 4, ((int) player.posZ) >> 4).isEmpty()) { 76 | if (player.ticksExisted < 100) { 77 | LOGGER.info("Waiting " + (100 - player.ticksExisted) + " ticks before testing..."); 78 | } else { 79 | LOGGER.info("Test successful!"); 80 | running = false; 81 | } 82 | } else { 83 | LOGGER.info("Players chunk not yet loaded, " + player + ": cores: " + Runtime.getRuntime().availableProcessors() 84 | + ", server running: " + (integratedServer == null ? "null" : integratedServer.isServerRunning())); 85 | } 86 | } else { 87 | LOGGER.info("Screen not yet null: " + currentScreen); 88 | } 89 | } else { 90 | LOGGER.info("Waiting for player to load..."); 91 | } 92 | } 93 | 94 | @Unique 95 | private void mc_runtime_test$loadSinglePlayerWorld() { 96 | displayGuiScreen(null); 97 | long seed = (new Random()).nextLong(); 98 | WorldSettings worldsettings = new WorldSettings(seed, GameType.SURVIVAL, true, false, WorldType.DEFAULT); 99 | launchIntegratedServer("new_world", "New World", worldsettings); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /1_12/src/main/java/me/earth/mc_runtime_test/tweaker/McRuntimeTestTweaker.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.tweaker; 2 | 3 | import io.github.impactdevelopment.simpletweaker.SimpleTweaker; 4 | import net.minecraft.launchwrapper.LaunchClassLoader; 5 | import org.spongepowered.asm.launch.MixinBootstrap; 6 | import org.spongepowered.asm.mixin.MixinEnvironment; 7 | import org.spongepowered.asm.mixin.Mixins; 8 | import org.spongepowered.tools.obfuscation.mcp.ObfuscationServiceMCP; 9 | 10 | import java.io.IOException; 11 | 12 | @SuppressWarnings("unused") 13 | public class McRuntimeTestTweaker extends SimpleTweaker { 14 | @Override 15 | public void injectIntoClassLoader(LaunchClassLoader classLoader) { 16 | super.injectIntoClassLoader(classLoader); 17 | MixinBootstrap.init(); 18 | 19 | String obfCtx = ObfuscationServiceMCP.NOTCH; 20 | try { 21 | if (classLoader.getClassBytes( 22 | "net.minecraftforge.common.ForgeHooks") != null) { 23 | obfCtx = ObfuscationServiceMCP.SEARGE; 24 | } 25 | } catch (IOException ignored) { } 26 | 27 | MixinEnvironment.getDefaultEnvironment() 28 | .setSide(MixinEnvironment.Side.CLIENT); 29 | MixinEnvironment.getDefaultEnvironment() 30 | .setObfuscationContext(obfCtx); 31 | 32 | Mixins.addConfiguration("mc_runtime_test.mixins.json"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /1_12/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.7.11", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "MixinMinecraft" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } -------------------------------------------------------------------------------- /1_12/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "Mc Runtime Test resources", 4 | "pack_format": 3 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /1_16/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /1_16/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.16.5 4 | mapping_version = 1 5 | lexforge_version = 36.2.42 6 | fabric_version = 0.14.24 7 | # Whether to use the headlessmc lwjgl agent or not 8 | hmc.lwjgl=true 9 | -------------------------------------------------------------------------------- /1_16/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_16/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_16/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /1_16/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_16/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.neoforged.net/releases" 6 | } 7 | maven { 8 | url = "https://maven.minecraftforge.net/" 9 | } 10 | maven { 11 | url = "https://maven.fabricmc.net/" 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/releases" 15 | } 16 | maven { 17 | url 'https://3arthqu4ke.github.io/maven' 18 | } 19 | maven { 20 | url = "https://maven.wagyourtail.xyz/snapshots" 21 | } 22 | gradlePluginPortal() { 23 | content { 24 | excludeGroup("org.apache.logging.log4j") 25 | } 26 | } 27 | } 28 | } 29 | 30 | include 'api' 31 | project(':api').projectDir = file('../api') 32 | 33 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_16/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.16.5", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_16/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_16/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.16.5]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_16/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_16/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import net.minecraft.core.BlockPos; 4 | import net.minecraft.gametest.framework.*; 5 | import net.minecraft.server.MinecraftServer; 6 | import net.minecraft.server.level.ServerLevel; 7 | import net.minecraft.world.entity.player.Player; 8 | import net.minecraft.world.level.block.Rotation; 9 | import net.minecraft.world.level.levelgen.Heightmap; 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | 13 | import java.util.Collection; 14 | import java.util.Objects; 15 | import java.util.UUID; 16 | import java.util.concurrent.ExecutionException; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.TimeoutException; 19 | 20 | /** 21 | * Similar to running the "/test runall" command. 22 | */ 23 | public class McGameTestRunner { 24 | private static final Logger LOGGER = LogManager.getLogger(); 25 | 26 | /** 27 | * Basically what happens in {@link TestCommand} when "runall" is used. 28 | * We just exit with an error code if a test fails. 29 | * 30 | * @param playerUUID the uuid of the player. 31 | * @param server the server to run the tests on. 32 | */ 33 | public static MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 34 | return server.submit(() -> { 35 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 36 | ServerLevel level = (ServerLevel) player.level; 37 | GameTestRunner.clearMarkers(level); 38 | Collection testFunctions = GameTestRegistry.getAllTestFunctions(); 39 | LOGGER.info("TestFunctions: " + testFunctions); 40 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 41 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 42 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 43 | } 44 | 45 | GameTestRegistry.forgetFailedTests(); 46 | 47 | BlockPos pos = createTestPositionAround(player, level); 48 | Rotation rotation = StructureUtils.getRotationForRotationSteps(0); 49 | Collection tests = GameTestRunner.runTests(testFunctions, pos, rotation, level, GameTestTicker.singleton, 8); 50 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(tests); 51 | multipleTestTracker.addFailureListener(gameTestInfo -> { 52 | LOGGER.error("Test failed: " + gameTestInfo); 53 | if (gameTestInfo.getError() != null) { 54 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 55 | } 56 | 57 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 58 | System.exit(-1); 59 | } 60 | }); 61 | 62 | return multipleTestTracker; 63 | }).get(60, TimeUnit.SECONDS); 64 | } 65 | 66 | private static BlockPos createTestPositionAround(Player player, ServerLevel level) { 67 | BlockPos blockPos = new BlockPos(player.position()); 68 | int y = level.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, blockPos).getY(); 69 | return new BlockPos(blockPos.getX(), y + 1, blockPos.getZ() + 3); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /1_16/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /1_16/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /1_17/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /1_17/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.17.1 4 | mapping_version = 1 5 | lexforge_version = 37.1.1 6 | fabric_version = 0.14.24 7 | # Whether to use the headlessmc lwjgl agent or not 8 | hmc.lwjgl=true 9 | -------------------------------------------------------------------------------- /1_17/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_17/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_17/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /1_17/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_17/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.neoforged.net/releases" 6 | } 7 | maven { 8 | url = "https://maven.minecraftforge.net/" 9 | } 10 | maven { 11 | url = "https://maven.fabricmc.net/" 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/releases" 15 | } 16 | maven { 17 | url 'https://3arthqu4ke.github.io/maven' 18 | } 19 | maven { 20 | url = "https://maven.wagyourtail.xyz/snapshots" 21 | } 22 | gradlePluginPortal() { 23 | content { 24 | excludeGroup("org.apache.logging.log4j") 25 | } 26 | } 27 | } 28 | } 29 | 30 | include 'api' 31 | project(':api').projectDir = file('../api') 32 | 33 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_17/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.17.1", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_17/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_17/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.17.1]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_17/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_17/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import net.minecraft.core.BlockPos; 4 | import net.minecraft.gametest.framework.*; 5 | import net.minecraft.server.MinecraftServer; 6 | import net.minecraft.server.level.ServerLevel; 7 | import net.minecraft.world.entity.player.Player; 8 | import net.minecraft.world.level.block.Rotation; 9 | import net.minecraft.world.level.levelgen.Heightmap; 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | 13 | import java.util.Collection; 14 | import java.util.Objects; 15 | import java.util.UUID; 16 | import java.util.concurrent.ExecutionException; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.TimeoutException; 19 | 20 | /** 21 | * Similar to running the "/test runall" command. 22 | */ 23 | public class McGameTestRunner { 24 | private static final Logger LOGGER = LogManager.getLogger(); 25 | 26 | /** 27 | * Basically what happens in {@link TestCommand} when "runall" is used. 28 | * We just exit with an error code if a test fails. 29 | * 30 | * @param playerUUID the uuid of the player. 31 | * @param server the server to run the tests on. 32 | */ 33 | public static MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 34 | return server.submit(() -> { 35 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 36 | ServerLevel level = (ServerLevel) player.level; 37 | GameTestRunner.clearMarkers(level); 38 | Collection testFunctions = GameTestRegistry.getAllTestFunctions(); 39 | LOGGER.info("TestFunctions: " + testFunctions); 40 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 41 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 42 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 43 | } 44 | 45 | GameTestRegistry.forgetFailedTests(); 46 | 47 | BlockPos pos = createTestPositionAround(player, level); 48 | Rotation rotation = StructureUtils.getRotationForRotationSteps(0); 49 | Collection tests = GameTestRunner.runTests(testFunctions, pos, rotation, level, GameTestTicker.SINGLETON, 8); 50 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(tests); 51 | multipleTestTracker.addFailureListener(gameTestInfo -> { 52 | LOGGER.error("Test failed: " + gameTestInfo); 53 | if (gameTestInfo.getError() != null) { 54 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 55 | } 56 | 57 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 58 | System.exit(-1); 59 | } 60 | }); 61 | 62 | return multipleTestTracker; 63 | }).get(60, TimeUnit.SECONDS); 64 | } 65 | 66 | private static BlockPos createTestPositionAround(Player player, ServerLevel level) { 67 | BlockPos blockPos = player.getOnPos(); 68 | int y = level.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, blockPos).getY(); 69 | return new BlockPos(blockPos.getX(), y + 1, blockPos.getZ() + 3); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /1_17/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /1_17/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /1_18/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /1_18/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.18.2 4 | mapping_version = 1 5 | lexforge_version = 40.2.18 6 | fabric_version = 0.14.24 7 | # Whether to use the headlessmc lwjgl agent or not 8 | hmc.lwjgl=true 9 | -------------------------------------------------------------------------------- /1_18/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_18/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_18/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /1_18/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_18/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.neoforged.net/releases" 6 | } 7 | maven { 8 | url = "https://maven.minecraftforge.net/" 9 | } 10 | maven { 11 | url = "https://maven.fabricmc.net/" 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/releases" 15 | } 16 | maven { 17 | url 'https://3arthqu4ke.github.io/maven' 18 | } 19 | maven { 20 | url = "https://maven.wagyourtail.xyz/snapshots" 21 | } 22 | gradlePluginPortal() { 23 | content { 24 | excludeGroup("org.apache.logging.log4j") 25 | } 26 | } 27 | } 28 | } 29 | 30 | include 'api' 31 | project(':api').projectDir = file('../api') 32 | 33 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_18/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.18.2", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_18/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_18/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.18.2]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_18/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_18/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.gametest.framework.*; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.server.level.ServerLevel; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.level.block.Rotation; 10 | import net.minecraft.world.level.levelgen.Heightmap; 11 | import org.slf4j.Logger; 12 | 13 | import java.util.Collection; 14 | import java.util.Objects; 15 | import java.util.UUID; 16 | import java.util.concurrent.ExecutionException; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.TimeoutException; 19 | 20 | /** 21 | * Similar to running the "/test runall" command. 22 | */ 23 | public class McGameTestRunner { 24 | private static final Logger LOGGER = LogUtils.getLogger(); 25 | 26 | /** 27 | * Basically what happens in {@link TestCommand} when "runall" is used. 28 | * We just exit with an error code if a test fails. 29 | * 30 | * @param playerUUID the uuid of the player. 31 | * @param server the server to run the tests on. 32 | */ 33 | public static MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 34 | return server.submit(() -> { 35 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 36 | ServerLevel level = (ServerLevel) player.level; 37 | GameTestRunner.clearMarkers(level); 38 | Collection testFunctions = GameTestRegistry.getAllTestFunctions(); 39 | LOGGER.info("TestFunctions: " + testFunctions); 40 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 41 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 42 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 43 | } 44 | 45 | GameTestRegistry.forgetFailedTests(); 46 | 47 | BlockPos pos = createTestPositionAround(player, level); 48 | Rotation rotation = StructureUtils.getRotationForRotationSteps(0); 49 | Collection tests = GameTestRunner.runTests(testFunctions, pos, rotation, level, GameTestTicker.SINGLETON, 8); 50 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(tests); 51 | multipleTestTracker.addFailureListener(gameTestInfo -> { 52 | LOGGER.error("Test failed: " + gameTestInfo); 53 | if (gameTestInfo.getError() != null) { 54 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 55 | } 56 | 57 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 58 | System.exit(-1); 59 | } 60 | }); 61 | 62 | return multipleTestTracker; 63 | }).get(60, TimeUnit.SECONDS); 64 | } 65 | 66 | private static BlockPos createTestPositionAround(Player player, ServerLevel level) { 67 | BlockPos blockPos = player.getOnPos(); 68 | int y = level.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, blockPos).getY(); 69 | return new BlockPos(blockPos.getX(), y + 1, blockPos.getZ() + 3); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /1_18/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /1_18/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /1_19/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /1_19/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.19.4 4 | mapping_version = 1 5 | lexforge_version = 45.2.9 6 | fabric_version = 0.14.24 7 | # Whether to use the headlessmc lwjgl agent or not 8 | hmc.lwjgl=true 9 | -------------------------------------------------------------------------------- /1_19/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_19/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_19/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /1_19/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_19/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.neoforged.net/releases" 6 | } 7 | maven { 8 | url = "https://maven.minecraftforge.net/" 9 | } 10 | maven { 11 | url = "https://maven.fabricmc.net/" 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/releases" 15 | } 16 | maven { 17 | url 'https://3arthqu4ke.github.io/maven' 18 | } 19 | maven { 20 | url = "https://maven.wagyourtail.xyz/snapshots" 21 | } 22 | gradlePluginPortal() { 23 | content { 24 | excludeGroup("org.apache.logging.log4j") 25 | } 26 | } 27 | } 28 | } 29 | 30 | include 'api' 31 | project(':api').projectDir = file('../api') 32 | 33 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_19/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.19.0", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_19/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_19/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.19,1.19.4]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_19/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_19/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.gametest.framework.*; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.server.level.ServerLevel; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.level.block.Rotation; 10 | import net.minecraft.world.level.levelgen.Heightmap; 11 | import org.slf4j.Logger; 12 | 13 | import java.util.Collection; 14 | import java.util.Objects; 15 | import java.util.UUID; 16 | import java.util.concurrent.ExecutionException; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.TimeoutException; 19 | 20 | /** 21 | * Similar to running the "/test runall" command. 22 | */ 23 | public class McGameTestRunner { 24 | private static final Logger LOGGER = LogUtils.getLogger(); 25 | 26 | /** 27 | * Basically what happens in {@link TestCommand} when "runall" is used. 28 | * We just exit with an error code if a test fails. 29 | * 30 | * @param playerUUID the uuid of the player. 31 | * @param server the server to run the tests on. 32 | */ 33 | public static MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 34 | return server.submit(() -> { 35 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 36 | ServerLevel level = (ServerLevel) player.level; 37 | GameTestRunner.clearMarkers(level); 38 | Collection testFunctions = GameTestRegistry.getAllTestFunctions(); 39 | LOGGER.info("TestFunctions: " + testFunctions); 40 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 41 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 42 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 43 | } 44 | 45 | GameTestRegistry.forgetFailedTests(); 46 | 47 | BlockPos pos = createTestPositionAround(player, level); 48 | Rotation rotation = StructureUtils.getRotationForRotationSteps(0); 49 | Collection tests = GameTestRunner.runTests(testFunctions, pos, rotation, level, GameTestTicker.SINGLETON, 8); 50 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(tests); 51 | multipleTestTracker.addFailureListener(gameTestInfo -> { 52 | LOGGER.error("Test failed: " + gameTestInfo); 53 | if (gameTestInfo.getError() != null) { 54 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 55 | } 56 | 57 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 58 | System.exit(-1); 59 | } 60 | }); 61 | 62 | return multipleTestTracker; 63 | }).get(60, TimeUnit.SECONDS); 64 | } 65 | 66 | private static BlockPos createTestPositionAround(Player player, ServerLevel level) { 67 | BlockPos blockPos = player.getOnPos(); 68 | int y = level.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, blockPos).getY(); 69 | return new BlockPos(blockPos.getX(), y + 1, blockPos.getZ() + 3); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /1_19/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /1_19/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /1_20/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /1_20/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.20.4 4 | mapping_version = 1 5 | neoforge_version = 1-beta 6 | lexforge_version = 49.0.30 7 | fabric_version = 0.15.9 8 | # Whether to use the headlessmc lwjgl agent or not 9 | hmc.lwjgl=false 10 | -------------------------------------------------------------------------------- /1_20/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_20/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_20/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /1_20/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_20/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.neoforged.net/releases" 6 | } 7 | maven { 8 | url = "https://maven.minecraftforge.net/" 9 | } 10 | maven { 11 | url = "https://maven.fabricmc.net/" 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/releases" 15 | } 16 | maven { 17 | url 'https://3arthqu4ke.github.io/maven' 18 | } 19 | maven { 20 | url = "https://maven.wagyourtail.xyz/snapshots" 21 | } 22 | gradlePluginPortal() { 23 | content { 24 | excludeGroup("org.apache.logging.log4j") 25 | } 26 | } 27 | } 28 | } 29 | 30 | include 'api' 31 | project(':api').projectDir = file('../api') 32 | 33 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_20/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.20.2", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_20/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_20/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.20.2,1.20.4]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_20/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_20/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.gametest.framework.*; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.server.level.ServerLevel; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.level.block.Rotation; 10 | import net.minecraft.world.level.levelgen.Heightmap; 11 | import org.slf4j.Logger; 12 | 13 | import java.util.Collection; 14 | import java.util.Objects; 15 | import java.util.UUID; 16 | import java.util.concurrent.ExecutionException; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.TimeoutException; 19 | 20 | /** 21 | * Similar to running the "/test runall" command. 22 | */ 23 | public class McGameTestRunner { 24 | private static final Logger LOGGER = LogUtils.getLogger(); 25 | 26 | /** 27 | * Basically what happens in {@link TestCommand} when "runall" is used. 28 | * We just exit with an error code if a test fails. 29 | * 30 | * @param playerUUID the uuid of the player. 31 | * @param server the server to run the tests on. 32 | */ 33 | public static MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 34 | return server.submit(() -> { 35 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 36 | ServerLevel level = (ServerLevel) player.level(); 37 | GameTestRunner.clearMarkers(level); 38 | Collection testFunctions = GameTestRegistry.getAllTestFunctions(); 39 | LOGGER.info("TestFunctions: " + testFunctions); 40 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 41 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 42 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 43 | } 44 | 45 | GameTestRegistry.forgetFailedTests(); 46 | 47 | BlockPos pos = createTestPositionAround(player, level); 48 | Rotation rotation = StructureUtils.getRotationForRotationSteps(0); 49 | Collection tests = GameTestRunner.runTests(testFunctions, pos, rotation, level, GameTestTicker.SINGLETON, 8); 50 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(tests); 51 | multipleTestTracker.addFailureListener(gameTestInfo -> { 52 | LOGGER.error("Test failed: " + gameTestInfo); 53 | if (gameTestInfo.getError() != null) { 54 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 55 | } 56 | 57 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 58 | System.exit(-1); 59 | } 60 | }); 61 | 62 | return multipleTestTracker; 63 | }).get(60, TimeUnit.SECONDS); 64 | } 65 | 66 | private static BlockPos createTestPositionAround(Player player, ServerLevel level) { 67 | BlockPos blockPos = player.getOnPos(); 68 | int y = level.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, blockPos).getY(); 69 | return new BlockPos(blockPos.getX(), y + 1, blockPos.getZ() + 3); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /1_20/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /1_20/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /1_20/src/neoforge/java/me/earth/mc_runtime_test/neoforge/NeoForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.neoforge; 2 | 3 | import net.neoforged.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class NeoForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_20/src/neoforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.20.2,1.20.4]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_20/src/neoforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_20_1/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /1_20_1/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.20.1 4 | mapping_version = 1 5 | lexforge_version = 47.2.23 6 | fabric_version = 0.14.24 7 | # Whether to use the headlessmc lwjgl agent or not 8 | hmc.lwjgl=true 9 | -------------------------------------------------------------------------------- /1_20_1/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_20_1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_20_1/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /1_20_1/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_20_1/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.minecraftforge.net/" 6 | } 7 | maven { 8 | url = "https://maven.fabricmc.net/" 9 | } 10 | maven { 11 | url = "https://maven.wagyourtail.xyz/releases" 12 | } 13 | maven { 14 | url 'https://3arthqu4ke.github.io/maven' 15 | } 16 | maven { 17 | url = "https://maven.wagyourtail.xyz/snapshots" 18 | } 19 | gradlePluginPortal() { 20 | content { 21 | excludeGroup("org.apache.logging.log4j") 22 | } 23 | } 24 | } 25 | } 26 | 27 | include 'api' 28 | project(':api').projectDir = file('../api') 29 | 30 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_20_1/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.20.1", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_20_1/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_20_1/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.20.1]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_20_1/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_20_1/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.gametest.framework.*; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.server.level.ServerLevel; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.level.block.Rotation; 10 | import net.minecraft.world.level.levelgen.Heightmap; 11 | import org.slf4j.Logger; 12 | 13 | import java.util.Collection; 14 | import java.util.Objects; 15 | import java.util.UUID; 16 | import java.util.concurrent.ExecutionException; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.TimeoutException; 19 | 20 | /** 21 | * Similar to running the "/test runall" command. 22 | */ 23 | public class McGameTestRunner { 24 | private static final Logger LOGGER = LogUtils.getLogger(); 25 | 26 | /** 27 | * Basically what happens in {@link TestCommand} when "runall" is used. 28 | * We just exit with an error code if a test fails. 29 | * 30 | * @param playerUUID the uuid of the player. 31 | * @param server the server to run the tests on. 32 | */ 33 | public static MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 34 | return server.submit(() -> { 35 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 36 | ServerLevel level = (ServerLevel) player.level(); 37 | GameTestRunner.clearMarkers(level); 38 | Collection testFunctions = GameTestRegistry.getAllTestFunctions(); 39 | LOGGER.info("TestFunctions: " + testFunctions); 40 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 41 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 42 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 43 | } 44 | 45 | GameTestRegistry.forgetFailedTests(); 46 | 47 | BlockPos pos = createTestPositionAround(player, level); 48 | Rotation rotation = StructureUtils.getRotationForRotationSteps(0); 49 | Collection tests = GameTestRunner.runTests(testFunctions, pos, rotation, level, GameTestTicker.SINGLETON, 8); 50 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(tests); 51 | multipleTestTracker.addFailureListener(gameTestInfo -> { 52 | LOGGER.error("Test failed: " + gameTestInfo); 53 | if (gameTestInfo.getError() != null) { 54 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 55 | } 56 | 57 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 58 | System.exit(-1); 59 | } 60 | }); 61 | 62 | return multipleTestTracker; 63 | }).get(60, TimeUnit.SECONDS); 64 | } 65 | 66 | private static BlockPos createTestPositionAround(Player player, ServerLevel level) { 67 | BlockPos blockPos = player.getOnPos(); 68 | int y = level.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, blockPos).getY(); 69 | return new BlockPos(blockPos.getX(), y + 1, blockPos.getZ() + 3); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /1_20_1/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /1_20_1/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /1_20_6/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /1_20_6/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.20.6 4 | mapping_version = 1 5 | neoforge_version = 119 6 | lexforge_version = 50.1.10 7 | fabric_version = 0.15.9 8 | # Whether to use the headlessmc lwjgl agent or not 9 | hmc.lwjgl=false 10 | -------------------------------------------------------------------------------- /1_20_6/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_20_6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_20_6/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /1_20_6/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_20_6/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.neoforged.net/releases" 6 | } 7 | maven { 8 | url = "https://maven.minecraftforge.net/" 9 | } 10 | maven { 11 | url = "https://maven.fabricmc.net/" 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/releases" 15 | } 16 | maven { 17 | url 'https://3arthqu4ke.github.io/maven' 18 | } 19 | maven { 20 | url = "https://maven.wagyourtail.xyz/snapshots" 21 | } 22 | gradlePluginPortal() { 23 | content { 24 | excludeGroup("org.apache.logging.log4j") 25 | } 26 | } 27 | } 28 | } 29 | 30 | include 'api' 31 | project(':api').projectDir = file('../api') 32 | 33 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_20_6/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.20.5", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_20_6/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_20_6/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.20.5,)" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_20_6/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_20_6/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.gametest.framework.*; 5 | import net.minecraft.server.MinecraftServer; 6 | import net.minecraft.server.level.ServerLevel; 7 | import net.minecraft.world.entity.player.Player; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.slf4j.Logger; 10 | 11 | import java.util.Collection; 12 | import java.util.Objects; 13 | import java.util.UUID; 14 | import java.util.concurrent.ExecutionException; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.concurrent.TimeoutException; 17 | 18 | /** 19 | * Similar to running the "/test runall" command. 20 | */ 21 | public class McGameTestRunner { 22 | private static final Logger LOGGER = LogUtils.getLogger(); 23 | 24 | /** 25 | * Basically what happens in {@link TestCommand} when "runall" is used. 26 | * We just exit with an error code if a test fails. 27 | * 28 | * @param playerUUID the uuid of the player. 29 | * @param server the server to run the tests on. 30 | */ 31 | public static @Nullable MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 32 | return server.submit(() -> { 33 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 34 | ServerLevel level = (ServerLevel) player.level(); 35 | GameTestRunner.clearMarkers(level); 36 | Collection testFunctions = GameTestRegistry.getAllTestFunctions(); 37 | LOGGER.info("TestFunctions: " + testFunctions); 38 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 39 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 40 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 41 | } else if (testFunctions.isEmpty()) { 42 | return null; 43 | } 44 | 45 | GameTestRegistry.forgetFailedTests(); 46 | 47 | Collection batches = GameTestBatchFactory.fromTestFunction(testFunctions, level); 48 | GameTestRunner gameTestRunner = GameTestRunner.Builder.fromBatches(batches, level).build(); 49 | gameTestRunner.start(); 50 | 51 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(gameTestRunner.getTestInfos()); 52 | multipleTestTracker.addFailureListener(gameTestInfo -> { 53 | LOGGER.error("Test failed: " + gameTestInfo); 54 | if (gameTestInfo.getError() != null) { 55 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 56 | } 57 | 58 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 59 | System.exit(-1); 60 | } 61 | }); 62 | 63 | return multipleTestTracker; 64 | }).get(60, TimeUnit.SECONDS); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /1_20_6/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /1_20_6/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /1_20_6/src/neoforge/java/me/earth/mc_runtime_test/neoforge/NeoForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.neoforge; 2 | 3 | import net.neoforged.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class NeoForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_20_6/src/neoforge/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.20.5,)" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_20_6/src/neoforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_21/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /1_21/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.21.3 4 | # TODO: can this be removed? 5 | mapping_version = 1 6 | neoforge_version = 11-beta 7 | lexforge_version = 53.0.7 8 | fabric_version = 0.15.9 9 | # Whether to use the headlessmc lwjgl agent or not 10 | hmc.lwjgl=false -------------------------------------------------------------------------------- /1_21/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_21/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_21/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /1_21/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_21/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.neoforged.net/releases" 6 | } 7 | maven { 8 | url = "https://maven.minecraftforge.net/" 9 | } 10 | maven { 11 | url = "https://maven.fabricmc.net/" 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/releases" 15 | } 16 | maven { 17 | url 'https://3arthqu4ke.github.io/maven' 18 | } 19 | maven { 20 | url = "https://maven.wagyourtail.xyz/snapshots" 21 | } 22 | gradlePluginPortal() { 23 | content { 24 | excludeGroup("org.apache.logging.log4j") 25 | } 26 | } 27 | } 28 | } 29 | 30 | include 'api' 31 | project(':api').projectDir = file('../api') 32 | 33 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_21/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.21.0", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_21/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_21/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.21.0,)" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_21/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_21/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.gametest.framework.*; 5 | import net.minecraft.server.MinecraftServer; 6 | import net.minecraft.server.level.ServerLevel; 7 | import net.minecraft.world.entity.player.Player; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.slf4j.Logger; 10 | 11 | import java.util.Collection; 12 | import java.util.Objects; 13 | import java.util.UUID; 14 | import java.util.concurrent.ExecutionException; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.concurrent.TimeoutException; 17 | 18 | /** 19 | * Similar to running the "/test runall" command. 20 | */ 21 | public class McGameTestRunner { 22 | private static final Logger LOGGER = LogUtils.getLogger(); 23 | 24 | /** 25 | * Basically what happens in {@link TestCommand} when "runall" is used. 26 | * We just exit with an error code if a test fails. 27 | * 28 | * @param playerUUID the uuid of the player. 29 | * @param server the server to run the tests on. 30 | */ 31 | public static @Nullable MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 32 | return server.submit(() -> { 33 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 34 | ServerLevel level = (ServerLevel) player.level(); 35 | GameTestRunner.clearMarkers(level); 36 | Collection testFunctions = GameTestRegistry.getAllTestFunctions(); 37 | LOGGER.info("TestFunctions: " + testFunctions); 38 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 39 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 40 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 41 | } else if (testFunctions.isEmpty()) { 42 | return null; 43 | } 44 | 45 | GameTestRegistry.forgetFailedTests(); 46 | 47 | Collection batches = GameTestBatchFactory.fromTestFunction(testFunctions, level); 48 | GameTestRunner gameTestRunner = GameTestRunner.Builder.fromBatches(batches, level).build(); 49 | gameTestRunner.start(); 50 | 51 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(gameTestRunner.getTestInfos()); 52 | multipleTestTracker.addFailureListener(gameTestInfo -> { 53 | LOGGER.error("Test failed: " + gameTestInfo); 54 | if (gameTestInfo.getError() != null) { 55 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 56 | } 57 | 58 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 59 | System.exit(-1); 60 | } 61 | }); 62 | 63 | return multipleTestTracker; 64 | }).get(60, TimeUnit.SECONDS); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /1_21/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /1_21/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /1_21/src/neoforge/java/me/earth/mc_runtime_test/neoforge/NeoForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.neoforge; 2 | 3 | import net.neoforged.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class NeoForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_21/src/neoforge/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.21.0,)" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_21/src/neoforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_21_5/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /1_21_5/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.21.5 4 | # TODO: can this be removed? 5 | mapping_version = 1 6 | neoforge_version = 24-beta 7 | lexforge_version = 55.0.3 8 | fabric_version = 0.15.9 9 | # Whether to use the headlessmc lwjgl agent or not 10 | hmc.lwjgl=false 11 | -------------------------------------------------------------------------------- /1_21_5/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_21_5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_21_5/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /1_21_5/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_21_5/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.neoforged.net/releases" 6 | } 7 | maven { 8 | url = "https://maven.minecraftforge.net/" 9 | } 10 | maven { 11 | url = "https://maven.fabricmc.net/" 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/releases" 15 | } 16 | maven { 17 | url 'https://3arthqu4ke.github.io/maven' 18 | } 19 | maven { 20 | url = "https://maven.wagyourtail.xyz/snapshots" 21 | } 22 | gradlePluginPortal() { 23 | content { 24 | excludeGroup("org.apache.logging.log4j") 25 | } 26 | } 27 | } 28 | } 29 | 30 | include 'api' 31 | project(':api').projectDir = file('../api') 32 | 33 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_21_5/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.21.0", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_21_5/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_21_5/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.21.0,)" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_21_5/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_21_5/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import com.mojang.brigadier.Command; 4 | import com.mojang.brigadier.CommandDispatcher; 5 | import com.mojang.brigadier.context.CommandContext; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import com.mojang.logging.LogUtils; 8 | import net.minecraft.commands.CommandSource; 9 | import net.minecraft.commands.CommandSourceStack; 10 | import net.minecraft.commands.Commands; 11 | import net.minecraft.core.Holder; 12 | import net.minecraft.gametest.framework.*; 13 | import net.minecraft.server.MinecraftServer; 14 | import net.minecraft.server.level.ServerLevel; 15 | import net.minecraft.world.entity.player.Player; 16 | import net.minecraft.world.level.block.Rotation; 17 | import org.jetbrains.annotations.Nullable; 18 | import org.slf4j.Logger; 19 | 20 | import java.util.Collection; 21 | import java.util.Objects; 22 | import java.util.Optional; 23 | import java.util.UUID; 24 | import java.util.concurrent.ExecutionException; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.concurrent.TimeoutException; 27 | import java.util.concurrent.atomic.AtomicReference; 28 | import java.util.stream.Stream; 29 | 30 | /** 31 | * Similar to running the "/test runall" command. 32 | */ 33 | public class McGameTestRunner { 34 | private static final Logger LOGGER = LogUtils.getLogger(); 35 | 36 | /** 37 | * Basically what happens in {@link TestCommand} when "runall" is used. 38 | * We just exit with an error code if a test fails. 39 | * 40 | * @param playerUUID the uuid of the player. 41 | * @param server the server to run the tests on. 42 | */ 43 | public static @Nullable MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 44 | return server.submit(() -> { 45 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 46 | ServerLevel level = (ServerLevel) player.level(); 47 | GameTestRunner.clearMarkers(level); 48 | 49 | AtomicReference> result = new AtomicReference<>(); 50 | CommandDispatcher dispatcher = new CommandDispatcher<>(); 51 | dispatcher.register(Commands.literal("test").executes(ctx -> { 52 | Collection> testFunctions = TestFinder.builder().allNearby(ctx).findTests().toList(); 53 | LOGGER.info("TestFunctions: " + testFunctions); 54 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 55 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 56 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 57 | } else if (testFunctions.isEmpty()) { 58 | result.set(Optional.empty()); 59 | return Command.SINGLE_SUCCESS; 60 | } 61 | 62 | GameTestBatchFactory.TestDecorator testDecorator = (reference, serverLevel) -> Stream.of( 63 | new GameTestInfo(reference, Rotation.NONE, serverLevel, RetryOptions.noRetries()) 64 | ); 65 | Collection batches = GameTestBatchFactory.divideIntoBatches(testFunctions, testDecorator, ctx.getSource().getLevel()); 66 | 67 | 68 | FailedTestTracker.forgetFailedTests(); 69 | 70 | GameTestRunner gameTestRunner = GameTestRunner.Builder.fromBatches(batches, level).build(); 71 | gameTestRunner.start(); 72 | 73 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(gameTestRunner.getTestInfos()); 74 | multipleTestTracker.addFailureListener(gameTestInfo -> { 75 | LOGGER.error("Test failed: " + gameTestInfo); 76 | if (gameTestInfo.getError() != null) { 77 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 78 | } 79 | 80 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 81 | System.exit(-1); 82 | } 83 | }); 84 | 85 | result.set(Optional.of(multipleTestTracker)); 86 | return Command.SINGLE_SUCCESS; 87 | })); 88 | 89 | try { 90 | dispatcher.execute("test", server.createCommandSourceStack()); 91 | } catch (CommandSyntaxException e) { 92 | throw new RuntimeException(e); 93 | } 94 | 95 | return result.get().orElse(null); 96 | }).get(60, TimeUnit.SECONDS); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /1_21_5/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /1_21_5/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /1_21_5/src/neoforge/java/me/earth/mc_runtime_test/neoforge/NeoForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.neoforge; 2 | 3 | import net.neoforged.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class NeoForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_21_5/src/neoforge/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.21.0,)" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /1_21_5/src/neoforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /1_7_10/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.7.10 4 | lexforge_version = 10.13.4.1614-1.7.10 5 | fabric_version = 0.14.20 -------------------------------------------------------------------------------- /1_7_10/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_7_10/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_7_10/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /1_7_10/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_7_10/settings.gradle: -------------------------------------------------------------------------------- 1 | 2 | pluginManagement { 3 | repositories { 4 | mavenCentral() 5 | maven { 6 | url = "https://maven.neoforged.net/releases" 7 | } 8 | maven { 9 | url = "https://maven.minecraftforge.net/" 10 | } 11 | maven { 12 | url = "https://maven.fabricmc.net/" 13 | } 14 | maven { 15 | url = "https://maven.wagyourtail.xyz/releases" 16 | } 17 | maven { 18 | url 'https://3arthqu4ke.github.io/maven' 19 | } 20 | maven { 21 | url = "https://maven.wagyourtail.xyz/snapshots" 22 | } 23 | gradlePluginPortal() { 24 | content { 25 | excludeGroup("org.apache.logging.log4j") 26 | } 27 | } 28 | } 29 | } 30 | 31 | include 'api' 32 | project(':api').projectDir = file('../api') 33 | 34 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_7_10/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.7.10", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_7_10/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import cpw.mods.fml.common.Mod; 4 | 5 | @Mod(modid = "mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_7_10/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.7.10]" 19 | ordering = "NONE" 20 | side = "BOTH" -------------------------------------------------------------------------------- /1_7_10/src/lexforge/resources/mcmod.info: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modid": "mcruntimetest", 4 | "name": "MC-Runtime-Test", 5 | "description": "Allows developers to test the client inside their CLI", 6 | "version": "${version}", 7 | "mcversion": "1.7.10", 8 | "url": "https://github.com/headlesshq/mc-runtime-test", 9 | "updateUrl": "", 10 | "authorList": ["3arthqu4ke"], 11 | "credits": "", 12 | "logoFile": "/assets/modid/icon.png", 13 | "screenshots": [], 14 | "dependencies": [] 15 | } 16 | ] -------------------------------------------------------------------------------- /1_7_10/src/main/java/me/earth/mc_runtime_test/WorldCreator.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import net.minecraft.client.gui.GuiButton; 4 | import net.minecraft.client.gui.GuiCreateWorld; 5 | import net.minecraft.client.gui.GuiScreen; 6 | 7 | /** 8 | * Because unimined 1.7.10 produces weird classes like {@link net.minecraft.world.WorldSettings$GameType}, which do not work properly. 9 | */ 10 | public class WorldCreator extends GuiCreateWorld { 11 | public WorldCreator(GuiScreen guiScreen) { 12 | super(guiScreen); 13 | } 14 | 15 | public void createNewWorld() { 16 | actionPerformed(new GuiButton(0, 0, 0, "")); // <- id 0, loads world 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /1_7_10/src/main/java/me/earth/mc_runtime_test/mixin/MixinMinecraft.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import me.earth.mc_runtime_test.McRuntimeTest; 4 | import me.earth.mc_runtime_test.WorldCreator; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.entity.EntityClientPlayerMP; 7 | import net.minecraft.client.entity.EntityPlayerSP; 8 | import net.minecraft.client.gui.GuiErrorScreen; 9 | import net.minecraft.client.gui.GuiGameOver; 10 | import net.minecraft.client.gui.GuiMainMenu; 11 | import net.minecraft.client.gui.GuiScreen; 12 | import net.minecraft.client.multiplayer.WorldClient; 13 | import net.minecraft.server.integrated.IntegratedServer; 14 | import org.apache.logging.log4j.Logger; 15 | import org.spongepowered.asm.mixin.Final; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.Shadow; 18 | import org.spongepowered.asm.mixin.Unique; 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 | @Mixin(Minecraft.class) 24 | public abstract class MixinMinecraft { 25 | @Shadow @Final private static Logger logger; 26 | 27 | @Shadow private IntegratedServer theIntegratedServer; 28 | @Shadow public GuiScreen currentScreen; 29 | @Shadow public EntityClientPlayerMP thePlayer; 30 | @Shadow public WorldClient theWorld; 31 | 32 | @Shadow 33 | volatile boolean running; 34 | 35 | @Shadow public abstract void displayGuiScreen(GuiScreen par1); 36 | 37 | @Unique 38 | private boolean mcRuntimeTest$startedLoadingSPWorld = false; 39 | 40 | @Inject(method = "displayGuiScreen", at = @At("HEAD")) 41 | private void displayGuiScreenHook(GuiScreen guiScreenIn, CallbackInfo ci) { 42 | if (!McRuntimeTest.screenHook()) { 43 | return; 44 | } 45 | 46 | if (guiScreenIn instanceof GuiErrorScreen) { 47 | running = false; 48 | throw new RuntimeException("Error Screen " + guiScreenIn); 49 | } else if (guiScreenIn instanceof GuiGameOver && thePlayer != null) { 50 | thePlayer.respawnPlayer(); 51 | } 52 | } 53 | 54 | @Inject(method = "runTick", at = @At("HEAD")) 55 | private void tickHook(CallbackInfo ci) { 56 | if (!McRuntimeTest.tickHook()) { 57 | return; 58 | } 59 | 60 | if (currentScreen instanceof GuiMainMenu) { 61 | if (!mcRuntimeTest$startedLoadingSPWorld) { 62 | mc_runtime_test$loadSinglePlayerWorld(); 63 | mcRuntimeTest$startedLoadingSPWorld = true; 64 | } 65 | } else { 66 | logger.info("Waiting for overlay to disappear..."); 67 | } 68 | 69 | if (thePlayer != null && theWorld != null) { 70 | if (currentScreen == null) { 71 | if (!theWorld.getChunkFromBlockCoords((int) thePlayer.posX, (int) thePlayer.posZ).isEmpty()) { 72 | if (thePlayer.ticksExisted < 100) { 73 | logger.info("Waiting " + (100 - thePlayer.ticksExisted) + " ticks before testing..."); 74 | } else { 75 | logger.info("Test successful!"); 76 | running = false; 77 | } 78 | } else { 79 | logger.info("Players chunk not yet loaded, " + thePlayer + ": cores: " + Runtime.getRuntime().availableProcessors() 80 | + ", server running: " + (theIntegratedServer == null ? "null" : theIntegratedServer.isServerRunning())); 81 | } 82 | } else { 83 | logger.info("Screen not yet null: " + currentScreen); 84 | } 85 | } else { 86 | logger.info("Waiting for player to load, screen: " + currentScreen + ", server: " + theIntegratedServer); 87 | } 88 | } 89 | 90 | @Unique 91 | private void mc_runtime_test$loadSinglePlayerWorld() { 92 | WorldCreator creator = new WorldCreator(new GuiMainMenu()); 93 | displayGuiScreen(creator); 94 | creator.createNewWorld(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /1_7_10/src/main/java/me/earth/mc_runtime_test/tweaker/McRuntimeTestTweaker.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.tweaker; 2 | 3 | import io.github.impactdevelopment.simpletweaker.SimpleTweaker; 4 | import net.minecraft.launchwrapper.LaunchClassLoader; 5 | import org.spongepowered.asm.launch.MixinBootstrap; 6 | import org.spongepowered.asm.mixin.MixinEnvironment; 7 | import org.spongepowered.asm.mixin.Mixins; 8 | import org.spongepowered.tools.obfuscation.mcp.ObfuscationServiceMCP; 9 | 10 | import java.io.IOException; 11 | 12 | @SuppressWarnings("unused") 13 | public class McRuntimeTestTweaker extends SimpleTweaker { 14 | @Override 15 | public void injectIntoClassLoader(LaunchClassLoader classLoader) { 16 | super.injectIntoClassLoader(classLoader); 17 | MixinBootstrap.init(); 18 | 19 | String obfCtx = ObfuscationServiceMCP.NOTCH; 20 | try { 21 | if (classLoader.getClassBytes( 22 | "net.minecraftforge.common.ForgeHooks") != null) { 23 | obfCtx = ObfuscationServiceMCP.SEARGE; 24 | } 25 | } catch (IOException ignored) { } 26 | 27 | MixinEnvironment.getDefaultEnvironment() 28 | .setSide(MixinEnvironment.Side.CLIENT); 29 | MixinEnvironment.getDefaultEnvironment() 30 | .setObfuscationContext(obfCtx); 31 | 32 | Mixins.addConfiguration("mc_runtime_test.mixins.json"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /1_7_10/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.7.11", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "MixinMinecraft" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } -------------------------------------------------------------------------------- /1_7_10/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "Mc Runtime Test resources", 4 | "pack_format": 3 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /1_8_9/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.8.9 4 | lexforge_version = 11.15.1.2318-1.8.9 5 | fabric_version = 0.14.20 6 | -------------------------------------------------------------------------------- /1_8_9/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/1_8_9/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /1_8_9/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /1_8_9/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /1_8_9/settings.gradle: -------------------------------------------------------------------------------- 1 | 2 | pluginManagement { 3 | repositories { 4 | mavenCentral() 5 | maven { 6 | url = "https://maven.neoforged.net/releases" 7 | } 8 | maven { 9 | url = "https://maven.minecraftforge.net/" 10 | } 11 | maven { 12 | url = "https://maven.fabricmc.net/" 13 | } 14 | maven { 15 | url = "https://maven.wagyourtail.xyz/releases" 16 | } 17 | maven { 18 | url 'https://3arthqu4ke.github.io/maven' 19 | } 20 | maven { 21 | url = "https://maven.wagyourtail.xyz/snapshots" 22 | } 23 | gradlePluginPortal() { 24 | content { 25 | excludeGroup("org.apache.logging.log4j") 26 | } 27 | } 28 | } 29 | } 30 | 31 | include 'api' 32 | project(':api').projectDir = file('../api') 33 | 34 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /1_8_9/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.8.9", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1_8_9/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod(modid = "mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /1_8_9/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.8.9]" 19 | ordering = "NONE" 20 | side = "BOTH" -------------------------------------------------------------------------------- /1_8_9/src/lexforge/resources/mcmod.info: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modid": "mcruntimetest", 4 | "name": "MC-Runtime-Test", 5 | "description": "Allows developers to test the client inside their CLI", 6 | "version": "${version}", 7 | "mcversion": "1.8.9", 8 | "url": "https://github.com/headlesshq/mc-runtime-test", 9 | "updateUrl": "", 10 | "authorList": ["3arthqu4ke"], 11 | "credits": "", 12 | "logoFile": "/assets/modid/icon.png", 13 | "screenshots": [], 14 | "dependencies": [] 15 | } 16 | ] -------------------------------------------------------------------------------- /1_8_9/src/main/java/me/earth/mc_runtime_test/mixin/MixinMinecraft.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import me.earth.mc_runtime_test.McRuntimeTest; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.client.entity.EntityPlayerSP; 6 | import net.minecraft.client.gui.GuiErrorScreen; 7 | import net.minecraft.client.gui.GuiGameOver; 8 | import net.minecraft.client.gui.GuiMainMenu; 9 | import net.minecraft.client.gui.GuiScreen; 10 | import net.minecraft.client.multiplayer.WorldClient; 11 | import net.minecraft.server.integrated.IntegratedServer; 12 | import net.minecraft.world.WorldSettings; 13 | import net.minecraft.world.WorldType; 14 | import org.apache.logging.log4j.Logger; 15 | import org.spongepowered.asm.mixin.Final; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.Shadow; 18 | import org.spongepowered.asm.mixin.Unique; 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 java.util.Random; 24 | 25 | @Mixin(Minecraft.class) 26 | public abstract class MixinMinecraft { 27 | @Shadow @Final private static Logger logger; 28 | 29 | @Shadow private IntegratedServer theIntegratedServer; 30 | @Shadow public GuiScreen currentScreen; 31 | @Shadow public EntityPlayerSP thePlayer; 32 | @Shadow public WorldClient theWorld; 33 | 34 | @Shadow 35 | volatile boolean running; 36 | @Unique 37 | private boolean mcRuntimeTest$startedLoadingSPWorld = false; 38 | 39 | @Shadow public abstract void launchIntegratedServer(String par1, String par2, WorldSettings par3); 40 | 41 | @Shadow public abstract void displayGuiScreen(GuiScreen par1); 42 | 43 | @Inject(method = "displayGuiScreen", at = @At("HEAD")) 44 | private void displayGuiScreenHook(GuiScreen guiScreenIn, CallbackInfo ci) { 45 | if (!McRuntimeTest.screenHook()) { 46 | return; 47 | } 48 | 49 | if (guiScreenIn instanceof GuiErrorScreen) { 50 | running = false; 51 | throw new RuntimeException("Error Screen " + guiScreenIn); 52 | } else if (guiScreenIn instanceof GuiGameOver && thePlayer != null) { 53 | thePlayer.respawnPlayer(); 54 | } 55 | } 56 | 57 | @Inject(method = "runTick", at = @At("HEAD")) 58 | private void tickHook(CallbackInfo ci) { 59 | if (!McRuntimeTest.tickHook()) { 60 | return; 61 | } 62 | 63 | if (currentScreen instanceof GuiMainMenu) { 64 | if (!mcRuntimeTest$startedLoadingSPWorld) { 65 | mc_runtime_test$loadSinglePlayerWorld(); 66 | mcRuntimeTest$startedLoadingSPWorld = true; 67 | } 68 | } else { 69 | logger.info("Waiting for overlay to disappear..."); 70 | } 71 | 72 | if (thePlayer != null && theWorld != null) { 73 | if (currentScreen == null) { 74 | if (!theWorld.getChunkFromChunkCoords(((int) thePlayer.posX) >> 4, ((int) thePlayer.posZ) >> 4).isEmpty()) { 75 | if (thePlayer.ticksExisted < 100) { 76 | logger.info("Waiting " + (100 - thePlayer.ticksExisted) + " ticks before testing..."); 77 | } else { 78 | logger.info("Test successful!"); 79 | running = false; 80 | } 81 | } else { 82 | logger.info("Players chunk not yet loaded, " + thePlayer + ": cores: " + Runtime.getRuntime().availableProcessors() 83 | + ", server running: " + (theIntegratedServer == null ? "null" : theIntegratedServer.isServerRunning())); 84 | } 85 | } else { 86 | logger.info("Screen not yet null: " + currentScreen); 87 | } 88 | } else { 89 | logger.info("Waiting for player to load..."); 90 | } 91 | } 92 | 93 | @Unique 94 | private void mc_runtime_test$loadSinglePlayerWorld() { 95 | displayGuiScreen(null); 96 | long seed = (new Random()).nextLong(); 97 | WorldSettings worldsettings = new WorldSettings(seed, WorldSettings.GameType.SURVIVAL, true, false, WorldType.DEFAULT); 98 | launchIntegratedServer("new_world", "New World", worldsettings); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /1_8_9/src/main/java/me/earth/mc_runtime_test/tweaker/McRuntimeTestTweaker.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.tweaker; 2 | 3 | import io.github.impactdevelopment.simpletweaker.SimpleTweaker; 4 | import net.minecraft.launchwrapper.LaunchClassLoader; 5 | import org.spongepowered.asm.launch.MixinBootstrap; 6 | import org.spongepowered.asm.mixin.MixinEnvironment; 7 | import org.spongepowered.asm.mixin.Mixins; 8 | import org.spongepowered.tools.obfuscation.mcp.ObfuscationServiceMCP; 9 | 10 | import java.io.IOException; 11 | 12 | @SuppressWarnings("unused") 13 | public class McRuntimeTestTweaker extends SimpleTweaker { 14 | @Override 15 | public void injectIntoClassLoader(LaunchClassLoader classLoader) { 16 | super.injectIntoClassLoader(classLoader); 17 | MixinBootstrap.init(); 18 | 19 | String obfCtx = ObfuscationServiceMCP.NOTCH; 20 | try { 21 | if (classLoader.getClassBytes( 22 | "net.minecraftforge.common.ForgeHooks") != null) { 23 | obfCtx = ObfuscationServiceMCP.SEARGE; 24 | } 25 | } catch (IOException ignored) { } 26 | 27 | MixinEnvironment.getDefaultEnvironment() 28 | .setSide(MixinEnvironment.Side.CLIENT); 29 | MixinEnvironment.getDefaultEnvironment() 30 | .setObfuscationContext(obfCtx); 31 | 32 | Mixins.addConfiguration("mc_runtime_test.mixins.json"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /1_8_9/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.7.11", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "MixinMinecraft" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } -------------------------------------------------------------------------------- /1_8_9/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "Mc Runtime Test resources", 4 | "pack_format": 3 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [3.1.1](https://github.com/headlesshq/mc-runtime-test/compare/3.1.0...3.1.1) (2025-05-01) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * **action:** Change cache keys to prevent prefix fallback to cache of incorrect version ([#71](https://github.com/headlesshq/mc-runtime-test/issues/71)) ([943bc16](https://github.com/headlesshq/mc-runtime-test/commit/943bc16d5f63fccc659fb01f57615307a58d2065)) 9 | 10 | ## [3.1.0](https://github.com/headlesshq/mc-runtime-test/compare/3.0.0...3.1.0) (2025-04-06) 11 | 12 | 13 | ### Features 14 | 15 | * **deps:** support 1.21.5 and update to HeadlessMc 2.5.1 ([63a0f2a](https://github.com/headlesshq/mc-runtime-test/commit/63a0f2a887f3c99d2629c2a7f913d5fe795657ce)) 16 | 17 | ## [3.0.0](https://github.com/headlesshq/mc-runtime-test/compare/2.4.2...3.0.0) (2024-12-05) 18 | 19 | 20 | ### ⚠ BREAKING CHANGES 21 | 22 | * **ci:** enable semantic releases ([#25](https://github.com/headlesshq/mc-runtime-test/issues/25)) 23 | 24 | ### Features 25 | 26 | * **ci:** enable semantic releases ([#25](https://github.com/headlesshq/mc-runtime-test/issues/25)) ([7d69be4](https://github.com/headlesshq/mc-runtime-test/commit/7d69be4f6a3c0481748551463ff381674be39845)) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 HeadlessHQ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: MC-Runtime-Test 2 | description: Runs the MC client inside your CI 3 | author: HeadlessHQ 4 | branding: 5 | icon: play 6 | color: green 7 | 8 | inputs: 9 | mc: 10 | description: Minecraft version to run 11 | required: true 12 | modloader: 13 | description: Modloader to install (forge, neoforge, fabric) 14 | required: true 15 | regex: 16 | description: Regex to match the modloader jar (i.e. .*fabric.*) 17 | required: true 18 | java: 19 | description: Java version to use (8, 16, 17, 21) 20 | required: true 21 | dummy-assets: 22 | description: Use dummy assets during testing 23 | default: "true" 24 | mc-runtime-test: 25 | description: MC-Runtime-Test jar to download (none, lexforge, neoforge, fabric) 26 | required: true 27 | xvfb: 28 | description: Runs the game with Xvfb (if false, add the -lwjgl argument) 29 | default: "true" 30 | headlessmc-command: 31 | description: Command-line arguments for HeadlessMC 32 | default: '--jvm "-Djava.awt.headless=true"' 33 | fabric-api: 34 | description: Fabric API version to download (e.g. 0.97.0) or none 35 | default: "none" 36 | fabric-gametest-api: 37 | description: Fabric GameTest API version (e.g. 1.3.5+85d85a934f) or none 38 | default: "none" 39 | download-hmc: 40 | description: Download HeadlessMC 41 | default: "true" 42 | hmc-version: 43 | description: HeadlessMC version 44 | default: "2.5.1" 45 | cache-mc: 46 | description: Cache .minecraft 47 | default: "true" 48 | 49 | runs: 50 | using: composite 51 | steps: 52 | - name: Bootstrap HeadlessMC 53 | run: | 54 | mkdir -p HeadlessMC run/mods 55 | cat <> HeadlessMC/config.properties 56 | hmc.java.versions=$JAVA_HOME/bin/java 57 | hmc.gamedir=$PWD/run 58 | hmc.offline=true 59 | hmc.rethrow.launch.exceptions=true 60 | hmc.exit.on.failed.command=true 61 | EOF 62 | shell: bash 63 | 64 | - if: inputs.dummy-assets == 'true' 65 | name: Configure Dummy Assets 66 | run: echo hmc.assets.dummy=true >> HeadlessMC/config.properties 67 | shell: bash 68 | 69 | - if: inputs.download-hmc == 'true' 70 | name: Get HeadlessMC 71 | uses: robinraju/release-downloader@a96f54c1b5f5e09e47d9504526e96febd949d4c2 # v1.11 72 | with: 73 | repository: 3arthqu4ke/headlessmc 74 | tag: ${{ inputs.hmc-version }} 75 | fileName: headlessmc-launcher-${{ inputs.hmc-version }}.jar 76 | 77 | - if: inputs.cache-mc == 'true' 78 | name: Cache Minecraft 79 | uses: useblacksmith/cache@c5fe29eb0efdf1cf4186b9f7fcbbcbc0cf025662 # v5 80 | with: 81 | path: /home/runner/.minecraft 82 | key: ${{ inputs.mc }}-${{ inputs.modloader }}-hmc 83 | 84 | - name: Download ${{ inputs.modloader }}-${{ inputs.mc }} 85 | run: | 86 | if [ ! -f "$HOME/.minecraft/versions/${{ inputs.mc }}/${{ inputs.mc }}.json" ]; then 87 | java -jar headlessmc-launcher-${{ inputs.hmc-version }}.jar --command download ${{ inputs.mc }} 88 | java -jar headlessmc-launcher-${{ inputs.hmc-version }}.jar --command ${{ inputs.modloader }} ${{ inputs.mc }} --java ${{ inputs.java }} 89 | fi 90 | shell: bash 91 | 92 | - id: get-mcrt 93 | if: inputs.mc-runtime-test != 'none' 94 | name: Get mc-runtime-test 95 | uses: robinraju/release-downloader@a96f54c1b5f5e09e47d9504526e96febd949d4c2 # v1.11 96 | with: 97 | repository: headlesshq/mc-runtime-test 98 | tag: "3.1.1" # x-release-please-version 99 | fileName: 'mc-runtime-test-${{ inputs.mc }}-*-${{ inputs.mc-runtime-test }}-release.jar' 100 | out-file-path: run/mods 101 | 102 | - if: inputs.fabric-api != 'none' 103 | name: Download fabric-api v${{ inputs.fabric-api }} 104 | run: > 105 | wget -O run/mods/fabric-api-${{ inputs.fabric-api }}+${{ inputs.mc }}.jar 106 | https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api/${{ inputs.fabric-api }}+${{ inputs.mc }}/fabric-api-${{ inputs.fabric-api }}+${{ inputs.mc }}.jar 107 | shell: bash 108 | 109 | - if: inputs.fabric-gametest-api != 'none' 110 | name: Download fabric-gametest-api v${{ inputs.fabric-gametest-api }} 111 | run: > 112 | wget -O run/mods/fabric-gametest-api-v1-${{ inputs.fabric-gametest-api }}.jar 113 | https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-gametest-api-v1/${{ inputs.fabric-gametest-api }}/fabric-gametest-api-v1-${{ inputs.fabric-gametest-api }}.jar 114 | shell: bash 115 | 116 | - name: Configure game 117 | run: | 118 | cat <> run/options.txt 119 | onboardAccessibility:false 120 | pauseOnLostFocus:false 121 | EOF 122 | shell: bash 123 | 124 | - if: inputs.xvfb == 'true' 125 | name: Run game with xvfb 126 | run: | # TODO: install xrandr conditionally per #22 127 | sudo DEBIAN_FRONTEND=noninteractive apt-get install -y x11-xserver-utils 128 | xvfb-run java -Dhmc.check.xvfb=true -jar headlessmc-launcher-${{ inputs.hmc-version }}.jar --command launch ${{ inputs.regex }} -regex ${{ inputs.headlessmc-command }} 129 | shell: bash 130 | 131 | - if: inputs.xvfb != 'true' 132 | name: Run game 133 | run: java -jar headlessmc-launcher-${{ inputs.hmc-version }}.jar --command launch ${{ inputs.regex }} -regex ${{ inputs.headlessmc-command }} 134 | shell: bash 135 | -------------------------------------------------------------------------------- /api/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'maven-publish' 4 | } 5 | 6 | group 'me.earth' 7 | version "$project_version" 8 | 9 | sourceCompatibility = JavaVersion.VERSION_1_8 10 | targetCompatibility = JavaVersion.VERSION_1_8 11 | 12 | compileJava { 13 | if (JavaVersion.current().isJava9Compatible()) { 14 | options.compilerArgs.addAll(['--release', '8']) 15 | } 16 | } 17 | 18 | afterEvaluate { 19 | publishing { 20 | publications { 21 | "${name.toLowerCase()}"(MavenPublication) { 22 | ((MavenPublication) it).groupId "${group}" 23 | ((MavenPublication) it).artifactId "${archivesBaseName.toLowerCase()}" 24 | ((MavenPublication) it).version "${version}" 25 | from components.java 26 | } 27 | } 28 | 29 | repositories { 30 | if (System.getenv('DEPLOY_TO_GITHUB_PACKAGES_URL') == null) { 31 | maven { 32 | name = 'BuildDirMaven' 33 | url = rootProject.projectDir.toPath().parent.resolve('build').resolve('maven') 34 | } 35 | } else { 36 | maven { 37 | name = 'GithubPagesMaven' 38 | url = System.getenv('DEPLOY_TO_GITHUB_PACKAGES_URL') 39 | credentials { 40 | username = System.getenv('GITHUB_ACTOR') 41 | password = System.getenv('GITHUB_TOKEN') 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /api/gradle.properties: -------------------------------------------------------------------------------- 1 | # x-release-please-start-version 2 | project_version = 3.1.1 3 | # x-release-please-end 4 | -------------------------------------------------------------------------------- /api/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/api/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /api/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /api/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /api/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'mc-runtime-test-api' -------------------------------------------------------------------------------- /api/src/main/java/me/earth/mc_runtime_test/McRuntimeTest.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | /** 4 | * A controller class outside of our Mixin. 5 | * To modify the behaviour of McRuntimeTest you can hook into the methods of this class via a Mixin. 6 | */ 7 | public class McRuntimeTest { 8 | /** 9 | * Whether to run Minecrafts game tests or not. 10 | */ 11 | public static final boolean RUN_GAME_TESTS = Boolean.parseBoolean(System.getProperty("McRuntimeGameTest", "true")); 12 | /** 13 | * Whether to fail if an optional gametest fails or not. 14 | */ 15 | public static final boolean GAME_TESTS_FAIL_ON_OPTIONAL = Boolean.parseBoolean(System.getProperty("McRuntimeGameTestFailOnOptional", "true")); 16 | /** 17 | * Fails if less game tests than this get found. 18 | */ 19 | public static final int MIN_GAME_TESTS_TO_FIND = Integer.parseInt(System.getProperty("McRuntimeGameTestMinExpectedGameTests", "0")); 20 | /** 21 | * TODO: Boots directly into a world without going through the world creation screens. 22 | */ 23 | public static final boolean FAST_WORLD = Boolean.parseBoolean(System.getProperty("McRuntimeGameTestFastWorld", "false")); 24 | /** 25 | * Close the CreateWorldScreen if player and world have been loaded. Prevents flaky 1.19-1.19.3 tests that get stuck on the CreateWorldScreen. 26 | */ 27 | public static final boolean CLOSE_CREATE_WORLD_SCREEN = Boolean.parseBoolean(System.getProperty("McRuntimeGameCloseCreateWorldScreen", "true")); 28 | /** 29 | * Same as {@link #CLOSE_CREATE_WORLD_SCREEN} but for any screen displayed after world and player have been loaded. 30 | */ 31 | public static final boolean CLOSE_ANY_SCREEN = Boolean.parseBoolean(System.getProperty("McRuntimeGameTestCloseAnyScreen", "false")); 32 | 33 | /** 34 | * Our Hook in Minecrafts setScreen/displayGuiScreen. 35 | * If an error screen is displayed we make Minecraft exit. 36 | * If a DeathScreen is displayed we respawn the player. 37 | * You can hook into this method with a Mixin and return {@code false} to add your own custom behaviour. 38 | * 39 | * @return {@code true} if this hook should be used. 40 | */ 41 | public static boolean screenHook() { 42 | return true; 43 | } 44 | 45 | /** 46 | * Our Hook in Minecrafts ticks that run every 50ms. 47 | * Here we join a SinglePlayer world and wait for the chunk around the player to load. 48 | * Then we wait an additional amount of ticks until we quit the game. 49 | * You can hook into this method with a Mixin and return {@code false} to add your own custom behaviour. 50 | * 51 | * @return {@code true} if this hook should be used. 52 | */ 53 | public static boolean tickHook() { 54 | return true; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /gametest/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /gametest/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.20.4 4 | mapping_version = 1 5 | neoforge_version = 1-beta 6 | lexforge_version = 49.0.30 7 | fabric_version = 0.15.9 8 | # Whether to use the headlessmc lwjgl agent or not 9 | hmc.lwjgl=true 10 | fabric_api_version=0.96.11+1.20.4 11 | fabric_gametest_api_version=1.3.16+1172e8970d 12 | -------------------------------------------------------------------------------- /gametest/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/gametest/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gametest/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /gametest/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /gametest/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.neoforged.net/releases" 6 | } 7 | maven { 8 | url = "https://maven.minecraftforge.net/" 9 | } 10 | maven { 11 | url = "https://maven.fabricmc.net/" 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/releases" 15 | } 16 | maven { 17 | url 'https://3arthqu4ke.github.io/maven' 18 | } 19 | maven { 20 | url = "https://maven.wagyourtail.xyz/snapshots" 21 | } 22 | gradlePluginPortal() { 23 | content { 24 | excludeGroup("org.apache.logging.log4j") 25 | } 26 | } 27 | } 28 | } 29 | 30 | include 'api' 31 | project(':api').projectDir = file('../api') 32 | 33 | rootProject.name = 'clientgametest' -------------------------------------------------------------------------------- /gametest/src/fabric/java/me/earth/clientgametest/FabricEntryPoint.java: -------------------------------------------------------------------------------- 1 | package me.earth.clientgametest; 2 | 3 | import net.fabricmc.api.ModInitializer; 4 | 5 | public class FabricEntryPoint implements ModInitializer { 6 | @Override 7 | public void onInitialize() { 8 | 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /gametest/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "clientgametest", 4 | "version": "${version}", 5 | 6 | "name": "GameTest", 7 | "description": "Mod to test the Minecraft GameTest framework", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "entrypoints": { 20 | "main": [ 21 | "me.earth.clientgametest.FabricEntryPoint" 22 | ], 23 | "fabric-gametest": [ 24 | "me.earth.clientgametest.GameTests" 25 | ] 26 | }, 27 | "mixins": [ 28 | "clientgametest.mixins.json" 29 | ], 30 | 31 | "depends": { 32 | "fabricloader": ">=0.14.19", 33 | "minecraft": "~1.20.2", 34 | "java": ">=8" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /gametest/src/lexforge/java/me/earth/clientgametest/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.clientgametest.forge; 2 | 3 | import me.earth.clientgametest.GameTests; 4 | import net.minecraft.gametest.framework.GameTestRegistry; 5 | import net.minecraftforge.fml.common.Mod; 6 | 7 | @Mod("clientgametest") 8 | public class ForgeMod { 9 | @Deprecated 10 | public ForgeMod() { 11 | // Do not do this, we have to :( 12 | GameTestRegistry.register(GameTests.class); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /gametest/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "clientgametest" 8 | version = "${version}" 9 | displayName = "GameTest" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Mod to test the Minecraft clientgametest framework 13 | ''' 14 | 15 | [[dependencies.clientgametest]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.20.2,1.20.6]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /gametest/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "clientgametest resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /gametest/src/main/java/me/earth/clientgametest/GameTests.java: -------------------------------------------------------------------------------- 1 | package me.earth.clientgametest; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.gametest.framework.GameTest; 6 | import net.minecraft.gametest.framework.GameTestHelper; 7 | import net.minecraft.world.level.block.Blocks; 8 | import org.slf4j.Logger; 9 | 10 | public class GameTests { 11 | private static final Logger LOGGER = LogUtils.getLogger(); 12 | 13 | @GameTest(template = "clientgametest:empty") 14 | public void dummyGameTest(GameTestHelper context) { 15 | LOGGER.info("Hi from dummy GameTest!"); 16 | context.setBlock(0, 2, 0, Blocks.OBSIDIAN); 17 | context.succeedWhen(() -> context.assertBlock(new BlockPos(0, 2, 0), (block) -> block == Blocks.OBSIDIAN, "Expect block to be obsidian")); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /gametest/src/main/java/me/earth/clientgametest/mixin/MixinCommands.java: -------------------------------------------------------------------------------- 1 | package me.earth.clientgametest.mixin; 2 | 3 | import net.minecraft.commands.Commands; 4 | import org.slf4j.Logger; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Redirect; 8 | 9 | @Mixin(Commands.class) 10 | public class MixinCommands { 11 | @Redirect(method = "performCommand", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;isDebugEnabled()Z", remap = false), require = 0) 12 | private boolean performCommandHook(Logger instance) { 13 | return true; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /gametest/src/main/java/me/earth/clientgametest/mixin/MixinGameTestRegistry.java: -------------------------------------------------------------------------------- 1 | package me.earth.clientgametest.mixin; 2 | 3 | import net.minecraft.gametest.framework.GameTestRegistry; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.ModifyArg; 7 | 8 | @Mixin(GameTestRegistry.class) 9 | public class MixinGameTestRegistry { 10 | /** 11 | * ONLY USE IF YOU KNOW WHAT YOU ARE DOING! 12 | * This is here just because this mod has been made to support Neoforge, Fabric and Lexforge. 13 | * Lexforge and Neoforge prefix the name of the Structure template in a weird way. 14 | * 15 | * @return an unprefixed name of the gametest template to use. 16 | */ 17 | @ModifyArg( 18 | method = "turnMethodIntoTestFunction", 19 | at = @At( 20 | value = "INVOKE", 21 | target = "Lnet/minecraft/gametest/framework/TestFunction;(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lnet/minecraft/world/level/block/Rotation;IJZIILjava/util/function/Consumer;)V"), 22 | index = 2) 23 | private static String turnMethodIntoTestFunctionHook(String string) { 24 | if (string.contains("clientgametest")) { 25 | return "clientgametest:empty"; 26 | } 27 | 28 | return string; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /gametest/src/main/resources/assets/clientgametest/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/gametest/src/main/resources/assets/clientgametest/icon.png -------------------------------------------------------------------------------- /gametest/src/main/resources/clientgametest.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.clientgametest.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | ], 8 | "injectors": { 9 | "defaultRequire": 1 10 | }, 11 | "mixins": [ 12 | "MixinCommands", 13 | "MixinGameTestRegistry" 14 | ] 15 | } -------------------------------------------------------------------------------- /gametest/src/main/resources/data/clientgametest/gametest/structures/empty.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/gametest/src/main/resources/data/clientgametest/gametest/structures/empty.nbt -------------------------------------------------------------------------------- /gametest/src/main/resources/data/clientgametest/structures/empty.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/gametest/src/main/resources/data/clientgametest/structures/empty.nbt -------------------------------------------------------------------------------- /gametest/src/neoforge/java/me/earth/clientgametest/neoforge/NeoForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.clientgametest.neoforge; 2 | 3 | import me.earth.clientgametest.GameTests; 4 | import net.minecraft.gametest.framework.GameTestRegistry; 5 | import net.neoforged.fml.common.Mod; 6 | 7 | @Mod("clientgametest") 8 | public class NeoForgeMod { 9 | @Deprecated 10 | public NeoForgeMod() { 11 | // Do not do this, we have to :( 12 | GameTestRegistry.register(GameTests.class); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /gametest/src/neoforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "clientgametest" 8 | version = "${version}" 9 | displayName = "GameTest" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Mod to test the Minecraft clientgametest framework 13 | ''' 14 | 15 | [[dependencies.clientgametest]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.20.2,1.20.6]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /gametest/src/neoforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "clientgametest resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", 3 | "bootstrap-sha": "42445f09578667ad2a3b620ca669ba1c13c1c1ac", 4 | "packages": { 5 | ".": { 6 | "include-component-in-tag": false, 7 | "include-v-in-tag": false, 8 | "package-name": "mc-runtime-test", 9 | "release-type": "simple", 10 | "extra-files": [ 11 | "action.yml", 12 | "api/gradle.properties", 13 | "README.md" 14 | ] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /snapshots/.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | /libs/ 42 | /logs/ 43 | /.architectury-transformer/ 44 | -------------------------------------------------------------------------------- /snapshots/build.gradle: -------------------------------------------------------------------------------- 1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 2 | 3 | plugins { 4 | id 'com.github.johnrengelman.shadow' version '7.1.2' 5 | id 'java' 6 | id 'maven-publish' 7 | id 'xyz.wagyourtail.unimined' version '1.3.10-SNAPSHOT' 8 | } 9 | 10 | group 'me.earth' 11 | version "$minecraft_version-${project(':api').project_version}" 12 | 13 | base { 14 | archivesName = 'mc-runtime-test' 15 | } 16 | 17 | sourceSets { 18 | fabric 19 | } 20 | 21 | repositories { 22 | mavenCentral() 23 | maven { 24 | name = "sponge" 25 | url = "https://repo.spongepowered.org/maven" 26 | } 27 | maven { 28 | url = "https://maven.wagyourtail.xyz/releases" 29 | } 30 | 31 | maven { 32 | name = '3arthMaven' 33 | url = 'https://3arthqu4ke.github.io/maven' 34 | } 35 | } 36 | 37 | unimined.minecraft { 38 | version project.minecraft_version 39 | 40 | mappings { 41 | mojmap() 42 | // intermediary() 43 | // yarn(1) 44 | 45 | devFallbackNamespace "mojmap" 46 | 47 | /*stub.withMappings("intermediary", ["yarn"]) { 48 | c("net/minecraft/class_1927", []) { 49 | m("method_55109", "()Lnet/minecraft/class_243;", ["getPos"]) 50 | } 51 | }*/ 52 | } 53 | 54 | defaultRemapJar = false 55 | } 56 | 57 | unimined.minecraft(sourceSets.fabric) { 58 | combineWith(sourceSets.main) 59 | 60 | fabric { 61 | loader project.fabric_version 62 | } 63 | 64 | defaultRemapJar = true 65 | } 66 | 67 | configurations { 68 | mainImplementation 69 | lwjglAgent.extendsFrom runtimeOnly 70 | jarLibs 71 | implementation.extendsFrom jarLibs 72 | } 73 | 74 | for (String platform_capitalized : ['Fabric']) { 75 | def platform = platform_capitalized.toLowerCase() 76 | def remapJarTask = tasks.named("remap${platform_capitalized}Jar", AbstractArchiveTask).get() 77 | def shadowTask = tasks.register("${platform}ShadowJar", ShadowJar) { 78 | dependsOn(remapJarTask) 79 | it.group = 'build' 80 | it.archiveClassifier = "${platform}-release" 81 | from remapJarTask.outputs 82 | it.configurations += [ project.configurations.jarLibs ] 83 | exclude "**/module-info.class" 84 | } 85 | tasks.named('build') { finalizedBy(shadowTask) } 86 | } 87 | 88 | dependencies { 89 | compileOnly 'org.spongepowered:mixin:0.8.5-SNAPSHOT' 90 | compileOnly 'me.earth.headlessmc:headlessmc:1.8.1' 91 | lwjglAgent 'me.earth.headlessmc:headlessmc-lwjgl:1.8.1' 92 | // yes, I actually want this at runtime to use assertions! 93 | jarLibs 'org.junit.jupiter:junit-jupiter-api:5.10.1' 94 | jarLibs project(':api') 95 | } 96 | 97 | afterEvaluate { 98 | fabricRunClient { 99 | standardInput = System.in 100 | if (rootProject.property('hmc.lwjgl').toBoolean()) { 101 | jvmArgs += ["-javaagent:${configurations.lwjglAgent.files.iterator().next()}"] 102 | systemProperties['joml.nounsafe'] = 'true' 103 | systemProperties['fabric.systemLibraries'] = "${configurations.lwjglAgent.files.iterator().next()}" 104 | } 105 | } 106 | } 107 | 108 | processFabricResources { 109 | inputs.property "version", project.version 110 | 111 | filesMatching("fabric.mod.json") { 112 | expand "version": project.version 113 | } 114 | } 115 | 116 | tasks.withType(org.gradle.jvm.tasks.Jar).configureEach { 117 | from("LICENSE") { 118 | duplicatesStrategy = DuplicatesStrategy.INCLUDE 119 | rename { "${it}_${project.archivesBaseName}" } 120 | } 121 | 122 | manifest { 123 | attributes( 124 | 'Implementation-Title': 'MC-Runtime-Test', 125 | 'MixinConfigs': "mc_runtime_test.mixins.json", 126 | 'Implementation-Version': project.version, 127 | ) 128 | } 129 | } 130 | 131 | afterEvaluate { 132 | publishing { 133 | publications { 134 | "${name.toLowerCase()}"(MavenPublication) { 135 | ((MavenPublication) it).groupId "${group}" 136 | ((MavenPublication) it).artifactId "${archivesBaseName.toLowerCase()}" 137 | ((MavenPublication) it).version "${version}" 138 | from components.java 139 | for (String platform: ['Fabric']) { 140 | String platform_lower = platform.toLowerCase() 141 | artifact tasks.named("${platform_lower}Jar").get() 142 | artifact tasks.named("remap${platform}Jar").get() 143 | artifact tasks.named("${platform_lower}ShadowJar").get() 144 | } 145 | } 146 | } 147 | 148 | repositories { 149 | if (System.getenv('DEPLOY_TO_GITHUB_PACKAGES_URL') == null) { 150 | maven { 151 | name = 'BuildDirMaven' 152 | url = rootProject.projectDir.toPath().parent.resolve('build').resolve('maven') 153 | } 154 | } else { 155 | maven { 156 | name = 'GithubPagesMaven' 157 | url = System.getenv('DEPLOY_TO_GITHUB_PACKAGES_URL') 158 | credentials { 159 | username = System.getenv('GITHUB_USER') 160 | password = System.getenv('GITHUB_TOKEN') 161 | } 162 | } 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /snapshots/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx2G 2 | 3 | minecraft_version = 1.20.5-pre1 4 | fabric_version = 0.15.9 5 | # Whether to use the headlessmc lwjgl agent or not 6 | hmc.lwjgl=true 7 | -------------------------------------------------------------------------------- /snapshots/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headlesshq/mc-runtime-test/3dea483a2f228cd917d269a0dfcec6b88434cc85/snapshots/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /snapshots/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /snapshots/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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /snapshots/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url = "https://maven.fabricmc.net/" 6 | } 7 | maven { 8 | url = "https://maven.wagyourtail.xyz/releases" 9 | } 10 | maven { 11 | url 'https://3arthqu4ke.github.io/maven' 12 | } 13 | maven { 14 | url = "https://maven.wagyourtail.xyz/snapshots" 15 | } 16 | gradlePluginPortal() { 17 | content { 18 | excludeGroup("org.apache.logging.log4j") 19 | } 20 | } 21 | } 22 | } 23 | 24 | include 'api' 25 | project(':api').projectDir = file('../api') 26 | 27 | rootProject.name = 'mc-runtime-test' -------------------------------------------------------------------------------- /snapshots/src/fabric/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mc_runtime_test", 4 | "version": "${version}", 5 | 6 | "name": "MC-Runtime-Test", 7 | "description": "Run tests on the Minecraft client at runtime", 8 | "authors": [ 9 | "3arthqu4ke" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/headlesshq/mc-runtime-test", 13 | "sources": "https://github.com/headlesshq/mc-runtime-test" 14 | }, 15 | 16 | "license": "MIT", 17 | 18 | "environment": "*", 19 | "mixins": [ 20 | "mc_runtime_test.mixins.json" 21 | ], 22 | 23 | "depends": { 24 | "fabricloader": ">=0.14.19", 25 | "minecraft": "~1.20.2", 26 | "java": ">=8" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /snapshots/src/lexforge/java/me/earth/mc_runtime_test/forge/ForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.forge; 2 | 3 | import net.minecraftforge.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class ForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /snapshots/src/lexforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.20.2,1.20.4]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /snapshots/src/lexforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } -------------------------------------------------------------------------------- /snapshots/src/main/java/me/earth/mc_runtime_test/McGameTestRunner.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.gametest.framework.*; 5 | import net.minecraft.server.MinecraftServer; 6 | import net.minecraft.server.level.ServerLevel; 7 | import net.minecraft.world.entity.player.Player; 8 | import net.minecraft.world.level.block.Rotation; 9 | import org.slf4j.Logger; 10 | 11 | import java.util.Collection; 12 | import java.util.Objects; 13 | import java.util.UUID; 14 | import java.util.concurrent.ExecutionException; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.concurrent.TimeoutException; 17 | 18 | /** 19 | * Similar to running the "/test runall" command. 20 | */ 21 | public class McGameTestRunner { 22 | private static final Logger LOGGER = LogUtils.getLogger(); 23 | 24 | /** 25 | * Basically what happens in {@link TestCommand} when "runall" is used. 26 | * We just exit with an error code if a test fails. 27 | * 28 | * @param playerUUID the uuid of the player. 29 | * @param server the server to run the tests on. 30 | */ 31 | public static MultipleTestTracker runGameTests(UUID playerUUID, MinecraftServer server) throws ExecutionException, InterruptedException, TimeoutException { 32 | return server.submit(() -> { 33 | Player player = Objects.requireNonNull(server.getPlayerList().getPlayer(playerUUID)); 34 | ServerLevel level = (ServerLevel) player.level(); 35 | GameTestRunner.clearMarkers(level); 36 | Collection testFunctions = GameTestRegistry.getAllTestFunctions(); 37 | LOGGER.info("TestFunctions: " + testFunctions); 38 | if (testFunctions.size() < McRuntimeTest.MIN_GAME_TESTS_TO_FIND) { 39 | LOGGER.error("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 40 | throw new IllegalStateException("Failed to find the minimum amount of gametests, expected " + McRuntimeTest.MIN_GAME_TESTS_TO_FIND + ", but found " + testFunctions.size()); 41 | } 42 | 43 | GameTestRegistry.forgetFailedTests(); 44 | Rotation rotation = StructureUtils.getRotationForRotationSteps(0); 45 | Collection tests = GameTestRegistry.getAllTestFunctions() 46 | .stream() 47 | .filter(f -> !f.manualOnly()) 48 | .map(testFunction -> new GameTestInfo(testFunction, rotation, level, RetryOptions.noRetries())) 49 | .toList(); 50 | 51 | MultipleTestTracker multipleTestTracker = new MultipleTestTracker(tests); 52 | multipleTestTracker.addFailureListener(gameTestInfo -> { 53 | LOGGER.error("Test failed: " + gameTestInfo); 54 | if (gameTestInfo.getError() != null) { 55 | LOGGER.error(String.valueOf(gameTestInfo), gameTestInfo.getError()); 56 | } 57 | 58 | if (!gameTestInfo.isOptional() || McRuntimeTest.GAME_TESTS_FAIL_ON_OPTIONAL) { 59 | System.exit(-1); 60 | } 61 | }); 62 | 63 | return multipleTestTracker; 64 | }).get(60, TimeUnit.SECONDS); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /snapshots/src/main/java/me/earth/mc_runtime_test/mixin/ICreateWorldScreen.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.mixin; 2 | 3 | import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(CreateWorldScreen.class) 8 | public interface ICreateWorldScreen { 9 | @Invoker("onCreate") 10 | void invokeOnCreate(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /snapshots/src/main/resources/mc_runtime_test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.earth.mc_runtime_test.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "ICreateWorldScreen", 8 | "MixinMinecraft" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } -------------------------------------------------------------------------------- /snapshots/src/neoforge/java/me/earth/mc_runtime_test/neoforge/NeoForgeMod.java: -------------------------------------------------------------------------------- 1 | package me.earth.mc_runtime_test.neoforge; 2 | 3 | import net.neoforged.fml.common.Mod; 4 | 5 | @Mod("mc_runtime_test") 6 | public class NeoForgeMod { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /snapshots/src/neoforge/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | issueTrackerURL = "https://github.com/headlesshq/mc-runtime-test" 4 | license = "MIT" 5 | 6 | [[mods]] 7 | modId = "mc_runtime_test" 8 | version = "${version}" 9 | displayName = "MC-Runtime-Test" 10 | authors = "3arthqu4ke" 11 | description = ''' 12 | Run tests on the Minecraft client at runtime 13 | ''' 14 | 15 | [[dependencies.mc_runtime_test]] 16 | modId = "minecraft" 17 | mandatory = true 18 | versionRange = "[1.20.2,1.20.4]" 19 | ordering = "NONE" 20 | side = "BOTH" 21 | -------------------------------------------------------------------------------- /snapshots/src/neoforge/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "mc_runtime_test resources", 4 | "pack_format": 6 5 | } 6 | } --------------------------------------------------------------------------------