diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..2dc7c427 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto +*.java text eol=lf +*.gradle text eol=lf +*.json text eol=lf +*.yml text eol=lf +*.py text eol=lf +*.sh text eol=lf +gradlew text eol=lf +gradlew.bat text eol=crlf +*.png binary +*.jar binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..edabbad7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,230 @@ +name: Modern Metals 1.11 CI + +on: + push: + branches: + - master-1.11.2 + - 'feature/**' + pull_request: + branches: + - master-1.11.2 + +permissions: + contents: read + +concurrency: + group: modernmetals-1.11-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + cold-forge-bootstrap: + name: Cold Forge bootstrap + runs-on: ubuntu-latest + timeout-minutes: 75 + + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + + - name: Install pinned Java 25 ForgeGradle Mavenizer runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '25.0.3+9.0.LTS' + + - name: Install pinned Java 8 compilation toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '8.0.502+7' + + - name: Install pinned Java 17 Gradle runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '17.0.1+12' + + - name: Stage checksum-verified legacy dependencies + id: dependencies + shell: bash + run: | + set -euo pipefail + mirror="$RUNNER_TEMP/legacy-dependency-mirror" + bash gradle/stage-legacy-dependencies.sh "$mirror" + echo "repository=$mirror" >> "$GITHUB_OUTPUT" + + - name: Bootstrap Forge from an empty cache + shell: bash + env: + GRADLE_USER_HOME: ${{ runner.temp }}/modernmetals-cold-gradle + run: | + set -euo pipefail + test ! -e .gradle + test ! -e "$GRADLE_USER_HOME" + mkdir -p "$GRADLE_USER_HOME" + chmod +x ./gradlew + gradle_args=( + classes verifyReleaseDependencies + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + --no-daemon --no-build-cache --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + + - name: Verify same-cache bootstrap offline + shell: bash + env: + GRADLE_USER_HOME: ${{ runner.temp }}/modernmetals-cold-gradle + run: | + set -euo pipefail + gradle_args=( + classes verifyReleaseDependencies + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + --rerun-tasks --offline --no-daemon --no-build-cache --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + + build: + name: Build, test, and audit + runs-on: ubuntu-latest + timeout-minutes: 90 + + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + + - name: Install pinned Java 25 ForgeGradle Mavenizer runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '25.0.3+9.0.LTS' + + - name: Install pinned Java 8 compilation and runtime toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '8.0.502+7' + + - name: Install pinned Java 17 Gradle runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '17.0.1+12' + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 + + - name: Stage checksum-verified dependencies + id: dependencies + shell: bash + run: | + set -euo pipefail + mirror="$RUNNER_TEMP/legacy-dependency-mirror" + bash gradle/stage-legacy-dependencies.sh "$mirror" + echo "repository=$mirror" >> "$GITHUB_OUTPUT" + + - name: Prepare clean packaged Forge runtime + id: forge + shell: bash + run: | + set -euo pipefail + runtime="$RUNNER_TEMP/forge-1.11.2-2588-runtime" + bash gradle/prepare-packaged-forge-runtime.sh "$runtime" "$JAVA_HOME_8_X64/bin/java" + echo "runtime=$runtime" >> "$GITHUB_OUTPUT" + + - name: Install world-audit tools + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: '3.12' + + - name: Install pinned schema and workflow validators + run: python -m pip install -r tools/validation/requirements.txt + + - name: Build, test, audit, qualify runtimes, and generate Eclipse launches + shell: bash + run: | + set -euo pipefail + chmod +x ./gradlew + gradle_args=( + clean check build javadoc verifyReleaseDependencies verifyReleaseArtifacts + writeReleaseChecksums prepareEclipse verifyProjectResources packagedForgeRuntimeTest + -PpythonExecutable=python3 + -PpackagedForgeRuntimeOutput="$RUNNER_TEMP/modernmetals-packaged-checks" + -PreleaseBuild=true + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + -PpackagedForgeRuntimeRoot="${{ steps.forge.outputs.runtime }}" + --no-daemon --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + cp build/release/SHA256SUMS "$RUNNER_TEMP/first-SHA256SUMS" + + - name: Verify the prepared release bundle + shell: bash + run: | + set -euo pipefail + bundle="$RUNNER_TEMP/release-input" + mkdir -p "$bundle" + cp build/libs/ModernMetals-2.5.1.111021{,-sources,-javadoc}.jar "$bundle/" + cp build/release/SHA256SUMS CHANGELOG.txt "$bundle/" + ./gradlew verifyPreparedReleaseArtifacts verifyMavenCoordinates \ + -PpreparedReleaseDir="$bundle" \ + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" \ + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" \ + -Porg.gradle.java.installations.auto-detect=false \ + -Porg.gradle.java.installations.auto-download=false \ + --offline --no-daemon --stacktrace --max-workers=2 + + - name: Verify reproducible release archives + shell: bash + run: | + set -euo pipefail + gradle_args=( + clean build javadoc verifyReleaseArtifacts writeReleaseChecksums + -PreleaseBuild=true + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + --offline --no-daemon --no-build-cache --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + cmp "$RUNNER_TEMP/first-SHA256SUMS" build/release/SHA256SUMS + + - name: Upload audited release candidate + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: ModernMetals-1.11.2-${{ github.sha }} + if-no-files-found: error + retention-days: 30 + path: | + build/libs/ModernMetals-2.5.1.111021.jar + build/libs/ModernMetals-2.5.1.111021-sources.jar + build/libs/ModernMetals-2.5.1.111021-javadoc.jar + build/release/SHA256SUMS + CHANGELOG.txt + + - name: Upload bounded diagnostics on failure + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: ModernMetals-1.11.2-diagnostics-${{ github.sha }} + if-no-files-found: ignore + retention-days: 14 + path: | + build/test-results/** + build/reports/** + ${{ runner.temp }}/modernmetals-packaged-checks/**/*-console.log + ${{ runner.temp }}/modernmetals-packaged-checks/**/logs/*.log + ${{ runner.temp }}/modernmetals-packaged-checks/**/crash-reports/*.txt + ${{ runner.temp }}/modernmetals-packaged-checks/**/*-modern.json + ${{ runner.temp }}/modernmetals-packaged-checks/**/*-review.json + ${{ runner.temp }}/modernmetals-packaged-checks/**/*-launch.json + build/problems/** diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..6f8b40a3 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,80 @@ +name: CodeQL + +on: + push: + branches: + - master-1.11.2 + - 'feature/**' + pull_request: + branches: + - master-1.11.2 + schedule: + - cron: '47 7 * * 4' + +permissions: + actions: read + contents: read + security-events: write + +jobs: + analyze: + name: Analyze Java + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + + - name: Install pinned Java 25 ForgeGradle Mavenizer runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '25.0.3+9.0.LTS' + + - name: Install pinned Java 8 compilation toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '8.0.502+7' + + - name: Install pinned Java 17 Gradle runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '17.0.1+12' + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 + + - name: Initialize CodeQL + uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 + with: + languages: java-kotlin + + - name: Stage checksum-verified dependencies + id: dependencies + shell: bash + run: | + set -euo pipefail + mirror="$RUNNER_TEMP/legacy-dependency-mirror" + bash gradle/stage-legacy-dependencies.sh "$mirror" + echo "repository=$mirror" >> "$GITHUB_OUTPUT" + + - name: Compile production code + shell: bash + run: | + set -euo pipefail + chmod +x ./gradlew + gradle_args=( + clean classes + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + --no-daemon --no-build-cache --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + + - name: Analyze + uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml new file mode 100644 index 00000000..ddaec55b --- /dev/null +++ b/.github/workflows/deploy-release.yml @@ -0,0 +1,799 @@ +name: Release Modern Metals + +run-name: Release Modern Metals ${{ inputs.release_version }} (CurseForge ${{ inputs.curseforge_release_level }}) + +on: + workflow_dispatch: + inputs: + release_version: + description: Four-component Modern Metals version; the target branch is derived from its fourth component. + required: true + type: string + release_commit: + description: Full commit SHA of the checked MMD target branch. + required: true + type: string + tested_jar_sha256: + description: SHA-256 of the verified Modern Metals game jar. + required: true + type: string + curseforge_release_level: + description: CurseForge release level only; this does not change the GitHub Release type. + required: true + default: release + type: choice + options: + - release + - beta + - alpha + confirm_live_publication: + description: Confirm publication to Maven, CurseForge, and GitHub after the build passes. + required: true + default: false + type: boolean + +permissions: + actions: read + contents: read + +concurrency: + group: modernmetals-release-${{ inputs.release_version }} + cancel-in-progress: false + +jobs: + preflight: + name: Validate release request + runs-on: ubuntu-latest + timeout-minutes: 10 + outputs: + target_branch: ${{ steps.route.outputs.target_branch }} + release_sha: ${{ steps.validate.outputs.release_sha }} + release_version: ${{ steps.validate.outputs.release_version }} + release_tag: ${{ steps.validate.outputs.release_tag }} + minecraft_version: ${{ steps.validate.outputs.minecraft_version }} + loader_name: ${{ steps.validate.outputs.loader_name }} + loader_display: ${{ steps.validate.outputs.loader_display }} + java_version: ${{ steps.validate.outputs.java_version }} + java_toolchain_version: ${{ steps.validate.outputs.java_toolchain_version }} + java_setup_version: ${{ steps.validate.outputs.java_setup_version }} + gradle_java_version: ${{ steps.validate.outputs.gradle_java_version }} + gradle_java_setup_version: ${{ steps.validate.outputs.gradle_java_setup_version }} + install_gradle_java: ${{ steps.validate.outputs.install_gradle_java }} + curseforge_project_id: ${{ steps.validate.outputs.curseforge_project_id }} + curseforge_dependencies: ${{ steps.validate.outputs.curseforge_dependencies }} + main_jar: ${{ steps.validate.outputs.main_jar }} + sources_jar: ${{ steps.validate.outputs.sources_jar }} + javadoc_jar: ${{ steps.validate.outputs.javadoc_jar }} + + steps: + - name: Resolve target branch from version + id: route + env: + GH_TOKEN: ${{ github.token }} + RELEASE_VERSION: ${{ inputs.release_version }} + run: | + set -euo pipefail + + if [[ "$GITHUB_REPOSITORY" != "MinecraftModDevelopmentMods/ModernMetals" ]]; then + echo "Releases can only run in MinecraftModDevelopmentMods/ModernMetals" >&2 + exit 1 + fi + if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.([0-9]+)$ ]]; then + echo "release_version must contain four numeric components" >&2 + exit 1 + fi + + target_suffix="${BASH_REMATCH[1]}" + if (( ${#target_suffix} < 6 )); then + echo "Invalid target suffix $target_suffix" >&2 + exit 1 + fi + + if [[ "$target_suffix" != 110021 && "$target_suffix" != 111021 ]]; then + echo 'Only the Minecraft 1.10.2 and 1.11.2 Forge targets are supported by this dispatcher' >&2; exit 1 + fi + + loader_code="${target_suffix: -1}" + target_digits="${target_suffix::-1}" + mc_patch_padded="${target_digits: -2}" + without_patch="${target_digits::-2}" + mc_minor_padded="${without_patch: -2}" + mc_major="${without_patch::-2}" + + if [[ -z "$mc_major" || ! "$mc_major" =~ ^[0-9]+$ ]]; then + echo "Invalid Minecraft major version in target suffix $target_suffix" >&2 + exit 1 + fi + mc_minor="$((10#$mc_minor_padded))" + mc_patch="$((10#$mc_patch_padded))" + case "$loader_code" in + 1) loader_suffix= ;; + 2) loader_suffix=-neo ;; + *) echo "Unknown loader code $loader_code in target suffix $target_suffix" >&2; exit 1 ;; + esac + + candidates=( + "master-$mc_major.$mc_minor.$mc_patch$loader_suffix" + "master-$mc_major.$mc_minor$loader_suffix" + ) + target_branch= + for candidate in "${candidates[@]}"; do + if gh api "repos/$GITHUB_REPOSITORY/branches/$candidate" >/dev/null 2>&1; then + target_branch="$candidate" + break + fi + done + if [[ -z "$target_branch" ]]; then + echo "No MMD release branch matches target suffix $target_suffix" >&2 + printf 'Checked branch %s\n' "${candidates[@]}" >&2 + exit 1 + fi + + echo "target_branch=$target_branch" >> "$GITHUB_OUTPUT" + echo "Resolved $RELEASE_VERSION to $target_branch" + + - name: Check out derived release branch + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ steps.route.outputs.target_branch }} + fetch-depth: 0 + + - name: Validate version, target, tag, and prior CI + id: validate + env: + GH_TOKEN: ${{ github.token }} + REQUESTED_VERSION: ${{ inputs.release_version }} + REQUESTED_COMMIT: ${{ inputs.release_commit }} + TESTED_JAR_SHA256: ${{ inputs.tested_jar_sha256 }} + run: | + set -euo pipefail + + value() { sed -n "s/^$1=//p" gradle.properties; } + release_version="$(value mod_version)" + minecraft_version="$(value minecraft_version)" + loader_name="$(value loader_name)" + loader_code="$(value loader_code)" + java_version="$(value java_version)" + java_toolchain_version="$(value java_toolchain_version)" + java_setup_version="$(value java_setup_version)" + gradle_java_version="$(value gradle_java_version)" + gradle_java_setup_version="$(value gradle_java_setup_version)" + curseforge_project_id="$(value curseforge_project_id)" + cf_requirements="$(value cf_requirements)" + orespawn_project_id="$(value orespawn_curse_project_id)" + + for required in release_version minecraft_version loader_name loader_code \ + java_version gradle_java_version curseforge_project_id; do + if [[ -z "${!required}" ]]; then + echo "Selected ref is missing required release metadata: $required" >&2 + exit 1 + fi + done + java_toolchain_version="${java_toolchain_version:-$java_version}" + java_setup_version="${java_setup_version:-$java_toolchain_version}" + + java_major() { + local version="$1" + if [[ ! "$version" =~ ^([0-9]+)([._+-].*)?$ ]]; then + echo "Invalid Java toolchain version $version" >&2 + return 1 + fi + printf '%s' "${BASH_REMATCH[1]}" + } + requires_separate_gradle_java() { + local toolchain_major + toolchain_major="$(java_major "$1")" + [[ "$toolchain_major" != "$2" ]] + } + + if [[ "$(java_major "$java_setup_version")" != "$(java_major "$java_toolchain_version")" ]]; then + echo "Hosted Java selector $java_setup_version does not match toolchain $java_toolchain_version" >&2 + exit 1 + fi + + java_setup_contracts=( + '8.0.502+7 17 true' + '16.0.2+7 17 true' + '17.0.1+12 17 false' + '17 17 false' + '21.0.8+9 21 false' + ) + for contract in "${java_setup_contracts[@]}"; do + read -r contract_toolchain contract_gradle expected <<<"$contract" + actual=false + if requires_separate_gradle_java "$contract_toolchain" "$contract_gradle"; then + actual=true + fi + if [[ "$actual" != "$expected" ]]; then + echo "Java setup contract failed for toolchain $contract_toolchain and Gradle $contract_gradle" >&2 + exit 1 + fi + done + + install_gradle_java=false + if requires_separate_gradle_java "$java_toolchain_version" "$gradle_java_version"; then + install_gradle_java=true + echo "Gradle Java $gradle_java_version will be installed separately from toolchain $java_toolchain_version" + else + echo "Pinned toolchain $java_toolchain_version also satisfies Gradle Java $gradle_java_version" + fi + + IFS=. read -r mc_major mc_minor mc_patch extra <<<"$minecraft_version" + if [[ -n "${extra:-}" || -z "${mc_major:-}" || -z "${mc_minor:-}" ]]; then + echo "Invalid minecraft_version=$minecraft_version" >&2 + exit 1 + fi + mc_patch="${mc_patch:-0}" + if [[ ! "$mc_major" =~ ^[0-9]+$ || ! "$mc_minor" =~ ^[0-9]+$ || ! "$mc_patch" =~ ^[0-9]+$ ]]; then + echo "Invalid minecraft_version=$minecraft_version" >&2 + exit 1 + fi + case "$loader_name:$loader_code" in + forge:1) loader_display=Forge ;; + neoforge:2) loader_display=NeoForge ;; + *) echo "Invalid loader metadata $loader_name/$loader_code" >&2; exit 1 ;; + esac + if [[ ! "$java_version" =~ ^[0-9]+$ || ! "$gradle_java_version" =~ ^[0-9]+$ ]]; then + echo "Invalid Java metadata $java_version/$gradle_java_version" >&2 + exit 1 + fi + if [[ "$curseforge_project_id" != "244357" ]]; then + echo "Unexpected Modern Metals CurseForge project $curseforge_project_id" >&2 + exit 1 + fi + + printf -v minor_padded '%02d' "$((10#$mc_minor))" + printf -v patch_padded '%02d' "$((10#$mc_patch))" + target_suffix="${mc_major}${minor_padded}${patch_padded}${loader_code}" + if [[ ! "$release_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.${target_suffix}$ ]]; then + echo "mod_version $release_version does not match $minecraft_version $loader_display target $target_suffix" >&2 + exit 1 + fi + if [[ "$REQUESTED_VERSION" != "$release_version" ]]; then + echo "Requested version $REQUESTED_VERSION does not match ${{ steps.route.outputs.target_branch }} mod_version $release_version" >&2 + exit 1 + fi + if [[ -z "$gradle_java_setup_version" ]]; then + if [[ "$install_gradle_java" == false ]]; then + gradle_java_setup_version="$java_setup_version" + elif [[ "$gradle_java_version" == 17 ]]; then + # Legacy targets keep the same pinned Gradle JDK as their CI builds. + gradle_java_setup_version=17.0.1+12 + else + gradle_java_setup_version="$gradle_java_version" + fi + fi + if [[ "$(java_major "$java_toolchain_version")" != "$java_version" \ + || "$(java_major "$gradle_java_setup_version")" != "$gradle_java_version" ]]; then + echo "Java setup selectors do not match the declared game and Gradle Java versions" >&2 + exit 1 + fi + + if [[ -n "$orespawn_project_id" && "$orespawn_project_id" != 245586 ]]; then + echo "Unexpected OreSpawn CurseForge project $orespawn_project_id" >&2 + exit 1 + fi + if [[ -z "$cf_requirements" && "$orespawn_project_id" == 245586 ]]; then + # MMDLib-free ports declare their required OreSpawn dependency directly. + cf_requirements=mmd-orespawn + fi + + curseforge_dependencies=() + IFS=',' read -ra requirement_slugs <<<"$cf_requirements" + for slug in "${requirement_slugs[@]}"; do + case "$slug" in + mmd-orespawn) curseforge_dependencies+=('245586(required)') ;; + base-metals) curseforge_dependencies+=('240967(required)') ;; + mmdlib) curseforge_dependencies+=('261744(required)') ;; + '') ;; + *) echo "Unsupported required CurseForge relation $slug" >&2; exit 1 ;; + esac + done + if [[ " ${curseforge_dependencies[*]} " != *' 245586(required) '* ]]; then + echo "The required OreSpawn CurseForge relation was not declared" >&2 + exit 1 + fi + + curseforge_dependencies+=('243157(optional)') + if [[ " ${curseforge_dependencies[*]} " != *' 240967(required) '* ]]; then + echo 'Base Metals must be a required relation for this release' >&2; exit 1 + fi + + release_sha="$(git rev-parse HEAD)" + if [[ ! "$REQUESTED_COMMIT" =~ ^[0-9a-f]{40}$ || "$REQUESTED_COMMIT" != "$release_sha" ]]; then + echo 'The target branch no longer points at the checked release commit' >&2 + exit 1 + fi + if [[ ! "$TESTED_JAR_SHA256" =~ ^[0-9a-f]{64}$ ]]; then + echo 'Supply the lowercase SHA-256 of the verified game jar' >&2 + exit 1 + fi + release_tag="$release_version" + echo "release_sha=$release_sha" >> "$GITHUB_OUTPUT" + echo "release_version=$release_version" >> "$GITHUB_OUTPUT" + echo "release_tag=$release_tag" >> "$GITHUB_OUTPUT" + echo "minecraft_version=$minecraft_version" >> "$GITHUB_OUTPUT" + echo "loader_name=$loader_name" >> "$GITHUB_OUTPUT" + echo "loader_display=$loader_display" >> "$GITHUB_OUTPUT" + echo "java_version=$java_version" >> "$GITHUB_OUTPUT" + echo "java_toolchain_version=$java_toolchain_version" >> "$GITHUB_OUTPUT" + echo "java_setup_version=$java_setup_version" >> "$GITHUB_OUTPUT" + echo "gradle_java_version=$gradle_java_version" >> "$GITHUB_OUTPUT" + echo "gradle_java_setup_version=$gradle_java_setup_version" >> "$GITHUB_OUTPUT" + echo "install_gradle_java=$install_gradle_java" >> "$GITHUB_OUTPUT" + echo "curseforge_project_id=$curseforge_project_id" >> "$GITHUB_OUTPUT" + { + echo 'curseforge_dependencies<> "$GITHUB_OUTPUT" + echo "main_jar=ModernMetals-$release_version.jar" >> "$GITHUB_OUTPUT" + echo "sources_jar=ModernMetals-$release_version-sources.jar" >> "$GITHUB_OUTPUT" + echo "javadoc_jar=ModernMetals-$release_version-javadoc.jar" >> "$GITHUB_OUTPUT" + + successful_ci="$(gh api \ + "repos/$GITHUB_REPOSITORY/commits/$release_sha/check-runs?per_page=100" \ + --jq '[.check_runs[] | select(.name == "Build, test, and audit" and .conclusion == "success")] | length')" + if [[ "$successful_ci" -lt 1 ]]; then + echo "The selected commit has no successful Build, test, and audit check" >&2 + exit 1 + fi + + if tag_json="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$release_tag" 2>/dev/null)"; then + tag_sha="$(jq -r '.object.sha' <<<"$tag_json")" + if [[ "$tag_sha" != "$release_sha" ]]; then + echo "Existing tag $release_tag resolves to $tag_sha instead of $release_sha" >&2 + exit 1 + fi + fi + + build: + name: Build immutable release artifact + needs: preflight + runs-on: ubuntu-latest + timeout-minutes: 60 + env: + MAIN_JAR: ${{ needs.preflight.outputs.main_jar }} + SOURCES_JAR: ${{ needs.preflight.outputs.sources_jar }} + JAVADOC_JAR: ${{ needs.preflight.outputs.javadoc_jar }} + outputs: + artifact_name: ${{ steps.artifact.outputs.name }} + + steps: + - name: Check out validated release ref + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ needs.preflight.outputs.release_sha }} + + - name: Install pinned Java 25 Mavenizer runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '25.0.3+9.0.LTS' + + - name: Install pinned Java 8 launcher toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '8.0.502+7' + + - name: Install target Java toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: ${{ needs.preflight.outputs.java_setup_version }} + + - name: Record target Java toolchain path + shell: bash + run: echo "MODERNMETALS_TARGET_JAVA_HOME=$JAVA_HOME" >> "$GITHUB_ENV" + + - name: Install separate Java for Gradle + if: needs.preflight.outputs.install_gradle_java == 'true' + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: ${{ needs.preflight.outputs.gradle_java_setup_version }} + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 + + - name: Resolve explicit Gradle Java installations + id: java-paths + shell: bash + run: | + set -euo pipefail + candidates=( + "$JAVA_HOME_25_X64" + "$JAVA_HOME_8_X64" + "$MODERNMETALS_TARGET_JAVA_HOME" + "$JAVA_HOME" + ) + paths= + for candidate in "${candidates[@]}"; do + test -x "$candidate/bin/java" + canonical="$(cd "$candidate" && pwd -P)" + if [[ ",$paths," != *",$canonical,"* ]]; then + paths="${paths:+$paths,}$canonical" + fi + done + echo "paths=$paths" >> "$GITHUB_OUTPUT" + + - name: Stage exact target dependencies + id: dependencies + run: | + set -euo pipefail + mirror="$RUNNER_TEMP/dependency-mirror" + if [[ -f gradle/stage-legacy-dependencies.sh ]]; then + bash gradle/stage-legacy-dependencies.sh "$mirror" + property=legacyDependencyVerificationRepository + elif [[ -f gradle/stage-orespawn-release.sh ]]; then + bash gradle/stage-orespawn-release.sh "$mirror" + property=orespawnVerificationRepository + else + echo "Selected target has no checksum-sealed dependency staging script" >&2 + exit 1 + fi + echo "repository=$mirror" >> "$GITHUB_OUTPUT" + echo "property=$property" >> "$GITHUB_OUTPUT" + + - name: Build, test, and audit once + env: + LOADER_NAME: ${{ needs.preflight.outputs.loader_name }} + run: | + set -euo pipefail + chmod +x ./gradlew + java_args=( + "-Dorg.gradle.java.installations.paths=${{ steps.java-paths.outputs.paths }}" + -Dorg.gradle.java.installations.auto-detect=false + -Dorg.gradle.java.installations.auto-download=false + ) + dependency_argument="-P${{ steps.dependencies.outputs.property }}=${{ steps.dependencies.outputs.repository }}" + if [[ "$LOADER_NAME" == neoforge ]]; then + # NeoGradle prepares inputs in build/tmp during configuration. Clean first. + ./gradlew clean "$dependency_argument" \ + --no-daemon --stacktrace --max-workers=2 "${java_args[@]}" + ./gradlew check build javadoc verifyReleaseArtifacts writeReleaseChecksums \ + "$dependency_argument" \ + --no-daemon --stacktrace --max-workers=2 "${java_args[@]}" + else + ./gradlew clean check build javadoc verifyReleaseArtifacts writeReleaseChecksums \ + "$dependency_argument" \ + --no-daemon --stacktrace "${java_args[@]}" + fi + + - name: Stage the immutable release artifact + id: artifact + env: + TESTED_JAR_SHA256: ${{ inputs.tested_jar_sha256 }} + run: | + set -euo pipefail + bundle="$RUNNER_TEMP/release-bundle" + mkdir -p "$bundle" + cp "build/libs/$MAIN_JAR" "$bundle/" + cp "build/libs/$SOURCES_JAR" "$bundle/" + cp "build/libs/$JAVADOC_JAR" "$bundle/" + cp build/release/SHA256SUMS "$bundle/" + cp CHANGELOG.txt "$bundle/" + (cd "$bundle" && sha256sum --check SHA256SUMS) + printf '%s %s\n' "$TESTED_JAR_SHA256" "$bundle/$MAIN_JAR" | sha256sum --check - + echo "name=ModernMetals-${{ needs.preflight.outputs.minecraft_version }}-${{ needs.preflight.outputs.loader_name }}-${{ needs.preflight.outputs.release_sha }}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" >> "$GITHUB_OUTPUT" + + - name: Upload immutable release artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: ${{ steps.artifact.outputs.name }} + if-no-files-found: error + retention-days: 90 + path: ${{ runner.temp }}/release-bundle/* + + - name: Summarize the prepared release candidate + env: + RELEASE_BRANCH: ${{ needs.preflight.outputs.target_branch }} + RELEASE_LEVEL: ${{ inputs.curseforge_release_level }} + RELEASE_SHA: ${{ needs.preflight.outputs.release_sha }} + RELEASE_VERSION: ${{ needs.preflight.outputs.release_version }} + run: | + { + echo "## Prepared release candidate" + echo + echo "- Version: \`$RELEASE_VERSION\`" + echo "- Target branch: \`$RELEASE_BRANCH\`" + echo "- Commit: \`$RELEASE_SHA\`" + echo "- CurseForge release level: \`$RELEASE_LEVEL\`" + echo "- GitHub Release type: ordinary release" + echo + echo "### Public artifact checksums" + echo '```text' + cat build/release/SHA256SUMS + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + + release_confirmation: + name: Confirm ${{ inputs.release_version }} live publication + needs: + - preflight + - build + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Confirm publication and release-secret access + env: + CONFIRM_LIVE_PUBLICATION: ${{ inputs.confirm_live_publication }} + CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} + MAVEN_UPLOAD_URL: ${{ secrets.MAVEN_UPLOAD_URL }} + MAVEN_UPLOAD_USERNAME: ${{ secrets.MAVEN_UPLOAD_USERNAME }} + MAVEN_UPLOAD_PASSWORD: ${{ secrets.MAVEN_UPLOAD_PASSWORD }} + run: | + set -euo pipefail + if [[ "$CONFIRM_LIVE_PUBLICATION" != "true" ]]; then + echo "Confirm live publication in the workflow form before releasing ${{ needs.preflight.outputs.release_tag }}" >&2 + exit 1 + fi + echo "Live publication confirmed for ${{ needs.preflight.outputs.release_tag }}" + missing=() + for name in CURSEFORGE_TOKEN MAVEN_UPLOAD_URL MAVEN_UPLOAD_USERNAME MAVEN_UPLOAD_PASSWORD; do + if [[ -z "${!name:-}" ]]; then + missing+=("$name") + fi + done + if (( ${#missing[@]} > 0 )); then + printf 'Required release secret is unavailable: %s\n' "${missing[@]}" >&2 + exit 1 + fi + echo "All four required release secrets are available." + + create_release_tag: + name: Create validated release tag + needs: + - preflight + - build + - release_confirmation + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + actions: read + contents: write + + steps: + - name: Create or verify the exact tag + env: + GH_TOKEN: ${{ github.token }} + RELEASE_SHA: ${{ needs.preflight.outputs.release_sha }} + RELEASE_TAG: ${{ needs.preflight.outputs.release_tag }} + run: | + set -euo pipefail + + if tag_json="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$RELEASE_TAG" 2>/dev/null)"; then + tag_sha="$(jq -r '.object.sha' <<<"$tag_json")" + if [[ "$tag_sha" != "$RELEASE_SHA" ]]; then + echo "Existing tag $RELEASE_TAG resolves to $tag_sha instead of $RELEASE_SHA" >&2 + exit 1 + fi + echo "Existing tag $RELEASE_TAG already points to $RELEASE_SHA" + else + gh api --method POST "repos/$GITHUB_REPOSITORY/git/refs" \ + -f ref="refs/tags/$RELEASE_TAG" \ + -f sha="$RELEASE_SHA" >/dev/null + echo "Created tag $RELEASE_TAG at $RELEASE_SHA" + fi + + created_sha="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$RELEASE_TAG" --jq '.object.sha')" + if [[ "$created_sha" != "$RELEASE_SHA" ]]; then + echo "Tag verification returned $created_sha instead of $RELEASE_SHA" >&2 + exit 1 + fi + + publish_maven: + name: Publish Maven + needs: + - preflight + - build + - create_release_tag + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Check out the tagged publication definition + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ needs.preflight.outputs.release_sha }} + + - name: Install pinned Java 25 Mavenizer runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '25.0.3+9.0.LTS' + + - name: Install pinned Java 8 launcher toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '8.0.502+7' + + - name: Install target Java toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: ${{ needs.preflight.outputs.java_setup_version }} + + - name: Record target Java toolchain path + shell: bash + run: echo "MODERNMETALS_TARGET_JAVA_HOME=$JAVA_HOME" >> "$GITHUB_ENV" + + - name: Install separate Java for Gradle + if: needs.preflight.outputs.install_gradle_java == 'true' + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: ${{ needs.preflight.outputs.gradle_java_setup_version }} + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 + + - name: Resolve explicit Gradle Java installations + id: java-paths + shell: bash + run: | + set -euo pipefail + candidates=( + "$JAVA_HOME_25_X64" + "$JAVA_HOME_8_X64" + "$MODERNMETALS_TARGET_JAVA_HOME" + "$JAVA_HOME" + ) + paths= + for candidate in "${candidates[@]}"; do + test -x "$candidate/bin/java" + canonical="$(cd "$candidate" && pwd -P)" + if [[ ",$paths," != *",$canonical,"* ]]; then + paths="${paths:+$paths,}$canonical" + fi + done + echo "paths=$paths" >> "$GITHUB_OUTPUT" + + - name: Download immutable release artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: ${{ needs.build.outputs.artifact_name }} + path: ${{ runner.temp }}/release-input + + - name: Stage exact target dependencies + id: dependencies + shell: bash + run: | + set -euo pipefail + mirror="$RUNNER_TEMP/dependency-mirror" + if [[ -f gradle/stage-legacy-dependencies.sh ]]; then + bash gradle/stage-legacy-dependencies.sh "$mirror" + property=legacyDependencyVerificationRepository + elif [[ -f gradle/stage-orespawn-release.sh ]]; then + bash gradle/stage-orespawn-release.sh "$mirror" + property=orespawnVerificationRepository + else + echo "Selected target has no checksum-sealed dependency staging script" >&2 + exit 1 + fi + echo "repository=$mirror" >> "$GITHUB_OUTPUT" + echo "property=$property" >> "$GITHUB_OUTPUT" + + - name: Verify artifact and Maven credentials + env: + MAVEN_UPLOAD_URL: ${{ secrets.MAVEN_UPLOAD_URL }} + MAVEN_UPLOAD_USERNAME: ${{ secrets.MAVEN_UPLOAD_USERNAME }} + MAVEN_UPLOAD_PASSWORD: ${{ secrets.MAVEN_UPLOAD_PASSWORD }} + run: | + set -euo pipefail + (cd "$RUNNER_TEMP/release-input" && sha256sum --check SHA256SUMS) + if [[ -z "$MAVEN_UPLOAD_URL" || -z "$MAVEN_UPLOAD_USERNAME" || -z "$MAVEN_UPLOAD_PASSWORD" ]]; then + echo "Maven upload URL and credentials must all be configured" >&2 + exit 1 + fi + if [[ "$MAVEN_UPLOAD_URL" == file:* ]]; then + echo "Maven publication cannot target a runner-local repository" >&2 + exit 1 + fi + + - name: Publish the prepared Maven artifacts + env: + MAVEN_UPLOAD_URL: ${{ secrets.MAVEN_UPLOAD_URL }} + MAVEN_UPLOAD_USERNAME: ${{ secrets.MAVEN_UPLOAD_USERNAME }} + MAVEN_UPLOAD_PASSWORD: ${{ secrets.MAVEN_UPLOAD_PASSWORD }} + run: | + chmod +x ./gradlew + dependency_argument="-P${{ steps.dependencies.outputs.property }}=${{ steps.dependencies.outputs.repository }}" + ./gradlew publishMavenJavaPublicationToReleaseRepository \ + -PpreparedReleaseDir="$RUNNER_TEMP/release-input" \ + "$dependency_argument" \ + --no-daemon --stacktrace \ + -Dorg.gradle.java.installations.paths="${{ steps.java-paths.outputs.paths }}" \ + -Dorg.gradle.java.installations.auto-detect=false \ + -Dorg.gradle.java.installations.auto-download=false + + publish_curseforge: + name: Publish CurseForge + needs: + - preflight + - build + - publish_maven + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Download immutable release artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: ${{ needs.build.outputs.artifact_name }} + path: ${{ runner.temp }}/release-input + + - name: Verify artifact and CurseForge credential + env: + CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} + run: | + set -euo pipefail + (cd "$RUNNER_TEMP/release-input" && sha256sum --check SHA256SUMS) + if [[ -z "$CURSEFORGE_TOKEN" ]]; then + echo "CURSEFORGE_TOKEN must be configured" >&2 + exit 1 + fi + + - name: Publish Modern Metals + uses: Kira-NT/mc-publish@52307b03863581dec6b652b83e597aec02ebb075 # v3.3.1 + with: + curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} + curseforge-id: ${{ needs.preflight.outputs.curseforge_project_id }} + name: Modern Metals ${{ needs.preflight.outputs.release_version }} for Minecraft ${{ needs.preflight.outputs.minecraft_version }} (${{ needs.preflight.outputs.loader_display }}) + version: ${{ needs.preflight.outputs.release_version }} + version-type: ${{ inputs.curseforge_release_level }} + changelog-file: ${{ runner.temp }}/release-input/CHANGELOG.txt + loaders: ${{ needs.preflight.outputs.loader_name }} + game-versions: ${{ needs.preflight.outputs.minecraft_version }} + game-version-filter: none + environment: client | server + java: Java ${{ needs.preflight.outputs.java_version }} + curseforge-dependencies: ${{ needs.preflight.outputs.curseforge_dependencies }} + fail-mode: fail + files: | + ${{ runner.temp }}/release-input/${{ needs.preflight.outputs.main_jar }} + ${{ runner.temp }}/release-input/${{ needs.preflight.outputs.sources_jar }} + ${{ runner.temp }}/release-input/${{ needs.preflight.outputs.javadoc_jar }} + + publish_github: + name: Publish GitHub Release + needs: + - preflight + - build + - publish_curseforge + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + actions: read + contents: write + + steps: + - name: Download immutable release artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: ${{ needs.build.outputs.artifact_name }} + path: ${{ runner.temp }}/release-input + + - name: Verify artifact + run: (cd "$RUNNER_TEMP/release-input" && sha256sum --check SHA256SUMS) + + - name: Publish GitHub Release last + uses: Kira-NT/mc-publish@52307b03863581dec6b652b83e597aec02ebb075 # v3.3.1 + with: + github-token: ${{ github.token }} + github-tag: ${{ needs.preflight.outputs.release_tag }} + github-commitish: ${{ needs.preflight.outputs.release_sha }} + github-draft: false + github-prerelease: false + name: Modern Metals ${{ needs.preflight.outputs.release_version }} for Minecraft ${{ needs.preflight.outputs.minecraft_version }} (${{ needs.preflight.outputs.loader_display }}) + version: ${{ needs.preflight.outputs.release_version }} + version-type: release + changelog-file: ${{ runner.temp }}/release-input/CHANGELOG.txt + fail-mode: fail + files: | + ${{ runner.temp }}/release-input/${{ needs.preflight.outputs.main_jar }} + ${{ runner.temp }}/release-input/${{ needs.preflight.outputs.sources_jar }} + ${{ runner.temp }}/release-input/${{ needs.preflight.outputs.javadoc_jar }} + ${{ runner.temp }}/release-input/SHA256SUMS diff --git a/.github/workflows/release-on-tag.yml b/.github/workflows/release-on-tag.yml new file mode 100644 index 00000000..718b4afa --- /dev/null +++ b/.github/workflows/release-on-tag.yml @@ -0,0 +1,89 @@ +name: Start Modern Metals release from tag + +on: + push: + tags: + - '*.*.*.*' + +permissions: + actions: read + contents: read + +concurrency: + group: modernmetals-release-starter-${{ github.ref_name }} + cancel-in-progress: false + +jobs: + validate-release-tag: + name: Validate tag for manual release confirmation + if: github.repository == 'MinecraftModDevelopmentMods/ModernMetals' + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Check out tagged source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + + - name: Validate release tag, target metadata, and prior CI + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + + value() { sed -n "s/^$1=//p" gradle.properties; } + release_version="$(value mod_version)" + minecraft_version="$(value minecraft_version)" + loader_name="$(value loader_name)" + loader_code="$(value loader_code)" + + IFS=. read -r mc_major mc_minor mc_patch extra <<<"$minecraft_version" + mc_patch="${mc_patch:-0}" + if [[ -n "${extra:-}" || ! "$mc_major" =~ ^[0-9]+$ || ! "$mc_minor" =~ ^[0-9]+$ \ + || ! "$mc_patch" =~ ^[0-9]+$ || "$loader_name:$loader_code" != 'forge:1' ]]; then + echo "Invalid target metadata $minecraft_version/$loader_name/$loader_code" >&2 + exit 1 + fi + printf -v minor_padded '%02d' "$((10#$mc_minor))" + printf -v patch_padded '%02d' "$((10#$mc_patch))" + target_suffix="${mc_major}${minor_padded}${patch_padded}${loader_code}" + + if [[ ! "$release_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.${target_suffix}$ ]]; then + echo "mod_version $release_version does not match target $target_suffix" >&2 + exit 1 + fi + if [[ "$GITHUB_REF_NAME" != "$release_version" ]]; then + echo "Release tag must equal mod_version $release_version; found $GITHUB_REF_NAME" >&2 + exit 1 + fi + + successful_ci="$(gh api \ + "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/check-runs?per_page=100" \ + --jq '[.check_runs[] | select(.name == "Build, test, and audit" and .conclusion == "success")] | length')" + if [[ "$successful_ci" -lt 1 ]]; then + echo "The tagged commit has no successful Build, test, and audit check" >&2 + exit 1 + fi + + - name: Record the required manual publication step + env: + RELEASE_WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/workflows/deploy-release.yml + shell: bash + run: | + { + echo "## Release candidate validated" + echo + echo "Tag \`$GITHUB_REF_NAME\` matches the selected target and has a successful Build, test, and audit check." + echo + echo "**Nothing has been published.**" + echo + echo "To continue, open [Release Modern Metals]($RELEASE_WORKFLOW_URL), select **Run workflow**, and enter:" + echo + echo "- release_version: \`$GITHUB_REF_NAME\`" + echo "- release_commit: \`$GITHUB_SHA\`" + echo "- tested_jar_sha256: SHA-256 of the tested game jar" + echo "- curseforge_release_level: \`release\`, \`beta\`, or \`alpha\`" + echo "- confirm_live_publication: \`true\`" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/validate-gradle-build.yml b/.github/workflows/validate-gradle-build.yml new file mode 100644 index 00000000..0b533e41 --- /dev/null +++ b/.github/workflows/validate-gradle-build.yml @@ -0,0 +1,23 @@ +name: Validate Gradle Wrapper + +on: + push: + branches: + - master-1.11.2 + - 'feature/**' + pull_request: + branches: + - master-1.11.2 + +permissions: + contents: read + +jobs: + validation: + name: Validation + runs-on: ubuntu-latest + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - name: Validate wrapper integrity + uses: gradle/actions/wrapper-validation@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 diff --git a/.gitignore b/.gitignore index 3f67a97d..3a14b223 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,15 @@ run /README.txt /forge-*-changelog.txt -secret.json \ No newline at end of file +secret.json +# Local context and generated verification +/AGENTS.md +/agent-notes/ +/.codex/ +/.verification*/ +/release-candidates/ +__pycache__/ +*.pyc +.DS_Store +Thumbs.db +desktop.ini diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4553e94c..00000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: java -sudo: false -cache: - directories: - - $HOME/.gradle -notifications: - email: false -jdk: - - oraclejdk8 -before_install: - - chmod a+x gradlew -install: ./gradlew setupCIWorkspace -S -script: ./gradlew clean build -S diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 97b106a9..8abbc745 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,15 +1,14 @@ -1) Tinkers Construct Tools -2) Code cleanups -3) Support Items for Mekanism -4) Support Items for IC2 -5) Balanced metals (They now have different stats) -6) Fixed Fishing Rod item name ($metal_fishing_rod instead of $metal_fishingrod) -7) Added Buttons, Levers, Stairs, Pressure Plates, Walls & Slabs -8) Building Bricks integration support -9) Tungsten oredicted as wolfram for more mod compatibility -10) Small Blends/small piles of powder oredicted as dustTiny instead of smalldust -11) Fishing Rods now actually work. (Thanks to lclc98 for the assistance) -12) Veinminer integration -13) Ender IO Integration -14) More Zinc Stuff -15) Ported to 1.11.2 +2.5.1.111021 for 1.11.2 + +This brings the 1.11.2 branch up to date. Use Java 8, Forge 13.20.1.2588 or later for 1.11.2, Base Metals 2.5.1.111021 and MMD OreSpawn 4.1.5.111021. + +- Kept boron, beryllium and thorium with their ores, molten forms, buckets and recipes. +- Kept titanium's rutile and magnesium recipe. Titanium ore generation is optional and starts off. +- Moved ore generation to OreSpawn 4. Existing OreSpawn settings can carry forward and retrogen stays off. +- Used KiriCattus's ore overlays so the stone in your resource pack shows through. Thorium keeps its original texture. +- Put Modern Metals settings in the Forge Mods menu and OreSpawn Mods directory. The screens have German, Simplified Chinese and Russian translations, with regional English files also included. +- Added High and Low Fantasy and a switch for new villager trades. Restart after changing them. Clients need the same Fantasy Level as their server. +- Fixed IC2 and Mekanism processing and a Tinkers casting issue when the aluminum fluid was already registered. +- Use the Base Metals crusher registry for all 20 ores. +- Restored old door, fishing rod, horse armor, alloy ore and molten aluminum brass IDs for 0.11.0 worlds. Back up an older world before opening it with the new jars. +- Updated the build and Eclipse setup to ForgeGradle 7. The game still runs on Java 8. diff --git a/README.md b/README.md index 4fece25c..e1afbc9f 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,75 @@ -# ModernMetals [![Build Status](https://travis-ci.org/jriwanek/ModernMetals.svg?branch=master)](https://travis-ci.org/jriwanek/ModernMetals) -Addon to BaseMetals supporting Modern Metals +# Modern Metals for Minecraft 1.11.2 -Content -------- -Bars +Modern Metals adds industrial metals and alloys to Base Metals, with ores, +building blocks, tools, armour and molten metals. This branch is for Minecraft +**1.11.2**, local candidate **2.5.1.111021**. -Doors +## Installation -Ores +Use Java **8** and Forge **13.20.1.2588 or newer for Minecraft 1.11.2**. +For local testing, put these three jars in `mods/` on both the client and server. -Plates +- Modern Metals `2.5.1.111021`. +- Base Metals `2.5.1.111021` from the staged 1.11.2 build, supported range `[2.5.1.111021,2.6.0)`. +- MMD OreSpawn `4.1.5.111021` from the staged 1.11.2 build, supported range `[4.1.5.111021,5.0.0)`. -Trapdoors +Remove older copies first. Use the normal mod jars rather than the sources, +Javadocs or development jars. This version uses Base Metals' shared library, so +you don't need a separate MMDLib or Mineralogy jar. -Armor +## Materials -Tools: Crackhammers, Axes, Hoes, Pickaxes, Shovels, Fishing Rods +The 21 configurable materials are aluminum, aluminum brass, beryllium, boron, +cadmium, chromium, galvanized steel, iridium, magnesium, manganese, nichrome, +osmium, plutonium, rutile, stainless steel, tantalum, thorium, titanium, tungsten, +uranium and zirconium. Chromium/Chrome, Tungsten/Wolfram and Aluminum/Aluminium +Ore Dictionary names are kept for compatibility. -Ores, Blocks, Ingots & Nuggets +Boron, beryllium and thorium have ores, molten forms, buckets and processing +recipes. Titanium is still made from **rutile and magnesium**, using +titanium blend and smelting, or molten alloying and casting. Titanium ore generation +is **off by default**. If enabled, it uses the same placement settings as rutile. -Powders & Blends (Ingot and Nugget size) +## Configuration -Rods +Open **Mods -> Modern Metals -> Config**, or use the Modern Metals cog under +**Create World -> OreSpawn -> Mods**. Both save to `config/ModernMetals.cfg`. +Done saves your changes. Escape cancels them. Restart the game or server to apply +them. Keep materials enabled in worlds that use their blocks and items. -Gears +**Fantasy Level** defaults to High Fantasy, which keeps the usual recipes. Low +Fantasy restricts unrealistic equipment and building recipes across the materials, +including trades and Tinkers parts. In 1.11 this is a Modern Metals setting and is +independent of Base Metals. See [the fantasy settings](docs/CONTENT_MODE.md) for +the full list of restrictions. A multiplayer server and its clients need the +same Fantasy Level. Use **Villager Trades** to turn off new Modern Metals offers +in either mode. -Weapons: Swords, Bows, Arrows, Crossbows & Bolts +The config screen is available in English, German, Simplified Chinese and +Russian. Regional English strings are included for Australia, Canada, the UK, +New Zealand and Pirate Speak. -Materials ---------- -Aluminum +Ore generation is handled by OreSpawn 4. Use its editor to change ore settings. +Existing chunks are left alone and retrogen stays off. See [ore generation and +migration](docs/ORE_GENERATION.md) for defaults, ore groups and older config files. +Ores use KiriCattus's overlays over the stone texture, so stone resource packs +also change the background of the ore. Thorium keeps its original texture. -Aluminum Brass +Modern Metals ores work in the Base Metals Crack Hammer and in the rock +crushers that use the same Base Metals registry. They use the Base Metals crusher +settings, including the powder yield and any ores you disable there. -Cadmium +## Updating an older 1.11.2 world -Chromium +Keep a backup before opening the world with the new jars. See the [migration +notes](docs/ORE_GENERATION.md) before moving an existing configuration. Modern +Metals resolves the old door, fishing rod, horse armor and molten aluminum brass IDs from 0.11.0. +Placed galvanized steel, nichrome and stainless steel ores still load. Those +three ores have no default OreSpawn rules. Keep the materials enabled when first +opening a world that contains them. -Iridium +## Development -Lithium - -Magnesium - -Manganese - -Niter - -Osmium - -Phosphorous - -Plutonium - -Rutile - -Silicon - -Sulfur - -Tantalum - -Titanium - -Tungsten - -Uranium - -Zirconium +The build uses ForgeGradle 7 and still produces Java 8 jars. See [building and +Eclipse](docs/BUILDING.md), [integration checks and known limits](tools/validation/legacy111/README.md) +and [release preparation](docs/RELEASING.md). diff --git a/build.gradle b/build.gradle index 1d83d820..c38bd3be 100644 --- a/build.gradle +++ b/build.gradle @@ -1,367 +1,190 @@ -def corePlugin = '' +import org.apache.tools.ant.filters.FixCrLfFilter -buildscript { - repositories { - jcenter() - maven { - name = 'forge' - url = 'http://files.minecraftforge.net/maven' - } - maven { - name = 'gradle' - url 'https://plugins.gradle.org/m2/' - } - maven { - name = 'sonatype' - url = 'https://oss.sonatype.org/content/groups/public' - } - } - dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT' - classpath 'gradle.plugin.com.matthewprenger:CurseGradle:1.0.11' - classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.2' - classpath 'org.junit.platform:junit-platform-gradle-plugin:1.2.0' - } +plugins { + id 'java' + id 'eclipse' + id 'idea' + id 'maven-publish' + id 'net.minecraftforge.renamer' version '1.1.5' + id 'net.minecraftforge.gradle' version '7.0.34' } -apply plugin: 'net.minecraftforge.gradle.forge' -apply plugin: 'com.matthewprenger.cursegradle' -apply plugin: 'maven-publish' -apply plugin: 'org.sonarqube' -apply plugin: 'org.junit.platform.gradle.plugin' -apply plugin: 'jacoco' +group = project.mod_group +version = project.mod_version +base.archivesName = 'ModernMetals' -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - testCompile('org.junit.jupiter:junit-jupiter-api:5.2.+') - testCompile('org.junit.platform:junit-platform-launcher:1.2.+') - testRuntime('org.junit.jupiter:junit-jupiter-engine:5.2.+') - testCompile('org.mockito:mockito-core:2.+') +if (!(project.version ==~ /\d+\.\d+\.\d+\.111021/)) { + throw new GradleException('Use Major.Minor.Patch.111021 for Minecraft 1.11.2 on Forge.') } -project.afterEvaluate { - def junitPlatformTestTask = project.tasks.getByName('junitPlatformTest') - - // configure jacoco to analyze the junitPlatformTest task - jacoco { - // this tool version is compatible with - toolVersion = '0.8.1' - applyTo junitPlatformTestTask - } - - // create junit platform jacoco task - project.task(type: JacocoReport, "junitPlatformJacocoReport", - { - sourceDirectories = files("./src/main") - classDirectories = files("$buildDir/classes/main") - executionData junitPlatformTestTask - }) +// Gradle and Mavenizer use modern JDKs; the mod and game still use Java 8. +java { + toolchain.languageVersion = JavaLanguageVersion.of(8) + withSourcesJar() + withJavadocJar() } - -junitPlatform { - platformVersion '1.0.3' // optional, defaults to plugin version - //logManager 'org.apache.logging.log4j.jul.LogManager' - reportsDir file('build/test-results/junit-platform') // this is the default - // enableStandardTestTask true - // selectors (optional) - // filters (optional) +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' + sourceCompatibility = targetCompatibility = '1.8' + options.compilerArgs.addAll(['-Xmaxerrs', '1000']) } - -javadoc { +tasks.named('compileTestJava', JavaCompile) { options.compilerArgs.add('-proc:none') } +tasks.withType(Test).configureEach { useJUnitPlatform() } +tasks.withType(Javadoc).configureEach { failOnError = false + options.encoding = 'UTF-8' + options.addStringOption('Xdoclint:none', '-quiet') + options.addStringOption('docencoding', 'UTF-8') + options.addStringOption('charset', 'UTF-8') + options.addBooleanOption('notimestamp', true) } - -description = 'Modern Metals' -def mod_file = getModFile() -def mc_version = '1.11.2' - -ext { - short_version = getVersion('VERSION', mod_file) +tasks.withType(AbstractArchiveTask).configureEach { + preserveFileTimestamps = false + reproducibleFileOrder = true + includeEmptyDirs = false + exclude '**/desktop.ini', '**/Thumbs.db', '**/.DS_Store' } - -version = mc_version + '-' + short_version - -if (System.getenv().BUILD_NUMBER) { - version += '.' + System.getenv().BUILD_NUMBER +// Mapped dependency bytes no longer match their original signatures. Keep the +// verified downloads intact and strip signatures only from renamed copies. +tasks.withType(net.minecraftforge.renamer.gradle.RenameJar).configureEach { + additionalArgs.addAll('--strip-sigs', 'ALL', '--disable-abstract-param') } -group = 'com.mcmoddev' -archivesBaseName = 'ModernMetals' -sourceCompatibility = targetCompatibility = '1.8' - -class Secrets { - def data = null - def getProperty(String key) { - return data ? data[key] : '' - } +def textPatterns = ['cfg', 'css', 'html', 'info', 'java', 'js', 'json', 'lang', 'mcmeta', 'md', 'properties', 'txt', 'xml'] + .collect { "**/*.${it}".toString() } +def normalizeText = { details -> + details.filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('lf'), + eof: FixCrLfFilter.AddAsisRemove.newInstance('asis')) } -import groovy.json.JsonSlurper - -def secretFile -if (System.getenv().SECRET_FILE) { - secretFile = file System.getenv().SECRET_FILE -} else { - secretFile = file 'secret.json' +// Preserve the tested game jar's line endings for this compatibility list. +def legacyApiList = 'META-INF/modernmetals-legacy-api.list' +def normalizeLegacyApi = { details -> + details.filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf'), + eof: FixCrLfFilter.AddAsisRemove.newInstance('asis')) } -project.ext.secret = new Secrets() -if (secretFile.exists()) { - secretFile.withReader { - project.ext.secret.data = new JsonSlurper().parse it - } +def developmentOutput = layout.buildDirectory.dir('forge11-development/main') +def prepareDevelopmentMod = tasks.register('prepareDevelopmentMod', Sync) { + group = 'ide' + description = 'Merges production classes and processed resources for Forge 1.11 launches.' + dependsOn tasks.named('classes') + from tasks.named('compileJava', JavaCompile).flatMap { it.destinationDirectory } + from sourceSets.main.output.resourcesDir + into developmentOutput } minecraft { - version = '1.11.2-13.20.1.2386' - runDir = 'run' - mappings = 'stable_32' -// coreMod = corePlugin -// clientJvmArgs = ["-Dfml.coreMods.load=$corePlugin"] -// serverJvmArgs = ["-Dfml.coreMods.load=$corePlugin"] - - replace '@FINGERPRINT@', project.findProperty('signSHA1') -} - -repositories { - maven { // Mantle, TCon, JEI - name 'DVS1 Maven FS' - url 'http://dvs1.progwml6.com/files/maven/' - } - maven { // CCL, CCC, NEI - name 'Chickenbones Repo' - url 'http://chickenbones.net/maven/' - } - maven { // The One Probe - name 'tterrag' - url 'http://maven.tterrag.com/' - } - maven { - name 'MMD' - url 'https://maven.mcmoddev.com/' - } - maven { // MCMultipart - name 'amadornes' - url 'http://maven.amadornes.com/' - } - maven { // CraftTweaker, ZenScript - name 'jared maven' - url 'http://blamejared.com/maven/' - } - maven { // IC2 - name 'industrialcraft' - url 'http://maven.ic2.player.to/' - } - maven { // WAILA/HWYLA - name 'tehnut' - url 'http://tehnut.info/maven/' - } - maven { // CoFH - name 'Covers Maven' - url 'http://maven.covers1624.net/' - } - maven { - name = 'CurseForge' - url = 'https://minecraft.curseforge.com/api/maven/' - } - maven { - name 'opencomputers' - url 'http://maven.cil.li/' - } - maven { // Mekanism, TAIGA - name 'jitpack' - url 'https://jitpack.io/' - } - maven { // Buildcraft - name 'buildcraft' - url 'https://mod-buildcraft.com/maven/' - } - maven { // Storage Drawers - name 'thiakil' - url 'http://maven.thiakil.com/' + mappings channel: project.mappings_channel, version: project.mappings_version + if (gradle.startParameter.offline) mavenizerArguments.add('--offline') + runs { + configureEach { + workingDir.convention layout.projectDirectory.dir( + providers.gradleProperty('modernMetalsRunDirectory').getOrElse('run')) + environment 'MC_VERSION', project.minecraft_version + environment 'MOD_CLASSES', developmentOutput.get().asFile.absolutePath + systemProperty 'fml.coreMods.load', project.coreplugin + systemProperty 'log4j.skipJansi', 'true' + systemProperty 'forge.logging.console.level', 'info' + systemProperty 'log4j.configurationFile', file('gradle/ide/log4j2.xml').toURI().toString() + if (providers.gradleProperty('modernMetalsLaunchProbePhase').isPresent()) { + systemProperty 'launch.phase', providers.gradleProperty('modernMetalsLaunchProbePhase').get() + } + } + register('client') + register('server') { args 'nogui' } } } -dependencies { - deobfCompile "com.mcmoddev:AdditionalLootTables:${mc_version}-${alt_version}" - deobfCompile "com.mcmoddev:OreSpawn:${mc_version}-${orespawn_version}" - deobfCompile "com.mcmoddev:BaseMetals:${mc_version}-${bme_version}" - deobfCompile "slimeknights:TConstruct:${mc_version}-${tconstruct_version}" - deobfCompile "slimeknights.mantle:Mantle:${mc_version}-${mantle_version}" - // CoFHCore for TE integration - deobfCompile ("cofh:CoFHCore:${mc_version}-${cofhcore_version}:universal") { - exclude group: 'mezz.jei' +tasks.matching { it.name in ['runClient', 'runServer'] }.configureEach { + dependsOn prepareDevelopmentMod + dependsOn 'prepareJava8Launcher' + doFirst { + Set splitOutputs = sourceSets.main.output.files.collect { it.canonicalFile } as Set + classpath = files(developmentOutput, tasks.named('prepareJava8Launcher'), classpath.filter { + !splitOutputs.contains(it.canonicalFile) && it.name != 'slime-launcher-0.2.2.jar' + }) + environment 'MC_VERSION', project.minecraft_version + environment 'MOD_CLASSES', developmentOutput.get().asFile.absolutePath } } -processResources { - inputs.property 'version', project.version - inputs.property 'mcversion', project.minecraft.version - - from (sourceSets.main.resources.srcDirs) { - include 'mcmod.info' - expand 'version': short_version, 'mcversion': project.minecraft.version - } - - from (sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' - } -} +def legacyMirror = file(providers.gradleProperty('legacyDependencyVerificationRepository') + .orElse(providers.gradleProperty('legacyDependencyRepository')) + .getOrElse('.gradle/legacy-dependencies')) +def legacyRepository = { String remote -> legacyMirror.isDirectory() ? legacyMirror.toURI() : uri(remote) } -jar { - manifest { -// attributes 'FMLCorePluginContainsFMLMod': 'true' -// attributes 'FMLCorePlugin': corePlugin -// attributes 'FMLAT' : '' +repositories { + minecraft.mavenizer(it) + maven fg.forgeMaven + maven fg.minecraftLibsMaven + maven { + name = 'VerifiedLegacyMirror' + url = legacyMirror.toURI() + metadataSources { artifact() } } -} - -task deobfJar(type: Jar) { - classifier = 'deobf' - from sourceSets.main.output -} - -task signJar(type: SignJar, dependsOn: reobfJar) { - - // Skips if the keyStore property is missing. - onlyIf { - project.hasProperty('keyStore') + exclusiveContent { + forRepository { maven { url = legacyRepository('https://www.cursemaven.com'); metadataSources { artifact() } } } + filter { includeGroup 'curse.maven' } } - - // findProperty allows us to reference the property without it existing. - // Using project.propName would cause the script to fail validation if - // the property did not exist. - keyStore = project.findProperty('keyStore') - alias = project.findProperty('keyStoreAlias') - storePass = project.findProperty('keyStorePass') - keyPass = project.findProperty('keyStoreKeyPass') - inputFile = jar.archivePath - outputFile = jar.archivePath -} - -build.dependsOn signJar - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.getDestinationDir() -} - -artifacts { - archives deobfJar - archives sourceJar - archives javadocJar -} - -publishing { - publications { - mavenJava(MavenPublication) { - groupId project.group - artifactId project.archivesBaseName - version project.version - from components.java - artifact sourceJar { - classifier 'sources' - } - artifact deobfJar { - classifier 'deobf' - } - artifact javadocJar { - classifier 'javadoc' - } - } + exclusiveContent { + forRepository { maven { url = legacyRepository('https://chickenbones.net/maven/'); metadataSources { artifact() } } } + filter { includeGroup 'codechicken' } } - repositories { - maven { - credentials { - username secret.username - password secret.password - } - url secret.url - } + exclusiveContent { + forRepository { maven { url = legacyRepository('https://dvs1.progwml6.com/files/maven/'); metadataSources { artifact() } } } + filter { includeGroup 'slimeknights'; includeGroup 'slimeknights.mantle'; includeGroup 'mezz.jei' } } -} - -curseforge { - apiKey = secret.curseforgeAPIKey - project { - id = '244357' - changelog = file('CHANGELOG.txt') - releaseType = 'release' - addGameVersion(project.mc_version) - def projectName = 'Modern Metals' - def displayVersion = getVersion('VERSION', mod_file) - if (System.getenv().BUILD_NUMBER) { - displayVersion += '.' + System.getenv().BUILD_NUMBER - } - mainArtifact(jar) { - displayName = "$project.description $displayVersion" - relations { - requiredLibrary 'mmd-orespawn' - requiredLibrary 'base-metals' - optionalLibrary 'additional-loot-tables' - } - } - addArtifact(sourceJar) { - displayName = "$project.description $displayVersion Sources" - } - addArtifact(deobfJar) { - displayName = "$project.description $displayVersion Development" - } - addArtifact(javadocJar) { - displayName = "$project.description $displayVersion Javadoc" - } + exclusiveContent { + forRepository { maven { url = legacyRepository('https://maven.ic2.player.to/'); metadataSources { artifact() } } } + filter { includeGroup 'net.industrial-craft' } } -} - -sonarqube { - properties { - property 'sonar.host.url', secret.sonarHost - property 'sonar.organization', secret.sonarOrganization - property 'sonar.login', secret.sonarToken - property 'sonar.projectName', project.archivesBaseName - property 'sonar.projectKey', "$project.group:$project.archivesBaseName" + exclusiveContent { + forRepository { maven { url = legacyRepository('https://maven.covers1624.net/'); metadataSources { artifact() } } } + filter { includeGroup 'cofh' } } -} - -String getModFile() { - String path = '' - FileTree tree = fileTree(dir: 'src/main/java') - tree.include '**/*.java' - tree.visit { element -> - if (element.file.isFile()) { - element.file.eachLine { String s -> - s = s.trim() - if (s.startsWith('@Mod(')) { - path = "src/main/java/$element.relativePath" - } - } - } + exclusiveContent { + forRepository { maven { url = legacyRepository('https://maven.moddev.zone/releases'); metadataSources { artifact() } } } + filter { includeGroup 'zone.moddev.mc.basemetals'; includeGroup 'zone.moddev.mc.orespawn' } } - return path + mavenCentral() } -String getVersion(String type, String mod_file) { - String major = '0' - String revision = '0' - String patch = '0' - File file = file(mod_file) - def prefix = ~/^(?:public|protected|private) static final String $type = ".*$/ - file.eachLine { String s -> - s = s.trim() - if (s ==~ prefix || prefix.matcher(s).matches()) { - String[] bits = s.split("=") - String interesting = bits[1].trim() - interesting = interesting.substring(1, interesting.length() - 2) - String[] pts = interesting.trim().split("\\.") +apply from: 'gradle/dependencies.gradle' +renamer.mappings.from minecraft.dependency.toSrgFile - major = pts[0] ?: '0' - revision = pts[1] ?: '0' - patch = pts[2] ?: '0' - } - } - return "$major.$revision.$patch" -} +tasks.named('processResources', ProcessResources) { + filteringCharset = 'UTF-8' + exclude '**/desktop.ini', '**/Thumbs.db', '**/.DS_Store' + inputs.property 'version', project.version + inputs.property 'mcversion', project.minecraft_version + filesMatching('mcmod.info') { expand version: project.version, mcversion: project.minecraft_version } + rename '(.+_at.cfg)', 'META-INF/$1' + filesMatching('**/*.lang') { details -> details.name = details.name.toLowerCase(Locale.ROOT) } + filesMatching(textPatterns, normalizeText) + filesMatching(legacyApiList, normalizeLegacyApi) +} + +tasks.named('jar', Jar) { + archiveClassifier = 'deobf' + destinationDirectory = layout.buildDirectory.dir('libs-dev') + manifest.attributes( + 'Implementation-Title': 'ModernMetals', 'Implementation-Version': project.version, + 'Maven-Artifact': "${project.group}:ModernMetals:${project.version}", + 'Built-On-Java': '8', 'Built-On': "${project.minecraft_version}-${project.forge_version}", + 'FMLCorePluginContainsFMLMod': 'true', 'FMLCorePlugin': project.coreplugin) +} +['sourcesJar', 'javadocJar'].each { name -> + tasks.named(name, Jar) { + filteringCharset = 'UTF-8' + filesMatching(textPatterns, normalizeText) + filesMatching(legacyApiList, normalizeLegacyApi) + } +} + +ext.modernMetalsBuild = [developmentOutput: developmentOutput].asImmutable() +apply from: 'gradle/ore-models.gradle' +apply from: 'gradle/release/artifacts.gradle' +apply from: 'gradle/release/publishing.gradle' +apply from: 'gradle/verification/runtime.gradle' +apply from: 'gradle/ide/eclipse.gradle' diff --git a/docs/BUILDING.md b/docs/BUILDING.md new file mode 100644 index 00000000..e780903d --- /dev/null +++ b/docs/BUILDING.md @@ -0,0 +1,62 @@ +# Building Modern Metals 1.11.2 + +Use Gradle 9.6.1, ForgeGradle 7.0.34, Renamer 1.1.5 and Foojay 1.0.0. Gradle runs +on Java 17 and Mavenizer uses Java 25. Compilation, bytecode and Minecraft use Java 8. +Forge is 13.20.1.2588 with stable-32 mappings. Release pins are Temurin 8u502-b07, +17.0.1+12 and 25.0.3+9-LTS. Install them or pass their homes with +`org.gradle.java.installations.paths`. Do not launch Gradle on Java 8. + +Use Java 17 for `JAVA_HOME` and `PATH`. Until the pinned Base Metals and +OreSpawn builds are published, start with the staged checksum-verified Maven +mirror and pass its location as +`-PlegacyDependencyVerificationRepository=`. The mirror stores OreSpawn +at `zone/moddev/mc/orespawn/OreSpawn/4.1.5.111021/OreSpawn-4.1.5.111021.jar`. +The dependency audit checks its bytes against `gradle/dependencies.json`. +Once both artifacts are available, create the mirror and build as follows. + +```powershell +$env:GRADLE_USER_HOME='' +python gradle/stage-legacy-dependencies.py .gradle/legacy-dependencies +python -m pip install -r tools/validation/requirements.txt +.\gradlew.bat clean check build javadoc verifyReleaseArtifacts verifyProjectResources prepareEclipse +``` + +Use `-PpythonExecutable=` if Python is not named `python3`. +`gradle/dependencies.json` pins production downloads and SHA-256. Staging creates +a verified Maven mirror. Use `-PlegacyDependencyVerificationRepository=` +to select another location. Remapping and signature removal apply to development +copies. No floating mod versions, JCenter or +`mavenLocal()` are used. A normal build never publishes. + +`build/libs/ModernMetals-2.5.1.111021.jar` is the installed jar. Sources/Javadocs +are separate. Test probes are isolated from all release archives. + +## Eclipse + +Run `prepareEclipse` and import the inner Git/Gradle project using Buildship. Use +Java 17 for Gradle, a Java 8 project JRE and one physical cache for Eclipse and the +terminal. Production profiles contain Modern Metals, Base Metals and OreSpawn. +Optional APIs are compilation dependencies. Test profiles are separate. Regenerate +profiles when dependency/toolchain locations change. FG7 uses `genEclipseRuns` and +`prepareEclipse`. + +Gradle launches merge production classes and processed resources into one mod +directory. Eclipse uses `bin/main`, with resources from `build/resources/main` so +metadata is not duplicated. `verifyEclipseProductionClasspath` checks isolation. + +## Verification + +`generateOreModels` generates the ore block models as part of the normal build. +See [ore overlays](ORE_OVERLAYS.md) for the inputs, output and client checks. + +`test` covers config switches, provider conversion, titanium defaults, fantasy +policy and namespace compatibility. `verifyProjectResources` parses resources, +checks material assets, validates the provider against the pinned OreSpawn schema +and audits workflow pins. Packaged server checks require a clean Forge template: + +```text +gradlew packagedForgeRuntimeTest -PpackagedForgeRuntimeRoot= -PpackagedForgeRuntimeOutput= +``` + +The output directory must not already exist. See the validation README for exact +client commands, migration scenarios and limitations. diff --git a/docs/CONTENT_MODE.md b/docs/CONTENT_MODE.md new file mode 100644 index 00000000..a0227be8 --- /dev/null +++ b/docs/CONTENT_MODE.md @@ -0,0 +1,57 @@ +# Modern Metals fantasy setting + +In Minecraft 1.11.2, Modern Metals owns its fantasy setting. Open **Mods → Modern +Metals → Config → Fantasy Level** and choose **High Fantasy** or **Low Fantasy**. +The same selector is available through OreSpawn's child Mods list. Save with Done +and restart the game or server to apply the recipes. Forge checks for matching +settings before joining a multiplayer world and explains how to fix a mismatch. +Use the same build on both sides so the check is available. + +The setting is stored in **`config/ModernMetals.cfg`**: + +```text +general { + S:contentMode=low_fantasy + B:enableVillagerTrades=true +} +``` + +High Fantasy is the default. Missing or invalid values use High Fantasy. Invalid +file values are normalized at startup. Changes remain pending until a full restart +and do not rewrite live registries or recipes. + +**Villager Trades** defaults to on. Turn it off to stop new Modern Metals +blacksmith offers in either fantasy mode. The change applies after restart. +Offers already stored in a villager's inventory and offers from Base Metals +keep their existing behavior. + +The 1.11 version does not read or change Base Metals' fantasy setting or recipes. +If an earlier local candidate used `BaseMetals.cfg`, move the desired value to +`ModernMetals.cfg`. Core-mod inheritance is reserved for the 1.12 modernization. + +## Low Fantasy + +| Form | Allowed Modern Metals materials | +| --- | --- | +| Tools, swords, ammunition, Tinkers tool parts | Galvanized steel, stainless steel, titanium | +| Armor, shields, horse armor | Aluminum, aluminum brass, galvanized steel, stainless steel, titanium | +| Bows, crossbows, fishing rods, Tinkers bow parts | None | +| Gears | Aluminum, aluminum brass, galvanized steel, stainless steel, titanium | +| Anvils | Galvanized steel, stainless steel | +| Doors, bars, stairs, slabs, fences, walls, mechanisms and other structural products | Aluminum, aluminum brass, galvanized steel, nichrome, stainless steel, tantalum, titanium, tungsten, zirconium | +| Ores, ingots, powders, fluids, buckets, plates and storage blocks | Unchanged | + +Plutonium pickaxes remain registered so old stacks load, while their crafting +recipes and new equipment trade offers are unavailable. Titanium retains its +rutile-and-magnesium route. Molten Tinkers processing remains available for +materials that cannot supply parts. Restricted handles and ranged parts cannot +bypass the policy. Dynamic shared-library repair/upgrade recipes check their +resulting Modern Metals item too. + +The policy covers every material and product family. +it is not a chemistry or radiation simulation. For example, elemental boron, +rutile and radioactive materials stay useful as processing inputs but cannot be +turned into structural doors, stairs or gears in Low Fantasy. + +High Fantasy retains historical recipes, trades and integration behavior. Neither +mode unregisters IDs, rewrites saved inventories or enables retrogen. diff --git a/docs/ORE_GENERATION.md b/docs/ORE_GENERATION.md new file mode 100644 index 00000000..36b346fd --- /dev/null +++ b/docs/ORE_GENERATION.md @@ -0,0 +1,70 @@ +# Native OreSpawn 4 generation + +Modern Metals enqueues a schema-5 provider through the public OreSpawn API after +blocks register. It supplies ore declarations only. No OS3 plugin, legacy IMC +generator or second fallback generator is packaged. It supplies no terrain, +rock/strata, biome or fluid-deposit policy and requires no Mineralogy. + +Rules use `modernmetals:legacy/_ore` with their existing block IDs. +Disabled or missing blocks are omitted. Empty legacy dimension lists mean ordinary dimensions except +the vanilla Nether and End, rather than Overworld only. + +## Defaults + +The legacy exclusive maximum becomes inclusive. Quantity conversion is +`size - variation` through `size + variation - 1`, clamped to 1..64 as documented +by OreSpawn. The log reports minimum clamps at startup. + +| Ore | Attempts/chunk | Quantity | Inclusive Y | +| --- | ---: | --- | --- | +| Boron | 8 | 1..15 | 0..31 | +| Thorium | 8 | 4..11 | 0..31 | +| Beryllium | 20 | 2..7 | 0..127 | +| Aluminum | 8 | 2..17 | 0..95 | +| Cadmium | 8 | 1..11 | 0..95 | +| Chromium | 3 | 1..5 | 0..31 | +| Iridium | 5 | 2..9 | 0..63 | +| Magnesium | 8 | 2..9 | 0..95 | +| Manganese | 6 | 2..9 | 0..63 | +| Osmium | 8 | 6..13 | 0..95 | +| Plutonium | 4 | 1..5 | 0..31 | +| Rutile | 6 | 2..9 | 0..63 | +| Tantalum | 6 | 2..9 | 0..63 | +| Tungsten | 6 | 1..7 | 0..31 | +| Uranium | 6 | 1..5 | 0..31 | +| Zirconium | 6 | 4..11 | 0..63 | +| Titanium (disabled) | 6 | 2..9 | 0..63 | + +Titanium is independent and disabled by default. Its placement equals rutile's. +Enable it in the world's OreSpawn editor for future chunks of an existing world. +For future-world defaults, use `config/modernmetals-orespawn.json`. `test` exports +the full native declaration to `build/reports/native-provider.json` for pack authors. +There is no packaged JSON competing with the typed declaration. + +## Migration and saved choices + +OreSpawn 4.1.5 imports OS1 `config/orespawn/modernmetals.json` and OS3 +`config/orespawn3/modernmetals.json` into native overrides. It preserves +existing native overrides and saved world settings. Keep the legacy files +until you have checked the imported settings. + +Each world keeps `serverconfig/orespawn-worldgen.json`. Global edits do not replace +saved choices. New provider entries may merge into existing profiles while existing +entries retain their settings. Retrogen stays off: generated chunks and saved +stacks remain intact. Enabled ores can appear in later chunks. + +## Groups and overlaps + +The 1.11.2 Base Metals and historical Base Minerals defaults have no matching +generated material. Nether Metals +and End Metals have corresponding variants in distinct dimension domains. Custom +configs can create real same-domain conflicts that need pack-specific review. + +Each native rule declares its canonical `orespawn:` and the +`orespawn:standard` placement channel. The Ore Sources editor can group +equivalent sources and choose which source controls placement. The legacy Ore +Dictionary aliases still identify equivalent blocks and items. + +External IC2/Mekanism candidates in OreSpawn's catalog may be unloaded. A catalog +entry alone does not prove control over their native generation. Qualify the exact +installed pack before changing its source policies. diff --git a/docs/ORE_OVERLAYS.md b/docs/ORE_OVERLAYS.md new file mode 100644 index 00000000..d8f2bdce --- /dev/null +++ b/docs/ORE_OVERLAYS.md @@ -0,0 +1,31 @@ +# Ore overlays in Forge 1.11.2 + +Ore models use Minecraft's stone texture underneath KiriCattus's transparent +ore art. A resource pack that replaces stone also changes the stone part of +the ore. Thorium still uses its original full texture. + +Forge 1.11 predates Minecraft's data generator. Run `gradlew generateOreModels` +to generate the models without starting the game. The normal build runs it +automatically, and Eclipse uses the processed resources. The inputs are the +existing ore blockstates and PNGs in +`src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/`. +Generated models go to `build/generated/ore-models`. Existing blockstate and +item model IDs still point to the same block +model names. + +The generated models use the two cubes from Base Metals' generator. The outer +cube sits 0.05 model units above the host faces so they do not fight over the +same depth. On Forge 1.11 both cubes render in the cutout layer with mipmaps, +so the transparent pixels leave the host visible. The ore blocks still inherit +their mining and drop behaviour from Base Metals. + +The 20 overlay files come from [KiriCattus's art commit](https://github.com/MinecraftModDevelopmentMods/ModernMetals/commit/6c892309ab9c2cd19371a8a65ad59b250a647216). +Nineteen are used by registered ores, including the three alloy ores restored +for worlds saved by 0.11.0. The aluminum brass overlay is available as an asset. +OreSpawn still uses the same 17 rules, with titanium disabled by default. + +`verifyProjectResources` checks the generated resources and texture references. +The packaged client probe checks the placed and inventory models, face offsets +and render layer. Add `--ore-host-pack` on its reload run to test a replacement +stone texture and capture the ore display. See the +[validation guide](../tools/validation/legacy111/README.md). diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 00000000..e98805d6 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,34 @@ +# Release preparation + +Normal builds never publish. Main, sources, Javadocs and SHA-256 form one audited +candidate. Gradle enforces Java 8 bytecode, deterministic archives, processed +metadata, dependency hashes and exclusion of tests, local context and shared +library copies. Prepared bundles contain exactly those three jars, `SHA256SUMS` +and `CHANGELOG.txt`. `verifyPreparedReleaseArtifacts` audits the bundle as supplied. + +CI has a cold bootstrap/offline rebuild, build/tests, resource/schema checks, +packaged server qualification, Eclipse generation and clean reproducibility check. +CodeQL/wrapper checks are separate. Actions use full commit hashes. + +The 1.11.2 version uses `Major.Minor.Patch.111021`. Tags validate metadata/prior CI without +publishing. The dispatcher requires `confirm_live_publication`, the upstream +repository, the full checked target commit, the verified game jar's SHA-256 +and successful hosted checks. It publishes +the same candidate bytes to Maven, CurseForge **244357**, then GitHub. CurseForge +requires Base Metals **240967** and MMD OreSpawn **245586**, with Additional Loot +Tables **243157** optional. Credentials and an HTTPS Maven target are required. + +The dispatcher must be installed on the repository's default branch before a +manual release can be started. The current dispatcher on the 1.12 default +branch routes 1.10.2. Install the reviewed 1.11.2 routing there as part of an +authorized release. If an upload times out, check its status before trying again. + +For the default-branch change, copy only the audited `deploy-release.yml`. It +routes each legacy suffix to its target branch and builds there. After a +release is publicly available, update the Forge 1.11.2 entries in `update.json` +to the exact version reported by Forge. + +The checksum-pinned Base Metals 1.11.2 and OreSpawn 4.1.5.111021 jars are +staged locally. Hosted CI requires matching files at their public Maven +coordinates. Reverify the pins if the published bytes differ from the +locally tested artifacts. diff --git a/gradle.properties b/gradle.properties index 81447da3..7635e2f8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,25 +1,33 @@ -mc_version=1.11.2 -top_mc_version=1.1x -ccl_version=2.7.0.+ -ccc_version=2.4.1.+ -nei_version=2.3.0.+ -jei_version=4.4.1.+ -mantle_version=1.2.0.+ -tconstruct_version=2.7.0.+ -mcmp_version=2.0.1 -mcmp_experimental_version=2.0.1_31 -top_version=1.4.7-57 -alt_version=2.0.+ -orespawn_version=3.2.+ -tesla_version=1.3.0.+ -bme_version=2.5.0-beta+ -mme_version=2.5.0-beta+ -fme_version=2.5.0-beta+ -bmi_version=2.5.0-beta+ -pa_version=2.3.0 -cofhcore_version=4.2.8.+ -mekanism_version=471563e083 -taiga_version=b874a41abb -ic2_version=2.7.71-ex111 -minetweaker_version=3.+ -hwyla_version=+ +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false +org.gradle.configuration-cache=false +org.gradle.caching=true +org.gradle.parallel=false +net.minecraftforge.gradle.merge-source-sets=false + +minecraft_version=1.11.2 +forge_version=13.20.1.2588 +mappings_channel=stable +mappings_version=32-1.11 + +mod_id=modernmetals +mod_group=zone.moddev.mc.modernmetals +mod_version=2.5.1.111021 +mod_artifact=ModernMetals +loader_name=forge +loader_code=1 +java_version=8 +java_toolchain_version=8.0.502+7 +java_setup_version=8.0.502+7 +gradle_java_version=17 +gradle_java_setup_version=17.0.1+12 +mavenizer_java_version=25.0.3+9 +curseforge_project_id=244357 +cf_requirements=mmd-orespawn,base-metals +orespawn_curse_project_id=245586 +coreplugin=zone.moddev.mc.modernmetals.compat.ModernMetalsLoadingPlugin +orespawn_version=4.1.5.111021 +orespawn_version_range=[4.1.5.111021,5.0.0) + +basemetals_version=2.5.1.111021 +basemetals_version_range=[2.5.1.111021,2.6.0) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle new file mode 100644 index 00000000..9748783b --- /dev/null +++ b/gradle/dependencies.gradle @@ -0,0 +1,75 @@ +import groovy.json.JsonSlurper +import java.security.MessageDigest + +def specs = new JsonSlurper().parse(file('gradle/dependencies.json')) +def mapped = specs.collectEntries { [(it.id): renamer.dependency(it.coordinate)] } + +configurations { + legacyModArtifacts { canBeConsumed = false; transitive = false } + java8LauncherArtifact { canBeConsumed = false; transitive = false } +} +dependencies { + implementation minecraft.dependency("net.minecraftforge:forge:${minecraft_version}-${forge_version}") + java8LauncherArtifact 'net.minecraftforge:slime-launcher:0.2.2' + compileOnly 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' + specs.each { spec -> + if (spec.runtime) implementation mapped[spec.id] + else compileOnly mapped[spec.id] + legacyModArtifacts spec.coordinate + } + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.2' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.2' +} + +// Forge 1.11's mod scanner cannot read Java 11 multi-release entries. They are +// unused on Java 8, so omit them from the development launcher copy. +tasks.register('prepareJava8Launcher', Jar) { + dependsOn configurations.java8LauncherArtifact + archiveFileName = 'slime-launcher-0.2.2-java8.jar' + destinationDirectory = layout.buildDirectory.dir('development-dependencies') + from { zipTree(configurations.java8LauncherArtifact.singleFile) } + exclude 'META-INF/versions/**', 'META-INF/MANIFEST.MF', 'META-INF/*.SF', 'META-INF/*.RSA', 'META-INF/*.DSA' +} + +// Verify the original files, not renamed copies whose bytes depend on mappings. +tasks.register('verifyReleaseDependencies') { + group = 'verification' + description = 'Checks every pinned integration API against its SHA-256.' + inputs.file 'gradle/dependencies.json' + doLast { + def artifacts = configurations.legacyModArtifacts.resolvedConfiguration.resolvedArtifacts + def orespawn = specs.find { it.id == 'orespawn' } + if (orespawn.coordinate != 'zone.moddev.mc.orespawn:OreSpawn:4.1.5.111021' + || orespawn.sha256 != '5B200A9CE09DF96089FBD7781A026D15B6A7566A0F11B025EBBF486AB8A3655A') { + throw new GradleException('Use the checksum-pinned OreSpawn 4.1.5.111021 jar.') + } + def basemetals = specs.find { it.id == 'basemetals' } + if (basemetals.coordinate != 'zone.moddev.mc.basemetals:BaseMetals:2.5.1.111021' + || basemetals.sha256 != 'F56C77ACE4963991AB56A76468EB398F555E4F6D059710C41DD7483054D76B8C') { + throw new GradleException('Use the verified Base Metals 2.5.1.111021 release.') + } + specs.each { spec -> + def parts = spec.coordinate.split(':') + def artifact = artifacts.find { it.moduleVersion.id.group == parts[0] + && it.name == parts[1] && it.moduleVersion.id.version == parts[2] } + if (artifact == null) throw new GradleException("Missing ${spec.coordinate}") + String actual = MessageDigest.getInstance('SHA-256').digest(artifact.file.bytes).encodeHex().toString() + if (!actual.equalsIgnoreCase(spec.sha256)) { + throw new GradleException("Checksum mismatch for ${spec.coordinate}: ${actual}") + } + if (spec.id == 'orespawn') { + new java.util.jar.JarFile(artifact.file).withCloseable { jar -> + def metadata = new JsonSlurper().parse(jar.getInputStream(jar.getJarEntry('mcmod.info'))) + if (!metadata.any { it.modid == 'orespawn' && it.version == project.orespawn_version } + || jar.getJarEntry('zone/moddev/mc/orespawn/api/OreSpawnApi.class') == null + || jar.manifest.mainAttributes.getValue('OreSpawn-API-Version') != '1') { + throw new GradleException('Wrong OreSpawn identity or public API.') + } + } + } + } + } +} +tasks.named('compileJava') { dependsOn tasks.named('verifyReleaseDependencies') } +tasks.named('check') { dependsOn tasks.named('verifyReleaseDependencies') } diff --git a/gradle/dependencies.json b/gradle/dependencies.json new file mode 100644 index 00000000..35d602d2 --- /dev/null +++ b/gradle/dependencies.json @@ -0,0 +1,52 @@ +[ + { + "id": "basemetals", + "coordinate": "zone.moddev.mc.basemetals:BaseMetals:2.5.1.111021", + "sha256": "F56C77ACE4963991AB56A76468EB398F555E4F6D059710C41DD7483054D76B8C", + "repository": "https://maven.moddev.zone/releases", + "runtime": true + }, + { + "id": "orespawn", + "coordinate": "zone.moddev.mc.orespawn:OreSpawn:4.1.5.111021", + "sha256": "5B200A9CE09DF96089FBD7781A026D15B6A7566A0F11B025EBBF486AB8A3655A", + "repository": "https://maven.moddev.zone/releases", + "runtime": true + }, + { + "id": "mantle", + "coordinate": "curse.maven:mantle-74924:2413993", + "sha256": "6C1C79BD8491106D65240D4DF139E2BEA7914BA2622FAE0662ADB3047D5EDF9D", + "repository": "https://www.cursemaven.com" + }, + { + "id": "tconstruct", + "coordinate": "curse.maven:tinkers-construct-74072:2451489", + "sha256": "2D7F4A08EDDA2881A432CD80FF26575CCEA593FCC8A597A435652C1A53B14CB8", + "repository": "https://www.cursemaven.com" + }, + { + "id": "ic2", + "coordinate": "curse.maven:industrial-craft-242638:2457397", + "sha256": "ACCFA9A503A4B194CFF9528EA521AB42E00A85620F13DDE268BE14C112305B98", + "repository": "https://www.cursemaven.com" + }, + { + "id": "cofh", + "coordinate": "curse.maven:cofh-core-69162:2469613", + "sha256": "7C3EC573F62AE6F3F98B34361123E9D706CFDF03419AE6242293515960391C11", + "repository": "https://www.cursemaven.com" + }, + { + "id": "mekanism", + "coordinate": "curse.maven:mekanism-268560:2475809", + "sha256": "AF013AB6843174E4E766EC98A576D60047EAEA372CD01F6BF5460678EFE86D19", + "repository": "https://www.cursemaven.com" + }, + { + "id": "veinminer", + "coordinate": "curse.maven:veinminer-67133:2528292", + "sha256": "75B37AC8778261875A692D468ABAA3B1BD8AFF61D18B4EA900C26AA2C8528AD7", + "repository": "https://www.cursemaven.com" + } +] diff --git a/gradle/ide/eclipse.gradle b/gradle/ide/eclipse.gradle new file mode 100644 index 00000000..b63025ae --- /dev/null +++ b/gradle/ide/eclipse.gradle @@ -0,0 +1,206 @@ +def escapeXml = { String text -> + text.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>') +} + +eclipse { + project { + buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder' + natures 'org.eclipse.buildship.core.gradleprojectnature' + } + classpath { + downloadSources = true + downloadJavadoc = true + file.beforeMerged { classpath -> + Set managed = ['src/main/java', 'src/main/resources', 'src/test/java', + 'build/resources/main', 'tools/validation/legacy111', + 'tools/validation/legacy111/migration-probe', + 'tools/validation/legacy111/launch-probe', + 'tools/validation/legacy111/orespawn-probe'] as Set + classpath.entries.removeAll { it.kind == 'src' && managed.contains(it.path) } + } + file.whenMerged { classpath -> + // Eclipse must copy the expanded metadata, not the source placeholders. + classpath.entries.findAll { it.kind == 'src' && it.path == 'src/main/resources' }.each { + it.path = 'build/resources/main' + } + classpath.entries.findAll { it.kind == 'src' && !it.path.startsWith('src/main/') }.each { + if (it.path != 'build/resources/main') it.entryAttributes['test'] = 'true' + } + } + file.withXml { provider -> + Node root = provider.asNode() + root.classpathentry.findAll { it.@kind == 'lib' }.each { root.remove(it) } + if (!root.classpathentry.any { it.@path == 'org.eclipse.buildship.core.gradleclasspathcontainer' }) { + root.appendNode('classpathentry', [kind: 'con', path: 'org.eclipse.buildship.core.gradleclasspathcontainer']) + } + } + } + synchronizationTasks 'genEclipseRuns' +} +idea.module { downloadSources = true; downloadJavadoc = true } + +def configureBuildship = tasks.register('configureEclipseBuildship') { + group = 'ide' + doLast { + File target = file('.settings/org.eclipse.buildship.core.prefs') + Properties values = new Properties() + if (target.isFile()) target.withInputStream { values.load(it) } + [ + 'eclipse.preferences.version': '1', + 'connection.gradle.distribution': 'GRADLE_DISTRIBUTION(WRAPPER)', + 'connection.gradle.user.home': gradle.gradleUserHomeDir.canonicalPath, + 'gradle.user.home': gradle.gradleUserHomeDir.canonicalPath, + 'connection.java.home': System.getProperty('java.home'), + 'java.home': System.getProperty('java.home'), + 'connection.project.dir': '', 'override.workspace.settings': 'true' + ].each { key, value -> values.setProperty(key, value) } + target.parentFile.mkdirs() + target.withOutputStream { values.store(it, 'Generated by configureEclipseBuildship.') } + } +} + +tasks.register('isolateEclipseProductionRuns') { + group = 'ide' + description = 'Quotes launcher paths and keeps optional API jars and tests out of ordinary launches.' + dependsOn configureBuildship, tasks.named('processResources'), tasks.named('prepareJava8Launcher') + doLast { + // Copy, rather than Sync: Eclipse owns and compiles the classes in bin/main. + copy { from layout.buildDirectory.dir('resources/main'); into file('bin/main') } + file('run').mkdirs() + List runtime = configurations.runtimeClasspath.files.collect { it.canonicalFile } + ['runClient.launch', 'runServer.launch'].each { name -> + File launch = file(name) + if (!launch.isFile()) throw new GradleException("Missing generated launch: ${name}") + String text = launch.getText('UTF-8').replace('\r\n', '\n') + text = text.replace(escapeXml(configurations.java8LauncherArtifact.singleFile.canonicalPath), + escapeXml(tasks.named('prepareJava8Launcher', Jar).get().archiveFile.get().asFile.canonicalPath)) + def arguments = text =~ /()/ + if (arguments.count != 1) throw new GradleException("Malformed launcher arguments: ${name}") + String args = arguments[0][2] + ['cache', 'metadata', 'to-srg', 'to-obf'].each { flag -> + int start = args.indexOf("--${flag} ") + if (start < 0) throw new GradleException("Missing --${flag} in ${name}") + start += flag.length() + 3 + int end = args.indexOf(' --', start) + if (end < 0) throw new GradleException("Unterminated --${flag} in ${name}") + String value = args.substring(start, end) + if (!value.startsWith('"')) args = args.substring(0, start) + '"' + value + '"' + args.substring(end) + } + text = text.replace(arguments[0][0], arguments[0][1] + args + arguments[0][3]) + text = text.replaceAll(//, + '') + String modClasses = '' + if (text.contains('key="MOD_CLASSES"')) { + text = text.replaceAll(//, + java.util.regex.Matcher.quoteReplacement(modClasses)) + } else { + text = text.replace('', ' ' + modClasses + '\n ') + } + List lines = text.split('\n', -1) as List + def entries = (0.. + String memento = '' + ' ' + }.join('\n') + text = lines.join('\n') + if (!text.contains('ATTR_EXCLUDE_TEST_CODE')) { + text = text.replace('', + ' \n') + } + launch.setText(text, 'UTF-8') + } + } +} +tasks.named('genEclipseRuns') { finalizedBy tasks.named('isolateEclipseProductionRuns') } +tasks.named('eclipse') { dependsOn tasks.named('genEclipseRuns') } + +def verifyJavaSourceLayout = tasks.register('verifyJavaSourceLayout') { + group = 'verification' + description = 'Checks that Java package names match their source folders, as Eclipse requires.' + inputs.files(provider { sourceSets.collect { it.java } }) + doLast { + List mismatches = [] + sourceSets.each { sourceSet -> + sourceSet.java.visit { entry -> + if (entry.directory || !entry.name.endsWith('.java')) return + + String path = entry.relativePath.pathString + int separator = path.lastIndexOf('/') + String expected = separator < 0 ? '' : path.substring(0, separator).replace('/', '.') + def declaration = entry.file.getText('UTF-8') =~ /(?m)^\s*package\s+([\w.]+)\s*;/ + String declared = declaration.find() ? declaration.group(1) : '' + + if (declared != expected) { + mismatches.add("${sourceSet.name}: ${path} declares '${declared}', expected '${expected}'") + } + } + } + if (!mismatches.isEmpty()) { + throw new GradleException('Java packages do not match their source folders:\n' + mismatches.join('\n')) + } + } +} + +tasks.named('check') { dependsOn verifyJavaSourceLayout } + +tasks.register('verifyEclipseProductionClasspath') { + group = 'verification' + dependsOn tasks.named('genEclipseRuns'), verifyJavaSourceLayout + mustRunAfter tasks.named('eclipse') + doLast { + List runtime = configurations.runtimeClasspath.files.collect { it.canonicalFile } + Set optional = configurations.compileClasspath.files.collect { it.canonicalFile } as Set + optional.removeAll(runtime) + ['runClient.launch', 'runServer.launch'].each { name -> + String text = file(name).getText('UTF-8').replace('\\', '/') + if (!text.contains('MAIN_TYPE" value="net.minecraftforge.launcher.Main"') + || !text.contains('StandardVMType/JavaSE-8') + || !text.contains('MC_VERSION" value="1.11.2"') + || !text.contains('ATTR_EXCLUDE_TEST_CODE') + || text.contains('gradleclasspathcontainer') || text.contains('bin/test') + || text.contains('slime-launcher-0.2.2.jar') + || text.contains('junit-') || text.contains('legacy111-probe')) { + throw new GradleException("Unsafe production launch: ${name}") + } + if (runtime.any { !text.contains(it.path.replace('\\', '/')) } + || optional.any { text.contains(it.path.replace('\\', '/')) }) { + throw new GradleException("Runtime/compile-only separation failed in ${name}") + } + ['cache', 'metadata', 'to-srg', 'to-obf'].each { flag -> + if (!text.contains("--${flag} "")) throw new GradleException("Unquoted --${flag} in ${name}") + } + } + String info = file('bin/main/mcmod.info').getText('UTF-8') + if (info.contains('${version}') || !info.contains(project.version.toString())) { + throw new GradleException('Eclipse is using unprocessed resources.') + } + def classpath = new groovy.xml.XmlSlurper().parse(file('.classpath')) + List sourcePaths = classpath.classpathentry.findAll { it.@kind == 'src' }.collect { it.@path.text() } + if (sourcePaths.size() != sourcePaths.unique(false).size() + || sourcePaths.contains('tools/validation/legacy111')) { + throw new GradleException('Eclipse retains stale or duplicate source-folder entries.') + } + classpath.classpathentry.findAll { it.@kind == 'src' && !it.@path.text().startsWith('src/main/') + && it.@path.text() != 'build/resources/main' }.each { + if (!it.attributes.attribute.any { it.@name == 'test' && it.@value == 'true' }) { + throw new GradleException("Non-production Eclipse source is not marked as test code: ${it.@path}") + } + } + } +} +tasks.register('prepareEclipse') { + group = 'ide' + description = 'Generates Eclipse metadata and verifies Java 8 production-only launches.' + dependsOn tasks.named('eclipse'), tasks.named('verifyEclipseProductionClasspath'), 'writeEclipseCompileClasspath' +} + +tasks.register('writeEclipseCompileClasspath') { + group = 'verification' + description = 'Writes the resolved editor classpath for headless Eclipse compiler checks.' + doLast { + File target = layout.buildDirectory.file('ide/compile-classpath.txt').get().asFile + target.parentFile.mkdirs() + target.setText(configurations.compileClasspath.asPath, 'UTF-8') + } +} diff --git a/gradle/ide/log4j2.xml b/gradle/ide/log4j2.xml new file mode 100644 index 00000000..a770e981 --- /dev/null +++ b/gradle/ide/log4j2.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/ore-models.gradle b/gradle/ore-models.gradle new file mode 100644 index 00000000..5f7de636 --- /dev/null +++ b/gradle/ore-models.gradle @@ -0,0 +1,71 @@ +import groovy.json.JsonOutput + +// Forge 1.11 has no GatherDataEvent. Generate the equivalent model resources +// before processResources, without launching Minecraft or writing into src. +def assets = layout.projectDirectory.dir('src/main/resources/assets/modernmetals') +def overlayFiles = fileTree(assets.dir('textures/blocks/ore_overlays')) { include '*_ore.png' } +def oreStates = fileTree(assets.dir('blockstates')) { include '*_ore.json' } +def generated = layout.buildDirectory.dir('generated/ore-models') + +def generateOreModels = tasks.register('generateOreModels') { + group = 'build' + description = 'Generates the Forge 1.11 ore models from KiriCattus’s overlays.' + inputs.files(overlayFiles, oreStates).withPathSensitivity(PathSensitivity.RELATIVE) + outputs.dir(generated) + + doLast { + def names = overlayFiles.files.collect { it.name - '.png' }.sort() + def expected = oreStates.files.collect { it.name - '.json' }.findAll { it != 'thorium_ore' }.sort() + if (names != expected) { + throw new GradleException("Ore overlays do not match ore blockstates: missing ${expected - names}, extra ${names - expected}") + } + + def dir = generated.get().dir('assets/modernmetals/models/block').asFile + dir.mkdirs() + def writeModel = { String name, Map model -> + new File(dir, "${name}.json").setText(JsonOutput.prettyPrint(JsonOutput.toJson(model)) + '\n', 'UTF-8') + } + def cube = { Number from, Number to, String texture -> + def faces = ['down', 'up', 'north', 'south', 'west', 'east'].collectEntries { face -> + [(face): [texture: texture, cullface: face, uv: [0, 0, 16, 16]]] + } + [from: [from, from, from], to: [to, to, to], faces: faces] + } + // Same two cubes and offset as Base Metals, with the legacy texture paths. + writeModel('ore_overlay', [ + parent: 'minecraft:block/block', + elements: [cube(0, 16, '#base'), cube(-0.05, 16.05, '#overlay')] + ]) + names.each { name -> + writeModel(name, [ + parent: 'modernmetals:block/ore_overlay', + textures: [particle: 'minecraft:blocks/stone', base: 'minecraft:blocks/stone', + overlay: "modernmetals:blocks/ore_overlays/${name}"] + ]) + } + // Clean up only obsolete ore JSON files in this task's own output directory. + dir.listFiles().findAll { it.isFile() && it.name.endsWith('_ore.json') && !(it.name - '.json' in names) }.each { + if (!it.delete()) throw new GradleException("Could not remove stale generated ore model ${it.name}") + } + } +} + +tasks.named('processResources', ProcessResources) { + dependsOn generateOreModels + from generated + duplicatesStrategy = DuplicatesStrategy.FAIL + doFirst { + // Copy tasks can leave the old flat textures behind after an incremental upgrade. + overlayFiles.files.each { overlay -> + def flat = new File(destinationDir, "assets/modernmetals/textures/blocks/${overlay.name}") + if (flat.isFile() && !flat.delete()) { + throw new GradleException("Could not remove obsolete processed texture ${flat.name}") + } + } + } +} + +tasks.named('sourcesJar', Jar) { + dependsOn generateOreModels + from generated +} diff --git a/gradle/prepare-packaged-forge-runtime.sh b/gradle/prepare-packaged-forge-runtime.sh new file mode 100644 index 00000000..577a08a4 --- /dev/null +++ b/gradle/prepare-packaged-forge-runtime.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ne 2 ]]; then + echo "usage: $0 " >&2 + exit 2 +fi +runtime="$1" +java8="$2" +version=1.11.2-13.20.1.2588 +expected=00AEA3DDF1E78BBB4D5D05E8C22FB3E849DE09B47D828E57DAD373A16BB8A26F + +if [[ -e "$runtime" && -n "$(find "$runtime" -mindepth 1 -maxdepth 1 -print -quit)" ]]; then + echo "Packaged Forge destination must be empty: $runtime" >&2 + exit 1 +fi +mkdir -p "$runtime" +installer="$runtime/forge-$version-installer.jar" +curl --fail --location --silent --show-error --retry 3 --connect-timeout 20 --max-time 180 \ + "https://maven.minecraftforge.net/net/minecraftforge/forge/$version/forge-$version-installer.jar" \ + --output "$installer" +actual="$(sha256sum "$installer" | awk '{print toupper($1)}')" +if [[ "$actual" != "$expected" ]]; then + echo "Forge installer checksum mismatch: $actual" >&2 + exit 1 +fi +# The legacy installer writes to its working directory. +(cd "$runtime" && "$java8" -jar "forge-$version-installer.jar" --installServer) +if [[ ! -f "$runtime/forge-$version-universal.jar" && -f "$runtime/forge-$version.jar" ]]; then + cp "$runtime/forge-$version.jar" "$runtime/forge-$version-universal.jar" +fi +test -f "$runtime/forge-$version-universal.jar" +test -f "$runtime/minecraft_server.1.11.2.jar" +test -d "$runtime/libraries" diff --git a/gradle/release/artifacts.gradle b/gradle/release/artifacts.gradle new file mode 100644 index 00000000..5406280d --- /dev/null +++ b/gradle/release/artifacts.gradle @@ -0,0 +1,184 @@ +import groovy.json.JsonSlurper +import java.security.MessageDigest +import java.util.jar.Manifest +import java.util.zip.ZipFile + +def releaseJar = renamer.classes(tasks.named('jar', Jar)) { + map.from minecraft.dependency.toSrgFile + archiveClassifier = null + output = layout.buildDirectory.file("libs/ModernMetals-${project.version}.jar") +} +['apiElements', 'runtimeElements'].each { name -> + configurations.named(name) { artifacts.clear() } + artifacts.add(name, releaseJar) +} +tasks.named('assemble') { dependsOn releaseJar, tasks.named('sourcesJar'), tasks.named('javadocJar') } + +tasks.register('verifyJava8Toolchain') { + group = 'verification' + description = 'Checks Java 8 compatibility; exact compiler versions are required only for release builds.' + doLast { + def compiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(8) }.get().metadata + if (compiler.languageVersion.asInt() != 8) throw new GradleException('Minecraft 1.11 requires Java 8.') + if (providers.gradleProperty('releaseBuild').getOrElse('false').toBoolean() + && (compiler.javaRuntimeVersion != '1.8.0_502-b07' + || System.getProperty('java.runtime.version') != '17.0.1+12')) { + throw new GradleException('Release builds require Temurin 8u502-b07 and Gradle on 17.0.1+12.') + } + if (providers.gradleProperty('releaseBuild').getOrElse('false').toBoolean()) { + def mavenizer = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(25) }.get().metadata + if (mavenizer.javaRuntimeVersion != '25.0.3+9-LTS') { + throw new GradleException('Release setup requires Mavenizer on Temurin 25.0.3+9.') + } + } + } +} + +tasks.register('verifyReleaseConfiguration') { + group = 'verification' + dependsOn tasks.named('verifyJava8Toolchain') + doLast { + if (project.group != 'zone.moddev.mc.modernmetals' || project.version != '2.5.1.111021' + || project.minecraft_version != '1.11.2' || project.forge_version != '13.20.1.2588' + || project.mappings_channel != 'stable' || project.mappings_version != '32-1.11') { + throw new GradleException('Modern Metals target, mappings or Maven identity changed.') + } + String source = file('src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java').getText('UTF-8') + if (!source.contains('VERSION = "' + project.version + '"') || !source.contains('MODID = "modernmetals"')) { + throw new GradleException('The mod identity differs from the build metadata.') + } + if (project.orespawn_version != '4.1.5.111021' + || project.orespawn_version_range != '[4.1.5.111021,5.0.0)' + || !source.contains('required-after:orespawn@' + project.orespawn_version_range) + || !source.contains('required-after:forge@[13.20.1.2588,)')) { + throw new GradleException('The OreSpawn or Forge runtime requirement changed.') + } + if (!source.contains('required-after:basemetals@[2.5.1.111021,2.6.0)')) { + throw new GradleException('The published Base Metals runtime requirement changed.') + } + } +} + +def expectedFiles = ['', '-sources', '-javadoc'].collect { "ModernMetals-${project.version}${it}.jar".toString() }.asImmutable() +def preparedDirectory = providers.gradleProperty('preparedReleaseDir') +def auditJars = { File directory -> + ['','-sources','-javadoc'].each { suffix -> + File jar = new File(directory, "ModernMetals-${project.version}${suffix}.jar") + if (!jar.isFile() || jar.length() == 0) throw new GradleException("Missing ${jar.name}") + new ZipFile(jar).withCloseable { zip -> + zip.entries().each { entry -> + String path = entry.name.toLowerCase(Locale.ROOT) + if (path.startsWith('review/') || path.contains('probe') || path.contains('fixture') + || path.contains('agent-notes') || path.contains('agents.md') || path.contains('.codex') + || path.contains('.agents') || path.contains('claude') || path.contains('gemini') + || path.tokenize('/').any { it in ['desktop.ini', 'thumbs.db', '.ds_store'] } + || path.startsWith('tools/') || path.startsWith('reference/') + || path.startsWith('com/mcmoddev/lib/') || path.startsWith('zone/moddev/mc/basemetals/') + || path.startsWith('com/mcmoddev/orespawn/') + || path == 'assets/modernmetals/orespawn/modernmetals.json' + || path.startsWith('org/junit/') || path.startsWith('net/minecraft/')) { + throw new GradleException("Non-production entry in ${jar.name}: ${entry.name}") + } + def date = entry.timeLocal + if (!entry.directory && !(date.toString() in ['1980-01-01T00:00', '1980-02-01T00:00'])) { + throw new GradleException("Volatile archive timestamp: ${entry.name}") + } + if (!entry.directory && path ==~ /.*\.(java|html|css|js|json|lang|mcmeta|md|txt|xml|cfg|info|properties)/) { + def decoder = java.nio.charset.StandardCharsets.UTF_8.newDecoder() + .onMalformedInput(java.nio.charset.CodingErrorAction.REPORT) + .onUnmappableCharacter(java.nio.charset.CodingErrorAction.REPORT) + String content = decoder.decode(java.nio.ByteBuffer.wrap(zip.getInputStream(entry).bytes)).toString() + if (content.contains('\r')) throw new GradleException("Non-LF text in ${entry.name}") + } + if (!entry.directory && path ==~ /.*\.(class|java|html|json|info|xml|properties|txt|md|lang|cfg)/) { + String content = new String(zip.getInputStream(entry).bytes, + java.nio.charset.StandardCharsets.ISO_8859_1) + if (content.contains(project.projectDir.absolutePath) + || content.contains('/home/runner/work/')) { + throw new GradleException("Local build path in ${entry.name}") + } + } + if (entry.name.endsWith('.class')) { + byte[] data = zip.getInputStream(entry).bytes + int major = ((data[6] & 255) << 8) | (data[7] & 255) + if (major != 52) throw new GradleException("Not Java 8 bytecode: ${entry.name}") + String constants = new String(data, java.nio.charset.StandardCharsets.ISO_8859_1) + if (constants.contains(project.projectDir.absolutePath)) { + throw new GradleException("Local path in ${entry.name}") + } + } + } + if (suffix == '') { + def manifest = new Manifest(zip.getInputStream(zip.getEntry('META-INF/MANIFEST.MF'))).mainAttributes + if (manifest.getValue('Maven-Artifact') != "${project.group}:ModernMetals:${project.version}" + || manifest.getValue('FMLCorePlugin') != project.coreplugin + || manifest.keySet().any { it.toString().toLowerCase().contains('timestamp') }) { + throw new GradleException('Incorrect release manifest.') + } + def info = new JsonSlurper().parse(zip.getInputStream(zip.getEntry('mcmod.info')))[0] + if (info.modid != 'modernmetals' || info.version != project.version || info.mcversion != '1.11.2') { + throw new GradleException('Incorrect processed mcmod.info.') + } + if (zip.getEntry('zone/moddev/mc/modernmetals/ModernMetals.class') == null + || zip.getEntry('META-INF/modernmetals-legacy-api.list') == null) { + throw new GradleException('Missing mod entry point or legacy API map.') + } + } + } + } +} + +tasks.register('verifyReleaseArtifacts') { + group = 'verification' + description = 'Audits the shipped jars for Java 8 bytecode, metadata and test isolation.' + dependsOn tasks.named('assemble'), tasks.named('verifyReleaseConfiguration'), tasks.named('verifyReleaseDependencies') + doLast { auditJars(layout.buildDirectory.dir('libs').get().asFile) } +} + +tasks.register('writeReleaseChecksums') { + group = 'verification' + dependsOn tasks.named('verifyReleaseArtifacts') + doLast { + File directory = layout.buildDirectory.dir('libs').get().asFile + List lines = ['','-sources','-javadoc'].collect { suffix -> + File jar = new File(directory, "ModernMetals-${project.version}${suffix}.jar") + MessageDigest.getInstance('SHA-256').digest(jar.bytes).encodeHex().toString() + ' ' + jar.name + } + File sums = layout.buildDirectory.file('release/SHA256SUMS').get().asFile + sums.parentFile.mkdirs() + sums.setText(lines.join('\n') + '\n', 'UTF-8') + } +} +tasks.named('check') { dependsOn tasks.named('verifyReleaseConfiguration') } + +tasks.register('verifyPreparedReleaseArtifacts') { + group = 'verification' + description = 'Verifies a previously built bundle without rebuilding its jars.' + doLast { + if (!preparedDirectory.isPresent()) throw new GradleException('Set preparedReleaseDir to the immutable release bundle.') + File directory = file(preparedDirectory.get()) + Set expected = (expectedFiles + ['SHA256SUMS', 'CHANGELOG.txt']) as Set + if (!directory.isDirectory() || directory.listFiles().collect { it.name }.toSet() != expected) { + throw new GradleException('The prepared release must contain exactly three jars, SHA256SUMS and CHANGELOG.txt.') + } + List lines = new File(directory, 'SHA256SUMS').readLines('UTF-8') + if (lines.size() != 3) throw new GradleException('The prepared checksum list must contain exactly three files.') + Set checked = [] as Set + lines.each { line -> + def match = line =~ /^([a-fA-F0-9]{64}) (ModernMetals-[^\/\\]+\.jar)$/ + if (!match.matches() || !expectedFiles.contains(match[0][2]) || !checked.add(match[0][2])) { + throw new GradleException('Invalid or duplicate prepared checksum entry.') + } + File jar = new File(directory, match[0][2]) + String actual = MessageDigest.getInstance('SHA-256').digest(jar.bytes).encodeHex().toString() + if (!actual.equalsIgnoreCase(match[0][1])) throw new GradleException("Prepared checksum mismatch: ${jar.name}") + } + if (!new File(directory, 'CHANGELOG.txt').getText('UTF-8').startsWith(project.version.toString() + ' for 1.11.2')) { + throw new GradleException('The prepared changelog belongs to a different release.') + } + auditJars(directory) + } +} + +ext.modernMetalsRelease = [jar: releaseJar, expectedFiles: expectedFiles, + preparedDirectory: preparedDirectory].asImmutable() diff --git a/gradle/release/publishing.gradle b/gradle/release/publishing.gradle new file mode 100644 index 00000000..3f6513cc --- /dev/null +++ b/gradle/release/publishing.gradle @@ -0,0 +1,96 @@ +import groovy.xml.XmlSlurper + +def releaseJar = project.ext.modernMetalsRelease.jar +def expectedReleaseFiles = project.ext.modernMetalsRelease.expectedFiles +def preparedReleaseDir = project.ext.modernMetalsRelease.preparedDirectory + +// A normal build does not publish anything; publishing needs credentials. +def mavenUploadUrl = providers.environmentVariable('MAVEN_UPLOAD_URL') + .map { it.trim() }.filter { !it.isEmpty() } + .orElse('https://invalid.invalid/missing-maven-upload-url') +def mavenUploadUsername = providers.environmentVariable('MAVEN_UPLOAD_USERNAME') +def mavenUploadPassword = providers.environmentVariable('MAVEN_UPLOAD_PASSWORD') + +publishing { + publications { + mavenJava(MavenPublication) { + groupId = project.group.toString() + artifactId = base.archivesName.get() + version = project.version.toString() + if (preparedReleaseDir.isPresent()) { + File prepared = file(preparedReleaseDir.get()) + artifact(new File(prepared, expectedReleaseFiles[0])) + artifact(new File(prepared, expectedReleaseFiles[1])) { classifier = 'sources' } + artifact(new File(prepared, expectedReleaseFiles[2])) { classifier = 'javadoc' } + } else { + artifact(releaseJar) + artifact(tasks.named('sourcesJar')) + artifact(tasks.named('javadocJar')) + } + pom { + name = 'Modern Metals' + description = 'Metals, alloys, equipment and Crack Hammer processing for Minecraft 1.11.2.' + url = 'https://github.com/MinecraftModDevelopmentMods/ModernMetals' + licenses { + license { + name = 'GNU Lesser General Public License, Version 2.1' + url = 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt' + } + } + scm { + connection = 'scm:git:https://github.com/MinecraftModDevelopmentMods/ModernMetals.git' + developerConnection = 'scm:git:ssh://git@github.com/MinecraftModDevelopmentMods/ModernMetals.git' + url = 'https://github.com/MinecraftModDevelopmentMods/ModernMetals' + } + } + } + } + repositories { + maven { + name = 'release' + url = uri(mavenUploadUrl.get()) + credentials { + username = mavenUploadUsername.orNull ?: '' + password = mavenUploadPassword.orNull ?: '' + } + } + } +} + +tasks.register('validateMavenReleaseCredentials') { + group = 'publishing' + doLast { + if (!providers.environmentVariable('MAVEN_UPLOAD_URL').getOrElse('').trim() + || !mavenUploadUsername.getOrElse('').trim() || !mavenUploadPassword.getOrElse('').trim()) { + throw new GradleException('MAVEN_UPLOAD_URL, MAVEN_UPLOAD_USERNAME, and MAVEN_UPLOAD_PASSWORD are required') + } + URI target = uri(providers.environmentVariable('MAVEN_UPLOAD_URL').get()) + if (target.scheme != 'https' || !target.host || target.host == 'invalid.invalid' + || !mavenUploadUsername.get().trim() || !mavenUploadPassword.get().trim()) { + throw new GradleException('Maven release publication requires an HTTPS repository and non-empty credentials') + } + } +} + +tasks.withType(PublishToMavenRepository).configureEach { + dependsOn tasks.named('validateMavenReleaseCredentials') + dependsOn 'verifyMavenCoordinates' + if (preparedReleaseDir.isPresent()) { + dependsOn tasks.named('verifyPreparedReleaseArtifacts') + } else { + dependsOn tasks.named('verifyReleaseArtifacts') + } +} + +tasks.register('verifyMavenCoordinates') { + group = 'verification' + dependsOn tasks.named('generatePomFileForMavenJavaPublication') + doLast { + def pom = new XmlSlurper(false, false).parse(layout.buildDirectory.file('publications/mavenJava/pom-default.xml').get().asFile) + def actual = [pom.groupId.text(), pom.artifactId.text(), pom.version.text()] + def expected = ['zone.moddev.mc.modernmetals', 'ModernMetals', project.version.toString()] + if (actual != expected) throw new GradleException("Wrong Maven coordinate: ${actual.join(':')}") + } +} + +tasks.named('check') { dependsOn tasks.named('verifyMavenCoordinates') } diff --git a/gradle/stage-legacy-dependencies.py b/gradle/stage-legacy-dependencies.py new file mode 100644 index 00000000..48030759 --- /dev/null +++ b/gradle/stage-legacy-dependencies.py @@ -0,0 +1,53 @@ +"""Build a checksum-verified Maven mirror from downloads or an existing Gradle cache.""" + +import argparse +import hashlib +import json +import time +import urllib.error +import urllib.request +from pathlib import Path + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("destination", type=Path) + parser.add_argument("--cache", type=Path, help="Existing Gradle user home; matching files are verified before reuse") + args = parser.parse_args() + specs = json.loads(Path(__file__).with_name("dependencies.json").read_text(encoding="utf-8")) + + for spec in specs: + group, artifact, version, *classifier = spec["coordinate"].split(":") + filename = f"{artifact}-{version}" + ("-" + classifier[0] if classifier else "") + ".jar" + relative = Path(group.replace(".", "/")) / artifact / version / filename + target = args.destination.resolve() / relative + target.parent.mkdir(parents=True, exist_ok=True) + + if not target.exists(): + cached = list((args.cache / "caches/modules-2/files-2.1" / group / artifact / version).glob("*/" + filename)) if args.cache else [] + if cached: + data = cached[0].read_bytes() + else: + url = spec["repository"].rstrip("/") + "/" + relative.as_posix() + request = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) + for attempt in range(3): + try: + with urllib.request.urlopen(request, timeout=30) as response: + data = response.read() + break + except (OSError, urllib.error.URLError): + if attempt == 2: + raise + print(f"Retrying {spec['coordinate']}", flush=True) + time.sleep(2) + if hashlib.sha256(data).hexdigest().upper() != spec["sha256"]: + raise ValueError(f"Checksum mismatch: {spec['coordinate']}") + target.write_bytes(data) + + if hashlib.sha256(target.read_bytes()).hexdigest().upper() != spec["sha256"]: + raise ValueError(f"Checksum mismatch in mirror: {target}") + print(spec["id"], spec["sha256"], flush=True) + + +if __name__ == "__main__": + main() diff --git a/gradle/stage-legacy-dependencies.sh b/gradle/stage-legacy-dependencies.sh new file mode 100644 index 00000000..530f8933 --- /dev/null +++ b/gradle/stage-legacy-dependencies.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ne 1 ]]; then + echo "usage: $0 " >&2 + exit 2 +fi + +"${PYTHON:-python3}" "$(dirname "$0")/stage-legacy-dependencies.py" "$1" diff --git a/gradle/verification/runtime.gradle b/gradle/verification/runtime.gradle new file mode 100644 index 00000000..38da7e41 --- /dev/null +++ b/gradle/verification/runtime.gradle @@ -0,0 +1,48 @@ +// Isolated probes are never included in ordinary launches or release jars. +sourceSets { + runtimeProbe { + java.srcDirs = ['tools/validation/legacy111/runtime-probe'] + compileClasspath = sourceSets.main.compileClasspath + sourceSets.main.output + } +} +def probe = tasks.register('runtimeProbeJar', Jar) { + group = 'verification' + archiveFileName = 'modernmetals-probe.jar' + destinationDirectory = layout.buildDirectory.dir('test-mods') + from sourceSets.runtimeProbe.output +} +def packagedProbe = renamer.classes(probe) { + map.from minecraft.dependency.toSrgFile + output = layout.buildDirectory.file('test-mods/modernmetals-probe-packaged.jar') +} +tasks.register('packagedRuntimeProbeJar') { dependsOn packagedProbe } + +// The schema audit also needs this deterministic JUnit export after a cache hit. +tasks.named('test', Test) { + outputs.file(layout.buildDirectory.file('reports/native-provider.json')) +} + +tasks.register('verifyProjectResources', Exec) { + group = 'verification' + dependsOn tasks.named('test') + dependsOn tasks.named('processResources') + doFirst { + commandLine providers.gradleProperty('pythonExecutable').getOrElse('python3'), + 'tools/validation/legacy111/audit_resources.py', '--mirror', + file(providers.gradleProperty('legacyDependencyVerificationRepository').getOrElse('.gradle/legacy-dependencies')).absolutePath + } +} +tasks.register('packagedForgeRuntimeTest', Exec) { + group = 'verification' + dependsOn 'verifyReleaseArtifacts', 'packagedRuntimeProbeJar', 'test' + doFirst { + if (!providers.gradleProperty('packagedForgeRuntimeRoot').isPresent()) + throw new GradleException('Set packagedForgeRuntimeRoot to a disposable Forge server template.') + final def java8 = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(8) }.get() + commandLine providers.gradleProperty('pythonExecutable').getOrElse('python3'), + 'tools/validation/legacy111/run_packaged_suite.py', '--java', java8.executablePath.asFile.absolutePath, + '--template', file(providers.gradleProperty('packagedForgeRuntimeRoot').get()).absolutePath, + '--output', file(providers.gradleProperty('packagedForgeRuntimeOutput').getOrElse('build/packaged-forge-checks')).absolutePath, + '--mirror', file(providers.gradleProperty('legacyDependencyVerificationRepository').getOrElse('.gradle/legacy-dependencies')).absolutePath + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 0d4a9516..b1b8ef56 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a95009c3..dbe66e1d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,10 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip +distributionSha256Sum=9c0f7faeeb306cb14e4279a3e084ca6b596894089a0638e68a07c945a32c9e14 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip +networkTimeout=10000 +retries=0 +retryBackOffMs=500 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d5..249efbb0 100755 --- a/gradlew +++ b/gradlew @@ -1,78 +1,128 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# gradlew start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh gradlew +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,92 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f9553162..a51ec4f5 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,82 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem gradlew startup script for Windows @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 -:mainEnd -if "%OS%"=="Windows_NT" endlocal +@rem Execute gradlew +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..c4091f15 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,5 @@ +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +} + +rootProject.name = 'ModernMetals' diff --git a/src/main/java/com/mcmoddev/alt/ModernMetalsALT.java b/src/main/java/com/mcmoddev/alt/ModernMetalsALT.java deleted file mode 100644 index a2e1e862..00000000 --- a/src/main/java/com/mcmoddev/alt/ModernMetalsALT.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.mcmoddev.alt; -import com.mcmoddev.alt.api.IALTPlugin; - -//@ALTPlugin(modid = "modernmetals") -class ModernMetalsALT implements IALTPlugin { - // Intentionally left blank -} \ No newline at end of file diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Achievements.java b/src/main/java/com/mcmoddev/modernmetals/init/Achievements.java deleted file mode 100644 index e312b1ac..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/init/Achievements.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.mcmoddev.modernmetals.init; - -import com.mcmoddev.lib.data.Names; -import com.mcmoddev.modernmetals.data.AchievementNames; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.lib.util.ConfigBase.Options; - -import net.minecraft.stats.Achievement; -import net.minecraftforge.common.AchievementPage; -import net.minecraftforge.fml.common.Loader; - -/** initializer for achievements */ -public class Achievements extends com.mcmoddev.lib.init.Achievements { - - private static boolean initDone = false; - - private Achievements() { - throw new IllegalAccessError("Not a instantiable class"); - } - - /** - * - */ - public static void init() { - if (initDone) { - return; - } - - if (Options.enableAchievements()) { - AchievementPage page = new AchievementPage(Loader.instance().activeModContainer().getModId()); - AchievementPage.registerAchievementPage(page); - - final Achievement metallurgy = getAchievementByName(AchievementNames.METALLURGY); - if (metallurgy != null) { - if (Materials.hasMaterial(MaterialNames.ALUMINUM_BRASS)) { - makeAchievement(AchievementNames.ALUMINUM_BRASS_MAKER, metallurgy, 0, 0, Materials.getMaterialByName(MaterialNames.ALUMINUM_BRASS).getItem(Names.INGOT), page); - } - - if (Materials.hasMaterial(MaterialNames.GALVANIZED_STEEL)) { - makeAchievement(AchievementNames.GALVANIZED_STEEL_MAKER, metallurgy, 0, 1, Materials.getMaterialByName(MaterialNames.GALVANIZED_STEEL).getItem(Names.INGOT), page); - } - - if (Materials.hasMaterial(MaterialNames.NICHROME)) { - makeAchievement(AchievementNames.NICHROME_MAKER, metallurgy, 0, 2, Materials.getMaterialByName(MaterialNames.NICHROME).getItem(Names.INGOT), page); - } - - if (Materials.hasMaterial(MaterialNames.STAINLESS_STEEL)) { - makeAchievement(AchievementNames.STAINLESS_STEEL_MAKER, metallurgy, 0, 3, Materials.getMaterialByName(MaterialNames.STAINLESS_STEEL).getItem(Names.INGOT), page); - } - - if (Materials.hasMaterial(MaterialNames.STAINLESS_STEEL)) { - makeAchievement(AchievementNames.TITANIUM_MAKER, metallurgy, 0, 4, Materials.getMaterialByName(MaterialNames.TITANIUM).getItem(Names.INGOT), page); - } - } - } - - initDone = true; - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Fluids.java b/src/main/java/com/mcmoddev/modernmetals/init/Fluids.java deleted file mode 100644 index d7485ae0..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/init/Fluids.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.mcmoddev.modernmetals.init; - -import java.util.Arrays; -import java.util.List; - -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.lib.data.SharedStrings; -import com.mcmoddev.lib.init.Materials; - -/** - * This class initializes all fluids in Modern Metals. - * - * @author Jasmine Iwanek - * - */ -public class Fluids extends com.mcmoddev.lib.init.Fluids { - - private static boolean initDone = false; - - private Fluids() { - throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); - } - - /** - * - */ - public static void init() { - if (initDone) { - return; - } - - if (Materials.hasMaterial(MaterialNames.ALUMINUM)) { - addFluid(MaterialNames.ALUMINUM, 2000, 10000, 330, 10); - addFluidBlock(MaterialNames.ALUMINUM); - } - - if (Materials.hasMaterial(MaterialNames.ALUMINUM_BRASS)) { - addFluid(MaterialNames.ALUMINUM_BRASS, 2000, 10000, 500, 10); - addFluidBlock(MaterialNames.ALUMINUM_BRASS); - } - - if (Materials.hasMaterial(MaterialNames.BERYLLIUM)) { - addFluid(MaterialNames.BERYLLIUM, 2000, 10000, 400, 10); - addFluidBlock(MaterialNames.BERYLLIUM); - } - - if (Materials.hasMaterial(MaterialNames.BORON)) { - addFluid(MaterialNames.BORON, 2000, 10000, 400, 10); - addFluidBlock(MaterialNames.BORON); - } - - if (Materials.hasMaterial(MaterialNames.CADMIUM)) { - addFluid(MaterialNames.CADMIUM, 2000, 10000, 300, 10); - addFluidBlock(MaterialNames.CADMIUM); - } - - List standardMaterialsMap = Arrays.asList(MaterialNames.CHROMIUM, - MaterialNames.GALVANIZED_STEEL, - MaterialNames.IRIDIUM, - MaterialNames.MAGNESIUM, - MaterialNames.MANGANESE, - MaterialNames.NICHROME, - MaterialNames.OSMIUM, - MaterialNames.PLUTONIUM, - MaterialNames.RUTILE, - MaterialNames.STAINLESS_STEEL, - MaterialNames.TANTALUM, - MaterialNames.TITANIUM, - MaterialNames.TUNGSTEN, - MaterialNames.URANIUM, - MaterialNames.ZIRCONIUM - ); - - standardMaterialsMap.stream() - .filter(Materials::hasMaterial) - .forEach(name -> { - addFluid(name, 2000, 10000, 769, 10); - addFluidBlock(name); - }); - - if (Materials.hasMaterial(MaterialNames.THORIUM)) { - addFluid(MaterialNames.THORIUM, 2000, 10000, 514, 10); - addFluidBlock(MaterialNames.THORIUM); - } - - initDone = true; - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Materials.java b/src/main/java/com/mcmoddev/modernmetals/init/Materials.java deleted file mode 100644 index f6d2210e..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/init/Materials.java +++ /dev/null @@ -1,247 +0,0 @@ -package com.mcmoddev.modernmetals.init; - - -import java.util.Arrays; -import java.util.List; - -import javax.annotation.Nonnull; - -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.lib.data.SharedStrings; -import com.mcmoddev.lib.material.MMDMaterial.MaterialType; -import com.mcmoddev.lib.util.ConfigBase.Options; - -/** - * This class initializes all of the materials in Modern Metals. - * - * @author Jasmine Iwanek - * - */ -public class Materials extends com.mcmoddev.lib.init.Materials { - - private static boolean initDone = false; - - private Materials() { - throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); - } - - /** - * - */ - public static void init() { - if (initDone) { - return; - } - - List modMaterials = Arrays.asList( - MaterialNames.ALUMINUM, - MaterialNames.BERYLLIUM, - MaterialNames.BORON, - MaterialNames.CHROMIUM, - MaterialNames.MAGNESIUM, - MaterialNames.MANGANESE, - MaterialNames.OSMIUM, - MaterialNames.RUTILE, - MaterialNames.TANTALUM, - MaterialNames.THORIUM, - MaterialNames.TUNGSTEN, - MaterialNames.ZIRCONIUM - ); - - List rareMaterials = Arrays.asList( - MaterialNames.CADMIUM, - MaterialNames.IRIDIUM, - MaterialNames.PLUTONIUM, - MaterialNames.URANIUM - ); - - List alloyMaterials = Arrays.asList( - MaterialNames.ALUMINUM_BRASS, - MaterialNames.GALVANIZED_STEEL, - MaterialNames.NICHROME, - MaterialNames.STAINLESS_STEEL - ); - - modMaterials.stream() - .filter(Options::isMaterialEnabled) - .forEach(name -> createMaterial(name, MaterialType.METAL, getHardness(name), getStrength(name), getMagic(name), getColor(name))); - - alloyMaterials.stream() - .filter(Options::isMaterialEnabled) - .forEach(name -> createAlloyMaterial(name, MaterialType.METAL, getHardness(name), getStrength(name), getMagic(name), getColor(name))); - - rareMaterials.stream() - .filter(Options::isMaterialEnabled) - .forEach(name -> createRareMaterial(name, MaterialType.METAL, getHardness(name), getStrength(name), getMagic(name), getColor(name))); - - if (Options.isMaterialEnabled(MaterialNames.TITANIUM)) { - final String name = MaterialNames.TITANIUM; - createSpecialMaterial(name, MaterialType.METAL, getHardness(name), getStrength(name), getMagic(name), getColor(name)); - } - - initDone = true; - } - - private static int getColor(@Nonnull final String name) { - switch (name) { - case MaterialNames.ALUMINUM: - return 0xFFC5C8C1; - case MaterialNames.ALUMINUM_BRASS: - return 0xFFEBAA56; - case MaterialNames.CADMIUM: - return 0xFFC9D4DA; - case MaterialNames.CHROMIUM: - return 0xFFCDCDCF; - case MaterialNames.GALVANIZED_STEEL: - return 0xFF9BA6A2; - case MaterialNames.IRIDIUM: - return 0xFFF8EDCC; - case MaterialNames.MAGNESIUM: - return 0xFF7F7F77; - case MaterialNames.MANGANESE: - return 0xFFF5CFDA; - case MaterialNames.NICHROME: - return 0xFFDEA054; - case MaterialNames.OSMIUM: - return 0xFF7C8E99; - case MaterialNames.PLUTONIUM: - return 0xFFB333EA; - case MaterialNames.RUTILE: - return 0xFFBF928B; - case MaterialNames.STAINLESS_STEEL: - return 0xFFC5BFC1; - case MaterialNames.TANTALUM: - return 0xFFC4BEC2; - case MaterialNames.TITANIUM: - return 0xFF73787E; - case MaterialNames.TUNGSTEN: - return 0xFF969696; - case MaterialNames.URANIUM: - return 0xFFA7B345; - case MaterialNames.ZIRCONIUM: - return 0xFF929793; - case MaterialNames.BERYLLIUM: - return 0xFFD5D3D5; - case MaterialNames.BORON: - return 0xFF888888; - case MaterialNames.THORIUM: - return 0xFF4F5A5A; - default: - return 0x00000000; - } - } - - private static double getMagic(@Nonnull final String name) { - switch (name) { - case MaterialNames.ALUMINUM: - case MaterialNames.ALUMINUM_BRASS: - case MaterialNames.CADMIUM: - case MaterialNames.CHROMIUM: - case MaterialNames.GALVANIZED_STEEL: - case MaterialNames.IRIDIUM: - case MaterialNames.MAGNESIUM: - case MaterialNames.MANGANESE: - case MaterialNames.NICHROME: - case MaterialNames.OSMIUM: - case MaterialNames.PLUTONIUM: - case MaterialNames.RUTILE: - case MaterialNames.STAINLESS_STEEL: - case MaterialNames.TANTALUM: - case MaterialNames.TITANIUM: - case MaterialNames.TUNGSTEN: - case MaterialNames.URANIUM: - case MaterialNames.ZIRCONIUM: - return 4.5d; - case MaterialNames.BERYLLIUM: - case MaterialNames.BORON: - case MaterialNames.THORIUM: - return 1.0d; - default: - return 1.0d; - } - } - - private static double getStrength(@Nonnull final String name) { - switch (name) { - case MaterialNames.TANTALUM: - return 17.5d; - case MaterialNames.TITANIUM: - case MaterialNames.URANIUM: - return 16.25d; - case MaterialNames.STAINLESS_STEEL: - return 16d; - case MaterialNames.NICHROME: - return 15.5d; - case MaterialNames.GALVANIZED_STEEL: - return 15.25d; - case MaterialNames.TUNGSTEN: - return 12.5d; - case MaterialNames.BORON: - return 8d; - case MaterialNames.ALUMINUM_BRASS: - case MaterialNames.PLUTONIUM: - return 7.5d; - case MaterialNames.ALUMINUM: - return 3.75d; - case MaterialNames.MAGNESIUM: - case MaterialNames.ZIRCONIUM: - return 3.5d; - case MaterialNames.CHROMIUM: - case MaterialNames.IRIDIUM: - return 3d; - case MaterialNames.MANGANESE: - case MaterialNames.OSMIUM: - return 2.75d; - case MaterialNames.BERYLLIUM: - return 2.5d; - case MaterialNames.CADMIUM: - case MaterialNames.THORIUM: - return 1d; - case MaterialNames.RUTILE: - return 0.25d; - default: - return 1.0d; - } - } - - private static double getHardness(@Nonnull final String name) { - switch (name) { - case MaterialNames.BORON: - return 9.5d; - case MaterialNames.CHROMIUM: - return 9d; - case MaterialNames.TUNGSTEN: - return 7.5d; - case MaterialNames.OSMIUM: - return 7d; - case MaterialNames.IRIDIUM: - case MaterialNames.TANTALUM: - case MaterialNames.TITANIUM: - return 6.5d; - case MaterialNames.NICHROME: - case MaterialNames.RUTILE: - case MaterialNames.STAINLESS_STEEL: - case MaterialNames.URANIUM: - return 6d; - case MaterialNames.BERYLLIUM: - case MaterialNames.GALVANIZED_STEEL: - return 5.5d; - case MaterialNames.MANGANESE: - case MaterialNames.ZIRCONIUM: - return 5d; - case MaterialNames.PLUTONIUM: - return 4d; - case MaterialNames.THORIUM: - return 3d; - case MaterialNames.ALUMINUM: - case MaterialNames.MAGNESIUM: - return 2.5d; - case MaterialNames.CADMIUM: - return 2d; - case MaterialNames.ALUMINUM_BRASS: - return 1.0d; - default: - return 1.0d; - } - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Recipes.java b/src/main/java/com/mcmoddev/modernmetals/init/Recipes.java deleted file mode 100644 index 18f14e21..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/init/Recipes.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.mcmoddev.modernmetals.init; - -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.lib.init.Materials; -import com.mcmoddev.lib.data.SharedStrings; -import com.mcmoddev.lib.material.MMDMaterial; - -/** - * - * @author Jasmine Iwanek - * - */ -public class Recipes extends com.mcmoddev.lib.init.Recipes { - - private static boolean initDone = false; - - private Recipes() { - throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); - } - - /** - * - */ - public static void init() { - if (initDone) { - return; - } - - Materials.init(); - Blocks.init(); - Items.init(); - - initModSpecificRecipes(); - - initDone = true; - } - - private static void initModSpecificRecipes() { - if (Materials.hasMaterial(MaterialNames.ALUMINUM)) { - final MMDMaterial aluminum = Materials.getMaterialByName(MaterialNames.ALUMINUM); - addAdditionalOredicts(aluminum, "Aluminium"); - addAdditionalOredicts(aluminum, "Bauxite"); - } - - if (Materials.hasMaterial(MaterialNames.ALUMINUM_BRASS)) { - final MMDMaterial aluminumBrass = Materials.getMaterialByName(MaterialNames.ALUMINUM_BRASS); - addSimpleAlloyRecipe(aluminumBrass, 2, MaterialNames.ALUMINUM, com.mcmoddev.basemetals.data.MaterialNames.BRASS); - addAdditionalOredicts(aluminumBrass, "AluminumBrass"); - addAdditionalOredicts(aluminumBrass, "Aluminiumbrass"); - addAdditionalOredicts(aluminumBrass, "AluminiumBrass"); - addAdditionalOredicts(aluminumBrass, "Alubrass"); - addAdditionalOredicts(aluminumBrass, "AluBrass"); - } - - if (Materials.hasMaterial(MaterialNames.CHROMIUM)) { - addAdditionalOredicts(MaterialNames.CHROMIUM, "Chrome"); - } - - if (Materials.hasMaterial(MaterialNames.GALVANIZED_STEEL)) { - final MMDMaterial galvanizedSteel = Materials.getMaterialByName(MaterialNames.GALVANIZED_STEEL); - addSimpleAlloyRecipe(galvanizedSteel, 2, com.mcmoddev.basemetals.data.MaterialNames.STEEL, com.mcmoddev.basemetals.data.MaterialNames.ZINC); - addAdditionalOredicts(galvanizedSteel, "GalvinizedSteel"); - } - - if (Materials.hasMaterial(MaterialNames.NICHROME)) { - addSimpleAlloyRecipe(MaterialNames.NICHROME, 2, com.mcmoddev.basemetals.data.MaterialNames.NICKEL, MaterialNames.CHROMIUM); - } - - if (Materials.hasMaterial(MaterialNames.STAINLESS_STEEL)) { - final MMDMaterial stainlessSteel = Materials.getMaterialByName(MaterialNames.STAINLESS_STEEL); - addSimpleAlloyRecipe(stainlessSteel, 2, com.mcmoddev.basemetals.data.MaterialNames.STEEL, MaterialNames.CHROMIUM); - addAdditionalOredicts(stainlessSteel, "StainlessSteel"); - } - - if (Materials.hasMaterial(MaterialNames.TITANIUM)) { - addSimpleAlloyRecipe(MaterialNames.TITANIUM, 2, MaterialNames.RUTILE, MaterialNames.MAGNESIUM); - } - - if (Materials.hasMaterial(MaterialNames.TUNGSTEN)) { - addAdditionalOredicts(MaterialNames.TUNGSTEN, "Wolfram"); - } - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/init/VillagerTrades.java b/src/main/java/com/mcmoddev/modernmetals/init/VillagerTrades.java deleted file mode 100644 index e8187e45..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/init/VillagerTrades.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mcmoddev.modernmetals.init; - -import com.mcmoddev.lib.data.SharedStrings; - -/** - * - * @author Jasmine Iwanek - * - */ -public class VillagerTrades extends com.mcmoddev.lib.init.VillagerTrades { - - private static boolean initDone = false; - - private VillagerTrades() { - throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); - } - - /** - * - */ - public static void init() { - if (initDone) { - return; - } - - Materials.init(); - Items.init(); - - registerCommonTrades(); - - initDone = true; - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/EnderIO.java b/src/main/java/com/mcmoddev/modernmetals/integration/plugins/EnderIO.java deleted file mode 100644 index 563dc0fc..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/EnderIO.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.mcmoddev.modernmetals.integration.plugins; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.lib.util.ConfigBase.Options; -import com.mcmoddev.lib.init.Materials; -import com.mcmoddev.lib.integration.MMDPlugin; -import com.mcmoddev.lib.integration.IIntegration; - -/** - * - * @author Jasmine Iwanek - * - */ -@MMDPlugin(addonId = ModernMetals.MODID, pluginId = EnderIO.PLUGIN_MODID) -public class EnderIO extends com.mcmoddev.lib.integration.plugins.EnderIOBase implements IIntegration { - - private static boolean initDone = false; - - /** - * - */ - @Override - public void init() { - if (initDone || !Options.isModEnabled(EnderIO.PLUGIN_MODID)) { - return; - } - - final String[] baseNames = new String[] { - MaterialNames.ALUMINUM, - MaterialNames.ALUMINUM_BRASS, - MaterialNames.CADMIUM, - MaterialNames.CHROMIUM, - MaterialNames.GALVANIZED_STEEL, - MaterialNames.IRIDIUM, - MaterialNames.MAGNESIUM, - MaterialNames.MANGANESE, - MaterialNames.NICHROME, - MaterialNames.OSMIUM, - MaterialNames.PLUTONIUM, - MaterialNames.RUTILE, - MaterialNames.STAINLESS_STEEL, - MaterialNames.TANTALUM, - MaterialNames.TITANIUM, - MaterialNames.TUNGSTEN, - MaterialNames.URANIUM, - MaterialNames.ZIRCONIUM - }; - - for (final String materialName : baseNames) { - if (Materials.hasMaterial(materialName)) { - addSagMillRecipe(materialName, 3600); - } - } - - initDone = true; - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/IC2.java b/src/main/java/com/mcmoddev/modernmetals/integration/plugins/IC2.java deleted file mode 100644 index fca31d3c..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/IC2.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mcmoddev.modernmetals.integration.plugins; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.lib.integration.MMDPlugin; -import com.mcmoddev.lib.util.ConfigBase.Options; -import com.mcmoddev.lib.init.Materials; -import com.mcmoddev.lib.integration.IIntegration; - -@MMDPlugin(addonId = ModernMetals.MODID, pluginId = IC2.PLUGIN_MODID) -public class IC2 extends com.mcmoddev.lib.integration.plugins.IC2Base implements IIntegration { - - private static boolean initDone = false; - - @Override - public void init() { - if (initDone || !Options.isModEnabled(IC2.PLUGIN_MODID)) { - return; - } - - initDone = true; - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/Mekanism.java b/src/main/java/com/mcmoddev/modernmetals/integration/plugins/Mekanism.java deleted file mode 100644 index bf0ceabd..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/Mekanism.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.mcmoddev.modernmetals.integration.plugins; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.lib.util.ConfigBase.Options; -import com.mcmoddev.lib.init.Materials; -import com.mcmoddev.lib.integration.MMDPlugin; -import com.mcmoddev.lib.integration.IIntegration; - -@MMDPlugin(addonId = ModernMetals.MODID, pluginId = Mekanism.PLUGIN_MODID) -public class Mekanism extends com.mcmoddev.lib.integration.plugins.MekanismBase implements IIntegration { - - private static boolean initDone = false; - - @Override - public void init() { - if (initDone || !Options.isModEnabled(Mekanism.PLUGIN_MODID)) { - return; - } - - final String[] baseNames = new String[] { - MaterialNames.ALUMINUM, - MaterialNames.BERYLLIUM, - MaterialNames.BORON, - MaterialNames.CADMIUM, - MaterialNames.CHROMIUM, - MaterialNames.IRIDIUM, - MaterialNames.MAGNESIUM, - MaterialNames.MANGANESE, - MaterialNames.PLUTONIUM, - MaterialNames.RUTILE, - MaterialNames.TANTALUM, - MaterialNames.THORIUM, - MaterialNames.TITANIUM, - MaterialNames.TUNGSTEN, - MaterialNames.URANIUM, - MaterialNames.ZIRCONIUM - }; - - for (final String materialName : baseNames) { - if (Materials.hasMaterial(materialName)) { - addOreMultiplicationRecipes(materialName); - } - } - - initDone = true; - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/ThermalExpansion.java b/src/main/java/com/mcmoddev/modernmetals/integration/plugins/ThermalExpansion.java deleted file mode 100755 index 41821497..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/ThermalExpansion.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.mcmoddev.modernmetals.integration.plugins; - -import java.util.HashMap; -import java.util.Map; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.lib.init.Materials; -import com.mcmoddev.lib.util.ConfigBase.Options; -import com.mcmoddev.lib.data.Names; -import com.mcmoddev.lib.integration.IIntegration; -import com.mcmoddev.lib.integration.MMDPlugin; -import com.mcmoddev.lib.material.MMDMaterial; -import cofh.api.util.ThermalExpansionHelper; -import net.minecraft.item.ItemStack; - -@MMDPlugin(addonId = ModernMetals.MODID, pluginId = ThermalExpansion.PLUGIN_MODID) -public class ThermalExpansion extends com.mcmoddev.lib.integration.plugins.ThermalExpansionBase implements IIntegration { - - private static boolean initDone = false; - - public void init() { - if (initDone || !Options.isModEnabled(ThermalExpansion.PLUGIN_MODID)) { - return; - } - - final String[] baseNames = new String[] { - MaterialNames.ALUMINUM_BRASS, - MaterialNames.BERYLLIUM, - MaterialNames.BORON, - MaterialNames.CADMIUM, - MaterialNames.CHROMIUM, - MaterialNames.GALVANIZED_STEEL, - MaterialNames.MAGNESIUM, - MaterialNames.MANGANESE, - MaterialNames.NICHROME, - MaterialNames.OSMIUM, - MaterialNames.PLUTONIUM, - MaterialNames.RUTILE, - MaterialNames.STAINLESS_STEEL, - MaterialNames.TANTALUM, - MaterialNames.THORIUM, - MaterialNames.TITANIUM, - MaterialNames.TUNGSTEN, - MaterialNames.URANIUM, - MaterialNames.ZIRCONIUM - }; - - for (final String materialName : baseNames) { - boolean enabled = Materials.hasMaterial(materialName); - if (Materials.hasMaterial(materialName)) { - addFurnace(materialName); - addCrucible(materialName); - addPlatePress(materialName); - addPressStorage(materialName); - } - } - - if (Materials.hasMaterial(MaterialNames.PLUTONIUM)) { - ThermalExpansionHelper.addMagmaticFuel(Materials.getMaterialByName(MaterialNames.PLUTONIUM).getFluid().getName(), 1000000); - } - - if (Materials.hasMaterial(MaterialNames.URANIUM)) { - ThermalExpansionHelper.addMagmaticFuel(Materials.getMaterialByName(MaterialNames.URANIUM).getFluid().getName(), 750000); - } - - if ((Materials.hasMaterial(com.mcmoddev.basemetals.data.MaterialNames.STEEL))) { - final MMDMaterial steel = Materials.getMaterialByName(com.mcmoddev.basemetals.data.MaterialNames.STEEL); - if ((Materials.hasMaterial(MaterialNames.CHROMIUM) && (Materials.hasMaterial(MaterialNames.STAINLESS_STEEL)))) { - ThermalExpansionHelper.addSmelterRecipe(4000, new ItemStack(steel.getItem(Names.INGOT), 1), new ItemStack(Materials.getMaterialByName(MaterialNames.CHROMIUM).getItem(Names.INGOT), 1), new ItemStack(Materials.getMaterialByName(MaterialNames.STAINLESS_STEEL).getItem(Names.INGOT), 2)); - } - - if ((Materials.hasMaterial(com.mcmoddev.basemetals.data.MaterialNames.ZINC) && (Materials.hasMaterial(MaterialNames.GALVANIZED_STEEL)))) { - ThermalExpansionHelper.addSmelterRecipe(4000, new ItemStack(steel.getItem(Names.INGOT), 1), new ItemStack(Materials.getMaterialByName(com.mcmoddev.basemetals.data.MaterialNames.ZINC).getItem(Names.INGOT), 1), new ItemStack(Materials.getMaterialByName(MaterialNames.GALVANIZED_STEEL).getItem(Names.INGOT), 2)); - } - } - - if ((Materials.hasMaterial(MaterialNames.RUTILE) && Materials.hasMaterial(MaterialNames.MAGNESIUM) && (Materials.hasMaterial(MaterialNames.TITANIUM)))) { - ThermalExpansionHelper.addSmelterRecipe(4000, new ItemStack(Materials.getMaterialByName(MaterialNames.RUTILE).getItem(Names.INGOT), 1), new ItemStack(Materials.getMaterialByName(MaterialNames.MAGNESIUM).getItem(Names.INGOT), 1), new ItemStack( Materials.getMaterialByName(MaterialNames.TITANIUM).getItem(Names.INGOT), 2)); - } - - initDone = true; - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/TinkersConstruct.java b/src/main/java/com/mcmoddev/modernmetals/integration/plugins/TinkersConstruct.java deleted file mode 100644 index 5a820a62..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/TinkersConstruct.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.mcmoddev.modernmetals.integration.plugins; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.modernmetals.data.TraitNames; - - -import com.mcmoddev.lib.util.ConfigBase.Options; -import com.mcmoddev.lib.init.Materials; -import com.mcmoddev.lib.integration.MMDPlugin; -import com.mcmoddev.lib.integration.plugins.tinkers.TraitLocations; -import com.mcmoddev.lib.integration.IIntegration; - -/** - * - * @author Jasmine Iwanek - * - */ -@MMDPlugin(addonId = ModernMetals.MODID, pluginId = TinkersConstruct.PLUGIN_MODID) -public class TinkersConstruct extends com.mcmoddev.lib.integration.plugins.TinkersConstructBase implements IIntegration { - - private static boolean initDone = false; - - @Override - public void init() { - if (initDone || !Options.isModEnabled(TinkersConstruct.PLUGIN_MODID)) { - return; - } - - if (Materials.hasMaterial(MaterialNames.ALUMINUM)) { - registry.getMaterial(MaterialNames.ALUMINUM, Materials.getMaterialByName(MaterialNames.ALUMINUM)).addTrait(TraitNames.LIGHTWEIGHT).setCastable(true).settle(); - } - - if (Materials.hasMaterial(MaterialNames.ALUMINUM_BRASS)) { - registry.getMaterial(MaterialNames.ALUMINUM_BRASS, Materials.getMaterialByName(MaterialNames.ALUMINUM_BRASS)).setCastable(true).settle(); - } - - if (Materials.hasMaterial(MaterialNames.CADMIUM)) { - registry.getMaterial(MaterialNames.CADMIUM, Materials.getMaterialByName(MaterialNames.CADMIUM)).setCastable(true).settle(); - } - - if (Materials.hasMaterial(MaterialNames.CHROMIUM)) { - registry.getMaterial(MaterialNames.CHROMIUM, Materials.getMaterialByName(MaterialNames.CHROMIUM)).setCastable(true).addTrait(TraitNames.MAGNETIC).addTrait(TraitNames.MAGNETIC2,TraitLocations.HEAD).settle(); - } - - if (Materials.hasMaterial(MaterialNames.GALVANIZED_STEEL)) { - registry.getMaterial(MaterialNames.GALVANIZED_STEEL, Materials.getMaterialByName(MaterialNames.GALVANIZED_STEEL)).addTrait(TraitNames.SHARP, TraitLocations.HEAD).addTrait(TraitNames.STIFF).setCastable(true).settle(); - registry.registerAlloy(MaterialNames.GALVANIZED_STEEL, Materials.getMaterialByName(MaterialNames.GALVANIZED_STEEL).getFluid(), 2, com.mcmoddev.basemetals.data.MaterialNames.STEEL, 1, com.mcmoddev.basemetals.data.MaterialNames.ZINC, 1); - } - - if (Materials.hasMaterial(MaterialNames.IRIDIUM)) { - registry.getMaterial(MaterialNames.IRIDIUM, Materials.getMaterialByName(MaterialNames.IRIDIUM)).setCastable(true).addTrait(TraitNames.HEAVY).addTrait(TraitNames.BRITTLE).settle(); - } - - if (Materials.hasMaterial(MaterialNames.MAGNESIUM)) { - registry.getMaterial(MaterialNames.MAGNESIUM, Materials.getMaterialByName(MaterialNames.MAGNESIUM)).setCastable(true).addTrait(TraitNames.REACTIVE).settle(); - } - - if (Materials.hasMaterial(MaterialNames.MANGANESE)) { - registry.getMaterial(MaterialNames.MANGANESE, Materials.getMaterialByName(MaterialNames.MANGANESE)).addTrait(TraitNames.BRITTLE).setCastable(true).settle(); - } - - if (Materials.hasMaterial(MaterialNames.NICHROME)) { - registry.getMaterial(MaterialNames.NICHROME, Materials.getMaterialByName(MaterialNames.NICHROME)).setCastable(true).settle(); - registry.registerAlloy(MaterialNames.NICHROME, Materials.getMaterialByName(MaterialNames.NICHROME).getFluid(), 2, com.mcmoddev.basemetals.data.MaterialNames.NICKEL, 1, MaterialNames.CHROMIUM, 1); - } - - if (Materials.hasMaterial(MaterialNames.OSMIUM)) { - registry.getMaterial(MaterialNames.OSMIUM, Materials.getMaterialByName(MaterialNames.OSMIUM)).setCastable(true).addTrait(TraitNames.HEAVY).addTrait(TraitNames.BRITTLE).settle(); - } - - if (Materials.hasMaterial(MaterialNames.PLUTONIUM)) { - registry.getMaterial(MaterialNames.PLUTONIUM, Materials.getMaterialByName(MaterialNames.PLUTONIUM)).addTrait(TraitNames.RADIOACTIVE).addTrait(TraitNames.TOXIC, TraitLocations.HEAD).addTrait(TraitNames.TOXIC, TraitLocations.PROJECTILE).setCastable(true).settle(); - } - - // TODO: request is for custom modifier item to grant +50% durability - // This is not yet in the API - might require extra classes in MMDLib - if (Materials.hasMaterial(MaterialNames.RUTILE)) { - registry.getMaterial(MaterialNames.RUTILE, Materials.getMaterialByName(MaterialNames.RUTILE)).setCastable(true).settle(); - } - - if (Materials.hasMaterial(MaterialNames.STAINLESS_STEEL)) { - registry.getMaterial(MaterialNames.STAINLESS_STEEL, Materials.getMaterialByName(MaterialNames.STAINLESS_STEEL)).setCastable(true).addTrait(TraitNames.SHARP, TraitLocations.HEAD).addTrait(TraitNames.STIFF).settle(); - registry.registerAlloy(MaterialNames.STAINLESS_STEEL, Materials.getMaterialByName(MaterialNames.STAINLESS_STEEL).getFluid(), 2, com.mcmoddev.basemetals.data.MaterialNames.STEEL, 1, MaterialNames.CHROMIUM, 1); - } - - if (Materials.hasMaterial(MaterialNames.TANTALUM)) { - registry.getMaterial(MaterialNames.TANTALUM, Materials.getMaterialByName(MaterialNames.TANTALUM)).setCastable(true).settle(); - } - - if (Materials.hasMaterial(MaterialNames.TITANIUM)) { - registry.getMaterial(MaterialNames.TITANIUM, Materials.getMaterialByName(MaterialNames.TITANIUM)).setCastable(true).settle(); - registry.registerAlloy(MaterialNames.TITANIUM, Materials.getMaterialByName(MaterialNames.TITANIUM).getFluid(), 2, MaterialNames.RUTILE, 1, MaterialNames.MAGNESIUM, 1 ); - } - - if (Materials.hasMaterial(MaterialNames.TUNGSTEN)) { - registry.getMaterial(MaterialNames.TUNGSTEN, Materials.getMaterialByName(MaterialNames.TUNGSTEN)).setCastable(true).settle(); - } - - if (Materials.hasMaterial(MaterialNames.URANIUM)) { - registry.getMaterial(MaterialNames.URANIUM, Materials.getMaterialByName(MaterialNames.URANIUM)).setCastable(true).addTrait(TraitNames.POISONOUS, TraitLocations.HEAD).addTrait(TraitNames.POISONOUS, TraitLocations.PROJECTILE).addTrait(TraitNames.RADIOACTIVE).settle(); - } - - if (Materials.hasMaterial(MaterialNames.ZIRCONIUM)) { - registry.getMaterial(MaterialNames.ZIRCONIUM, Materials.getMaterialByName(MaterialNames.ZIRCONIUM)).setCastable(true).settle(); - } - - registry.registerAll(); - initDone = true; - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/VeinMiner.java b/src/main/java/com/mcmoddev/modernmetals/integration/plugins/VeinMiner.java deleted file mode 100644 index 3155ed01..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/VeinMiner.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.mcmoddev.modernmetals.integration.plugins; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.lib.util.ConfigBase.Options; -import com.mcmoddev.lib.integration.MMDPlugin; -import com.mcmoddev.lib.integration.IIntegration; - -/** - * VeinMiner Integration Plugin - * - * @author Jasmine Iwanek - * - */ -@MMDPlugin(addonId = ModernMetals.MODID, pluginId = VeinMiner.PLUGIN_MODID) -public class VeinMiner extends com.mcmoddev.lib.integration.plugins.VeinMinerBase implements IIntegration { - - private static boolean initDone = false; - - @Override - public void init() { - if (initDone || !Options.isModEnabled(VeinMiner.PLUGIN_MODID)) { - return; - } - - final String[] baseNames = new String[] { - MaterialNames.ALUMINUM, - MaterialNames.ALUMINUM_BRASS, - MaterialNames.CADMIUM, - MaterialNames.CHROMIUM, - MaterialNames.GALVANIZED_STEEL, - MaterialNames.IRIDIUM, - MaterialNames.MAGNESIUM, - MaterialNames.MANGANESE, - MaterialNames.NICHROME, - MaterialNames.OSMIUM, - MaterialNames.PLUTONIUM, - MaterialNames.RUTILE, - MaterialNames.STAINLESS_STEEL, - MaterialNames.TANTALUM, - MaterialNames.TITANIUM, - MaterialNames.TUNGSTEN, - MaterialNames.URANIUM, - MaterialNames.ZIRCONIUM - }; - - for (final String materialName : baseNames) { - if (Options.isMaterialEnabled(materialName)) { - addToolsForMaterial(materialName); - } - } - - initDone = true; - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/proxy/ClientProxy.java b/src/main/java/com/mcmoddev/modernmetals/proxy/ClientProxy.java deleted file mode 100644 index 4e26b91e..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/proxy/ClientProxy.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mcmoddev.modernmetals.proxy; - -import com.mcmoddev.lib.client.registrations.RegistrationHelper; -import com.mcmoddev.modernmetals.init.Blocks; -import com.mcmoddev.modernmetals.init.Fluids; -import com.mcmoddev.modernmetals.init.Items; - -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; - -/** - * Modern Metals Client Proxy - * - * @author Jasmine Iwanek - * - */ -public class ClientProxy extends CommonProxy { - - @Override - public void preInit(FMLPreInitializationEvent event) { - super.preInit(event); - for (final String name : Items.getItemRegistry().keySet()) { - RegistrationHelper.registerItemRender(name); - } - - for (final String name : Blocks.getBlockRegistry().keySet()) { - RegistrationHelper.registerBlockRender(name); - } - - for (final String name : Fluids.getFluidBlockRegistry().keySet()) { - RegistrationHelper.registerFluidRender(name); - } - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/proxy/CommonProxy.java b/src/main/java/com/mcmoddev/modernmetals/proxy/CommonProxy.java deleted file mode 100644 index a0c1cf2c..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/proxy/CommonProxy.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.mcmoddev.modernmetals.proxy; - -import java.util.HashSet; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.modernmetals.init.*; -import com.mcmoddev.modernmetals.util.Config; -import com.mcmoddev.modernmetals.util.EventHandler; -import com.mcmoddev.lib.integration.IntegrationManager; -import com.mcmoddev.lib.oregen.FallbackGenerator; -import com.mcmoddev.lib.util.ConfigBase.Options; - -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.common.Loader; -import net.minecraftforge.fml.common.MissingModsException; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.registry.GameRegistry; -import net.minecraftforge.fml.common.versioning.ArtifactVersion; -import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; - -/** - * Modern Metals Common Proxy - * - * @author Jasmine Iwanek - * - */ -public class CommonProxy { - - public void preInit(FMLPreInitializationEvent event) { - - Config.init(); - - if ((Options.requireMMDOreSpawn()) && (!Loader.isModLoaded("orespawn"))) { - if(Options.fallbackOrespawn()) { - GameRegistry.registerWorldGenerator(new FallbackGenerator(), 0); - } else { - final HashSet orespawnMod = new HashSet<>(); - orespawnMod.add(new DefaultArtifactVersion("3.2.0")); - throw new MissingModsException(orespawnMod, "orespawn", "MMD Ore Spawn Mod (fallback generator disabled, MMD OreSpawn enabled)"); - } - } - - Materials.init(); - Fluids.init(); - ItemGroups.init(); - Blocks.init(); - Items.init(); - - VillagerTrades.init(); - - IntegrationManager.INSTANCE.preInit(event); - } - - /* Right now this isn't needed, but might be at some point - public void onRemap(FMLMissingMappingsEvent event) { - for (final MissingMapping mapping : event.get()) { - if (mapping.resourceLocation.getResourceDomain().equals(ModernMetals.MODID)) { - if (mapping.type.equals(GameRegistry.Type.BLOCK)) { - // Dummy - } else if (mapping.type.equals(GameRegistry.Type.ITEM)) { - // Dummy - } - } - } - }*/ - - public void init(FMLInitializationEvent event) { - Recipes.init(); - - Achievements.init(); - - MinecraftForge.EVENT_BUS.register(new EventHandler()); - - ItemGroups.setupIcons(MaterialNames.ALUMINUM); - } - - public void postInit(FMLPostInitializationEvent event) { - Config.postInit(); - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/util/Config.java b/src/main/java/com/mcmoddev/modernmetals/util/Config.java deleted file mode 100644 index 7e7bef14..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/util/Config.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.mcmoddev.modernmetals.util; - -import java.io.File; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.lib.registry.CrusherRecipeRegistry; - -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.config.Configuration; -import net.minecraftforge.fml.client.event.ConfigChangedEvent; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - -/** - * @author Jasmine Iwanek - * - */ -public class Config extends com.mcmoddev.lib.util.ConfigBase { - - private static Configuration configuration; - private static final String CONFIG_FILE = "config/ModernMetals.cfg"; - private static final String MATERIALS_CAT = "Metals"; - - @SubscribeEvent - public void onConfigChange(ConfigChangedEvent.OnConfigChangedEvent e) { - if (e.getModID().equals(ModernMetals.MODID)) { - init(); - } - } - - public static void init() { - if (configuration == null) { - configuration = new Configuration(new File(CONFIG_FILE)); - MinecraftForge.EVENT_BUS.register(new Config()); - } - - // METALS - Options.materialEnabled(MaterialNames.ALUMINUM, configuration.getBoolean("EnableAluminum", MATERIALS_CAT, true, "Enable Aluminum Items and Materials")); - Options.materialEnabled(MaterialNames.ALUMINUM_BRASS, configuration.getBoolean("EnableAluminumBrass", MATERIALS_CAT, true, "Enable Aluminum Brass Items and Materials")); - Options.materialEnabled(MaterialNames.BERYLLIUM, configuration.getBoolean("EnableBeryllium", MATERIALS_CAT, true, "Enable Beryllium Items and Materials")); - Options.materialEnabled(MaterialNames.BORON, configuration.getBoolean("EnableBoron", MATERIALS_CAT, true, "Enable Boron Items and Materials")); - Options.materialEnabled(MaterialNames.CADMIUM, configuration.getBoolean("EnableCadmium", MATERIALS_CAT, true, "Enable Cadmium Items and Materials")); - Options.materialEnabled(MaterialNames.CHROMIUM, configuration.getBoolean("EnableChromium", MATERIALS_CAT, true, "Enable Chromium Items and Materials")); - Options.materialEnabled(MaterialNames.GALVANIZED_STEEL, configuration.getBoolean("EnableGalvanizedSteel", MATERIALS_CAT, true, "Enable Galvanized Steel Items and Materials")); - Options.materialEnabled(MaterialNames.IRIDIUM, configuration.getBoolean("EnableIridium", MATERIALS_CAT, true, "Enable Iridium Items and Materials")); - Options.materialEnabled(MaterialNames.MAGNESIUM, configuration.getBoolean("EnableMagnesium", MATERIALS_CAT, true, "Enable Magnesium Items and Materials")); - Options.materialEnabled(MaterialNames.MANGANESE, configuration.getBoolean("EnableManganese", MATERIALS_CAT, true, "Enable Manganese Items and Materials")); - Options.materialEnabled(MaterialNames.NICHROME, configuration.getBoolean("EnableNichrome", MATERIALS_CAT, true, "Enable Nichrome Items and Materials")); - Options.materialEnabled(MaterialNames.OSMIUM, configuration.getBoolean("EnableOsmium", MATERIALS_CAT, true, "Enable Osmium Items and Materials")); - Options.materialEnabled(MaterialNames.PLUTONIUM, configuration.getBoolean("EnablePlutonium", MATERIALS_CAT, true, "Enable Plutonium Items and Materials")); - Options.materialEnabled(MaterialNames.RUTILE, configuration.getBoolean("EnableRutile", MATERIALS_CAT, true, "Enable Rutile Items and Materials")); - Options.materialEnabled(MaterialNames.STAINLESS_STEEL, configuration.getBoolean("EnableStainlessSteel", MATERIALS_CAT, true, "Enable Stainless Steel Items and Materials")); - Options.materialEnabled(MaterialNames.TANTALUM, configuration.getBoolean("EnableTantalum", MATERIALS_CAT, true, "Enable Tantalum Items and Materials")); - Options.materialEnabled(MaterialNames.THORIUM, configuration.getBoolean("EnableThorium", MATERIALS_CAT, true, "Enable Thorium Items and Materials")); - Options.materialEnabled(MaterialNames.TITANIUM, configuration.getBoolean("EnableTitanium", MATERIALS_CAT, true, "Enable Titanium Items and Materials")); - Options.materialEnabled(MaterialNames.TUNGSTEN, configuration.getBoolean("EnableTungsten", MATERIALS_CAT, true, "Enable Tungsten Items and Materials")); - Options.materialEnabled(MaterialNames.URANIUM, configuration.getBoolean("EnableUranium", MATERIALS_CAT, true, "Enable Uranium Items and Materials")); - Options.materialEnabled(MaterialNames.ZIRCONIUM, configuration.getBoolean("EnableZirconium", MATERIALS_CAT, true, "Enable Zirconium Items and Materials")); - - if (configuration.hasChanged()) { - configuration.save(); - } - } - - public static void postInit() { - CrusherRecipeRegistry.getInstance().clearCache(); - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/util/EventHandler.java b/src/main/java/com/mcmoddev/modernmetals/util/EventHandler.java deleted file mode 100644 index 0620d8a0..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/util/EventHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.mcmoddev.modernmetals.util; - -import com.mcmoddev.modernmetals.data.AchievementNames; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.modernmetals.init.Achievements; - -import com.mcmoddev.lib.material.IMMDObject; -import com.mcmoddev.lib.util.ConfigBase.Options; -import com.mcmoddev.lib.item.ItemMMDIngot; - -import net.minecraft.item.Item; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemSmeltedEvent; - -public class EventHandler { - - @SubscribeEvent - void event(ItemSmeltedEvent event) { - if (!(Options.enableAchievements())) { - return; - } - - final Item item = event.smelting.getItem(); - if (!(item instanceof IMMDObject)) { - return; - } - - final String materialName = ((IMMDObject) item).getMMDMaterial().getName(); - if (item instanceof ItemMMDIngot) { - if (materialName.equals(MaterialNames.ALUMINUM_BRASS)) { - event.player.addStat(Achievements.getAchievementByName(AchievementNames.ALUMINUM_BRASS_MAKER), 1); - } else if (materialName.equals(MaterialNames.GALVANIZED_STEEL)) { - event.player.addStat(Achievements.getAchievementByName(AchievementNames.GALVANIZED_STEEL_MAKER), 1); - } else if (materialName.equals(MaterialNames.NICHROME)) { - event.player.addStat(Achievements.getAchievementByName(AchievementNames.NICHROME_MAKER), 1); - } else if (materialName.equals(MaterialNames.STAINLESS_STEEL)) { - event.player.addStat(Achievements.getAchievementByName(AchievementNames.STAINLESS_STEEL_MAKER), 1); - } else if (materialName.equals(MaterialNames.TITANIUM)) { - event.player.addStat(Achievements.getAchievementByName(AchievementNames.TITANIUM_MAKER), 1); - } - } - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/ModernMetalsOreSpawn.java b/src/main/java/com/mcmoddev/orespawn/ModernMetalsOreSpawn.java deleted file mode 100644 index dbbd7401..00000000 --- a/src/main/java/com/mcmoddev/orespawn/ModernMetalsOreSpawn.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mcmoddev.orespawn; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.orespawn.api.os3.*; -import com.mcmoddev.orespawn.api.plugin.IOreSpawnPlugin; -import com.mcmoddev.orespawn.api.plugin.OreSpawnPlugin; - -@OreSpawnPlugin(modid = ModernMetals.MODID, resourcePath = "orespawn") -public class ModernMetalsOreSpawn implements IOreSpawnPlugin { - - @Override - public void register(OS3API apiInterface) { - // nothing for us to do - all of our ores are in the - // jar and the code handles that - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/ModernMetals.java b/src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java similarity index 56% rename from src/main/java/com/mcmoddev/modernmetals/ModernMetals.java rename to src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java index 7862f940..3f406c03 100644 --- a/src/main/java/com/mcmoddev/modernmetals/ModernMetals.java +++ b/src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java @@ -1,9 +1,10 @@ -package com.mcmoddev.modernmetals; +package zone.moddev.mc.modernmetals; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.mcmoddev.modernmetals.proxy.CommonProxy; +import zone.moddev.mc.modernmetals.proxy.CommonProxy; +import zone.moddev.mc.modernmetals.compat.LegacyRegistryMappings; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventHandler; @@ -12,6 +13,7 @@ import net.minecraftforge.fml.common.event.FMLFingerprintViolationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLMissingMappingsEvent; +import net.minecraftforge.fml.common.event.FMLModIdMappingEvent; import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; @@ -25,9 +27,9 @@ modid = ModernMetals.MODID, name = ModernMetals.NAME, version = ModernMetals.VERSION, - dependencies = "required-after:forge@[13.20.1.2386,);required-after:basemetals;before:buildingbricks", - acceptedMinecraftVersions = "[1.11.2,)", - certificateFingerprint = "@FINGERPRINT@", + dependencies = "required-after:forge@[13.20.1.2588,);required-after:basemetals@[2.5.1.111021,2.6.0);required-after:orespawn@[4.1.5.111021,5.0.0);after:tconstruct;after:ic2;after:cofhcore;after:thermalexpansion;after:mekanism;before:buildingbricks", + acceptedMinecraftVersions = "[1.11.2]", + guiFactory = "zone.moddev.mc.modernmetals.client.ModernMetalsGuiFactory", updateJSON = ModernMetals.UPDATEJSON) public class ModernMetals { @@ -38,25 +40,24 @@ public class ModernMetals { public static final String MODID = "modernmetals"; /** Display name of this Mod */ - static final String NAME = "Modern Metals"; + public static final String NAME = "Modern Metals"; /** - * Version number, in Major.Minor.Patch format. The minor number is + * Version number, in Major.Minor.Build format. The minor number is * increased whenever a change is made that has the potential to break * compatibility with other mods that depend on this one. */ - public static final String VERSION = "2.5.0-beta4"; + public static final String VERSION = "2.5.1.111021"; - static final String UPDATEJSON = "https://raw.githubusercontent.com/MinecraftModDevelopmentMods/ModernMetals/master/update.json"; + public static final String UPDATEJSON = "https://raw.githubusercontent.com/MinecraftModDevelopmentMods/ModernMetals/master-1.11.2/update.json"; - private static final String PROXY_BASE = "com.mcmoddev." + MODID + ".proxy."; + private static final String PROXY_BASE = "zone.moddev.mc.modernmetals.proxy."; @SidedProxy(clientSide = PROXY_BASE + "ClientProxy", serverSide = PROXY_BASE + "ServerProxy") public static CommonProxy proxy; public static final Logger logger = LogManager.getFormatterLogger(ModernMetals.MODID); - @EventHandler public void onFingerprintViolation(FMLFingerprintViolationEvent event) { logger.warn("Invalid fingerprint detected!"); } @@ -78,7 +79,18 @@ public static void postInit(FMLPostInitializationEvent event) { @EventHandler public static void onRemap(FMLMissingMappingsEvent event) { - // we are not using this right now... - // proxy.onRemap(event); + proxy.onRemap(event); + } + + @EventHandler + public static void onIdMapping(FMLModIdMappingEvent event) { + LegacyRegistryMappings.restoreItemAliases(); + } + + @EventHandler + public static void loadComplete(net.minecraftforge.fml.common.event.FMLLoadCompleteEvent event) { + zone.moddev.mc.modernmetals.content.ContentPolicy.filterRecipes(); + if (net.minecraftforge.fml.common.Loader.isModLoaded("tconstruct")) + zone.moddev.mc.modernmetals.integration.plugins.TinkersConstruct.applyContentPolicy(); } } diff --git a/src/main/java/zone/moddev/mc/modernmetals/block/OverlayOreBlock.java b/src/main/java/zone/moddev/mc/modernmetals/block/OverlayOreBlock.java new file mode 100644 index 00000000..9a877421 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/block/OverlayOreBlock.java @@ -0,0 +1,20 @@ +package zone.moddev.mc.modernmetals.block; + +import com.mcmoddev.lib.block.BlockMMDOre; +import com.mcmoddev.lib.material.MMDMaterial; +import net.minecraft.util.BlockRenderLayer; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** The shared ore block with alpha testing enabled for its model's outer faces. */ +public final class OverlayOreBlock extends BlockMMDOre { + public OverlayOreBlock(MMDMaterial material) { + super(material); + } + + @Override + @SideOnly(Side.CLIENT) + public BlockRenderLayer getBlockLayer() { + return BlockRenderLayer.CUTOUT_MIPPED; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsConfigScreen.java b/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsConfigScreen.java new file mode 100644 index 00000000..8e5f781d --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsConfigScreen.java @@ -0,0 +1,36 @@ +package zone.moddev.mc.modernmetals.client; + +import java.util.ArrayList; +import java.util.List; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraftforge.common.config.ConfigElement; +import net.minecraftforge.common.config.Property; +import net.minecraftforge.fml.client.config.GuiConfig; +import net.minecraftforge.fml.client.config.IConfigElement; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.util.Config; + +/** One parent-aware screen for Forge's mod list and OreSpawn's child-mod list. */ +@SideOnly(Side.CLIENT) +public final class ModernMetalsConfigScreen extends GuiConfig { + public ModernMetalsConfigScreen(final GuiScreen parent) { + super(parent, elements(), + ModernMetals.MODID, false, true, I18n.format("config.modernmetals.title"), + I18n.format("config.modernmetals.description")); + } + private static List elements() { + final List elements = new ArrayList<>( + new ConfigElement(Config.getConfiguration().getCategory("General")).getChildElements()); + for (final Property material : Config.getConfiguration().getCategory("Metals").getOrderedValues()) { + elements.add(new ConfigElement(material) { + @Override public String getComment() { + return I18n.format("config.modernmetals.material.tooltip"); + } + }); + } + return elements; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsGuiFactory.java b/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsGuiFactory.java new file mode 100644 index 00000000..573b5957 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsGuiFactory.java @@ -0,0 +1,20 @@ +package zone.moddev.mc.modernmetals.client; + +import java.util.Collections; +import java.util.Set; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraftforge.fml.client.IModGuiFactory; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** Forge 1.11's configuration factory; never loaded by a dedicated server. */ +@SideOnly(Side.CLIENT) +public final class ModernMetalsGuiFactory implements IModGuiFactory { + @Override public void initialize(final Minecraft minecraft) { } + @Override public Class mainConfigGuiClass() { return ModernMetalsConfigScreen.class; } + @Override public boolean hasConfigGui() { return true; } + @Override public GuiScreen createConfigGui(final GuiScreen parent) { return new ModernMetalsConfigScreen(parent); } + @Override public Set runtimeGuiCategories() { return Collections.emptySet(); } + @Override public RuntimeOptionGuiHandler getHandlerFor(final RuntimeOptionCategoryElement element) { return null; } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformer.java b/src/main/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformer.java new file mode 100644 index 00000000..0c3635d6 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformer.java @@ -0,0 +1,89 @@ +package zone.moddev.mc.modernmetals.compat; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.commons.Remapper; +import org.objectweb.asm.commons.RemappingClassAdapter; + +/** Relinks existing MMD add-ons to the same classes and registries as Modern Metals. */ +public final class LegacyNamespaceTransformer implements IClassTransformer { + + private static final String OLD = "com/mcmoddev/modernmetals/"; + private static final String CURRENT = "zone/moddev/mc/modernmetals/"; + private static final Set EXPORTS = loadExports(); + + @Override + public byte[] transform(final String name, final String transformedName, final byte[] bytes) { + if (bytes == null || name == null || name.startsWith("zone.moddev.mc.modernmetals.") + || !(contains(bytes, OLD) || contains(bytes, OLD.replace('/', '.')))) { + return bytes; + } + + final ClassWriter output = new ClassWriter(0); + final Remapper names = new Remapper() { + @Override + public String map(final String internalName) { + return currentName(internalName); + } + + @Override + public Object mapValue(final Object value) { + if (value instanceof String) { + final String text = (String) value; + final String internal = text.replace('.', '/'); + final String mapped = currentName(internal); + if (mapped.equals(internal)) return value; + return text.indexOf('/') < 0 ? mapped.replace('/', '.') : mapped; + } + return super.mapValue(value); + } + }; + + new ClassReader(bytes).accept(new RemappingClassAdapter(output, names), 0); + return output.toByteArray(); + } + + private static String currentName(final String name) { + if (!name.startsWith(OLD)) return name; + + final String suffix = name.substring(OLD.length()); + final int nested = suffix.indexOf('$'); + final String owner = nested < 0 ? suffix : suffix.substring(0, nested); + return EXPORTS.contains(owner) ? CURRENT + suffix : name; + } + + private static Set loadExports() { + final Set result = new HashSet<>(); + final InputStream stream = LegacyNamespaceTransformer.class.getClassLoader() + .getResourceAsStream("META-INF/modernmetals-legacy-api.list"); + if (stream == null) throw new IllegalStateException("Missing Modern Metals API name list"); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + if (!line.isEmpty() && !line.startsWith("#")) result.add(line); + } + } catch (final IOException failure) { + throw new IllegalStateException("Cannot read Modern Metals API names", failure); + } + return Collections.unmodifiableSet(result); + } + + private static boolean contains(final byte[] bytes, final String text) { + for (int offset = 0; offset <= bytes.length - text.length(); offset++) { + int index = 0; + while (index < text.length() && bytes[offset + index] == text.charAt(index)) index++; + if (index == text.length()) return true; + } + return false; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/compat/LegacyRegistryMappings.java b/src/main/java/zone/moddev/mc/modernmetals/compat/LegacyRegistryMappings.java new file mode 100644 index 00000000..24d52537 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/compat/LegacyRegistryMappings.java @@ -0,0 +1,58 @@ +package zone.moddev.mc.modernmetals.compat; + +import java.util.LinkedHashMap; +import java.util.Map; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.event.FMLMissingMappingsEvent; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import net.minecraftforge.fml.common.registry.GameData; +import net.minecraftforge.fml.common.registry.GameRegistry; +import zone.moddev.mc.modernmetals.ModernMetals; + +/** Registry names written by Modern Metals 0.11.0. */ +public final class LegacyRegistryMappings { + private static final String OLD_BRASS = "alubrass"; + private static final ResourceLocation MOLTEN_BRASS = id("aluminumbrass"); + private static final Map ITEM_ALIASES = itemAliases(); + + private LegacyRegistryMappings() {} + + public static void remap(final FMLMissingMappingsEvent event) { + for (final FMLMissingMappingsEvent.MissingMapping mapping : event.get()) { + if (mapping.type == GameRegistry.Type.BLOCK && mapping.resourceLocation.equals(id(OLD_BRASS)) + && ForgeRegistries.BLOCKS.containsKey(MOLTEN_BRASS)) { + mapping.remap(ForgeRegistries.BLOCKS.getValue(MOLTEN_BRASS)); + } else if (mapping.type == GameRegistry.Type.ITEM) { + final ResourceLocation replacement = ITEM_ALIASES.get(mapping.resourceLocation); + // Some saves contain both the door block's item ID and the old door item ID. + if (replacement != null && ForgeRegistries.ITEMS.containsKey(replacement)) mapping.ignore(); + } + } + } + + public static void restoreItemAliases() { + final Map aliases = new LinkedHashMap<>(); + ITEM_ALIASES.forEach((oldId, newId) -> { + if (ForgeRegistries.ITEMS.containsKey(newId)) aliases.put(oldId, newId); + }); + GameData.getItemRegistry().loadAliases(aliases); + } + + private static Map itemAliases() { + final Map aliases = new LinkedHashMap<>(); + aliases.put(id(OLD_BRASS), MOLTEN_BRASS); + for (final String material : new String[] { "aluminum", "aluminumbrass", "cadmium", "chromium", + "galvanizedsteel", "iridium", "magnesium", "manganese", "nichrome", "osmium", + "plutonium", "rutile", "stainlesssteel", "tantalum", "titanium", "tungsten", "uranium", "zirconium" }) { + aliases.put(id(material + "_door_item"), id(material + "_door")); + aliases.put(id(material + "_fishingrod"), id(material + "_fishing_rod")); + aliases.put(id(material + "_horsearmor"), id(material + "_horse_armor")); + } + return aliases; + } + + private static ResourceLocation id(final String path) { + return new ResourceLocation(ModernMetals.MODID, path); + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/compat/ModernMetalsLoadingPlugin.java b/src/main/java/zone/moddev/mc/modernmetals/compat/ModernMetalsLoadingPlugin.java new file mode 100644 index 00000000..5ca19d4f --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/compat/ModernMetalsLoadingPlugin.java @@ -0,0 +1,19 @@ +package zone.moddev.mc.modernmetals.compat; + +import java.util.Map; +import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; + +/** Relinks old add-on names without installing a second Base Metals transformer. */ +@IFMLLoadingPlugin.Name("ModernMetals") +@IFMLLoadingPlugin.MCVersion("1.11.2") +@IFMLLoadingPlugin.SortingIndex(1002) +@IFMLLoadingPlugin.TransformerExclusions("zone.moddev.mc.modernmetals.compat.") +public final class ModernMetalsLoadingPlugin implements IFMLLoadingPlugin { + @Override public String[] getASMTransformerClass() { + return new String[] { "zone.moddev.mc.modernmetals.compat.LegacyNamespaceTransformer" }; + } + @Override public String getModContainerClass() { return null; } + @Override public String getSetupClass() { return null; } + @Override public void injectData(final Map data) { } + @Override public String getAccessTransformerClass() { return null; } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/content/ContentModeNetwork.java b/src/main/java/zone/moddev/mc/modernmetals/content/ContentModeNetwork.java new file mode 100644 index 00000000..c19b2699 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/content/ContentModeNetwork.java @@ -0,0 +1,64 @@ +package zone.moddev.mc.modernmetals.content; + +import java.util.Set; + +import net.minecraft.network.NetworkManager; +import net.minecraft.network.play.server.SPacketDisconnect; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.network.FMLNetworkEvent.CustomPacketRegistrationEvent; +import net.minecraftforge.fml.common.network.NetworkRegistry; + +/** Advertise the active recipe mode in Forge's login channel registration. */ +public final class ContentModeNetwork { + private static final String HIGH = "ModernMetals|High"; + private static final String LOW = "ModernMetals|Low"; + + private ContentModeNetwork() {} + + public static void register() { + NetworkRegistry.INSTANCE.newEventDrivenChannel(ContentPolicy.active().lowFantasy() ? LOW : HIGH); + MinecraftForge.EVENT_BUS.register(new ContentModeNetwork()); + } + + @SubscribeEvent + public void checkMode(final CustomPacketRegistrationEvent event) { + final Set channels = event.getRegistrations(); + // Forge includes FML|HS in its login registration, not ordinary channel updates. + if (!"REGISTER".equals(event.getOperation()) || !channels.contains("FML|HS")) return; + + final boolean localLow = ContentPolicy.active().lowFantasy(); + final boolean remoteLow = channels.contains(LOW); + final boolean remoteHigh = channels.contains(HIGH); + final boolean server = event.getSide().isServer(); + final ITextComponent reason; + if (remoteLow == remoteHigh) { + // A client with no mode channel may also lack the translation key. + reason = server + ? new TextComponentString("Modern Metals could not check the Fantasy Level. " + + "Use the same Modern Metals build on the server and client.") + : new TextComponentTranslation("config.modernmetals.connection.protocol"); + } else if (remoteLow == localLow) { + return; + } else { + final boolean serverLow = server ? localLow : remoteLow; + reason = new TextComponentTranslation("config.modernmetals.connection.mismatch", + modeLabel(serverLow), modeLabel(!serverLow)); + } + + final NetworkManager manager = event.getManager(); + manager.channel().config().setAutoRead(false); + if (server) { + manager.sendPacket(new SPacketDisconnect(reason), future -> manager.closeChannel(reason)); + } else { + manager.closeChannel(reason); + } + } + + private static ITextComponent modeLabel(final boolean low) { + return new TextComponentTranslation(low ? "low_fantasy" : "high_fantasy"); + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/content/ContentPolicy.java b/src/main/java/zone/moddev/mc/modernmetals/content/ContentPolicy.java new file mode 100644 index 00000000..7597da5c --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/content/ContentPolicy.java @@ -0,0 +1,84 @@ +package zone.moddev.mc.modernmetals.content; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; +import zone.moddev.mc.modernmetals.ModernMetals; + +/** Restricts obtainability, while retaining every registered item and saved stack. */ +public final class ContentPolicy { + private static final Set TOOLS = names("axe","crackhammer","hoe","pickaxe","scythe","shears","shovel","sword","arrow","bolt"); + private static final Set PROTECTION = names("boots","chestplate","helmet","leggings","horse_armor","shield"); + private static final Set FULL_TOOLS = names("galvanizedsteel","stainlesssteel","titanium"); + private static final Set ARMOR = names("aluminum","aluminumbrass","galvanizedsteel","stainlesssteel","titanium"); + private static final Set GEARS = names("aluminum","aluminumbrass","galvanizedsteel","stainlesssteel","titanium"); + private static final Set STRUCTURAL_FORMS = names("bars","bookshelf","button","door","double_slab","fence","fence_gate","flower_pot","ladder","lever","pressure_plate","slab","stairs","trapdoor","tripwire_hook","wall"); + private static final Set STRUCTURAL_METALS = names("aluminum","aluminumbrass","galvanizedsteel","nichrome","stainlesssteel","tantalum","titanium","tungsten","zirconium"); + private static boolean initialized; + private static ContentPolicy active = forMode("high_fantasy"); + private final boolean low; + private ContentPolicy(final boolean low) { this.low=low; } + public static ContentPolicy forMode(final String mode) { + return new ContentPolicy(mode!=null && "low_fantasy".equals(mode.trim().toLowerCase(Locale.ROOT))); + } + public static ContentPolicy active() { return active; } + public boolean lowFantasy() { return low; } + /** Latch Modern Metals' own startup choice until the next full game restart. */ + public static void initialize(final String mode) { + if(initialized) return; + active=forMode(mode);initialized=true; + ModernMetals.logger.info("Modern Metals content mode: " + (active.low ? "low_fantasy" : "high_fantasy") + " (fixed until restart)"); + } + public boolean allows(final String material,final String form) { + if(!low || material==null || form==null) return true; + if(TOOLS.contains(form)) return FULL_TOOLS.contains(material); + if(PROTECTION.contains(form)) return ARMOR.contains(material); + if("bow".equals(form) || "crossbow".equals(form) || "fishing_rod".equals(form)) return false; + if("gear".equals(form)) return GEARS.contains(material); + if("anvil".equals(form)) return "stainlesssteel".equals(material) || "galvanizedsteel".equals(material); + if(STRUCTURAL_FORMS.contains(form)) return STRUCTURAL_METALS.contains(material); + return true; + } + public boolean allows(final ItemStack stack) { + if(!low || stack==null || stack.isEmpty()) return true; + final ResourceLocation id=stack.getItem().getRegistryName(); + if(id==null || !ModernMetals.MODID.equals(id.getResourceDomain())) return true; + final String path=id.getResourcePath().startsWith("double_") ? id.getResourcePath().substring(7) : id.getResourcePath(); + final int split=path.indexOf('_'); + return split<0 || allows(path.substring(0,split),path.substring(split+1)); + } + /** Called after all mods have registered their ordinary crafting recipes. */ + public static void filterRecipes() { + if(!active.low) return; + final int before=CraftingManager.getInstance().getRecipeList().size(); + CraftingManager.getInstance().getRecipeList().removeIf(recipe->!active.allows(recipe.getRecipeOutput())); + final java.util.List recipes=CraftingManager.getInstance().getRecipeList(); + for(int i=0;i getRemainingItems(final net.minecraft.inventory.InventoryCrafting grid) {return delegate.getRemainingItems(grid);} + } + private static Set names(final String...values) {return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(values)));} +} diff --git a/src/main/java/com/mcmoddev/modernmetals/data/AchievementNames.java b/src/main/java/zone/moddev/mc/modernmetals/data/AchievementNames.java similarity index 94% rename from src/main/java/com/mcmoddev/modernmetals/data/AchievementNames.java rename to src/main/java/zone/moddev/mc/modernmetals/data/AchievementNames.java index 5e14de51..ef77a790 100644 --- a/src/main/java/com/mcmoddev/modernmetals/data/AchievementNames.java +++ b/src/main/java/zone/moddev/mc/modernmetals/data/AchievementNames.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.data; +package zone.moddev.mc.modernmetals.data; public class AchievementNames { diff --git a/src/main/java/com/mcmoddev/modernmetals/data/MaterialNames.java b/src/main/java/zone/moddev/mc/modernmetals/data/MaterialNames.java similarity index 96% rename from src/main/java/com/mcmoddev/modernmetals/data/MaterialNames.java rename to src/main/java/zone/moddev/mc/modernmetals/data/MaterialNames.java index c732fede..32b57f88 100644 --- a/src/main/java/com/mcmoddev/modernmetals/data/MaterialNames.java +++ b/src/main/java/zone/moddev/mc/modernmetals/data/MaterialNames.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.data; +package zone.moddev.mc.modernmetals.data; import com.mcmoddev.lib.data.SharedStrings; @@ -29,4 +29,4 @@ public class MaterialNames { private MaterialNames() { throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); } -} +} \ No newline at end of file diff --git a/src/main/java/com/mcmoddev/modernmetals/data/TraitNames.java b/src/main/java/zone/moddev/mc/modernmetals/data/TraitNames.java similarity index 94% rename from src/main/java/com/mcmoddev/modernmetals/data/TraitNames.java rename to src/main/java/zone/moddev/mc/modernmetals/data/TraitNames.java index 301f6254..82ededc3 100644 --- a/src/main/java/com/mcmoddev/modernmetals/data/TraitNames.java +++ b/src/main/java/zone/moddev/mc/modernmetals/data/TraitNames.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.data; +package zone.moddev.mc.modernmetals.data; import com.mcmoddev.lib.data.SharedStrings; diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/Achievements.java b/src/main/java/zone/moddev/mc/modernmetals/init/Achievements.java new file mode 100644 index 00000000..4287d332 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Achievements.java @@ -0,0 +1,65 @@ +package zone.moddev.mc.modernmetals.init; + +import com.mcmoddev.lib.data.Names; +import zone.moddev.mc.modernmetals.util.Config.Options; + +import net.minecraft.stats.Achievement; +import net.minecraftforge.common.AchievementPage; +import net.minecraftforge.fml.common.Loader; + +/** initializer for achievements */ +public class Achievements extends com.mcmoddev.lib.init.Achievements { + + /** Make Aluminum Brass Blend */ + public static Achievement aluminumBrassMaker; + /** Make Galvanized Steel Blend */ + public static Achievement galvanizedSteelMaker; + /** Make Nichrome Blend */ + public static Achievement nichromeMaker; + /** Make Stainless Steel Blend */ + public static Achievement stainlessSteelMaker; + /** Make Titanium Blend */ + public static Achievement titaniumMaker; + + private static boolean initDone = false; + + private Achievements() { + throw new IllegalAccessError("Not a instantiable class"); + } + + /** + * + */ + public static void init() { + if (initDone) { + return; + } + + if (zone.moddev.mc.basemetals.util.Config.Options.enableAchievements()) { + AchievementPage page = new AchievementPage(Loader.instance().activeModContainer().getModId()); + AchievementPage.registerAchievementPage(page); + + if (Options.enableAluminumBrass) { + aluminumBrassMaker = makeAchievement("aluminum_brass_maker", zone.moddev.mc.basemetals.init.Achievements.getAchievementByName("metallurgy"), 0, 0, Materials.getMaterialByName("aluminumbrass").getItem(Names.INGOT), page); + } + + if (Options.enableGalvanizedSteel) { + galvanizedSteelMaker = makeAchievement("galvanized_steel_maker", zone.moddev.mc.basemetals.init.Achievements.getAchievementByName("metallurgy"), 0, 1, Materials.getMaterialByName("galvanizedsteel").getItem(Names.INGOT), page); + } + + if (Options.enableNichrome) { + nichromeMaker = makeAchievement("nichrome_maker", zone.moddev.mc.basemetals.init.Achievements.getAchievementByName("metallurgy"), 0, 2, Materials.getMaterialByName("nichrome").getItem(Names.INGOT), page); + } + + if (Options.enableStainlessSteel) { + stainlessSteelMaker = makeAchievement("stainless_steel_maker", zone.moddev.mc.basemetals.init.Achievements.getAchievementByName("metallurgy"), 0, 3, Materials.getMaterialByName("stainlesssteel").getItem(Names.INGOT), page); + } + + if (Options.enableTitanium) { + titaniumMaker = makeAchievement("titanium_maker", zone.moddev.mc.basemetals.init.Achievements.getAchievementByName("metallurgy"), 0, 4, Materials.getMaterialByName("titanium").getItem(Names.INGOT), page); + } + } + + initDone = true; + } +} diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Blocks.java b/src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java similarity index 63% rename from src/main/java/com/mcmoddev/modernmetals/init/Blocks.java rename to src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java index 0a6821e2..51384363 100644 --- a/src/main/java/com/mcmoddev/modernmetals/init/Blocks.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java @@ -1,14 +1,14 @@ -package com.mcmoddev.modernmetals.init; +package zone.moddev.mc.modernmetals.init; import java.util.Arrays; -import javax.annotation.Nonnull; - import com.mcmoddev.lib.data.Names; import com.mcmoddev.lib.data.SharedStrings; import com.mcmoddev.lib.init.Materials; import com.mcmoddev.lib.material.MMDMaterial; -import com.mcmoddev.modernmetals.data.MaterialNames; +import com.mcmoddev.lib.util.Oredicts; +import zone.moddev.mc.modernmetals.block.OverlayOreBlock; +import zone.moddev.mc.modernmetals.data.MaterialNames; import net.minecraft.block.Block; @@ -67,7 +67,7 @@ public static void init() { create(Names.BLOCK, material); create(Names.PLATE, material); - create(Names.ORE, material); + createOre(material); create(Names.BARS, material); create(Names.DOOR, material); create(Names.TRAPDOOR, material); @@ -83,4 +83,23 @@ public static void init() { initDone = true; } + + private static void createOre(MMDMaterial material) { + if (MaterialNames.THORIUM.equals(material.getName())) { + // Thorium still has its original opaque texture. + create(Names.ORE, material); + } else if ((material.hasOre() || hasLegacyOre(material.getName())) + && !material.hasBlock(Names.ORE) && isNameEnabled(Names.ORE)) { + final Block ore = addBlock(new OverlayOreBlock(material), Names.ORE, material, + ItemGroups.getTab(SharedStrings.TAB_BLOCKS)); + material.addNewBlock(Names.ORE.toString(), ore); + Oredicts.registerOre("ore" + material.getCapitalizedName(), ore); + } + } + + private static boolean hasLegacyOre(final String name) { + // 0.11.0 saved these alloy ores. OreSpawn has no placement rules for them. + return MaterialNames.GALVANIZED_STEEL.equals(name) || MaterialNames.NICHROME.equals(name) + || MaterialNames.STAINLESS_STEEL.equals(name); + } } diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java b/src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java new file mode 100644 index 00000000..4217fa4f --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java @@ -0,0 +1,177 @@ +package zone.moddev.mc.modernmetals.init; + +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import zone.moddev.mc.modernmetals.util.Config.Options; + +/** + * This class initializes all fluids in Modern Metals. + * + * @author Jasmine Iwanek + * + */ +public class Fluids extends com.mcmoddev.lib.init.Fluids { + + private static boolean initDone = false; + + private Fluids() { + throw new IllegalAccessError("Not a instantiable class"); + } + + /** Reuse another mod's registered fluid instead of retaining a rejected duplicate. */ + protected static net.minecraftforge.fluids.Fluid addFluid(final com.mcmoddev.lib.material.MMDMaterial material, + final int density, final int viscosity, final int temperature, final int luminosity) { + final net.minecraftforge.fluids.Fluid registered=net.minecraftforge.fluids.FluidRegistry.getFluid(material.getName()); + if(registered!=null) { + material.setFluid(registered); + net.minecraftforge.fluids.FluidRegistry.addBucketForFluid(registered); + return registered; + } + return com.mcmoddev.lib.init.Fluids.addFluid(material,density,viscosity,temperature,luminosity); + } + + /** Preserve the legacy Modern Metals query API for borrowed fluids too. */ + public static net.minecraftforge.fluids.Fluid getFluidByName(final String name) { + for(final com.mcmoddev.lib.material.MMDMaterial material:Materials.getMaterialsByMod("modernmetals")) + if(material.getName().equals(name)) return material.getFluid(); + return com.mcmoddev.lib.init.Fluids.getFluidByName(name); + } + public static String getNameOfFluid(final net.minecraftforge.fluids.Fluid fluid) { + if(fluid==null) return null; + for(final com.mcmoddev.lib.material.MMDMaterial material:Materials.getMaterialsByMod("modernmetals")) + if(material.getFluid()==fluid) return material.getName(); + return com.mcmoddev.lib.init.Fluids.getNameOfFluid(fluid); + } + public static java.util.Map getFluidRegistry() { + final java.util.Map view=new java.util.LinkedHashMap<>(com.mcmoddev.lib.init.Fluids.getFluidRegistry()); + for(final com.mcmoddev.lib.material.MMDMaterial material:Materials.getMaterialsByMod("modernmetals")) + if(material.getFluid()!=null) view.put(material.getName(),material.getFluid()); + return java.util.Collections.unmodifiableMap(view); + } + + /** + * + */ + public static void init() { + if (initDone) { + return; + } + + if (Options.enableAluminum) { + addFluid(Materials.aluminum, 2000, 10000, 330, 10); + addFluidBlock(Materials.aluminum); + } + + if (Options.enableAluminumBrass) { + final Fluid brass = addFluid(Materials.aluminumBrass, 2000, 10000, 500, 10); + addFluidBlock(Materials.aluminumBrass); + // Forge 1.11 saves the fluid name in buckets and tanks independently of the block ID. + if (FluidRegistry.getFluid("alubrass") == null) { + final Fluid legacy = new Fluid("alubrass", brass.getStill(), brass.getFlowing()) { + @Override public int getColor() { return brass.getColor(); } + } + .setUnlocalizedName("modernmetals.aluminumbrass").setDensity(brass.getDensity()) + .setViscosity(brass.getViscosity()).setTemperature(brass.getTemperature()) + .setLuminosity(brass.getLuminosity()).setBlock(Materials.aluminumBrass.getFluidBlock()); + FluidRegistry.registerFluid(legacy); + } + } + + if (Options.enableCadmium) { + addFluid(Materials.cadmium, 2000, 10000, 300, 10); + addFluidBlock(Materials.cadmium); + } + + if (Options.enableChromium) { + addFluid(Materials.chromium, 2000, 10000, 769, 10); + addFluidBlock(Materials.chromium); + } + + if (Options.enableGalvanizedSteel) { + addFluid(Materials.galvanizedSteel, 2000, 10000, 769, 10); + addFluidBlock(Materials.galvanizedSteel); + } + + if (Options.enableIridium) { + addFluid(Materials.iridium, 2000, 10000, 769, 10); + addFluidBlock(Materials.iridium); + } + + if (Options.enableMagnesium) { + addFluid(Materials.magnesium, 2000, 10000, 769, 10); + addFluidBlock(Materials.magnesium); + } + + if (Options.enableManganese) { + addFluid(Materials.manganese, 2000, 10000, 769, 10); + addFluidBlock(Materials.manganese); + } + + if (Options.enableNichrome) { + addFluid(Materials.nichrome, 2000, 10000, 769, 10); + addFluidBlock(Materials.nichrome); + } + + if (Options.enableOsmium) { + addFluid(Materials.osmium, 2000, 10000, 769, 10); + addFluidBlock(Materials.osmium); + } + + if (Options.enablePlutonium) { + addFluid(Materials.plutonium, 2000, 10000, 769, 10); + addFluidBlock(Materials.plutonium); + } + + if (Options.enableRutile) { + addFluid(Materials.rutile, 2000, 10000, 769, 10); + addFluidBlock(Materials.rutile); + } + + if (Options.enableStainlessSteel) { + addFluid(Materials.stainlessSteel, 2000, 10000, 769, 10); + addFluidBlock(Materials.stainlessSteel); + } + + if (Options.enableTantalum) { + addFluid(Materials.tantalum, 2000, 10000, 769, 10); + addFluidBlock(Materials.tantalum); + } + + if (Options.enableTitanium) { + addFluid(Materials.titanium, 2000, 10000, 769, 10); + addFluidBlock(Materials.titanium); + } + + if (Options.enableTungsten) { + addFluid(Materials.tungsten, 2000, 10000, 769, 10); + addFluidBlock(Materials.tungsten); + } + + if (Options.enableUranium) { + addFluid(Materials.uranium, 2000, 10000, 769, 10); + addFluidBlock(Materials.uranium); + } + + if (Options.enableZirconium) { + addFluid(Materials.zirconium, 2000, 10000, 769, 10); + addFluidBlock(Materials.zirconium); + } + + if (Options.enableBeryllium) { + addFluid(Materials.beryllium, 2000, 10000, 400, 10); + addFluidBlock(Materials.beryllium); + } + + if (Options.enableBoron) { + addFluid(Materials.boron, 2000, 10000, 400, 10); + addFluidBlock(Materials.boron); + } + + if (Options.enableThorium) { + addFluid(Materials.thorium, 2000, 10000, 514, 10); + addFluidBlock(Materials.thorium); + } + + initDone = true; + } +} diff --git a/src/main/java/com/mcmoddev/modernmetals/init/ItemGroups.java b/src/main/java/zone/moddev/mc/modernmetals/init/ItemGroups.java similarity index 89% rename from src/main/java/com/mcmoddev/modernmetals/init/ItemGroups.java rename to src/main/java/zone/moddev/mc/modernmetals/init/ItemGroups.java index 08599db2..930ce1b9 100644 --- a/src/main/java/com/mcmoddev/modernmetals/init/ItemGroups.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/ItemGroups.java @@ -1,6 +1,6 @@ -package com.mcmoddev.modernmetals.init; +package zone.moddev.mc.modernmetals.init; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.basemetals.data.MaterialNames; import com.mcmoddev.lib.data.Names; import com.mcmoddev.lib.data.SharedStrings; import com.mcmoddev.lib.init.Materials; @@ -8,6 +8,7 @@ import com.mcmoddev.lib.material.MMDMaterial; import net.minecraft.block.Block; +import net.minecraft.init.Items; import net.minecraft.item.Item; /** @@ -19,14 +20,13 @@ public class ItemGroups extends com.mcmoddev.lib.init.ItemGroups { private static boolean initDone = false; - private static final MMDCreativeTab blocksTab = addTab(SharedStrings.TAB_BLOCKS); private static final MMDCreativeTab itemsTab = addTab(SharedStrings.TAB_ITEMS); private static final MMDCreativeTab toolsTab = addTab(SharedStrings.TAB_TOOLS); private static final MMDCreativeTab combatTab = addTab(SharedStrings.TAB_COMBAT); private ItemGroups() { - throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); + throw new IllegalAccessError("Not a instantiable class"); } /** diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Items.java b/src/main/java/zone/moddev/mc/modernmetals/init/Items.java similarity index 97% rename from src/main/java/com/mcmoddev/modernmetals/init/Items.java rename to src/main/java/zone/moddev/mc/modernmetals/init/Items.java index 4efa2716..6053764f 100644 --- a/src/main/java/com/mcmoddev/modernmetals/init/Items.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Items.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.init; +package zone.moddev.mc.modernmetals.init; import java.util.Arrays; import java.util.List; @@ -9,7 +9,7 @@ import com.mcmoddev.lib.data.SharedStrings; import com.mcmoddev.lib.init.Materials; import com.mcmoddev.lib.material.MMDMaterial; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.data.MaterialNames; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.item.Item; diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/Materials.java b/src/main/java/zone/moddev/mc/modernmetals/init/Materials.java new file mode 100644 index 00000000..d6394c30 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Materials.java @@ -0,0 +1,162 @@ +package zone.moddev.mc.modernmetals.init; + +import zone.moddev.mc.modernmetals.util.Config.Options; + +import com.mcmoddev.lib.material.MMDMaterial; +import com.mcmoddev.lib.material.MMDMaterial.MaterialType; + +/** + * This class initializes all of the materials in Modern Metals. + * + * @author Jasmine Iwanek + * + */ +public class Materials extends com.mcmoddev.lib.init.Materials { + + /** Aluminum */ + public static MMDMaterial aluminum; + /** Aluminum-Brass*/ + public static MMDMaterial aluminumBrass; + /** Cadmium */ + public static MMDMaterial cadmium; + /** Chromium */ + public static MMDMaterial chromium; + /** Galvanized Steel */ + public static MMDMaterial galvanizedSteel; + /** Iridium */ + public static MMDMaterial iridium; + /** Magnesium */ + public static MMDMaterial magnesium; + /** Manganese */ + public static MMDMaterial manganese; + /** Nichrome */ + public static MMDMaterial nichrome; + /** Osmium */ + public static MMDMaterial osmium; + /** Plutonium */ + public static MMDMaterial plutonium; + /** Rutile */ + public static MMDMaterial rutile; + /** Stainless Steel */ + public static MMDMaterial stainlessSteel; + /** Tantalum */ + public static MMDMaterial tantalum; + /** Titanium */ + public static MMDMaterial titanium; + /** Tungsten */ + public static MMDMaterial tungsten; + /** Uranium */ + public static MMDMaterial uranium; + /** Zirconium */ + public static MMDMaterial zirconium; + + /** Beryllium */ + public static MMDMaterial beryllium; + + /** Boron */ + public static MMDMaterial boron; + + /** Thorium */ + public static MMDMaterial thorium; + + private static boolean initDone = false; + + protected Materials() { + throw new IllegalAccessError("Not a instantiable class"); + } + + /** + * + */ + public static void init() { + if (initDone) { + return; + } + + if (Options.enableAluminum) { + aluminum = createMaterial("aluminum", MaterialType.METAL, 2.5, 3.75, 4.5, 0xFFC5C8C1); + } + + if (Options.enableAluminumBrass) { + aluminumBrass = createAlloyMaterial("aluminumbrass", MaterialType.METAL, 1, 7.5, 4.5, 0xFFEBAA56); + } + + if (Options.enableCadmium) { + cadmium = createRareMaterial("cadmium", MaterialType.METAL, 2, 1, 4.5, 0xFFC9D4DA); + } + + if (Options.enableChromium) { + chromium = createMaterial("chromium", MaterialType.METAL, 9, 3, 4.5, 0xFFCDCDCF); + } + + if (Options.enableGalvanizedSteel) { + galvanizedSteel = createAlloyMaterial("galvanizedsteel", MaterialType.METAL, 5.5, 15.25, 4.5, 0xFF9BA6A2); + } + + if (Options.enableIridium) { + iridium = createRareMaterial("iridium", MaterialType.METAL, 6.5, 3, 4.5, 0xFFF8EDCC); + } + + if (Options.enableMagnesium) { + magnesium = createMaterial("magnesium", MaterialType.METAL, 2.5, 3.5, 4.5, 0xFF7F7F77); + } + + if (Options.enableManganese) { + manganese = createMaterial("manganese", MaterialType.METAL, 5, 2.75, 4.5, 0xFFF5CFDA); + } + + if (Options.enableNichrome) { + nichrome = createAlloyMaterial("nichrome", MaterialType.METAL, 6, 15.5, 4.5, 0xFFDEA054); + } + + if (Options.enableOsmium) { + osmium = createMaterial("osmium", MaterialType.METAL, 7, 2.75, 4.5, 0xFF7C8E99); + } + + if (Options.enablePlutonium) { + plutonium = createRareMaterial("plutonium", MaterialType.METAL, 4, 7.5, 4.5, 0xFFB333EA); + } + + if (Options.enableRutile) { + rutile = createMaterial("rutile", MaterialType.METAL, 6, 0.25, 4.5, 0xFFBF928B); + } + + if (Options.enableStainlessSteel) { + stainlessSteel = createAlloyMaterial("stainlesssteel", MaterialType.METAL, 6, 16, 4.5, 0xFFC5BFC1); + } + + if (Options.enableTantalum) { + tantalum = createMaterial("tantalum", MaterialType.METAL, 6.5, 17.5, 4.5, 0xFFC4BEC2); + } + + if (Options.enableTitanium) { + titanium = createSpecialMaterial("titanium", MaterialType.METAL, 6.5, 16.25, 4.5, 0xFF73787E); + } + + if (Options.enableTungsten) { + tungsten = createMaterial("tungsten", MaterialType.METAL, 7.5, 12.5, 4.5, 0xFF969696); + } + + if (Options.enableUranium) { + uranium = createRareMaterial("uranium", MaterialType.METAL, 6, 16.25, 4.5, 0xFFA7B345); + } + + if (Options.enableZirconium) { + zirconium = createMaterial("zirconium", MaterialType.METAL, 5, 3.5, 4.5, 0xFF929793); + } + + if (Options.enableBeryllium) { + beryllium = createMaterial("beryllium", MaterialType.METAL, 5.5, 2.5, 1.0, 0xFFD5D3D5); + } + + if (Options.enableBoron) { + boron = createMaterial("boron", MaterialType.METAL, 9.5, 8, 1.0, 0xFF888888); + } + + if (Options.enableThorium) { + thorium = createMaterial("thorium", MaterialType.METAL, 3, 1, 1.0, 0xFF4F5A5A); + } + + initDone = true; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/Recipes.java b/src/main/java/zone/moddev/mc/modernmetals/init/Recipes.java new file mode 100644 index 00000000..93b7ee15 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Recipes.java @@ -0,0 +1,86 @@ +package zone.moddev.mc.modernmetals.init; + +import zone.moddev.mc.modernmetals.init.Materials; +import zone.moddev.mc.modernmetals.util.Config.Options; +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.util.Oredicts; + +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Jasmine Iwanek + * + */ +public class Recipes extends com.mcmoddev.lib.init.Recipes { + + private static boolean initDone = false; + + private Recipes() { + throw new IllegalAccessError("Not a instantiable class"); + } + + /** + * + */ + public static void init() { + if (initDone) { + return; + } + + Materials.init(); + Blocks.init(); + Items.init(); + + initModSpecificRecipes(); + + initDone = true; + } + + private static void initModSpecificRecipes() { + if (Options.enableAluminum) { + String oreDictName = "Bauxite"; + + addAdditionalOredicts(Materials.aluminum, "Aluminium"); + OreDictionary.registerOre(Oredicts.ORE + oreDictName, Materials.getMaterialByName("aluminum").getBlock(Names.ORE)); + OreDictionary.registerOre(Oredicts.DUST + oreDictName, Materials.getMaterialByName("aluminum").getItem(Names.POWDER)); + OreDictionary.registerOre(Oredicts.DUST_TINY + oreDictName, Materials.getMaterialByName("aluminum").getItem(Names.SMALLPOWDER)); + OreDictionary.registerOre(Oredicts.DUST_SMALL + oreDictName, Materials.getMaterialByName("aluminum").getItem(Names.SMALLPOWDER)); + } + + if (Options.enableAluminumBrass) { + addSimpleAlloyRecipe(Materials.aluminumBrass, 2, "Aluminum", "Brass"); + addAdditionalOredicts(Materials.aluminumBrass, "AluminumBrass"); + addAdditionalOredicts(Materials.aluminumBrass, "Aluminiumbrass"); + addAdditionalOredicts(Materials.aluminumBrass, "AluminiumBrass"); + addAdditionalOredicts(Materials.aluminumBrass, "Alubrass"); + addAdditionalOredicts(Materials.aluminumBrass, "AluBrass"); + } + + if (Options.enableChromium) { + addAdditionalOredicts(Materials.chromium, "Chrome"); + } + + if (Options.enableGalvanizedSteel) { + addSimpleAlloyRecipe(Materials.galvanizedSteel, 2, "Steel", "Zinc"); + addAdditionalOredicts(Materials.galvanizedSteel, "GalvinizedSteel"); + } + + if (Options.enableNichrome) { + addSimpleAlloyRecipe(Materials.nichrome, 2, "Nickel", "Chromium"); + } + + if (Options.enableStainlessSteel) { + addSimpleAlloyRecipe(Materials.stainlessSteel, 2, "Steel", "Chromium"); + addAdditionalOredicts(Materials.stainlessSteel, "StainlessSteel"); + } + + if (Options.enableTitanium) { + addSimpleAlloyRecipe(Materials.titanium, 2, "Rutile", "Magnesium"); + } + + if (Options.enableTungsten) { + addAdditionalOredicts(Materials.tungsten, "Wolfram"); + } + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/VillagerTrades.java b/src/main/java/zone/moddev/mc/modernmetals/init/VillagerTrades.java new file mode 100644 index 00000000..322ac3a5 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/init/VillagerTrades.java @@ -0,0 +1,68 @@ +package zone.moddev.mc.modernmetals.init; + +import zone.moddev.mc.modernmetals.util.Config; + +/** + * + * @author Jasmine Iwanek + * + */ +public class VillagerTrades extends com.mcmoddev.lib.init.VillagerTrades { + + private static boolean initDone = false; + + private VillagerTrades() { + throw new IllegalAccessError("Not a instantiable class"); + } + + /** + * + */ + public static void init() { + if (initDone) { + return; + } + + Materials.init(); + Items.init(); + + if (Config.Options.enableVillagerTrades) { + if (zone.moddev.mc.modernmetals.content.ContentPolicy.active().lowFantasy()) registerRestrictedTrades(); + else registerCommonTrades(); + } + + initDone = true; + } + + /** Preserve common trade prices while omitting disallowed equipment offers. */ + private static void registerRestrictedTrades() { + final zone.moddev.mc.modernmetals.content.ContentPolicy policy=zone.moddev.mc.modernmetals.content.ContentPolicy.active(); + for(final com.mcmoddev.lib.material.MMDMaterial material:Materials.getMaterialsByMod("modernmetals")) { + if(material.isRare()) continue; + final float hardness=material.getStat(com.mcmoddev.lib.data.MaterialStats.HARDNESS); + final float value=hardness+material.getStat(com.mcmoddev.lib.data.MaterialStats.STRENGTH) + +material.getStat(com.mcmoddev.lib.data.MaterialStats.MAGICAFFINITY)+material.getToolHarvestLevel(); + final int purchase=emeraldPurchaseValue(value),sale=emeraldSaleValue(value),level=tradeLevel(value); + if(purchase>64 || sale>64) continue; + for(final com.mcmoddev.lib.data.Names form:new com.mcmoddev.lib.data.Names[]{ + com.mcmoddev.lib.data.Names.HELMET,com.mcmoddev.lib.data.Names.CHESTPLATE,com.mcmoddev.lib.data.Names.LEGGINGS, + com.mcmoddev.lib.data.Names.BOOTS,com.mcmoddev.lib.data.Names.CRACKHAMMER,com.mcmoddev.lib.data.Names.SWORD, + com.mcmoddev.lib.data.Names.AXE,com.mcmoddev.lib.data.Names.HOE,com.mcmoddev.lib.data.Names.SHOVEL,com.mcmoddev.lib.data.Names.PICKAXE}) { + if(!material.hasItem(form) || !policy.allows(material.getItemStack(form))) continue; + final boolean armor=form==com.mcmoddev.lib.data.Names.HELMET || form==com.mcmoddev.lib.data.Names.CHESTPLATE + || form==com.mcmoddev.lib.data.Names.LEGGINGS || form==com.mcmoddev.lib.data.Names.BOOTS; + final boolean weapon=form==com.mcmoddev.lib.data.Names.SWORD; + final int cost=armor?purchase+(int)(hardness/2):weapon?purchase+(int)(material.getBaseAttackDamage()/2)-1:purchase; + zone.moddev.mc.basemetals.util.VillagerTradeHelper.insertTrades(SMITH_RL, + armor?ARMOR_SMITH_ID:weapon?WEAPON_SMITH_ID:TOOL_SMITH_ID,level, + makeTradePalette(makePurchasePalette(cost,material.getItemStack(form)))); + } + if(material.hasItem(com.mcmoddev.lib.data.Names.INGOT)) { + final net.minecraft.item.ItemStack ingots=material.getItemStack(com.mcmoddev.lib.data.Names.INGOT,12); + for(final int career:new int[]{ARMOR_SMITH_ID,WEAPON_SMITH_ID,TOOL_SMITH_ID}) + zone.moddev.mc.basemetals.util.VillagerTradeHelper.insertTrades(SMITH_RL,career,level, + makeTradePalette(makePurchasePalette(purchase,ingots),makeSalePalette(sale,ingots))); + } + } + } +} diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/DenseOres.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/DenseOres.java similarity index 86% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/DenseOres.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/DenseOres.java index 95ce3bf3..5864cd93 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/DenseOres.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/DenseOres.java @@ -1,7 +1,7 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; import com.mcmoddev.lib.util.ConfigBase.Options; import com.mcmoddev.lib.init.Materials; import com.mcmoddev.lib.integration.IIntegration; @@ -33,6 +33,8 @@ public void init() { private static void registerOres() { final String[] baseNames = new String[] { MaterialNames.ALUMINUM, + MaterialNames.BERYLLIUM, + MaterialNames.BORON, MaterialNames.CADMIUM, MaterialNames.CHROMIUM, MaterialNames.IRIDIUM, @@ -42,6 +44,7 @@ private static void registerOres() { MaterialNames.PLUTONIUM, MaterialNames.RUTILE, MaterialNames.TANTALUM, + MaterialNames.THORIUM, MaterialNames.TITANIUM, MaterialNames.TUNGSTEN, MaterialNames.URANIUM, diff --git a/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/EnderIO.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/EnderIO.java new file mode 100644 index 00000000..3a8f5682 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/EnderIO.java @@ -0,0 +1,105 @@ +package zone.moddev.mc.modernmetals.integration.plugins; + +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.util.Config.Options; +import com.mcmoddev.lib.integration.MMDPlugin; +import com.mcmoddev.lib.integration.IIntegration; + +/** + * + * @author Jasmine Iwanek + * + */ +@MMDPlugin(addonId = ModernMetals.MODID, pluginId = EnderIO.PLUGIN_MODID) +public class EnderIO extends com.mcmoddev.lib.integration.plugins.EnderIOBase implements IIntegration { + + private static boolean initDone = false; + + /** + * + */ + @Override + public void init() { + if (initDone || !zone.moddev.mc.basemetals.util.Config.Options.isModEnabled("enderio")) { + return; + } + + if (Options.enableAluminum) { + addSagMillRecipe("aluminum", null, 3600); + } + + if (Options.enableAluminumBrass) { + addSagMillRecipe("aluminumbrass", null, 3600); + } + + if (Options.enableBeryllium) addSagMillRecipe("beryllium", null, 3600); + if (Options.enableBoron) addSagMillRecipe("boron", null, 3600); + if (Options.enableThorium) addSagMillRecipe("thorium", null, 3600); + + if (Options.enableCadmium) { + addSagMillRecipe("cadmium", null, 3600); + } + + if (Options.enableChromium) { + addSagMillRecipe("chromium", null, 3600); + } + + if (Options.enableGalvanizedSteel) { + addSagMillRecipe("galvanizedsteel", null, 3600); + } + + if (Options.enableIridium) { + addSagMillRecipe("iridium", null, 3600); + } + + if (Options.enableMagnesium) { + addSagMillRecipe("magnesium", null, 3600); + } + + if (Options.enableManganese) { + addSagMillRecipe("manganese", null, 3600); + } + + if (Options.enableNichrome) { + addSagMillRecipe("nichrome", null, 3600); + } + + if (Options.enableOsmium) { + addSagMillRecipe("osmium", null, 3600); + } + + if (Options.enablePlutonium) { + addSagMillRecipe("plutonium", null, 3600); + } + + if (Options.enableRutile) { + addSagMillRecipe("rutile", null, 3600); + } + + if (Options.enableStainlessSteel) { + addSagMillRecipe("stainlesssteel", null, 3600); + } + + if (Options.enableTantalum) { + addSagMillRecipe("tantalum", null, 3600); + } + + if (Options.enableTitanium) { + addSagMillRecipe("titanium", null, 3600); + } + + if (Options.enableTungsten) { + addSagMillRecipe("tungsten", null, 3600); + } + + if (Options.enableUranium) { + addSagMillRecipe("uranium", null, 3600); + } + + if (Options.enableZirconium) { + addSagMillRecipe("zirconium", null, 3600); + } + + initDone = true; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/IC2.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/IC2.java new file mode 100644 index 00000000..bf27759b --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/IC2.java @@ -0,0 +1,31 @@ +package zone.moddev.mc.modernmetals.integration.plugins; + +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.init.Materials; +import com.mcmoddev.lib.integration.IIntegration; +import com.mcmoddev.lib.integration.MMDPlugin; +import com.mcmoddev.lib.integration.plugins.IC2Base; +import com.mcmoddev.lib.material.MMDMaterial; +import com.mcmoddev.lib.util.ConfigBase.Options; +import zone.moddev.mc.modernmetals.ModernMetals; + +@MMDPlugin(addonId = ModernMetals.MODID, pluginId = IC2.PLUGIN_MODID) +public class IC2 extends IC2Base implements IIntegration { + private static boolean registered; + @Override public void init() { } + /** Recipe managers are available after IC2's pre-initialization. */ + public static void registerRecipes() { + if (registered || !Options.isModEnabled(PLUGIN_MODID)) return; + final IC2 integration = new IC2(); + for (final MMDMaterial material : Materials.getMaterialsByMod(ModernMetals.MODID)) { + if (!Materials.hasMaterial(material.getName()) || !material.hasItem(Names.CRUSHED)) continue; + integration.registerVanillaRecipes(material.getName()); + integration.addMaceratorRecipes(material.getName()); + integration.addOreWashingPlantRecipes(material.getName()); + integration.addThermalCentrifugeRecipes(material.getName()); + integration.addMetalFormerRecipes(material.getName()); + integration.addCompressorRecipes(material.getName()); + } + registered = true; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/Mekanism.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/Mekanism.java new file mode 100644 index 00000000..dc8a5795 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/Mekanism.java @@ -0,0 +1,68 @@ +package zone.moddev.mc.modernmetals.integration.plugins; + +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.init.Materials; +import com.mcmoddev.lib.integration.IIntegration; +import com.mcmoddev.lib.integration.MMDPlugin; +import com.mcmoddev.lib.integration.plugins.MekanismBase; +import com.mcmoddev.lib.material.MMDMaterial; +import com.mcmoddev.lib.util.ConfigBase.Options; +import zone.moddev.mc.modernmetals.ModernMetals; + +@MMDPlugin(addonId = ModernMetals.MODID, pluginId = Mekanism.PLUGIN_MODID) +public class Mekanism extends MekanismBase implements IIntegration { + private static boolean registered; + /** Discovery happens before the optional mod's registries are ready. */ + @Override public void init() { } + /** Called after Mekanism's initialization and before its IMC processing. */ + public static void registerRecipes() { + if (registered || !Options.isModEnabled(PLUGIN_MODID)) return; + for (final MMDMaterial material : Materials.getMaterialsByMod(ModernMetals.MODID)) { + if (!Materials.hasMaterial(material.getName()) || !material.hasOre() || !material.hasBlock(Names.ORE)) continue; + registerMaterial(material); + } + registered = true; + } + + private static void registerMaterial(final MMDMaterial material) { + final String name = material.getName(); + final mekanism.api.gas.Gas raw = gas(name, material.getCapitalizedName()); + final mekanism.api.gas.Gas clean = gas("clean" + name, "Clean" + material.getCapitalizedName()); + final net.minecraft.item.ItemStack ore = material.getBlockItemStack(Names.ORE); + if (material.hasItem(Names.POWDER)) { + mekanism.common.recipe.RecipeHandler.addEnrichmentChamberRecipe(ore, material.getItemStack(Names.POWDER, 2)); + mekanism.common.recipe.RecipeHandler.addCrusherRecipe(material.getItemStack(Names.INGOT), material.getItemStack(Names.POWDER)); + } + if (material.hasItem(Names.CLUMP)) + mekanism.common.recipe.RecipeHandler.addPurificationChamberRecipe(ore, material.getItemStack(Names.CLUMP, 3)); + if (material.hasItem(Names.POWDER_DIRTY) && material.hasItem(Names.CLUMP)) { + mekanism.common.recipe.RecipeHandler.addCrusherRecipe(material.getItemStack(Names.CLUMP), material.getItemStack(Names.POWDER_DIRTY)); + mekanism.common.recipe.RecipeHandler.addEnrichmentChamberRecipe(material.getItemStack(Names.POWDER_DIRTY), material.getItemStack(Names.POWDER)); + } + if (material.hasItem(Names.SHARD)) { + final mekanism.api.gas.Gas chloride = mekanism.api.gas.GasRegistry.getGas("hydrogenChloride"); + if (chloride == null) throw new IllegalStateException("Mekanism hydrogen chloride is unavailable after initialization"); + mekanism.common.recipe.RecipeHandler.addChemicalInjectionChamberRecipe(ore, chloride, material.getItemStack(Names.SHARD, 4)); + if (material.hasItem(Names.CLUMP)) + mekanism.common.recipe.RecipeHandler.addPurificationChamberRecipe(material.getItemStack(Names.SHARD), material.getItemStack(Names.CLUMP)); + } + if (material.hasItem(Names.CRYSTAL)) { + mekanism.common.recipe.RecipeHandler.addChemicalDissolutionChamberRecipe(ore, new mekanism.api.gas.GasStack(raw, 1000)); + mekanism.common.recipe.RecipeHandler.addChemicalWasherRecipe(new mekanism.api.gas.GasStack(raw, 1), new mekanism.api.gas.GasStack(clean, 1)); + mekanism.common.recipe.RecipeHandler.addChemicalCrystallizerRecipe(new mekanism.api.gas.GasStack(clean, 200), material.getItemStack(Names.CRYSTAL)); + if (material.hasItem(Names.SHARD)) + mekanism.common.recipe.RecipeHandler.addChemicalInjectionChamberRecipe(material.getItemStack(Names.CRYSTAL), + mekanism.api.gas.GasRegistry.getGas("hydrogenChloride"), material.getItemStack(Names.SHARD)); + } + } + + private static mekanism.api.gas.Gas gas(final String name, final String display) { + mekanism.api.gas.Gas gas = mekanism.api.gas.GasRegistry.getGas(name); + if (gas == null) { + gas = new mekanism.api.gas.Gas(name, name + "-Icon"); + gas.setUnlocalizedName("gas" + display); + mekanism.api.gas.GasRegistry.register(gas); + } + return gas; + } +} diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/Thaumcraft.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/Thaumcraft.java similarity index 64% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/Thaumcraft.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/Thaumcraft.java index b0b1f900..8cf998bf 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/Thaumcraft.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/Thaumcraft.java @@ -1,8 +1,7 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; -import com.mcmoddev.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.ModernMetals; import com.mcmoddev.lib.integration.MMDPlugin; -import com.mcmoddev.lib.util.ConfigBase.Options; import com.mcmoddev.lib.integration.IIntegration; @MMDPlugin(addonId = ModernMetals.MODID, pluginId = Thaumcraft.PLUGIN_MODID) @@ -12,7 +11,7 @@ public class Thaumcraft extends com.mcmoddev.lib.integration.plugins.ThaumcraftB @Override public void init() { - if (initDone || !Options.isModEnabled(Thaumcraft.PLUGIN_MODID)) { + if (initDone || !zone.moddev.mc.basemetals.util.Config.Options.isModEnabled("thaumcraft")) { return; } diff --git a/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/ThermalExpansion.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/ThermalExpansion.java new file mode 100644 index 00000000..cfb43ede --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/ThermalExpansion.java @@ -0,0 +1,76 @@ +package zone.moddev.mc.modernmetals.integration.plugins; + +import java.util.HashMap; +import java.util.Map; + +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.init.Materials; +import zone.moddev.mc.modernmetals.util.Config.Options; +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.integration.IIntegration; +import com.mcmoddev.lib.integration.MMDPlugin; +import cofh.api.util.ThermalExpansionHelper; +import net.minecraft.item.ItemStack; + +@MMDPlugin( addonId = ModernMetals.MODID, pluginId = ThermalExpansion.PLUGIN_MODID ) +public class ThermalExpansion extends com.mcmoddev.lib.integration.plugins.ThermalExpansionBase implements IIntegration { + + public ThermalExpansion() { + + } + + public void init() { + if (!zone.moddev.mc.basemetals.util.Config.Options.isModEnabled(PLUGIN_MODID)) return; + Map materials = new HashMap<>(); + materials.put("aluminum", Options.enableAluminum); + materials.put("iridium", Options.enableIridium); + materials.put("beryllium", Options.enableBeryllium); + materials.put("boron", Options.enableBoron); + materials.put("thorium", Options.enableThorium); + + materials.put("aluminumbrass", Options.enableAluminumBrass); + materials.put("cadmium", Options.enableCadmium); + materials.put("chromium", Options.enableChromium); + materials.put("galvanizedsteel", Options.enableGalvanizedSteel); + materials.put("magnesium", Options.enableMagnesium); + materials.put("manganese", Options.enableManganese); + materials.put("nichrome", Options.enableNichrome); + materials.put("osmium", Options.enableOsmium); + materials.put("plutonium", Options.enablePlutonium); + materials.put("rutile", Options.enableRutile); + materials.put("stainlesssteel", Options.enableStainlessSteel); + materials.put("tantalum", Options.enableTantalum); + materials.put("titanium", Options.enableTitanium); + materials.put("tungsten", Options.enableTungsten); + materials.put("uranium", Options.enableUranium); + materials.put("zirconium", Options.enableZirconium); + + for( Map.Entry e : materials.entrySet()) { + if (!e.getValue() || !Materials.hasMaterial(e.getKey())) continue; + addFurnace(e.getKey()); + addCrucible(e.getKey()); + addPlatePress(e.getKey()); + addPressStorage(e.getKey()); + } + + if( Options.enablePlutonium ) { + ThermalExpansionHelper.addMagmaticFuel(Materials.getMaterialByName("plutonium").getFluid().getName(), 1000000); + } + + if( Options.enableUranium ) { + ThermalExpansionHelper.addMagmaticFuel(Materials.getMaterialByName("uranium").getFluid().getName(), 750000); + } + + if( Options.enableChromium && Options.enableStainlessSteel && Materials.hasMaterial("steel")) { + ThermalExpansionHelper.addSmelterRecipe(4000, new ItemStack( Materials.getMaterialByName("steel").getItem(Names.INGOT), 1), new ItemStack(Materials.getMaterialByName("chromium").getItem(Names.INGOT), 1), new ItemStack(Materials.getMaterialByName("stainlesssteel").getItem(Names.INGOT), 2)); + } + + if( Options.enableGalvanizedSteel && Materials.hasMaterial("steel") && Materials.hasMaterial("zinc")) { + ThermalExpansionHelper.addSmelterRecipe(4000, new ItemStack( Materials.getMaterialByName("steel").getItem(Names.INGOT), 1), new ItemStack(Materials.getMaterialByName("zinc").getItem(Names.INGOT), 1), new ItemStack(Materials.getMaterialByName("galvanizedsteel").getItem(Names.INGOT), 2)); + } + + if( Options.enableRutile && Options.enableMagnesium && Options.enableTitanium ) { + ThermalExpansionHelper.addSmelterRecipe(4000, new ItemStack( Materials.getMaterialByName("rutile").getItem(Names.INGOT), 1), new ItemStack( Materials.getMaterialByName("magnesium").getItem(Names.INGOT), 1), new ItemStack( Materials.getMaterialByName("titanium").getItem(Names.INGOT), 2)); + } + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/TinkersConstruct.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/TinkersConstruct.java new file mode 100644 index 00000000..4c1ce1de --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/TinkersConstruct.java @@ -0,0 +1,151 @@ +package zone.moddev.mc.modernmetals.integration.plugins; + +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.init.Materials; +import zone.moddev.mc.modernmetals.util.Config.Options; +import com.mcmoddev.lib.integration.MMDPlugin; +import com.mcmoddev.lib.integration.plugins.tinkers.TraitLocations; + +import com.mcmoddev.lib.integration.IIntegration; + +/** + * + * @author Jasmine Iwanek + * + */ +@MMDPlugin(addonId = ModernMetals.MODID, pluginId = TinkersConstruct.PLUGIN_MODID) +public class TinkersConstruct extends com.mcmoddev.lib.integration.plugins.TinkersConstructBase implements IIntegration { + + private static boolean initDone = false; + + @Override + public void init() { + if (initDone || !zone.moddev.mc.basemetals.util.Config.Options.isModEnabled(PLUGIN_MODID)) { + return; + } + if (zone.moddev.mc.modernmetals.content.ContentPolicy.active().lowFantasy()) + net.minecraftforge.common.MinecraftForge.EVENT_BUS.register(this); + + if (Options.enableAluminum) { + registry.getMaterial("aluminum", Materials.getMaterialByName("aluminum")).addTrait("lightweight").setCastable(true).settle(); + } + + if (Options.enableAluminumBrass) { + registry.getMaterial("aluminumbrass", Materials.getMaterialByName("aluminumbrass")).setCastable(true).settle(); + } + + if (Options.enableCadmium) { + registry.getMaterial("cadmium", Materials.getMaterialByName("cadmium")).setCastable(true).settle(); + } + + if (Options.enableChromium) { + registry.getMaterial("chromium", Materials.getMaterialByName("chromium")).setCastable(true).addTrait("magnetic").addTrait("magnetic2",TraitLocations.HEAD).settle(); + } + + if (Options.enableGalvanizedSteel) { + registry.getMaterial("galvanizedsteel", Materials.getMaterialByName("galvanizedsteel")).addTrait("sharp", TraitLocations.HEAD).addTrait("stiff").setCastable(true).settle(); + registry.registerAlloy("galvanizedsteel",Materials.getMaterialByName("galvanizedsteel").getFluid(), 2, "steel", 1, "zinc", 1); + } + + if (Options.enableIridium) { + registry.getMaterial("iridium", Materials.getMaterialByName("iridium")).setCastable(true).addTrait("heavy").addTrait("brittle").settle(); + } + + if (Options.enableMagnesium) { + registry.getMaterial("magnesium", Materials.getMaterialByName("magnesium")).setCastable(true).addTrait("reactive").settle(); + } + + if (Options.enableManganese) { + registry.getMaterial("manganese", Materials.getMaterialByName("manganese")).addTrait("brittle").setCastable(true).settle(); + } + + if (Options.enableNichrome) { + registry.getMaterial("nichrome", Materials.getMaterialByName("nichrome")).setCastable(true).settle(); + registry.registerAlloy("nichrome", Materials.getMaterialByName("nichrome").getFluid(), 2, "nickel", 1, "chromium", 1); + } + + if (Options.enableOsmium) { + registry.getMaterial("osmium", Materials.getMaterialByName("osmium")).setCastable(true).addTrait("heavy").addTrait("brittle").settle(); + } + + if (Options.enablePlutonium) { + registry.getMaterial("plutonium", Materials.getMaterialByName("plutonium")).addTrait("radioactive").addTrait("toxic", TraitLocations.HEAD).addTrait("toxic", TraitLocations.PROJECTILE).setCastable(true).settle(); + } + + // request is for custom modifier item to grant +50% durability + // This is not yet in the API - might require extra classes in MMDLib + if (Options.enableRutile) { + registry.getMaterial("rutile", Materials.getMaterialByName("rutile")).setCastable(true).settle(); + } + + if (Options.enableStainlessSteel) { + registry.getMaterial("stainlesssteel", Materials.getMaterialByName("stainlesssteel")).setCastable(true).addTrait("sharp", TraitLocations.HEAD).addTrait("stiff").settle(); + registry.registerAlloy("stainlesssteel", Materials.getMaterialByName("stainlesssteel").getFluid(), 2, "steel", 1, "chromium", 1); + } + + if (Options.enableTantalum) { + registry.getMaterial("tantalum", Materials.getMaterialByName("tantalum")).setCastable(true).settle(); + } + + if (Options.enableTitanium) { + registry.getMaterial("titanium", Materials.getMaterialByName("titanium")).setCastable(true).settle(); + registry.registerAlloy("titanium", Materials.getMaterialByName("titanium").getFluid(), 2, "rutile", 1, "magnesium", 1 ); + } + + if (Options.enableTungsten) { + registry.getMaterial("tungsten", Materials.getMaterialByName("tungsten")).setCastable(true).settle(); + } + + if (Options.enableUranium) { + registry.getMaterial("uranium", Materials.getMaterialByName("uranium")).setCastable(true).addTrait("poisonous", TraitLocations.HEAD).addTrait("poisonous", TraitLocations.PROJECTILE).addTrait("radioactive").settle(); + } + + if (Options.enableZirconium) { + registry.getMaterial("zirconium", Materials.getMaterialByName("zirconium")).setCastable(true).settle(); + } + + if (Options.enableBeryllium) { registry.getMaterial("beryllium", Materials.getMaterialByName("beryllium")).setCastable(true).settle(); } + + if (Options.enableBoron) { registry.getMaterial("boron", Materials.getMaterialByName("boron")).setCastable(true).settle(); } + + if (Options.enableThorium) { registry.getMaterial("thorium", Materials.getMaterialByName("thorium")).setCastable(true).settle(); } + + registry.registerAll(); + initDone = true; + } + + /** Keep molten processing while preventing restricted tool-part and ranged bypasses. */ + public static void applyContentPolicy() { + if(!com.mcmoddev.lib.util.ConfigBase.Options.isModEnabled(PLUGIN_MODID)) return; + final zone.moddev.mc.modernmetals.content.ContentPolicy policy=zone.moddev.mc.modernmetals.content.ContentPolicy.active(); + if(!policy.lowFantasy()) return; + for(final com.mcmoddev.lib.material.MMDMaterial material:Materials.getMaterialsByMod(ModernMetals.MODID)) { + final slimeknights.tconstruct.library.materials.Material tc=slimeknights.tconstruct.library.TinkerRegistry.getMaterial(material.getName()); + if(tc==slimeknights.tconstruct.library.materials.Material.UNKNOWN) continue; + tc.getAllStats().removeIf(stat->!policy.allows(material.getName(),"pickaxe") + || "bow".equals(stat.getIdentifier()) || "bowstring".equals(stat.getIdentifier())); + } + } + + @net.minecraftforge.fml.common.eventhandler.SubscribeEvent + public void tableCasting(final slimeknights.tconstruct.library.events.TinkerRegisterEvent.TableCastingRegisterEvent event) { + restrictCasting(event); + } + @net.minecraftforge.fml.common.eventhandler.SubscribeEvent + public void basinCasting(final slimeknights.tconstruct.library.events.TinkerRegisterEvent.BasinCastingRegisterEvent event) { + restrictCasting(event); + } + /** Reject fixed restricted outputs before Tinkers freezes its casting recipes. */ + private static void restrictCasting(final slimeknights.tconstruct.library.events.TinkerRegisterEvent event) { + if(!(event.getRecipe() instanceof slimeknights.tconstruct.library.smeltery.CastingRecipe)) return; + final net.minecraft.item.ItemStack output=((slimeknights.tconstruct.library.smeltery.CastingRecipe)event.getRecipe()).getResult(); + final zone.moddev.mc.modernmetals.content.ContentPolicy policy=zone.moddev.mc.modernmetals.content.ContentPolicy.active(); + if(!policy.allows(output)) {event.setCanceled(true);return;} + if(output==null || !(output.getItem() instanceof slimeknights.tconstruct.library.tools.IToolPart)) return; + final slimeknights.tconstruct.library.tools.IToolPart part=(slimeknights.tconstruct.library.tools.IToolPart)output.getItem(); + final String name=part.getMaterialID(output); + final boolean ours=Materials.getMaterialsByMod(ModernMetals.MODID).stream().anyMatch(material->material.getName().equals(name)); + if(ours && (!policy.allows(name,"pickaxe") || part.hasUseForStat("bow") || part.hasUseForStat("bowstring"))) + event.setCanceled(true); + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/VeinMiner.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/VeinMiner.java new file mode 100644 index 00000000..d35fee94 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/VeinMiner.java @@ -0,0 +1,105 @@ +package zone.moddev.mc.modernmetals.integration.plugins; + +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.util.Config.Options; +import com.mcmoddev.lib.integration.MMDPlugin; +import com.mcmoddev.lib.integration.IIntegration; + +/** + * VeinMiner Integration Plugin + * + * @author Jasmine Iwanek + * + */ +@MMDPlugin(addonId = ModernMetals.MODID, pluginId = VeinMiner.PLUGIN_MODID) +public class VeinMiner extends com.mcmoddev.lib.integration.plugins.VeinMinerBase implements IIntegration { + + private static boolean initDone = false; + + @Override + public void init() { + if (initDone || !zone.moddev.mc.basemetals.util.Config.Options.isModEnabled("veinminer")) { + return; + } + + if (Options.enableAluminum) { + addToolsForMaterial("aluminum"); + } + + if (Options.enableAluminumBrass) { + addToolsForMaterial("aluminumbrass"); + } + + if (Options.enableCadmium) { + addToolsForMaterial("cadmium"); + } + + if (Options.enableChromium) { + addToolsForMaterial("chromium"); + } + + if (Options.enableGalvanizedSteel) { + addToolsForMaterial("galvanizedsteel"); + } + + if (Options.enableIridium) { + addToolsForMaterial("iridium"); + } + + if (Options.enableMagnesium) { + addToolsForMaterial("magnesium"); + } + + if (Options.enableManganese) { + addToolsForMaterial("manganese"); + } + + if (Options.enableNichrome) { + addToolsForMaterial("nichrome"); + } + + if (Options.enableOsmium) { + addToolsForMaterial("osmium"); + } + + if (Options.enablePlutonium) { + addToolsForMaterial("plutonium"); + } + + if (Options.enableRutile) { + addToolsForMaterial("rutile"); + } + + if (Options.enableStainlessSteel) { + addToolsForMaterial("stainlesssteel"); + } + + if (Options.enableTantalum) { + addToolsForMaterial("tantalum"); + } + + if (Options.enableTitanium) { + addToolsForMaterial("titanium"); + } + + if (Options.enableTungsten) { + addToolsForMaterial("tungsten"); + } + + if (Options.enableUranium) { + addToolsForMaterial("uranium"); + } + + if (Options.enableZirconium) { + addToolsForMaterial("zirconium"); + } + + if (Options.enableBeryllium) { addToolsForMaterial("beryllium"); } + + if (Options.enableBoron) { addToolsForMaterial("boron"); } + + if (Options.enableThorium) { addToolsForMaterial("thorium"); } + + initDone = true; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/proxy/ClientProxy.java b/src/main/java/zone/moddev/mc/modernmetals/proxy/ClientProxy.java new file mode 100644 index 00000000..571b3ff9 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/proxy/ClientProxy.java @@ -0,0 +1,86 @@ +package zone.moddev.mc.modernmetals.proxy; + +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.client.ModernMetalsConfigScreen; +import zone.moddev.mc.orespawn.api.client.WorldSettingsExtensionRegistry; +import zone.moddev.mc.modernmetals.init.Blocks; +import zone.moddev.mc.modernmetals.init.Fluids; +import zone.moddev.mc.modernmetals.init.Items; + +import net.minecraft.block.*; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ItemModelMesher; +import net.minecraft.client.renderer.block.model.ModelBakery; +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.minecraft.client.renderer.block.statemap.StateMapperBase; +import net.minecraft.item.Item; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +/** + * Modern Metals Client Proxy + * + * @author Jasmine Iwanek + * + */ +public class ClientProxy extends CommonProxy { + + @Override + public void preInit(FMLPreInitializationEvent event) { + super.preInit(event); + for (final String name : Fluids.getFluidBlockRegistry().keySet()) { + final Block block = Fluids.getFluidBlockByName(name); + final Item item = Item.getItemFromBlock(block); + if (!item.getRegistryName().getResourceDomain().equals(ModernMetals.MODID)) + continue; + final ModelResourceLocation fluidModelLocation = new ModelResourceLocation(item.getRegistryName().getResourceDomain() + ":" + name, "fluid"); + ModelBakery.registerItemVariants(item); + ModelLoader.setCustomMeshDefinition(item, stack -> fluidModelLocation); + ModelLoader.setCustomStateMapper(block, new StateMapperBase() { + @Override + protected ModelResourceLocation getModelResourceLocation(IBlockState state) { + return fluidModelLocation; + } + }); + } + } + + @Override + public void init(FMLInitializationEvent event) { + super.init(event); + WorldSettingsExtensionRegistry.registerConfigScreen(ModernMetals.MODID, ModernMetalsConfigScreen::new); + + for (final String name : Items.getItemRegistry().keySet()) { + registerRenderOuter(Items.getItemByName(name)); + } + + for (final String name : Blocks.getBlockRegistry().keySet()) { + registerRenderOuter(Blocks.getBlockByName(name)); + } + } + + public void registerRenderOuter(Item item) { + if (item != null) { + registerRender(item, Items.getNameOfItem(item)); + } + } + + public void registerRenderOuter(Block block) { + if ((block instanceof BlockDoor) || (block instanceof BlockSlab)) + return; // do not add door blocks or slabs + + if (block != null) { + registerRender(Item.getItemFromBlock(block), Blocks.getNameOfBlock(block)); + } + } + + public void registerRender(Item item, String name) { + final ItemModelMesher itemModelMesher = Minecraft.getMinecraft().getRenderItem().getItemModelMesher(); + if (!item.getRegistryName().getResourceDomain().equals(ModernMetals.MODID)) + return; + itemModelMesher.register(item, 0, new ModelResourceLocation(new ResourceLocation(item.getRegistryName().getResourceDomain(), name), "inventory")); + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/proxy/CommonProxy.java b/src/main/java/zone/moddev/mc/modernmetals/proxy/CommonProxy.java new file mode 100644 index 00000000..542f4f26 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/proxy/CommonProxy.java @@ -0,0 +1,63 @@ +package zone.moddev.mc.modernmetals.proxy; + +import zone.moddev.mc.modernmetals.content.ContentModeNetwork; +import zone.moddev.mc.modernmetals.compat.LegacyRegistryMappings; +import zone.moddev.mc.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.init.*; +import zone.moddev.mc.modernmetals.util.Config; +import zone.moddev.mc.modernmetals.util.EventHandler; +import com.mcmoddev.lib.integration.IntegrationManager; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLInterModComms; +import net.minecraftforge.fml.common.event.FMLMissingMappingsEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +/** + * Modern Metals Common Proxy + * + * @author Jasmine Iwanek + * + */ +public class CommonProxy { + + public void preInit(FMLPreInitializationEvent event) { + + Config.init(); + ContentModeNetwork.register(); + + Materials.init(); + Fluids.init(); + ItemGroups.init(); + Blocks.init(); + Items.init(); + VillagerTrades.init(); + + IntegrationManager.INSTANCE.preInit(event); + } + + public void onRemap(FMLMissingMappingsEvent event) { + LegacyRegistryMappings.remap(event); + } + + public void init(FMLInitializationEvent event) { + zone.moddev.mc.modernmetals.worldgen.ModernMetalsWorldgenProvider.enqueue(); + if (net.minecraftforge.fml.common.Loader.isModLoaded("mekanism")) + zone.moddev.mc.modernmetals.integration.plugins.Mekanism.registerRecipes(); + if (net.minecraftforge.fml.common.Loader.isModLoaded("ic2")) + zone.moddev.mc.modernmetals.integration.plugins.IC2.registerRecipes(); + Recipes.init(); + + Achievements.init(); + + MinecraftForge.EVENT_BUS.register(new EventHandler()); + + ItemGroups.setupIcons(MaterialNames.ALUMINUM); + } + + public void postInit(FMLPostInitializationEvent event) { + Config.postInit(); + } +} diff --git a/src/main/java/com/mcmoddev/modernmetals/proxy/ServerProxy.java b/src/main/java/zone/moddev/mc/modernmetals/proxy/ServerProxy.java similarity index 62% rename from src/main/java/com/mcmoddev/modernmetals/proxy/ServerProxy.java rename to src/main/java/zone/moddev/mc/modernmetals/proxy/ServerProxy.java index 1f889d39..b3e68984 100644 --- a/src/main/java/com/mcmoddev/modernmetals/proxy/ServerProxy.java +++ b/src/main/java/zone/moddev/mc/modernmetals/proxy/ServerProxy.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.proxy; +package zone.moddev.mc.modernmetals.proxy; /** * Modern Metals Server Proxy @@ -7,5 +7,4 @@ * */ public class ServerProxy extends CommonProxy { - // Nothing to see here people } diff --git a/src/main/java/zone/moddev/mc/modernmetals/util/Config.java b/src/main/java/zone/moddev/mc/modernmetals/util/Config.java new file mode 100644 index 00000000..ae3f4b3a --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/util/Config.java @@ -0,0 +1,145 @@ +package zone.moddev.mc.modernmetals.util; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.file.*; +import java.util.*; + +import zone.moddev.mc.modernmetals.ModernMetals; +import com.mcmoddev.lib.registry.CrusherRecipeRegistry; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.fml.client.event.ConfigChangedEvent; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.MissingModsException; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.versioning.ArtifactVersion; +import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; + +/** + * @author p455w0rd + * + */ +public class Config { + + private static Configuration configuration; + private static final String CONFIG_FILE = "config/ModernMetals.cfg"; + private static final String MATERIALS_CAT = "Metals"; + + @SubscribeEvent + public void onConfigChange(ConfigChangedEvent.OnConfigChangedEvent e) { + if (e.getModID().equals(ModernMetals.MODID)) { + configuration.save(); // Registry switches take effect on the next game start. + } + } + + public static void init() { + if (configuration == null) { + configuration = new Configuration(new File(CONFIG_FILE)); + MinecraftForge.EVENT_BUS.register(new Config()); + } + zone.moddev.mc.modernmetals.content.ContentPolicy.initialize(contentMode(configuration)); + Options.enableVillagerTrades = villagerTrades(configuration); + + // METALS + Options.enableThorium = material("thorium", "EnableThorium", "Thorium"); + Options.enableBoron = material("boron", "EnableBoron", "Boron"); + Options.enableBeryllium = material("beryllium", "EnableBeryllium", "Beryllium"); + Options.enableAluminum = material("aluminum", "EnableAluminum", "Aluminum"); + Options.enableAluminumBrass = material("aluminumbrass", "EnableAluminumBrass", "Aluminum Brass"); + Options.enableCadmium = material("cadmium", "EnableCadmium", "Cadmium"); + Options.enableChromium = material("chromium", "EnableChromium", "Chromium"); + Options.enableGalvanizedSteel = material("galvanizedsteel", "EnableGalvanizedSteel", "Galvanized Steel"); + Options.enableIridium = material("iridium", "EnableIridium", "Iridium"); + Options.enableMagnesium = material("magnesium", "EnableMagnesium", "Magnesium"); + Options.enableManganese = material("manganese", "EnableManganese", "Manganese"); + Options.enableNichrome = material("nichrome", "EnableNichrome", "Nichrome"); + Options.enableOsmium = material("osmium", "EnableOsmium", "Osmium"); + Options.enablePlutonium = material("plutonium", "EnablePlutonium", "Plutonium"); + Options.enableRutile = material("rutile", "EnableRutile", "Rutile"); + Options.enableStainlessSteel = material("stainlesssteel", "EnableStainlessSteel", "Stainless Steel"); + Options.enableTantalum = material("tantalum", "EnableTantalum", "Tantalum"); + Options.enableTitanium = material("titanium", "EnableTitanium", "Titanium"); + Options.enableTungsten = material("tungsten", "EnableTungsten", "Tungsten"); + Options.enableUranium = material("uranium", "EnableUranium", "Uranium"); + Options.enableZirconium = material("zirconium", "EnableZirconium", "Zirconium"); + + if (configuration.hasChanged()) { + configuration.save(); + } + } + + /** Configuration values are persisted immediately; live registries are never rebuilt. */ + public static Configuration getConfiguration() { + if (configuration == null) init(); + return configuration; + } + + /** Define the restart-required selector and normalize unsupported file values. */ + static String contentMode(final Configuration config) { + final net.minecraftforge.common.config.Property property = config.get("General", "contentMode", + "high_fantasy", "Fantasy level for Modern Metals recipes and equipment. Requires a game restart."); + property.setValidValues(new String[]{"high_fantasy", "low_fantasy"}); + property.setLanguageKey("config.modernmetals.content_mode"); + property.setRequiresMcRestart(true); + final String mode = zone.moddev.mc.modernmetals.content.ContentPolicy.forMode(property.getString()) + .lowFantasy() ? "low_fantasy" : "high_fantasy"; + property.set(mode); + return mode; + } + + static boolean villagerTrades(final Configuration config) { + final net.minecraftforge.common.config.Property property = config.get("General", "enableVillagerTrades", + true, "Let blacksmiths trade Modern Metals ingots and equipment. Requires a game restart."); + property.setLanguageKey("config.modernmetals.villager_trades"); + property.setRequiresMcRestart(true); + return property.getBoolean(); + } + + private static boolean material(final String name, final String key, final String display) { + final net.minecraftforge.common.config.Property property = configuration.get(MATERIALS_CAT, + key, true, "Enable " + display + " Items and Materials. Requires a game restart."); + property.setRequiresMcRestart(true); + property.setLanguageKey("config.modernmetals.enable_" + name); + final boolean enabled = property.getBoolean(); + com.mcmoddev.lib.util.ConfigBase.Options.materialEnabled(name, enabled); + com.mcmoddev.lib.util.ConfigBase.Options.fluidEnabled(name, enabled); + return enabled; + } + + public static class Options { + public static boolean enableVillagerTrades = true; + + // MATERIALS + public static boolean enableThorium = true; + public static boolean enableBoron = true; + public static boolean enableBeryllium = true; + public static boolean enableAluminum = true; + public static boolean enableAluminumBrass = true; + public static boolean enableCadmium= true; + public static boolean enableChromium = true; + public static boolean enableGalvanizedSteel = true; + public static boolean enableIridium = true; + public static boolean enableMagnesium = true; + public static boolean enableManganese = true; + public static boolean enableNichrome= true; + public static boolean enableOsmium = true; + public static boolean enablePlutonium = true; + public static boolean enableRutile = true; + public static boolean enableStainlessSteel = true; + public static boolean enableTantalum = true; + public static boolean enableTitanium = true; + public static boolean enableTungsten = true; + public static boolean enableUranium = true; + public static boolean enableZirconium = true; + + private Options() { + throw new IllegalAccessError("Not a instantiable class"); + } + } + + public static void postInit() { + CrusherRecipeRegistry.getInstance().clearCache(); + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java b/src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java new file mode 100644 index 00000000..cd784143 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java @@ -0,0 +1,54 @@ +package zone.moddev.mc.modernmetals.util; + +import zone.moddev.mc.modernmetals.init.Achievements; +import zone.moddev.mc.modernmetals.init.Materials; + +import com.mcmoddev.lib.material.IMMDObject; +import com.mcmoddev.lib.material.MMDMaterial; +import com.mcmoddev.lib.item.ItemMMDIngot; + +import net.minecraft.item.Item; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemSmeltedEvent; + +public class EventHandler { + +/* + @SubscribeEvent + void event(ItemCraftedEvent event) { + final Item item = event.crafting.getItem(); + if (item instanceof IMMDObject) { + final MMDMaterial material = ((IMMDObject) item).getMaterial(); + if (zone.moddev.mc.basemetals.util.Config.Options.enableAchievements) { + if (item instanceof ItemMetalBlend) { +// event.player.addStat(Achievements.metallurgy, 1); + } + } + } + } +*/ + + @SubscribeEvent + void event(ItemSmeltedEvent event) { + final Item item = event.smelting.getItem(); + if (item instanceof IMMDObject) { + final MMDMaterial material = ((IMMDObject) item).getMMDMaterial(); + if (zone.moddev.mc.basemetals.util.Config.Options.enableAchievements()) { + if (item instanceof ItemMMDIngot) { + // event.player.addStat(Achievements.this_is_new, 1); + if (material == Materials.aluminumBrass) { + event.player.addStat(Achievements.aluminumBrassMaker, 1); + } else if (material == Materials.galvanizedSteel) { + event.player.addStat(Achievements.galvanizedSteelMaker, 1); + } else if (material == Materials.nichrome) { + event.player.addStat(Achievements.nichromeMaker, 1); + } else if (material == Materials.stainlessSteel) { + event.player.addStat(Achievements.stainlessSteelMaker, 1); + } else if (material == Materials.titanium) { + event.player.addStat(Achievements.titaniumMaker, 1); + } + } + } + } + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/worldgen/ModernMetalsWorldgenProvider.java b/src/main/java/zone/moddev/mc/modernmetals/worldgen/ModernMetalsWorldgenProvider.java new file mode 100644 index 00000000..90260f0a --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/worldgen/ModernMetalsWorldgenProvider.java @@ -0,0 +1,85 @@ +package zone.moddev.mc.modernmetals.worldgen; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.orespawn.api.GeologyFamily; +import zone.moddev.mc.orespawn.api.OreDimensionSelector; +import zone.moddev.mc.orespawn.api.OrePattern; +import zone.moddev.mc.orespawn.api.OreSpawnApi; +import zone.moddev.mc.orespawn.api.WorldgenProvider; + +/** Native declarations only; OreSpawn resolves, bakes and executes generation. */ +public final class ModernMetalsWorldgenProvider { + private static final Ore[] ORES = { + new Ore("boron", 0, 31, 8D, 8, 8, true), + new Ore("thorium", 0, 31, 8D, 8, 4, true), + new Ore("beryllium", 0, 127, 20D, 5, 3, true), + new Ore("aluminum", 0, 95, 8D, 10, 8, true), + new Ore("cadmium", 0, 95, 8D, 4, 8, true), + new Ore("chromium", 0, 31, 3D, 2, 4, true), + new Ore("iridium", 0, 63, 5D, 6, 4, true), + new Ore("magnesium", 0, 95, 8D, 6, 4, true), + new Ore("manganese", 0, 63, 6D, 6, 4, true), + new Ore("osmium", 0, 95, 8D, 10, 4, true), + new Ore("plutonium", 0, 31, 4D, 2, 4, true), + new Ore("rutile", 0, 63, 6D, 6, 4, true), + new Ore("tantalum", 0, 63, 6D, 6, 4, true), + new Ore("tungsten", 0, 31, 6D, 4, 4, true), + new Ore("uranium", 0, 31, 6D, 2, 4, true), + new Ore("zirconium", 0, 63, 6D, 8, 4, true), + new Ore("titanium", 0, 63, 6D, 6, 4, false) + }; + private ModernMetalsWorldgenProvider() { } + public static void enqueue() { + build(ForgeRegistries.BLOCKS::containsKey).ifPresent(provider -> { + if (!OreSpawnApi.enqueue(provider)) throw new IllegalStateException("Modern Metals provider registered twice"); + }); + for (final Ore ore : ORES) { + final int minimum = ore.size - ore.variation; + if (minimum < 1 && ForgeRegistries.BLOCKS.containsKey(id("modernmetals", ore.name + "_ore"))) { + ModernMetals.logger.info("OreSpawn native quantity minimum for " + ore.name + + " is clamped from " + minimum + " to 1, matching the legacy importer"); + } + } + } + public static Optional build(final Predicate available) { + final List ores = new ArrayList<>(); + for (final Ore rule : ORES) { + final ResourceLocation block = id("modernmetals", rule.name + "_ore"); + if (!available.test(block)) continue; + final WorldgenProvider.OreDefinition.Builder ore = WorldgenProvider.OreDefinition + .builder(id("modernmetals", "legacy/" + rule.name + "_ore"), block) + .enabled(rule.enabled).nativeGeneration(false).retrogen(false) + .material(id("orespawn", rule.name)).output(block, 100D); + ore.dimensionSelector(OreDimensionSelector.ALL_EXCEPT_NETHER_AND_END, placement -> { + placement.yRange(rule.minY, rule.maxY).attempts(rule.frequency) + .quantityRange(Math.max(1, rule.size - rule.variation), Math.min(64, rule.size + rule.variation - 1)) + .pattern(OrePattern.DEFAULT).placementChannel(id("orespawn", "standard")) + .hostBlock(id("minecraft", "stone")).hostBlock(id("minecraft", "netherrack")) + .hostBlock(id("minecraft", "end_stone")); + for (final GeologyFamily family : GeologyFamily.values()) placement.hostFamily(family); + }); + ores.add(ore.build()); + } + if (ores.isEmpty()) return Optional.empty(); + final WorldgenProvider.Builder provider = WorldgenProvider.builder("modernmetals", 1); + ores.forEach(provider::ore); + return Optional.of(provider.build()); + } + private static ResourceLocation id(final String namespace, final String path) { return new ResourceLocation(namespace, path); } + private static final class Ore { + final String name; + final int minY, maxY, size, variation; + final double frequency; + final boolean enabled; + Ore(String name, int minY, int maxY, double frequency, int size, int variation, boolean enabled) { + this.name = name; this.minY = minY; this.maxY = maxY; this.frequency = frequency; + this.size = size; this.variation = variation; this.enabled = enabled; + } + } +} diff --git a/src/main/resources/META-INF/modernmetals-legacy-api.list b/src/main/resources/META-INF/modernmetals-legacy-api.list new file mode 100644 index 00000000..ad342c62 --- /dev/null +++ b/src/main/resources/META-INF/modernmetals-legacy-api.list @@ -0,0 +1,25 @@ +data/MaterialNames +init/Achievements +init/Blocks +init/Fluids +init/ItemGroups +init/Items +init/Materials +init/Recipes +init/VillagerTrades +integration/plugins/EnderIO +integration/plugins/IC2 +integration/plugins/Mekanism +integration/plugins/Thaumcraft +integration/plugins/ThermalExpansion +integration/plugins/TinkersConstruct +integration/plugins/VeinMiner +ModernMetals +proxy/ClientProxy +proxy/CommonProxy +proxy/ServerProxy +util/Config +util/EventHandler +data.AchievementNames +data.TraitNames +integration.plugins.DenseOres diff --git a/src/main/resources/assets/modernmetals/lang/de_DE.lang b/src/main/resources/assets/modernmetals/lang/de_DE.lang index 815157a6..7227e1b6 100644 --- a/src/main/resources/assets/modernmetals/lang/de_DE.lang +++ b/src/main/resources/assets/modernmetals/lang/de_DE.lang @@ -36,38 +36,38 @@ item.modernmetals.aluminum_smallpowder.name=Kleiner Aluminiumpulverhaufen item.modernmetals.aluminum_sword.name=Aluminiumschwert item.modernmetals.aluminum_trapdoor.name=Aluminiumfalltür -material.aluminumbrass.name=Duraluminium -item.modernmetals.aluminumbrass_arrow.name=Duraluminiumpfeil -item.modernmetals.aluminumbrass_axe.name=Duraluminiumaxt -item.modernmetals.aluminumbrass_bars.name=Duraluminiumgitter -item.modernmetals.aluminumbrass_blend.name=Duraluminiumgemisch -item.modernmetals.aluminumbrass_block.name=Duraluminiumblock -item.modernmetals.aluminumbrass_boots.name=Duraluminiumstiefel -item.modernmetals.aluminumbrass_bolt.name=Duraluminiumbolzen -item.modernmetals.aluminumbrass_bow.name=Duraluminiumbogen -item.modernmetals.aluminumbrass_chestplate.name=Duraluminiumbrustpanzer -item.modernmetals.aluminumbrass_crackhammer.name=Duraluminiumbruchhammer -item.modernmetals.aluminumbrass_crossbow.name=Duraluminiumarmbrust -item.modernmetals.aluminumbrass_door.name=Duraluminiumtür -item.modernmetals.aluminumbrass_fishing_rod.name=Duraluminiumangel -item.modernmetals.aluminumbrass_gear.name=Duraluminiumzahnrad -item.modernmetals.aluminumbrass_helmet.name=Duraluminiumhelm -item.modernmetals.aluminumbrass_hoe.name=Duraluminiumhacke -item.modernmetals.aluminumbrass_horse_armor.name=Duraluminiumpferderüstung -item.modernmetals.aluminumbrass_ingot.name=Duraluminiumbarren -item.modernmetals.aluminumbrass_leggings.name=Duraluminiumbeinrüstung -item.modernmetals.aluminumbrass_nugget.name=Duraluminiumnugget -item.modernmetals.aluminumbrass_ore.name=Duraluminiumerz -item.modernmetals.aluminumbrass_pickaxe.name=Duraluminiumspitzhacke -item.modernmetals.aluminumbrass_powder.name=Duraluminiumpulver -item.modernmetals.aluminumbrass_rod.name=Duraluminiumstange -item.modernmetals.aluminumbrass_shears.name=Duraluminiumschere -item.modernmetals.aluminumbrass_shield.name=Duraluminiumschild -item.modernmetals.aluminumbrass_shovel.name=Duraluminiumschaufel -item.modernmetals.aluminumbrass_smallblend.name=Kleiner Duraluminiumgemischhaufen -item.modernmetals.aluminumbrass_smallpowder.name=Kleiner Duraluminiumpulverhaufen -item.modernmetals.aluminumbrass_sword.name=Duraluminiumschwert -item.modernmetals.aluminumbrass_trapdoor.name=Duraluminiumfalltür +material.aluminumbrass.name=Aluminiummessing +item.modernmetals.aluminumbrass_arrow.name=Aluminiummessingpfeil +item.modernmetals.aluminumbrass_axe.name=Aluminiummessingaxt +item.modernmetals.aluminumbrass_bars.name=Aluminiummessinggitter +item.modernmetals.aluminumbrass_blend.name=Aluminiummessinggemisch +item.modernmetals.aluminumbrass_block.name=Aluminiummessingblock +item.modernmetals.aluminumbrass_boots.name=Aluminiummessingstiefel +item.modernmetals.aluminumbrass_bolt.name=Aluminiummessingbolzen +item.modernmetals.aluminumbrass_bow.name=Aluminiummessingbogen +item.modernmetals.aluminumbrass_chestplate.name=Aluminiummessingbrustpanzer +item.modernmetals.aluminumbrass_crackhammer.name=Aluminiummessingbruchhammer +item.modernmetals.aluminumbrass_crossbow.name=Aluminiummessingarmbrust +item.modernmetals.aluminumbrass_door.name=Aluminiummessingtür +item.modernmetals.aluminumbrass_fishing_rod.name=Aluminiummessingangel +item.modernmetals.aluminumbrass_gear.name=Aluminiummessingzahnrad +item.modernmetals.aluminumbrass_helmet.name=Aluminiummessinghelm +item.modernmetals.aluminumbrass_hoe.name=Aluminiummessinghacke +item.modernmetals.aluminumbrass_horse_armor.name=Aluminiummessingpferderüstung +item.modernmetals.aluminumbrass_ingot.name=Aluminiummessingbarren +item.modernmetals.aluminumbrass_leggings.name=Aluminiummessingbeinrüstung +item.modernmetals.aluminumbrass_nugget.name=Aluminiummessingnugget +item.modernmetals.aluminumbrass_ore.name=Aluminiummessingerz +item.modernmetals.aluminumbrass_pickaxe.name=Aluminiummessingspitzhacke +item.modernmetals.aluminumbrass_powder.name=Aluminiummessingpulver +item.modernmetals.aluminumbrass_rod.name=Aluminiummessingstange +item.modernmetals.aluminumbrass_shears.name=Aluminiummessingschere +item.modernmetals.aluminumbrass_shield.name=Aluminiummessingschild +item.modernmetals.aluminumbrass_shovel.name=Aluminiummessingschaufel +item.modernmetals.aluminumbrass_smallblend.name=Kleiner Aluminiummessinggemischhaufen +item.modernmetals.aluminumbrass_smallpowder.name=Kleiner Aluminiummessingpulverhaufen +item.modernmetals.aluminumbrass_sword.name=Aluminiummessingschwert +item.modernmetals.aluminumbrass_trapdoor.name=Aluminiummessingfalltür material.antimony.name=Antimon item.modernmetals.antimony_arrow.name=Antimonpfeil @@ -315,7 +315,7 @@ item.modernmetals.manganese_smallpowder.name=Kleiner Manganpulverhaufen item.modernmetals.manganese_sword.name=Manganschwert item.modernmetals.manganese_trapdoor.name=Manganfalltür -material.nichrome.name=Nickel-Chrom- +material.nichrome.name=Nickel-Chrom-Legierung item.modernmetals.nichrome_arrow.name=Nickel-Chrom-Pfeil item.modernmetals.nichrome_axe.name=Nickel-Chrom-Axt item.modernmetals.nichrome_bars.name=Nickel-Chrom-Gitter @@ -677,12 +677,12 @@ tile.modernmetals.aluminum_ore.name=Aluminiumerz tile.modernmetals.aluminum_plate.name=Aluminiumblech tile.modernmetals.aluminum_trapdoor.name=Aluminiumfalltür -tile.modernmetals.aluminumbrass_bars.name=Duraluminiumgitter -tile.modernmetals.aluminumbrass_block.name=Duraluminium -tile.modernmetals.aluminumbrass_door.name=Duraluminiumtür -tile.modernmetals.aluminumbrass_ore.name=Duraluminiumerz -tile.modernmetals.aluminumbrass_plate.name=Duraluminiumblech -tile.modernmetals.aluminumbrass_trapdoor.name=Duraluminiumfalltür +tile.modernmetals.aluminumbrass_bars.name=Aluminiummessinggitter +tile.modernmetals.aluminumbrass_block.name=Aluminiummessing +tile.modernmetals.aluminumbrass_door.name=Aluminiummessingtür +tile.modernmetals.aluminumbrass_ore.name=Aluminiummessingerz +tile.modernmetals.aluminumbrass_plate.name=Aluminiummessingblech +tile.modernmetals.aluminumbrass_trapdoor.name=Aluminiummessingfalltür tile.modernmetals.cadmium_bars.name=Cadmiumgitter tile.modernmetals.cadmium_block.name=Cadmium @@ -834,19 +834,19 @@ tooltip.tungsten.armor=Nichts Besonderes tooltip.uranium.armor=Nichts Besonderes tooltip.zirconium.armor=Nichts Besonderes -achievement.aluminumbrass_maker=Duraluminium +achievement.aluminumbrass_maker=Aluminiummessing achievement.galvanized_steel_maker=Verzinkter Stahl achievement.nichrome_maker=Nickel-Chrom- achievement.stainless_steel_maker=Edelstahl achievement.titanium_maker=Titan -achievement.aluminumbrass_maker.desc=Kombiniere Aluminiumpulver und Kupferpulver, um Duraluminium herzustellen +achievement.aluminumbrass_maker.desc=Kombiniere Aluminiumpulver und Kupferpulver, um Aluminiummessing herzustellen achievement.galvanized_steel_maker.desc=Kombininiere Stahlpulver und Zinkpulver, um verzinkten Stahl herzustellen achievement.nichrome_maker.desc=Kombiniere Nickelpulver und Chrompulver, um Nickel-Chrom herzustellen achievement.stainless_steel_maker.desc=Kombiniere Stahlpulver und Chrompulver, um Edelstahl herzustellen achievement.titanium_maker.desc=Kombiniere Rutilpulver und Magnesiumpulver, um Titan herzustellen fluid.modernmetals.aluminum=Geschmolzenes Aluminium -fluid.modernmetals.aluminumbrass=Geschmolzenes Duraluminium +fluid.modernmetals.aluminumbrass=Geschmolzenes Aluminiummessing fluid.modernmetals.cadmium=Geschmolzenes Cadmium fluid.modernmetals.chromium=Geschmolzenes Chrom fluid.modernmetals.galvanizedsteel=Geschmolzener, vernickelter Stahl @@ -865,7 +865,7 @@ fluid.modernmetals.uranium=Geschmolzenes Uran fluid.modernmetals.zirconium=Geschmolzenes Zirkon fluid.modernmetals.aluminum.name=Geschmolzenes Aluminium -fluid.modernmetals.aluminumbrass.name=Geschmolzenes Duraluminium +fluid.modernmetals.aluminumbrass.name=Geschmolzenes Aluminiummessing fluid.modernmetals.cadmium.name=Geschmolzenes Cadmium fluid.modernmetals.chromium.name=Geschmolzenes Chrom fluid.modernmetals.galvanizedsteel.name=Geschmolzenes Verzinkter Stahl @@ -884,7 +884,7 @@ fluid.modernmetals.uranium.name=Geschmolzenes Uran fluid.modernmetals.zirconium.name=Geschmolzenes Zirkon tile.modernmetals.aluminum.name=Geschmolzenes Aluminium -tile.modernmetals.aluminumbrass.name=Geschmolzenes Duraluminium +tile.modernmetals.aluminumbrass.name=Geschmolzenes Aluminiummessing tile.modernmetals.cadmium.name=Geschmolzenes Cadmium tile.modernmetals.chromium.name=Geschmolzenes Chrom tile.modernmetals.galvanizedsteel.name=Geschmolzenes Verzinkter Stahl @@ -925,27 +925,27 @@ tile.modernmetals.aluminum_stairs.name=Aluminiumtreppe tile.modernmetals.aluminum_wall.name=Aluminiummauer -item.modernmetals.aluminumbrass_clump.name=Duraluminiumklumpen -item.modernmetals.aluminumbrass_crushed.name=Zerkleinertes Duraluminiumerz -item.modernmetals.aluminumbrass_crushed_purified.name=Gereinigtes, zerkleinertes Duraluminiumerz -item.modernmetals.aluminumbrass_crystal.name=Duraluminiumkristall -item.modernmetals.aluminumbrass_dense_plate.name=Verdichtetes Duraluminiumblech -item.modernmetals.aluminumbrass_powder_dirty.name=Verunreinigtes Duraluminiumpulver -item.modernmetals.aluminumbrass_shard.name=Duraluminiumsplitter +item.modernmetals.aluminumbrass_clump.name=Aluminiummessingklumpen +item.modernmetals.aluminumbrass_crushed.name=Zerkleinertes Aluminiummessingerz +item.modernmetals.aluminumbrass_crushed_purified.name=Gereinigtes, zerkleinertes Aluminiummessingerz +item.modernmetals.aluminumbrass_crystal.name=Aluminiummessingkristall +item.modernmetals.aluminumbrass_dense_plate.name=Verdichtetes Aluminiummessingblech +item.modernmetals.aluminumbrass_powder_dirty.name=Verunreinigtes Aluminiummessingpulver +item.modernmetals.aluminumbrass_shard.name=Aluminiummessingsplitter -item.modernmetals.aluminumbrass_button.name=Duraluminiumschalter -item.modernmetals.aluminumbrass_lever.name=Duraluminiumhebel -item.modernmetals.aluminumbrass_pressure_plate.name=Duraluminiumdruckplatte -item.modernmetals.aluminumbrass_slab.name=Duraluminiumfliese -item.modernmetals.aluminumbrass_stairs.name=Duraluminiumtreppe -item.modernmetals.aluminumbrass_wall.name=Duraluminiummauer +item.modernmetals.aluminumbrass_button.name=Aluminiummessingschalter +item.modernmetals.aluminumbrass_lever.name=Aluminiummessinghebel +item.modernmetals.aluminumbrass_pressure_plate.name=Aluminiummessingdruckplatte +item.modernmetals.aluminumbrass_slab.name=Aluminiummessingfliese +item.modernmetals.aluminumbrass_stairs.name=Aluminiummessingtreppe +item.modernmetals.aluminumbrass_wall.name=Aluminiummessingmauer -tile.modernmetals.aluminumbrass_button.name=Duraluminiumschalter -tile.modernmetals.aluminumbrass_lever.name=Duraluminiumhebel -tile.modernmetals.aluminumbrass_pressure_plate.name=Duraluminiumdruckplatte -tile.modernmetals.aluminumbrass_slab.name=Duraluminiumfliese -tile.modernmetals.aluminumbrass_stairs.name=Duraluminiumtreppe -tile.modernmetals.aluminumbrass_wall.name=Duraluminiummauer +tile.modernmetals.aluminumbrass_button.name=Aluminiummessingschalter +tile.modernmetals.aluminumbrass_lever.name=Aluminiummessinghebel +tile.modernmetals.aluminumbrass_pressure_plate.name=Aluminiummessingdruckplatte +tile.modernmetals.aluminumbrass_slab.name=Aluminiummessingfliese +tile.modernmetals.aluminumbrass_stairs.name=Aluminiummessingtreppe +tile.modernmetals.aluminumbrass_wall.name=Aluminiummessingmauer item.modernmetals.cadmium_clump.name=Cadmiumklumpen @@ -1313,4 +1313,226 @@ tile.modernmetals.zirconium_lever.name=Zirkonhebel tile.modernmetals.zirconium_pressure_plate.name=Zirkondruckplatte tile.modernmetals.zirconium_slab.name=Zirkonfliese tile.modernmetals.zirconium_stairs.name=Zirkontreppe -tile.modernmetals.zirconium_wall.name=Zirkonmauer \ No newline at end of file +tile.modernmetals.zirconium_wall.name=Zirkonmauer +# Modern Metals settings and restored content +config.modernmetals.material.tooltip=Legt fest, ob die Blöcke, Gegenstände und Rezepte dieses Materials verfügbar sind. Ein Neustart ist nötig. +config.modernmetals.title=Modern Metals +config.modernmetals.description=Neustart erforderlich. Erze in OreSpawn einstellen. +config.modernmetals.content_mode=Fantasy-Stufe +config.modernmetals.content_mode.tooltip=Viel Fantasy behält alle Rezepte bei. Wenig Fantasy schränkt unrealistische Ausrüstung und Bauteile ein. Ein Neustart ist nötig. +high_fantasy=Viel Fantasy +low_fantasy=Wenig Fantasy +config.modernmetals.villager_trades=Dorfbewohnerhandel +config.modernmetals.villager_trades.tooltip=Schmiede handeln mit Barren und Ausrüstung aus Modern Metals. Ein Neustart ist nötig. +config.modernmetals.connection.mismatch=Der Server verwendet %s, dein Spiel verwendet %s. Gleiche die Fantasy-Stufe unter Mods > Modern Metals > Konfiguration an und starte das Spiel neu. +config.modernmetals.connection.protocol=Modern Metals konnte die Fantasy-Stufe nicht prüfen. Verwende auf dem Server und im Spiel denselben Build von Modern Metals. +itemGroup.modernmetals.combat=Modern Metals – Kampf +material.beryllium.name=Beryllium +item.modernmetals.beryllium_arrow.name=Berylliumpfeil +item.modernmetals.beryllium_axe.name=Berylliumaxt +item.modernmetals.beryllium_bars.name=Berylliumgitter +item.modernmetals.beryllium_blend.name=Berylliumgemisch +item.modernmetals.beryllium_block.name=Berylliumblock +item.modernmetals.beryllium_boots.name=Berylliumstiefel +item.modernmetals.beryllium_bolt.name=Berylliumbolzen +item.modernmetals.beryllium_bow.name=Berylliumbogen +item.modernmetals.beryllium_chestplate.name=Berylliumbrustpanzer +item.modernmetals.beryllium_crackhammer.name=Berylliumbruchhammer +item.modernmetals.beryllium_crossbow.name=Berylliumarmbrust +item.modernmetals.beryllium_door.name=Berylliumtür +item.modernmetals.beryllium_fishing_rod.name=Berylliumangel +item.modernmetals.beryllium_gear.name=Berylliumzahnrad +item.modernmetals.beryllium_helmet.name=Berylliumhelm +item.modernmetals.beryllium_hoe.name=Berylliumhacke +item.modernmetals.beryllium_horse_armor.name=Berylliumpferderüstung +item.modernmetals.beryllium_ingot.name=Berylliumbarren +item.modernmetals.beryllium_leggings.name=Berylliumbeinrüstung +item.modernmetals.beryllium_nugget.name=Berylliumnugget +item.modernmetals.beryllium_ore.name=Berylliumerz +item.modernmetals.beryllium_pickaxe.name=Berylliumspitzhacke +item.modernmetals.beryllium_powder.name=Berylliumpulver +item.modernmetals.beryllium_rod.name=Berylliumstange +item.modernmetals.beryllium_shears.name=Berylliumschere +item.modernmetals.beryllium_shield.name=Berylliumschild +item.modernmetals.beryllium_shovel.name=Berylliumschaufel +item.modernmetals.beryllium_smallblend.name=Kleiner Berylliumgemischhaufen +item.modernmetals.beryllium_smallpowder.name=Kleiner Berylliumpulverhaufen +item.modernmetals.beryllium_sword.name=Berylliumschwert +item.modernmetals.beryllium_trapdoor.name=Berylliumfalltür +material.boron.name=Bor +item.modernmetals.boron_arrow.name=Borpfeil +item.modernmetals.boron_axe.name=Boraxt +item.modernmetals.boron_bars.name=Borgitter +item.modernmetals.boron_blend.name=Borgemisch +item.modernmetals.boron_block.name=Borblock +item.modernmetals.boron_boots.name=Borstiefel +item.modernmetals.boron_bolt.name=Borbolzen +item.modernmetals.boron_bow.name=Borbogen +item.modernmetals.boron_chestplate.name=Borbrustpanzer +item.modernmetals.boron_crackhammer.name=Borbruchhammer +item.modernmetals.boron_crossbow.name=Borarmbrust +item.modernmetals.boron_door.name=Bortür +item.modernmetals.boron_fishing_rod.name=Borangel +item.modernmetals.boron_gear.name=Borzahnrad +item.modernmetals.boron_helmet.name=Borhelm +item.modernmetals.boron_hoe.name=Borhacke +item.modernmetals.boron_horse_armor.name=Borpferderüstung +item.modernmetals.boron_ingot.name=Borbarren +item.modernmetals.boron_leggings.name=Borbeinrüstung +item.modernmetals.boron_nugget.name=Bornugget +item.modernmetals.boron_ore.name=Borerz +item.modernmetals.boron_pickaxe.name=Borspitzhacke +item.modernmetals.boron_powder.name=Borpulver +item.modernmetals.boron_rod.name=Borstange +item.modernmetals.boron_shears.name=Borschere +item.modernmetals.boron_shield.name=Borschild +item.modernmetals.boron_shovel.name=Borschaufel +item.modernmetals.boron_smallblend.name=Kleiner Borgemischhaufen +item.modernmetals.boron_smallpowder.name=Kleiner Borpulverhaufen +item.modernmetals.boron_sword.name=Borschwert +item.modernmetals.boron_trapdoor.name=Borfalltür +tile.modernmetals.boron_bars.name=Borgitter +tile.modernmetals.boron_block.name=Bor +tile.modernmetals.boron_door.name=Bortür +tile.modernmetals.boron_ore.name=Borerz +tile.modernmetals.boron_plate.name=Borblech +tile.modernmetals.boron_trapdoor.name=Borfalltür +fluid.modernmetals.boron=Geschmolzenes Bor +fluid.modernmetals.beryllium=Geschmolzenes Beryllium +fluid.modernmetals.boron.name=Geschmolzenes Bor +fluid.modernmetals.beryllium.name=Geschmolzenes Beryllium +tile.modernmetals.boron.name=Geschmolzenes Bor +tile.modernmetals.beryllium_bars.name=Berylliumgitter +tile.modernmetals.beryllium_block.name=Beryllium +tile.modernmetals.beryllium_door.name=Berylliumtür +tile.modernmetals.beryllium_ore.name=Berylliumerz +tile.modernmetals.beryllium_plate.name=Berylliumblech +tile.modernmetals.beryllium_trapdoor.name=Berylliumfalltür +item.modernmetals.beryllium_clump.name=Berylliumklumpen +item.modernmetals.beryllium_crushed.name=Zerkleinertes Berylliumerz +item.modernmetals.beryllium_crushed_purified.name=Gereinigtes, zerkleinertes Berylliumerz +item.modernmetals.beryllium_crystal.name=Berylliumkristall +item.modernmetals.beryllium_dense_plate.name=Verdichtetes Berylliumblech +item.modernmetals.beryllium_powder_dirty.name=Verunreinigtes Berylliumpulver +item.modernmetals.beryllium_shard.name=Berylliumsplitter +item.modernmetals.beryllium_button.name=Berylliumschalter +item.modernmetals.beryllium_lever.name=Berylliumhebel +item.modernmetals.beryllium_pressure_plate.name=Berylliumdruckplatte +item.modernmetals.beryllium_slab.name=Berylliumfliese +item.modernmetals.beryllium_stairs.name=Berylliumtreppe +item.modernmetals.beryllium_wall.name=Berylliummauer +tile.modernmetals.beryllium.name=Geschmolzenes Beryllium +tile.modernmetals.beryllium_button.name=Berylliumschalter +tile.modernmetals.beryllium_lever.name=Berylliumhebel +tile.modernmetals.beryllium_pressure_plate.name=Berylliumdruckplatte +tile.modernmetals.beryllium_slab.name=Berylliumfliese +tile.modernmetals.beryllium_stairs.name=Berylliumtreppe +tile.modernmetals.beryllium_wall.name=Berylliummauer +item.modernmetals.boron_clump.name=Borklumpen +item.modernmetals.boron_crushed.name=Zerkleinertes Borerz +item.modernmetals.boron_crushed_purified.name=Gereinigtes, zerkleinertes Borerz +item.modernmetals.boron_crystal.name=Borkristall +item.modernmetals.boron_dense_plate.name=Verdichtetes Borblech +item.modernmetals.boron_powder_dirty.name=Verunreinigtes Borpulver +item.modernmetals.boron_shard.name=Borsplitter +item.modernmetals.boron_button.name=Borschalter +item.modernmetals.boron_lever.name=Borhebel +item.modernmetals.boron_pressure_plate.name=Bordruckplatte +item.modernmetals.boron_slab.name=Borfliese +item.modernmetals.boron_stairs.name=Bortreppe +item.modernmetals.boron_wall.name=Bormauer +tile.modernmetals.boron_button.name=Borschalter +tile.modernmetals.boron_lever.name=Borhebel +tile.modernmetals.boron_pressure_plate.name=Bordruckplatte +tile.modernmetals.boron_slab.name=Borfliese +tile.modernmetals.boron_stairs.name=Bortreppe +tile.modernmetals.boron_wall.name=Bormauer +material.thorium.name=Thorium +item.modernmetals.thorium_arrow.name=Thoriumpfeil +item.modernmetals.thorium_axe.name=Thoriumaxt +item.modernmetals.thorium_bars.name=Thoriumgitter +item.modernmetals.thorium_blend.name=Thoriumgemisch +item.modernmetals.thorium_block.name=Thoriumblock +item.modernmetals.thorium_boots.name=Thoriumstiefel +item.modernmetals.thorium_bolt.name=Thoriumbolzen +item.modernmetals.thorium_bow.name=Thoriumbogen +item.modernmetals.thorium_chestplate.name=Thoriumbrustpanzer +item.modernmetals.thorium_crackhammer.name=Thoriumbruchhammer +item.modernmetals.thorium_crossbow.name=Thoriumarmbrust +item.modernmetals.thorium_door.name=Thoriumtür +item.modernmetals.thorium_fishing_rod.name=Thoriumangel +item.modernmetals.thorium_gear.name=Thoriumzahnrad +item.modernmetals.thorium_helmet.name=Thoriumhelm +item.modernmetals.thorium_hoe.name=Thoriumhacke +item.modernmetals.thorium_horse_armor.name=Thoriumpferderüstung +item.modernmetals.thorium_ingot.name=Thoriumbarren +item.modernmetals.thorium_leggings.name=Thoriumbeinrüstung +item.modernmetals.thorium_nugget.name=Thoriumnugget +item.modernmetals.thorium_ore.name=Thoriumerz +item.modernmetals.thorium_pickaxe.name=Thoriumspitzhacke +item.modernmetals.thorium_powder.name=Thoriumpulver +item.modernmetals.thorium_rod.name=Thoriumstange +item.modernmetals.thorium_shears.name=Thoriumschere +item.modernmetals.thorium_shield.name=Thoriumschild +item.modernmetals.thorium_shovel.name=Thoriumschaufel +item.modernmetals.thorium_smallblend.name=Kleiner Thoriumgemischhaufen +item.modernmetals.thorium_smallpowder.name=Kleiner Thoriumpulverhaufen +item.modernmetals.thorium_sword.name=Thoriumschwert +item.modernmetals.thorium_trapdoor.name=Thoriumfalltür +tile.modernmetals.thorium_bars.name=Thoriumgitter +tile.modernmetals.thorium_block.name=Thorium +tile.modernmetals.thorium_door.name=Thoriumtür +tile.modernmetals.thorium_ore.name=Thoriumerz +tile.modernmetals.thorium_plate.name=Thoriumblech +tile.modernmetals.thorium_trapdoor.name=Thoriumfalltür +fluid.modernmetals.thorium=Geschmolzenes Thorium +fluid.modernmetals.thorium.name=Geschmolzenes Thorium +tile.modernmetals.thorium.name=Geschmolzenes Thorium +item.modernmetals.thorium_clump.name=Thoriumklumpen +item.modernmetals.thorium_crushed.name=Zerkleinertes Thoriumerz +item.modernmetals.thorium_crushed_purified.name=Gereinigtes, zerkleinertes Thoriumerz +item.modernmetals.thorium_crystal.name=Thoriumkristall +item.modernmetals.thorium_dense_plate.name=Verdichtetes Thoriumblech +item.modernmetals.thorium_powder_dirty.name=Verunreinigtes Thoriumpulver +item.modernmetals.thorium_shard.name=Thoriumsplitter +item.modernmetals.thorium_button.name=Thoriumschalter +item.modernmetals.thorium_lever.name=Thoriumhebel +item.modernmetals.thorium_pressure_plate.name=Thoriumdruckplatte +item.modernmetals.thorium_slab.name=Thoriumfliese +item.modernmetals.thorium_stairs.name=Thoriumtreppe +item.modernmetals.thorium_wall.name=Thoriummauer +tile.modernmetals.thorium_button.name=Thoriumschalter +tile.modernmetals.thorium_lever.name=Thoriumhebel +tile.modernmetals.thorium_pressure_plate.name=Thoriumdruckplatte +tile.modernmetals.thorium_slab.name=Thoriumfliese +tile.modernmetals.thorium_stairs.name=Thoriumtreppe +tile.modernmetals.thorium_wall.name=Thoriummauer +item.modernmetals.titanium_casing.name=Titangehäuse +item.modernmetals.beryllium_scythe.name=Berylliumsichel +item.modernmetals.boron_scythe.name=Borsichel +item.modernmetals.thorium_scythe.name=Thoriumsichel +item.modernmetals.titanium_scythe.name=Titansichel +config.modernmetals.enable_thorium=Thorium aktivieren +config.modernmetals.enable_boron=Bor aktivieren +config.modernmetals.enable_beryllium=Beryllium aktivieren +config.modernmetals.enable_cadmium=Cadmium aktivieren +config.modernmetals.enable_nichrome=Nickel-Chrom-Legierung aktivieren +config.modernmetals.enable_aluminum=Aluminium aktivieren +config.modernmetals.enable_aluminumbrass=Aluminiummessing aktivieren +config.modernmetals.enable_chromium=Chrom aktivieren +config.modernmetals.enable_galvanizedsteel=Verzinkter Stahl aktivieren +config.modernmetals.enable_iridium=Iridium aktivieren +config.modernmetals.enable_magnesium=Magnesium aktivieren +config.modernmetals.enable_manganese=Mangan aktivieren +config.modernmetals.enable_osmium=Osmium aktivieren +config.modernmetals.enable_plutonium=Plutonium aktivieren +config.modernmetals.enable_rutile=Rutil aktivieren +config.modernmetals.enable_stainlesssteel=Edelstahl aktivieren +config.modernmetals.enable_tantalum=Tantal aktivieren +config.modernmetals.enable_titanium=Titan aktivieren +config.modernmetals.enable_tungsten=Wolfram aktivieren +config.modernmetals.enable_uranium=Uran aktivieren +config.modernmetals.enable_zirconium=Zirkon aktivieren + +achievement.aluminum_brass_maker=Aluminiummessing +achievement.aluminum_brass_maker.desc=Kombiniere Aluminiumpulver und Kupferpulver, um Aluminiummessing herzustellen diff --git a/src/main/resources/assets/modernmetals/lang/en_AU.lang b/src/main/resources/assets/modernmetals/lang/en_AU.lang new file mode 100644 index 00000000..0f62b48b --- /dev/null +++ b/src/main/resources/assets/modernmetals/lang/en_AU.lang @@ -0,0 +1,51 @@ +# Regional strings. Minecraft falls back to en_US for other entries. + +# Regional English +item.modernmetals.aluminum_horse_armor.name=Aluminum Horse Armour +item.modernmetals.aluminum_leggings.name=Aluminum Leg Armour +item.modernmetals.aluminumbrass_horse_armor.name=Aluminum Brass Horse Armour +item.modernmetals.aluminumbrass_leggings.name=Aluminum Brass Leg Armour +item.modernmetals.antimony_leggings.name=Antimony Leg Armour +item.modernmetals.beryllium_horse_armor.name=Beryllium Horse Armour +item.modernmetals.beryllium_leggings.name=Beryllium Leg Armour +item.modernmetals.bismuth_leggings.name=Bismuth Leg Armour +item.modernmetals.boron_horse_armor.name=Boron Horse Armour +item.modernmetals.boron_leggings.name=Boron Leg Armour +item.modernmetals.cadmium_horse_armor.name=Cadmium Horse Armour +item.modernmetals.cadmium_leggings.name=Cadmium Leg Armour +item.modernmetals.chromium_horse_armor.name=Chromium Horse Armour +item.modernmetals.chromium_leggings.name=Chromium Leg Armour +item.modernmetals.galvanizedsteel_horse_armor.name=Galvanized Steel Horse Armour +item.modernmetals.galvanizedsteel_leggings.name=Galvanized Steel Leg Armour +item.modernmetals.iridium_horse_armor.name=Iridium Horse Armour +item.modernmetals.iridium_leggings.name=Iridium Leg Armour +item.modernmetals.magnesium_horse_armor.name=Magnesium Horse Armour +item.modernmetals.magnesium_leggings.name=Magnesium Leg Armour +item.modernmetals.manganese_horse_armor.name=Manganese Horse Armour +item.modernmetals.manganese_leggings.name=Manganese Leg Armour +item.modernmetals.nichrome_horse_armor.name=Nichrome Horse Armour +item.modernmetals.nichrome_leggings.name=Nichrome Leg Armour +item.modernmetals.osmium_horse_armor.name=Osmium Horse Armour +item.modernmetals.osmium_leggings.name=Osmium Leg Armour +item.modernmetals.pewter_leggings.name=Pewter Leg Armour +item.modernmetals.plutonium_horse_armor.name=Plutonium Horse Armour +item.modernmetals.plutonium_leggings.name=Plutonium Leg Armour +item.modernmetals.rutile_horse_armor.name=Rutile Horse Armour +item.modernmetals.rutile_leggings.name=Rutile Leg Armour +item.modernmetals.stainlesssteel_horse_armor.name=Stainless Steel Horse Armour +item.modernmetals.stainlesssteel_leggings.name=Stainless Steel Leg Armour +item.modernmetals.tantalum_horse_armor.name=Tantalum Horse Armour +item.modernmetals.tantalum_leggings.name=Tantalum Leg Armour +item.modernmetals.titanium_horse_armor.name=Titanium Horse Armour +item.modernmetals.titanium_leggings.name=Titanium Leg Armour +item.modernmetals.tungsten_horse_armor.name=Tungsten Horse Armour +item.modernmetals.tungsten_leggings.name=Tungsten Leg Armour +item.modernmetals.uranium_horse_armor.name=Uranium Horse Armour +item.modernmetals.uranium_leggings.name=Uranium Leg Armour +item.modernmetals.zirconium_horse_armor.name=Zirconium Horse Armour +item.modernmetals.zirconium_leggings.name=Zirconium Leg Armour +item.modernmetals.thorium_horse_armor.name=Thorium Horse Armour +item.modernmetals.thorium_leggings.name=Thorium Leg Armour +config.modernmetals.enable_aluminum=Enable Aluminium +config.modernmetals.enable_aluminumbrass=Enable Aluminium Brass +config.modernmetals.enable_galvanizedsteel=Enable Galvanised Steel diff --git a/src/main/resources/assets/modernmetals/lang/en_CA.lang b/src/main/resources/assets/modernmetals/lang/en_CA.lang new file mode 100644 index 00000000..0f62b48b --- /dev/null +++ b/src/main/resources/assets/modernmetals/lang/en_CA.lang @@ -0,0 +1,51 @@ +# Regional strings. Minecraft falls back to en_US for other entries. + +# Regional English +item.modernmetals.aluminum_horse_armor.name=Aluminum Horse Armour +item.modernmetals.aluminum_leggings.name=Aluminum Leg Armour +item.modernmetals.aluminumbrass_horse_armor.name=Aluminum Brass Horse Armour +item.modernmetals.aluminumbrass_leggings.name=Aluminum Brass Leg Armour +item.modernmetals.antimony_leggings.name=Antimony Leg Armour +item.modernmetals.beryllium_horse_armor.name=Beryllium Horse Armour +item.modernmetals.beryllium_leggings.name=Beryllium Leg Armour +item.modernmetals.bismuth_leggings.name=Bismuth Leg Armour +item.modernmetals.boron_horse_armor.name=Boron Horse Armour +item.modernmetals.boron_leggings.name=Boron Leg Armour +item.modernmetals.cadmium_horse_armor.name=Cadmium Horse Armour +item.modernmetals.cadmium_leggings.name=Cadmium Leg Armour +item.modernmetals.chromium_horse_armor.name=Chromium Horse Armour +item.modernmetals.chromium_leggings.name=Chromium Leg Armour +item.modernmetals.galvanizedsteel_horse_armor.name=Galvanized Steel Horse Armour +item.modernmetals.galvanizedsteel_leggings.name=Galvanized Steel Leg Armour +item.modernmetals.iridium_horse_armor.name=Iridium Horse Armour +item.modernmetals.iridium_leggings.name=Iridium Leg Armour +item.modernmetals.magnesium_horse_armor.name=Magnesium Horse Armour +item.modernmetals.magnesium_leggings.name=Magnesium Leg Armour +item.modernmetals.manganese_horse_armor.name=Manganese Horse Armour +item.modernmetals.manganese_leggings.name=Manganese Leg Armour +item.modernmetals.nichrome_horse_armor.name=Nichrome Horse Armour +item.modernmetals.nichrome_leggings.name=Nichrome Leg Armour +item.modernmetals.osmium_horse_armor.name=Osmium Horse Armour +item.modernmetals.osmium_leggings.name=Osmium Leg Armour +item.modernmetals.pewter_leggings.name=Pewter Leg Armour +item.modernmetals.plutonium_horse_armor.name=Plutonium Horse Armour +item.modernmetals.plutonium_leggings.name=Plutonium Leg Armour +item.modernmetals.rutile_horse_armor.name=Rutile Horse Armour +item.modernmetals.rutile_leggings.name=Rutile Leg Armour +item.modernmetals.stainlesssteel_horse_armor.name=Stainless Steel Horse Armour +item.modernmetals.stainlesssteel_leggings.name=Stainless Steel Leg Armour +item.modernmetals.tantalum_horse_armor.name=Tantalum Horse Armour +item.modernmetals.tantalum_leggings.name=Tantalum Leg Armour +item.modernmetals.titanium_horse_armor.name=Titanium Horse Armour +item.modernmetals.titanium_leggings.name=Titanium Leg Armour +item.modernmetals.tungsten_horse_armor.name=Tungsten Horse Armour +item.modernmetals.tungsten_leggings.name=Tungsten Leg Armour +item.modernmetals.uranium_horse_armor.name=Uranium Horse Armour +item.modernmetals.uranium_leggings.name=Uranium Leg Armour +item.modernmetals.zirconium_horse_armor.name=Zirconium Horse Armour +item.modernmetals.zirconium_leggings.name=Zirconium Leg Armour +item.modernmetals.thorium_horse_armor.name=Thorium Horse Armour +item.modernmetals.thorium_leggings.name=Thorium Leg Armour +config.modernmetals.enable_aluminum=Enable Aluminium +config.modernmetals.enable_aluminumbrass=Enable Aluminium Brass +config.modernmetals.enable_galvanizedsteel=Enable Galvanised Steel diff --git a/src/main/resources/assets/modernmetals/lang/en_GB.lang b/src/main/resources/assets/modernmetals/lang/en_GB.lang new file mode 100644 index 00000000..9ec41df4 --- /dev/null +++ b/src/main/resources/assets/modernmetals/lang/en_GB.lang @@ -0,0 +1,96 @@ +# Regional strings. Minecraft falls back to en_US for other entries. + +# Regional English +item.modernmetals.aluminum_block.name=Aluminum +item.modernmetals.aluminum_horse_armor.name=Aluminum Horse Armour +item.modernmetals.aluminum_leggings.name=Aluminum Leg Armour +item.modernmetals.aluminumbrass_block.name=Aluminum Brass Block +item.modernmetals.aluminumbrass_horse_armor.name=Aluminum Brass Horse Armour +item.modernmetals.aluminumbrass_leggings.name=Aluminum Brass Leg Armour +item.modernmetals.antimony_block.name=Antimony Block +item.modernmetals.antimony_leggings.name=Antimony Leg Armour +item.modernmetals.beryllium_block.name=Beryllium Block +item.modernmetals.beryllium_horse_armor.name=Beryllium Horse Armour +item.modernmetals.beryllium_leggings.name=Beryllium Leg Armour +item.modernmetals.bismuth_block.name=Bismuth Block +item.modernmetals.bismuth_leggings.name=Bismuth Leg Armour +item.modernmetals.boron_block.name=Boron Block +item.modernmetals.boron_horse_armor.name=Boron Horse Armour +item.modernmetals.boron_leggings.name=Boron Leg Armour +item.modernmetals.cadmium_block.name=Cadmium Block +item.modernmetals.cadmium_horse_armor.name=Cadmium Horse Armour +item.modernmetals.cadmium_leggings.name=Cadmium Leg Armour +item.modernmetals.chromium_block.name=Chromium Block +item.modernmetals.chromium_horse_armor.name=Chromium Horse Armour +item.modernmetals.chromium_leggings.name=Chromium Leg Armour +item.modernmetals.galvanizedsteel_block.name=Galvanized Steel Block +item.modernmetals.galvanizedsteel_horse_armor.name=Galvanized Steel Horse Armour +item.modernmetals.galvanizedsteel_leggings.name=Galvanized Steel Leg Armour +item.modernmetals.iridium_block.name=Iridium Block +item.modernmetals.iridium_horse_armor.name=Iridium Horse Armour +item.modernmetals.iridium_leggings.name=Iridium Leg Armour +item.modernmetals.magnesium_block.name=Magnesium Block +item.modernmetals.magnesium_horse_armor.name=Magnesium Horse Armour +item.modernmetals.magnesium_leggings.name=Magnesium Leg Armour +item.modernmetals.manganese_block.name=Manganese Block +item.modernmetals.manganese_horse_armor.name=Manganese Horse Armour +item.modernmetals.manganese_leggings.name=Manganese Leg Armour +item.modernmetals.nichrome_block.name=Nichrome Block +item.modernmetals.nichrome_horse_armor.name=Nichrome Horse Armour +item.modernmetals.nichrome_leggings.name=Nichrome Leg Armour +item.modernmetals.osmium_block.name=Osmium Block +item.modernmetals.osmium_horse_armor.name=Osmium Horse Armour +item.modernmetals.osmium_leggings.name=Osmium Leg Armour +item.modernmetals.pewter_block.name=Pewter Block +item.modernmetals.pewter_leggings.name=Pewter Leg Armour +item.modernmetals.plutonium_block.name=Plutonium Block +item.modernmetals.plutonium_horse_armor.name=Plutonium Horse Armour +item.modernmetals.plutonium_leggings.name=Plutonium Leg Armour +item.modernmetals.rutile_block.name=Rutile Block +item.modernmetals.rutile_horse_armor.name=Rutile Horse Armour +item.modernmetals.rutile_leggings.name=Rutile Leg Armour +item.modernmetals.stainlesssteel_block.name=Stainless Steel Block +item.modernmetals.stainlesssteel_horse_armor.name=Stainless Steel Horse Armour +item.modernmetals.stainlesssteel_leggings.name=Stainless Steel Leg Armour +item.modernmetals.tantalum_block.name=Tantalum Block +item.modernmetals.tantalum_horse_armor.name=Tantalum Horse Armour +item.modernmetals.tantalum_leggings.name=Tantalum Leg Armour +item.modernmetals.titanium_block.name=Titanium Block +item.modernmetals.titanium_horse_armor.name=Titanium Horse Armour +item.modernmetals.titanium_leggings.name=Titanium Leg Armour +item.modernmetals.tungsten_block.name=Tungsten Block +item.modernmetals.tungsten_horse_armor.name=Tungsten Horse Armour +item.modernmetals.tungsten_leggings.name=Tungsten Leg Armour +item.modernmetals.uranium_block.name=Uranium Block +item.modernmetals.uranium_horse_armor.name=Uranium Horse Armour +item.modernmetals.uranium_leggings.name=Uranium Leg Armour +item.modernmetals.zirconium_block.name=Zirconium Block +item.modernmetals.zirconium_horse_armor.name=Zirconium Horse Armour +item.modernmetals.zirconium_leggings.name=Zirconium Leg Armour +tile.modernmetals.aluminum_block.name=Aluminum Block +tile.modernmetals.aluminumbrass_block.name=Aluminum Brass Block +tile.modernmetals.boron_block.name=Boron Block +tile.modernmetals.cadmium_block.name=Cadmium Block +tile.modernmetals.chromium_block.name=Chromium Block +tile.modernmetals.galvanizedsteel_block.name=Galvanized Steel Block +tile.modernmetals.iridium_block.name=Iridium Block +tile.modernmetals.magnesium_block.name=Magnesium Block +tile.modernmetals.manganese_block.name=Manganese Block +tile.modernmetals.nichrome_block.name=Nichrome Block +tile.modernmetals.osmium_block.name=Osmium Block +tile.modernmetals.plutonium_block.name=Plutonium Block +tile.modernmetals.rutile_block.name=Rutile Block +tile.modernmetals.stainlesssteel_block.name=Stainless Steel Block +tile.modernmetals.tantalum_block.name=Tantalum +tile.modernmetals.titanium_block.name=Titanium +tile.modernmetals.tungsten_block.name=Tungsten +tile.modernmetals.uranium_block.name=Uranium +tile.modernmetals.zirconium_block.name=Zirconium +tile.modernmetals.beryllium_block.name=Beryllium +item.modernmetals.thorium_block.name=Thorium +item.modernmetals.thorium_horse_armor.name=Thorium Horse Armour +item.modernmetals.thorium_leggings.name=Thorium Leg Armour +tile.modernmetals.thorium_block.name=Thorium +config.modernmetals.enable_aluminum=Enable Aluminium +config.modernmetals.enable_aluminumbrass=Enable Aluminium Brass +config.modernmetals.enable_galvanizedsteel=Enable Galvanised Steel diff --git a/src/main/resources/assets/modernmetals/lang/en_NZ.lang b/src/main/resources/assets/modernmetals/lang/en_NZ.lang new file mode 100644 index 00000000..0f62b48b --- /dev/null +++ b/src/main/resources/assets/modernmetals/lang/en_NZ.lang @@ -0,0 +1,51 @@ +# Regional strings. Minecraft falls back to en_US for other entries. + +# Regional English +item.modernmetals.aluminum_horse_armor.name=Aluminum Horse Armour +item.modernmetals.aluminum_leggings.name=Aluminum Leg Armour +item.modernmetals.aluminumbrass_horse_armor.name=Aluminum Brass Horse Armour +item.modernmetals.aluminumbrass_leggings.name=Aluminum Brass Leg Armour +item.modernmetals.antimony_leggings.name=Antimony Leg Armour +item.modernmetals.beryllium_horse_armor.name=Beryllium Horse Armour +item.modernmetals.beryllium_leggings.name=Beryllium Leg Armour +item.modernmetals.bismuth_leggings.name=Bismuth Leg Armour +item.modernmetals.boron_horse_armor.name=Boron Horse Armour +item.modernmetals.boron_leggings.name=Boron Leg Armour +item.modernmetals.cadmium_horse_armor.name=Cadmium Horse Armour +item.modernmetals.cadmium_leggings.name=Cadmium Leg Armour +item.modernmetals.chromium_horse_armor.name=Chromium Horse Armour +item.modernmetals.chromium_leggings.name=Chromium Leg Armour +item.modernmetals.galvanizedsteel_horse_armor.name=Galvanized Steel Horse Armour +item.modernmetals.galvanizedsteel_leggings.name=Galvanized Steel Leg Armour +item.modernmetals.iridium_horse_armor.name=Iridium Horse Armour +item.modernmetals.iridium_leggings.name=Iridium Leg Armour +item.modernmetals.magnesium_horse_armor.name=Magnesium Horse Armour +item.modernmetals.magnesium_leggings.name=Magnesium Leg Armour +item.modernmetals.manganese_horse_armor.name=Manganese Horse Armour +item.modernmetals.manganese_leggings.name=Manganese Leg Armour +item.modernmetals.nichrome_horse_armor.name=Nichrome Horse Armour +item.modernmetals.nichrome_leggings.name=Nichrome Leg Armour +item.modernmetals.osmium_horse_armor.name=Osmium Horse Armour +item.modernmetals.osmium_leggings.name=Osmium Leg Armour +item.modernmetals.pewter_leggings.name=Pewter Leg Armour +item.modernmetals.plutonium_horse_armor.name=Plutonium Horse Armour +item.modernmetals.plutonium_leggings.name=Plutonium Leg Armour +item.modernmetals.rutile_horse_armor.name=Rutile Horse Armour +item.modernmetals.rutile_leggings.name=Rutile Leg Armour +item.modernmetals.stainlesssteel_horse_armor.name=Stainless Steel Horse Armour +item.modernmetals.stainlesssteel_leggings.name=Stainless Steel Leg Armour +item.modernmetals.tantalum_horse_armor.name=Tantalum Horse Armour +item.modernmetals.tantalum_leggings.name=Tantalum Leg Armour +item.modernmetals.titanium_horse_armor.name=Titanium Horse Armour +item.modernmetals.titanium_leggings.name=Titanium Leg Armour +item.modernmetals.tungsten_horse_armor.name=Tungsten Horse Armour +item.modernmetals.tungsten_leggings.name=Tungsten Leg Armour +item.modernmetals.uranium_horse_armor.name=Uranium Horse Armour +item.modernmetals.uranium_leggings.name=Uranium Leg Armour +item.modernmetals.zirconium_horse_armor.name=Zirconium Horse Armour +item.modernmetals.zirconium_leggings.name=Zirconium Leg Armour +item.modernmetals.thorium_horse_armor.name=Thorium Horse Armour +item.modernmetals.thorium_leggings.name=Thorium Leg Armour +config.modernmetals.enable_aluminum=Enable Aluminium +config.modernmetals.enable_aluminumbrass=Enable Aluminium Brass +config.modernmetals.enable_galvanizedsteel=Enable Galvanised Steel diff --git a/src/main/resources/assets/modernmetals/lang/en_PT.lang b/src/main/resources/assets/modernmetals/lang/en_PT.lang new file mode 100644 index 00000000..4b2fa57e --- /dev/null +++ b/src/main/resources/assets/modernmetals/lang/en_PT.lang @@ -0,0 +1,90 @@ +# Regional strings. Minecraft falls back to en_US for other entries. + +# Regional English +item.modernmetals.aluminum_block.name=Block o' Aluminum +item.modernmetals.aluminumbrass_block.name=Block o' Aluminum Brass +item.modernmetals.antimony_block.name=Block o' Antimony +item.modernmetals.beryllium_block.name=Block o' Beryllium +item.modernmetals.bismuth_block.name=Block o' Bismuth +item.modernmetals.boron_block.name=Block o' Boron +item.modernmetals.cadmium_block.name=Block o' Cadmium +item.modernmetals.chromium_block.name=Block o' Chromium +item.modernmetals.galvanizedsteel_block.name=Block o' Galvanized Steel +item.modernmetals.iridium_block.name=Block o' Iridium +item.modernmetals.magnesium_block.name=Block o' Magnesium +item.modernmetals.manganese_block.name=Block o' Manganese +item.modernmetals.nichrome_block.name=Block o' Nichrome +item.modernmetals.osmium_block.name=Block o' Osmium +item.modernmetals.pewter_block.name=Block o' Pewter +item.modernmetals.plutonium_block.name=Block o' Plutonium +item.modernmetals.rutile_block.name=Block o' Rutile +item.modernmetals.stainlesssteel_block.name=Block o' Stainless Steel +item.modernmetals.tantalum_block.name=Block o' Tantalum +item.modernmetals.titanium_block.name=Block o' Titanium +item.modernmetals.tungsten_block.name=Block o' Tungsten +item.modernmetals.uranium_block.name=Block o' Uranium +item.modernmetals.zirconium_block.name=Block o' Zirconium +tile.modernmetals.aluminum_block.name=Block o' Aluminum +tile.modernmetals.aluminum_plate.name=Plate o' Aluminum +tile.modernmetals.aluminumbrass_block.name=Block o' Aluminum Brass +tile.modernmetals.aluminumbrass_plate.name=Plate o' Aluminum Brass +tile.modernmetals.boron_block.name=Block o' Boron +tile.modernmetals.boron_plate.name=Plate o' Boron +tile.modernmetals.cadmium_block.name=Block o' Cadmium +tile.modernmetals.cadmium_plate.name=Plate o' Cadmium +tile.modernmetals.chromium_block.name=Block o' Chromium +tile.modernmetals.chromium_plate.name=Plate o' Chromium +tile.modernmetals.galvanizedsteel_block.name=Block o' Galvanized Steel +tile.modernmetals.galvanizedsteel_plate.name=Plate o' Galvanized Steel +tile.modernmetals.iridium_block.name=Block o' Iridium +tile.modernmetals.iridium_plate.name=Plate o' Iridium +tile.modernmetals.magnesium_block.name=Block o' Magnesium +tile.modernmetals.magnesium_plate.name=Plate o' Magnesium +tile.modernmetals.manganese_block.name=Block o' Manganese +tile.modernmetals.manganese_plate.name=Plate o' Manganese +tile.modernmetals.nichrome_block.name=Block o' Nichrome +tile.modernmetals.nichrome_plate.name=Plate o' Nichrome +tile.modernmetals.osmium_block.name=Block o' Osmium +tile.modernmetals.osmium_plate.name=Plate o' Osmium +tile.modernmetals.plutonium_block.name=Block o' Plutonium +tile.modernmetals.plutonium_plate.name=Plate o' Plutonium +tile.modernmetals.rutile_block.name=Block o' Rutile +tile.modernmetals.rutile_plate.name=Plate o' Rutile +tile.modernmetals.stainlesssteel_block.name=Block o' Stainless Steel +tile.modernmetals.stainlesssteel_plate.name=Plate o' Stainless Steel +tile.modernmetals.tantalum_block.name=Block o' Tantalum +tile.modernmetals.tantalum_plate.name=Plate o' Tantalum +tile.modernmetals.titanium_block.name=Block o' Titanium +tile.modernmetals.titanium_plate.name=Plate o' Titanium +tile.modernmetals.tungsten_block.name=Block o' Tungsten +tile.modernmetals.tungsten_plate.name=Plate o' Tungsten +tile.modernmetals.uranium_block.name=Block o' Uranium +tile.modernmetals.uranium_plate.name=Plate o' Uranium +tile.modernmetals.zirconium_block.name=Block o' Zirconium +tile.modernmetals.zirconium_plate.name=Plate o' Zirconium +item.modernmetals.aluminum_dense_plate.name=Dense Plate o' Aluminum +item.modernmetals.aluminumbrass_dense_plate.name=Dense Plate o' Aluminum Brass +tile.modernmetals.beryllium_block.name=Block o' Beryllium +tile.modernmetals.beryllium_plate.name=Plate o' Beryllium +item.modernmetals.beryllium_dense_plate.name=Dense Plate o' Beryllium +item.modernmetals.boron_dense_plate.name=Dense Plate o' Boron +item.modernmetals.cadmium_dense_plate.name=Dense Plate o' Cadmium +item.modernmetals.chromium_dense_plate.name=Dense Plate o' Chromium +item.modernmetals.galvanizedsteel_dense_plate.name=Dense Plate o' Galvanized Steel +item.modernmetals.iridium_dense_plate.name=Dense Plate o' Iridium +item.modernmetals.magnesium_dense_plate.name=Dense Plate o' Magnesium +item.modernmetals.manganese_dense_plate.name=Dense Plate o' Manganese +item.modernmetals.nichrome_dense_plate.name=Dense Plate o' Nichrome +item.modernmetals.osmium_dense_plate.name=Dense Plate o' Osmium +item.modernmetals.plutonium_dense_plate.name=Dense Plate o' Plutonium +item.modernmetals.rutile_dense_plate.name=Dense Plate o' Rutile +item.modernmetals.stainlesssteel_dense_plate.name=Dense Plate o' Stainless Steel +item.modernmetals.tantalum_dense_plate.name=Dense Plate o' Tantalum +item.modernmetals.titanium_dense_plate.name=Dense Plate o' Titanium +item.modernmetals.tungsten_dense_plate.name=Dense Plate o' Tungsten +item.modernmetals.uranium_dense_plate.name=Dense Plate o' Uranium +item.modernmetals.zirconium_dense_plate.name=Dense Plate o' Zirconium +item.modernmetals.thorium_block.name=Block o' Thorium +tile.modernmetals.thorium_block.name=Block o' Thorium +tile.modernmetals.thorium_plate.name=Plate o' Thorium +item.modernmetals.thorium_dense_plate.name=Dense Plate o' Thorium diff --git a/src/main/resources/assets/modernmetals/lang/en_US.lang b/src/main/resources/assets/modernmetals/lang/en_US.lang index 32080748..2c588b64 100644 --- a/src/main/resources/assets/modernmetals/lang/en_US.lang +++ b/src/main/resources/assets/modernmetals/lang/en_US.lang @@ -1544,3 +1544,42 @@ item.modernmetals.titanium_scythe.name=Titanium Sickle item.modernmetals.tungsten_scythe.name=Tungsten Sickle item.modernmetals.uranium_scythe.name=Uranium Sickle item.modernmetals.zirconium_scythe.name=Zirconium Sickle + +config.modernmetals.title=Modern Metals +config.modernmetals.description=Restart to apply changes. Edit ores in OreSpawn. +config.modernmetals.content_mode=Fantasy Level +config.modernmetals.content_mode.tooltip=High Fantasy keeps all recipes. Low Fantasy restricts unrealistic equipment and structures. Requires a game restart. +# Forge 1.10 cycle selectors translate their stored values directly. +high_fantasy=High Fantasy +low_fantasy=Low Fantasy +config.modernmetals.enable_thorium=Enable Thorium +config.modernmetals.enable_boron=Enable Boron +config.modernmetals.enable_beryllium=Enable Beryllium +config.modernmetals.enable_cadmium=Enable Cadmium +config.modernmetals.enable_nichrome=Enable Nichrome +config.modernmetals.enable_aluminum=Enable Aluminum +config.modernmetals.enable_aluminumbrass=Enable Aluminum Brass +config.modernmetals.enable_chromium=Enable Chromium +config.modernmetals.enable_galvanizedsteel=Enable Galvanized Steel +config.modernmetals.enable_iridium=Enable Iridium +config.modernmetals.enable_magnesium=Enable Magnesium +config.modernmetals.enable_manganese=Enable Manganese +config.modernmetals.enable_osmium=Enable Osmium +config.modernmetals.enable_plutonium=Enable Plutonium +config.modernmetals.enable_rutile=Enable Rutile +config.modernmetals.enable_stainlesssteel=Enable Stainless Steel +config.modernmetals.enable_tantalum=Enable Tantalum +config.modernmetals.enable_titanium=Enable Titanium +config.modernmetals.enable_tungsten=Enable Tungsten +config.modernmetals.enable_uranium=Enable Uranium +config.modernmetals.enable_zirconium=Enable Zirconium + +# Modern Metals settings and restored content +config.modernmetals.material.tooltip=Controls which blocks, items and recipes are available for this material. Requires a game restart. +config.modernmetals.villager_trades=Villager Trades +config.modernmetals.villager_trades.tooltip=Let blacksmiths trade Modern Metals ingots and equipment. Requires a game restart. +config.modernmetals.connection.mismatch=The server uses %s, but your game uses %s. Match the Fantasy Level in Mods > Modern Metals > Config and restart. +config.modernmetals.connection.protocol=Modern Metals could not check the Fantasy Level. Use the same Modern Metals build on the server and client. + +achievement.aluminum_brass_maker=Aluminum Brass +achievement.aluminum_brass_maker.desc=Combine powdered Aluminum and Brass to make Aluminum Brass diff --git a/src/main/resources/assets/modernmetals/lang/ru_RU.lang b/src/main/resources/assets/modernmetals/lang/ru_RU.lang new file mode 100644 index 00000000..02925cb4 --- /dev/null +++ b/src/main/resources/assets/modernmetals/lang/ru_RU.lang @@ -0,0 +1,1556 @@ +itemGroup.modernmetals.items=Modern Metals Предметы +itemGroup.modernmetals.tools=Modern Metals Инструменты +itemGroup.modernmetals.blocks=Modern Metals Блоки +nei.modernmetals.recipehandler.crusher.name=Crushed Blocks/Items + +material.aluminum.name=Алюминий +item.modernmetals.aluminum_arrow.name=Алюминиевая Стрела +item.modernmetals.aluminum_axe.name=Алюминиевый Топор +item.modernmetals.aluminum_bars.name=Алюминиевый Прутья +item.modernmetals.aluminum_blend.name=Алюминиевый Смеси +item.modernmetals.aluminum_block.name=Блок Алюминя +item.modernmetals.aluminum_boots.name=Алюминиевый Ботинки +item.modernmetals.aluminum_bolt.name=Алюминиевый Арбалетная Стрела +item.modernmetals.aluminum_bow.name=Алюминиевый Лук +item.modernmetals.aluminum_chestplate.name=Алюминиевый Нагрудник +item.modernmetals.aluminum_crackhammer.name=Алюминиевый Измельчающий Молот +item.modernmetals.aluminum_crossbow.name=Алюминиевый Арбалет +item.modernmetals.aluminum_door.name=Алюминиевый Двери +item.modernmetals.aluminum_fishing_rod.name=Алюминиевый Удочка +item.modernmetals.aluminum_gear.name=Алюминиевый Шестерня +item.modernmetals.aluminum_helmet.name=Алюминиевый Шлем +item.modernmetals.aluminum_hoe.name=Алюминиевый Мотыга +item.modernmetals.aluminum_horse_armor.name=Алюминиевый Конская Броня +item.modernmetals.aluminum_ingot.name=Алюминиевый Слиток +item.modernmetals.aluminum_leggings.name=Алюминиевый Поножки +item.modernmetals.aluminum_nugget.name=Алюминиевый Самородок +item.modernmetals.aluminum_ore.name=Алюминиевый Руда +item.modernmetals.aluminum_pickaxe.name=Алюминиевый Кирка +item.modernmetals.aluminum_powder.name=Измельчённый Алюминиевый +item.modernmetals.aluminum_rod.name=Алюминиевый Стержень +item.modernmetals.aluminum_shears.name=Алюминиевый Ножницы +item.modernmetals.aluminum_shield.name=Алюминиевый Щит +item.modernmetals.aluminum_shovel.name=Алюминиевый Лопата +item.modernmetals.aluminum_smallblend.name=Маленькая Кучка Алюминиевый Смеси +item.modernmetals.aluminum_smallpowder.name=Маленькая Кучка Измельчённого Алюминиевый +item.modernmetals.aluminum_sword.name=Алюминиевый Меч +item.modernmetals.aluminum_trapdoor.name=Алюминиевый Люк + +material.aluminumbrass.name=Алюминиевая Латунь +item.modernmetals.aluminumbrass_arrow.name=Алюминиево-Латунный Стрела +item.modernmetals.aluminumbrass_axe.name=Алюминиево-Латунный Топор +item.modernmetals.aluminumbrass_bars.name=Алюминиево-Латунный Прутья +item.modernmetals.aluminumbrass_blend.name=Алюминиево-Латунный Смеси +item.modernmetals.aluminumbrass_block.name=Блок Алюминиево-Латунный +item.modernmetals.aluminumbrass_boots.name=Алюминиево-Латунный Ботинки +item.modernmetals.aluminumbrass_bolt.name=Алюминиево-Латунный Арбалетная Стрела +item.modernmetals.aluminumbrass_bow.name=Алюминиево-Латунный Лук +item.modernmetals.aluminumbrass_chestplate.name=Алюминиево-Латунный Нагрудник +item.modernmetals.aluminumbrass_crackhammer.name=Алюминиево-Латунный Измельчающий Молот +item.modernmetals.aluminumbrass_crossbow.name=Алюминиево-Латунный Арбалет +item.modernmetals.aluminumbrass_door.name=Алюминиево-Латунный Двери +item.modernmetals.aluminumbrass_fishing_rod.name=Алюминиево-Латунный Удочка +item.modernmetals.aluminumbrass_gear.name=Алюминиево-Латунный Шестерня +item.modernmetals.aluminumbrass_helmet.name=Алюминиево-Латунный Шлем +item.modernmetals.aluminumbrass_hoe.name=Алюминиево-Латунный Мотыга +item.modernmetals.aluminumbrass_horse_armor.name=Алюминиево-Латунный Конская Броня +item.modernmetals.aluminumbrass_ingot.name=Алюминиево-Латунный Слиток +item.modernmetals.aluminumbrass_leggings.name=Алюминиево-Латунный Поножки +item.modernmetals.aluminumbrass_nugget.name=Алюминиево-Латунный Самородок +item.modernmetals.aluminumbrass_ore.name=Алюминиево-Латунный Руда +item.modernmetals.aluminumbrass_pickaxe.name=Алюминиево-Латунный Кирка +item.modernmetals.aluminumbrass_powder.name=Измельчённый Алюминиево-Латунный +item.modernmetals.aluminumbrass_rod.name=Алюминиево-Латунный Стержень +item.modernmetals.aluminumbrass_shears.name=Алюминиево-Латунный Ножницы +item.modernmetals.aluminumbrass_shield.name=Алюминиево-Латунный Щит +item.modernmetals.aluminumbrass_shovel.name=Алюминиево-Латунный Лопата +item.modernmetals.aluminumbrass_smallblend.name=Маленькая Кучка Алюминиево-Латунный Смеси +item.modernmetals.aluminumbrass_smallpowder.name=Маленькая Кучка Измельчённого Алюминиево-Латунный +item.modernmetals.aluminumbrass_sword.name=Алюминиево-Латунный Меч +item.modernmetals.aluminumbrass_trapdoor.name=Алюминиево-Латунный Люк + +material.antimony.name=Antimony +item.modernmetals.antimony_arrow.name=Antimony Стрела +item.modernmetals.antimony_axe.name=Antimony Топор +item.modernmetals.antimony_bars.name=Antimony Прутья +item.modernmetals.antimony_block.name=Блок Antimony +item.modernmetals.antimony_boots.name=Antimony Ботинки +item.modernmetals.antimony_chestplate.name=Antimony Нагрудник +item.modernmetals.antimony_crackhammer.name=Antimony Измельчающий Молот +item.modernmetals.antimony_door.name=Antimony Двери +item.modernmetals.antimony_helmet.name=Antimony Шлем +item.modernmetals.antimony_hoe.name=Antimony Мотыга +item.modernmetals.antimony_ingot.name=Antimony Слиток +item.modernmetals.antimony_leggings.name=Antimony Поножки +item.modernmetals.antimony_nugget.name=Antimony Самородок +item.modernmetals.antimony_ore.name=Antimony Руда +item.modernmetals.antimony_pickaxe.name=Antimony Кирка +item.modernmetals.antimony_powder.name=Измельчённый Antimony +item.modernmetals.antimony_shears.name=Antimony Ножницы +item.modernmetals.antimony_shield.name=Antimony Щит +item.modernmetals.antimony_shovel.name=Antimony Лопата +item.modernmetals.antimony_smallpowder.name=Маленькая Кучка Измельчённого Antimony +item.modernmetals.antimony_sword.name=Antimony Меч +item.modernmetals.antimony_trapdoor.name=Antimony Люк + +material.beryllium.name=Beryllium +item.modernmetals.beryllium_arrow.name=Beryllium Стрела +item.modernmetals.beryllium_axe.name=Beryllium Топор +item.modernmetals.beryllium_bars.name=Beryllium Прутья +item.modernmetals.beryllium_blend.name=Beryllium Смеси +item.modernmetals.beryllium_block.name=Блок Beryllium +item.modernmetals.beryllium_boots.name=Beryllium Ботинки +item.modernmetals.beryllium_bolt.name=Beryllium Арбалетная Стрела +item.modernmetals.beryllium_bow.name=Beryllium Лук +item.modernmetals.beryllium_chestplate.name=Beryllium Нагрудник +item.modernmetals.beryllium_crackhammer.name=Beryllium Измельчающий Молот +item.modernmetals.beryllium_crossbow.name=Beryllium Арбалет +item.modernmetals.beryllium_door.name=Beryllium Двери +item.modernmetals.beryllium_fishing_rod.name=Beryllium Удочка +item.modernmetals.beryllium_gear.name=Beryllium Шестерня +item.modernmetals.beryllium_helmet.name=Beryllium Шлем +item.modernmetals.beryllium_hoe.name=Beryllium Мотыга +item.modernmetals.beryllium_horse_armor.name=Beryllium Конская Броня +item.modernmetals.beryllium_ingot.name=Beryllium Слиток +item.modernmetals.beryllium_leggings.name=Beryllium Поножки +item.modernmetals.beryllium_nugget.name=Beryllium Самородок +item.modernmetals.beryllium_ore.name=Beryllium Руда +item.modernmetals.beryllium_pickaxe.name=Beryllium Кирка +item.modernmetals.beryllium_powder.name=Измельчённый Beryllium +item.modernmetals.beryllium_rod.name=Beryllium Стержень +item.modernmetals.beryllium_shears.name=Beryllium Ножницы +item.modernmetals.beryllium_shield.name=Beryllium Щит +item.modernmetals.beryllium_shovel.name=Beryllium Лопата +item.modernmetals.beryllium_smallblend.name=Маленькая Кучка Beryllium Смеси +item.modernmetals.beryllium_smallpowder.name=Маленькая Кучка Измельчённого Beryllium +item.modernmetals.beryllium_sword.name=Beryllium Меч +item.modernmetals.beryllium_trapdoor.name=Beryllium Люк + + +material.bismuth.name=Bismuth +item.modernmetals.bismuth_arrow.name=Bismuth Стрела +item.modernmetals.bismuth_axe.name=Bismuth Топор +item.modernmetals.bismuth_bars.name=Bismuth Прутья +item.modernmetals.bismuth_block.name=Блок Bismuth +item.modernmetals.bismuth_boots.name=Bismuth Ботинки +item.modernmetals.bismuth_chestplate.name=Bismuth Нагрудник +item.modernmetals.bismuth_crackhammer.name=Bismuth Измельчающий Молот +item.modernmetals.bismuth_door.name=Bismuth Двери +item.modernmetals.bismuth_helmet.name=Bismuth Шлем +item.modernmetals.bismuth_hoe.name=Bismuth Мотыга +item.modernmetals.bismuth_ingot.name=Bismuth Слиток +item.modernmetals.bismuth_leggings.name=Bismuth Поножки +item.modernmetals.bismuth_nugget.name=Bismuth Самородок +item.modernmetals.bismuth_ore.name=Bismuth Руда +item.modernmetals.bismuth_pickaxe.name=Bismuth Кирка +item.modernmetals.bismuth_powder.name=Измельчённый Bismuth +item.modernmetals.bismuth_shears.name=Bismuth Ножницы +item.modernmetals.bismuth_shovel.name=Bismuth Лопата +item.modernmetals.bismuth_shield.name=Bismuth Щит +item.modernmetals.bismuth_smallpowder.name=Маленькая Кучка Измельчённого Bismuth +item.modernmetals.bismuth_sword.name=Bismuth Меч +item.modernmetals.bismuth_trapdoor.name=Bismuth Люк + +material.boron.name=Boron +item.modernmetals.boron_arrow.name=Boron Стрела +item.modernmetals.boron_axe.name=Boron Топор +item.modernmetals.boron_bars.name=Boron Прутья +item.modernmetals.boron_blend.name=Boron Смеси +item.modernmetals.boron_block.name=Блок Boron +item.modernmetals.boron_boots.name=Boron Ботинки +item.modernmetals.boron_bolt.name=Boron Арбалетная Стрела +item.modernmetals.boron_bow.name=Boron Лук +item.modernmetals.boron_chestplate.name=Boron Нагрудник +item.modernmetals.boron_crackhammer.name=Boron Измельчающий Молот +item.modernmetals.boron_crossbow.name=Boron Арбалет +item.modernmetals.boron_door.name=Boron Двери +item.modernmetals.boron_fishing_rod.name=Boron Удочка +item.modernmetals.boron_gear.name=Boron Шестерня +item.modernmetals.boron_helmet.name=Boron Шлем +item.modernmetals.boron_hoe.name=Boron Мотыга +item.modernmetals.boron_horse_armor.name=Boron Конская Броня +item.modernmetals.boron_ingot.name=Boron Слиток +item.modernmetals.boron_leggings.name=Boron Поножки +item.modernmetals.boron_nugget.name=Boron Самородок +item.modernmetals.boron_ore.name=Boron Руда +item.modernmetals.boron_pickaxe.name=Boron Кирка +item.modernmetals.boron_powder.name=Измельчённый Boron +item.modernmetals.boron_rod.name=Boron Стержень +item.modernmetals.boron_shears.name=Boron Ножницы +item.modernmetals.boron_shield.name=Boron Щит +item.modernmetals.boron_shovel.name=Boron Лопата +item.modernmetals.boron_smallblend.name=Маленькая Кучка Boron Смеси +item.modernmetals.boron_smallpowder.name=Маленькая Кучка Измельчённого Boron +item.modernmetals.boron_sword.name=Boron Меч +item.modernmetals.boron_trapdoor.name=Boron Люк + +material.cadmium.name=Cadmium +item.modernmetals.cadmium_arrow.name=Cadmium Стрела +item.modernmetals.cadmium_axe.name=Cadmium Топор +item.modernmetals.cadmium_bars.name=Cadmium Прутья +item.modernmetals.cadmium_blend.name=Cadmium Смеси +item.modernmetals.cadmium_block.name=Блок Cadmium +item.modernmetals.cadmium_bolt.name=Cadmium Арбалетная Стрела +item.modernmetals.cadmium_boots.name=Cadmium Ботинки +item.modernmetals.cadmium_bow.name=Cadmium Лук +item.modernmetals.cadmium_chestplate.name=Cadmium Нагрудник +item.modernmetals.cadmium_crackhammer.name=Cadmium Измельчающий Молот +item.modernmetals.cadmium_crossbow.name=Cadmium Арбалет +item.modernmetals.cadmium_door.name=Cadmium Двери +item.modernmetals.cadmium_fishing_rod.name=Cadmium Удочка +item.modernmetals.cadmium_gear.name=Cadmium Шестерня +item.modernmetals.cadmium_helmet.name=Cadmium Шлем +item.modernmetals.cadmium_hoe.name=Cadmium Мотыга +item.modernmetals.cadmium_horse_armor.name=Cadmium Конская Броня + + +item.modernmetals.cadmium_ingot.name=Cadmium Слиток +item.modernmetals.cadmium_leggings.name=Cadmium Поножки +item.modernmetals.cadmium_nugget.name=Cadmium Самородок +item.modernmetals.cadmium_ore.name=Cadmium Руда +item.modernmetals.cadmium_pickaxe.name=Cadmium Кирка +item.modernmetals.cadmium_powder.name=Измельчённый Cadmium +item.modernmetals.cadmium_rod.name=Cadmium Стержень +item.modernmetals.cadmium_shears.name=Cadmium Ножницы +item.modernmetals.cadmium_shovel.name=Cadmium Лопата +item.modernmetals.cadmium_shield.name=Cadmium Щит +item.modernmetals.cadmium_smallblend.name=Маленькая Кучка Cadmium Смеси +item.modernmetals.cadmium_smallpowder.name=Маленькая Кучка Измельчённого Cadmium +item.modernmetals.cadmium_sword.name=Cadmium Меч +item.modernmetals.cadmium_trapdoor.name=Cadmium Люк + +material.chromium.name=Chromium +item.modernmetals.chromium_arrow.name=Chromium Стрела +item.modernmetals.chromium_axe.name=Chromium Топор +item.modernmetals.chromium_bars.name=Chromium Прутья +item.modernmetals.chromium_blend.name=Chromium Смеси +item.modernmetals.chromium_block.name=Блок Chromium +item.modernmetals.chromium_bolt.name=Chromium Арбалетная Стрела +item.modernmetals.chromium_boots.name=Chromium Ботинки +item.modernmetals.chromium_bow.name=Chromium Лук +item.modernmetals.chromium_chestplate.name=Chromium Нагрудник +item.modernmetals.chromium_crackhammer.name=Chromium Измельчающий Молот +item.modernmetals.chromium_crossbow.name=Chromium Арбалет +item.modernmetals.chromium_door.name=Chromium Двери +item.modernmetals.chromium_fishing_rod.name=Chromium Удочка +item.modernmetals.chromium_gear.name=Chromium Шестерня +item.modernmetals.chromium_helmet.name=Chromium Шлем +item.modernmetals.chromium_hoe.name=Chromium Мотыга +item.modernmetals.chromium_horse_armor.name=Chromium Конская Броня +item.modernmetals.chromium_ingot.name=Chromium Слиток +item.modernmetals.chromium_leggings.name=Chromium Поножки +item.modernmetals.chromium_nugget.name=Chromium Самородок +item.modernmetals.chromium_ore.name=Chromium Руда +item.modernmetals.chromium_pickaxe.name=Chromium Кирка +item.modernmetals.chromium_powder.name=Измельчённый Chromium +item.modernmetals.chromium_rod.name=Chromium Стержень +item.modernmetals.chromium_shears.name=Chromium Ножницы +item.modernmetals.chromium_shield.name=Chromium Щит +item.modernmetals.chromium_shovel.name=Chromium Лопата +item.modernmetals.chromium_smallblend.name=Маленькая Кучка Chromium Смеси +item.modernmetals.chromium_smallpowder.name=Маленькая Кучка Измельчённого Chromium +item.modernmetals.chromium_sword.name=Chromium Меч +item.modernmetals.chromium_trapdoor.name=Chromium Люк + +material.galvanizedsteel.name=Galvanized Steel +item.modernmetals.galvanizedsteel_arrow.name=Galvanized Steel Стрела +item.modernmetals.galvanizedsteel_axe.name=Galvanized Steel Топор +item.modernmetals.galvanizedsteel_bars.name=Galvanized Steel Прутья +item.modernmetals.galvanizedsteel_blend.name=Galvanized Steel Смеси +item.modernmetals.galvanizedsteel_block.name=Блок Galvanized Steel +item.modernmetals.galvanizedsteel_bolt.name=Galvanized Steel Арбалетная Стрела +item.modernmetals.galvanizedsteel_boots.name=Galvanized Steel Ботинки +item.modernmetals.galvanizedsteel_bow.name=Galvanized Steel Лук +item.modernmetals.galvanizedsteel_chestplate.name=Galvanized Steel Нагрудник +item.modernmetals.galvanizedsteel_crackhammer.name=Galvanized Steel Измельчающий Молот +item.modernmetals.galvanizedsteel_crossbow.name=Galvanized Steel Арбалет +item.modernmetals.galvanizedsteel_door.name=Galvanized Steel Двери +item.modernmetals.galvanizedsteel_fishing_rod.name=Galvanized Steel Удочка +item.modernmetals.galvanizedsteel_gear.name=Galvanized Steel Шестерня +item.modernmetals.galvanizedsteel_helmet.name=Galvanized Steel Шлем +item.modernmetals.galvanizedsteel_hoe.name=Galvanized Steel Мотыга +item.modernmetals.galvanizedsteel_horse_armor.name=Galvanized Steel Конская Броня +item.modernmetals.galvanizedsteel_ingot.name=Galvanized Steel Слиток +item.modernmetals.galvanizedsteel_leggings.name=Galvanized Steel Поножки +item.modernmetals.galvanizedsteel_nugget.name=Galvanized Steel Самородок +item.modernmetals.galvanizedsteel_ore.name=Galvanized Steel Руда +item.modernmetals.galvanizedsteel_pickaxe.name=Galvanized Steel Кирка +item.modernmetals.galvanizedsteel_powder.name=Измельчённый Galvanized Steel +item.modernmetals.galvanizedsteel_rod.name=Galvanized Steel Стержень +item.modernmetals.galvanizedsteel_shears.name=Galvanized Steel Ножницы +item.modernmetals.galvanizedsteel_shield.name=Galvanized Steel Щит +item.modernmetals.galvanizedsteel_shovel.name=Galvanized Steel Лопата +item.modernmetals.galvanizedsteel_smallblend.name=Маленькая Кучка Galvanized Steel Смеси +item.modernmetals.galvanizedsteel_smallpowder.name=Маленькая Кучка Измельчённого Galvanized Steel +item.modernmetals.galvanizedsteel_sword.name=Galvanized Steel Меч +item.modernmetals.galvanizedsteel_trapdoor.name=Galvanized Steel Люк + +material.iridium.name=Iridium +item.modernmetals.iridium_arrow.name=Iridium Стрела +item.modernmetals.iridium_axe.name=Iridium Топор +item.modernmetals.iridium_bars.name=Iridium Прутья +item.modernmetals.iridium_blend.name=Iridium Смеси +item.modernmetals.iridium_block.name=Блок Iridium +item.modernmetals.iridium_bolt.name=Iridium Арбалетная Стрела +item.modernmetals.iridium_boots.name=Iridium Ботинки +item.modernmetals.iridium_bow.name=Iridium Лук +item.modernmetals.iridium_chestplate.name=Iridium Нагрудник +item.modernmetals.iridium_crackhammer.name=Iridium Измельчающий Молот +item.modernmetals.iridium_crossbow.name=Iridium Арбалет +item.modernmetals.iridium_door.name=Iridium Двери +item.modernmetals.iridium_fishing_rod.name=Iridium Удочка +item.modernmetals.iridium_gear.name=Iridium Шестерня +item.modernmetals.iridium_helmet.name=Iridium Шлем +item.modernmetals.iridium_hoe.name=Iridium Мотыга +item.modernmetals.iridium_horse_armor.name=Iridium Конская Броня +item.modernmetals.iridium_ingot.name=Iridium Слиток +item.modernmetals.iridium_leggings.name=Iridium Поножки +item.modernmetals.iridium_nugget.name=Iridium Самородок +item.modernmetals.iridium_ore.name=Iridium Руда +item.modernmetals.iridium_pickaxe.name=Iridium Кирка +item.modernmetals.iridium_powder.name=Измельчённый Iridium +item.modernmetals.iridium_rod.name=Iridium Стержень +item.modernmetals.iridium_shears.name=Iridium Ножницы +item.modernmetals.iridium_shield.name=Iridium Щит +item.modernmetals.iridium_shovel.name=Iridium Лопата +item.modernmetals.iridium_smallblend.name=Маленькая Кучка Iridium Смеси +item.modernmetals.iridium_smallpowder.name=Маленькая Кучка Измельчённого Iridium +item.modernmetals.iridium_sword.name=Iridium Меч +item.modernmetals.iridium_trapdoor.name=Iridium Люк + +material.magnesium.name=Magnesium +item.modernmetals.magnesium_arrow.name=Magnesium Стрела +item.modernmetals.magnesium_axe.name=Magnesium Топор +item.modernmetals.magnesium_bars.name=Magnesium Прутья +item.modernmetals.magnesium_blend.name=Magnesium Смеси +item.modernmetals.magnesium_block.name=Блок Magnesium +item.modernmetals.magnesium_boots.name=Magnesium Ботинки +item.modernmetals.magnesium_bolt.name=Magnesium Арбалетная Стрела +item.modernmetals.magnesium_bow.name=Magnesium Лук +item.modernmetals.magnesium_chestplate.name=Magnesium Нагрудник +item.modernmetals.magnesium_crackhammer.name=Magnesium Измельчающий Молот +item.modernmetals.magnesium_crossbow.name=Magnesium Арбалет +item.modernmetals.magnesium_door.name=Magnesium Двери +item.modernmetals.magnesium_fishing_rod.name=Magnesium Удочка +item.modernmetals.magnesium_gear.name=Magnesium Шестерня +item.modernmetals.magnesium_helmet.name=Magnesium Шлем +item.modernmetals.magnesium_hoe.name=Magnesium Мотыга +item.modernmetals.magnesium_horse_armor.name=Magnesium Конская Броня +item.modernmetals.magnesium_ingot.name=Magnesium Слиток +item.modernmetals.magnesium_leggings.name=Magnesium Поножки +item.modernmetals.magnesium_nugget.name=Magnesium Самородок +item.modernmetals.magnesium_ore.name=Magnesium Руда +item.modernmetals.magnesium_pickaxe.name=Magnesium Кирка +item.modernmetals.magnesium_powder.name=Измельчённый Magnesium +item.modernmetals.magnesium_rod.name=Magnesium Стержень +item.modernmetals.magnesium_shears.name=Magnesium Ножницы +item.modernmetals.magnesium_shield.name=Magnesium Щит +item.modernmetals.magnesium_shovel.name=Magnesium Лопата +item.modernmetals.magnesium_smallblend.name=Маленькая Кучка Magnesium Смеси +item.modernmetals.magnesium_smallpowder.name=Маленькая Кучка Измельчённого Magnesium +item.modernmetals.magnesium_sword.name=Magnesium Меч +item.modernmetals.magnesium_trapdoor.name=Magnesium Люк + +material.manganese.name=Manganese +item.modernmetals.manganese_arrow.name=Manganese Стрела +item.modernmetals.manganese_axe.name=Manganese Топор +item.modernmetals.manganese_bars.name=Manganese Прутья +item.modernmetals.manganese_blend.name=Manganese Смеси +item.modernmetals.manganese_block.name=Блок Manganese +item.modernmetals.manganese_bolt.name=Manganese Арбалетная Стрела +item.modernmetals.manganese_boots.name=Manganese Ботинки +item.modernmetals.manganese_bow.name=Manganese Лук +item.modernmetals.manganese_chestplate.name=Manganese Нагрудник +item.modernmetals.manganese_crackhammer.name=Manganese Измельчающий Молот +item.modernmetals.manganese_crossbow.name=Manganese Арбалет +item.modernmetals.manganese_door.name=Manganese Двери +item.modernmetals.manganese_fishing_rod.name=Manganese Удочка +item.modernmetals.manganese_gear.name=Manganese Шестерня +item.modernmetals.manganese_helmet.name=Manganese Шлем +item.modernmetals.manganese_hoe.name=Manganese Мотыга +item.modernmetals.manganese_horse_armor.name=Manganese Конская Броня +item.modernmetals.manganese_ingot.name=Manganese Слиток +item.modernmetals.manganese_leggings.name=Manganese Поножки +item.modernmetals.manganese_nugget.name=Manganese Самородок +item.modernmetals.manganese_ore.name=Manganese Руда +item.modernmetals.manganese_pickaxe.name=Manganese Кирка +item.modernmetals.manganese_powder.name=Измельчённый Manganese +item.modernmetals.manganese_rod.name=Manganese Стержень +item.modernmetals.manganese_shears.name=Manganese Ножницы +item.modernmetals.manganese_shield.name=Manganese Щит +item.modernmetals.manganese_shovel.name=Manganese Лопата +item.modernmetals.manganese_smallblend.name=Маленькая Кучка Manganese Смеси +item.modernmetals.manganese_smallpowder.name=Маленькая Кучка Измельчённого Manganese +item.modernmetals.manganese_sword.name=Manganese Меч +item.modernmetals.manganese_trapdoor.name=Manganese Люк + +material.nichrome.name=Nichrome +item.modernmetals.nichrome_arrow.name=Nichrome Стрела +item.modernmetals.nichrome_axe.name=Nichrome Топор +item.modernmetals.nichrome_bars.name=Nichrome Прутья +item.modernmetals.nichrome_blend.name=Nichrome Смеси +item.modernmetals.nichrome_block.name=Блок Nichrome +item.modernmetals.nichrome_bolt.name=Nichrome Арбалетная Стрела +item.modernmetals.nichrome_boots.name=Nichrome Ботинки +item.modernmetals.nichrome_bow.name=Nichrome Лук +item.modernmetals.nichrome_chestplate.name=Nichrome Нагрудник +item.modernmetals.nichrome_crackhammer.name=Nichrome Измельчающий Молот +item.modernmetals.nichrome_crossbow.name=Nichrome Арбалет +item.modernmetals.nichrome_door.name=Nichrome Двери +item.modernmetals.nichrome_fishing_rod.name=Nichrome Удочка +item.modernmetals.nichrome_gear.name=Nichrome Шестерня +item.modernmetals.nichrome_helmet.name=Nichrome Шлем +item.modernmetals.nichrome_hoe.name=Nichrome Мотыга +item.modernmetals.nichrome_horse_armor.name=Nichrome Конская Броня +item.modernmetals.nichrome_ingot.name=Nichrome Слиток +item.modernmetals.nichrome_leggings.name=Nichrome Поножки +item.modernmetals.nichrome_nugget.name=Nichrome Самородок +item.modernmetals.nichrome_ore.name=Nichrome Руда +item.modernmetals.nichrome_pickaxe.name=Nichrome Кирка +item.modernmetals.nichrome_powder.name=Измельчённый Nichrome +item.modernmetals.nichrome_rod.name=Nichrome Стержень +item.modernmetals.nichrome_shears.name=Nichrome Ножницы +item.modernmetals.nichrome_shield.name=Nichrome Щит +item.modernmetals.nichrome_shovel.name=Nichrome Лопата +item.modernmetals.nichrome_smallblend.name=Маленькая Кучка Nichrome Смеси +item.modernmetals.nichrome_smallpowder.name=Маленькая Кучка Измельчённого Nichrome +item.modernmetals.nichrome_sword.name=Nichrome Меч +item.modernmetals.nichrome_trapdoor.name=Nichrome Люк + +material.osmium.name=Osmium +item.modernmetals.osmium_arrow.name=Osmium Стрела +item.modernmetals.osmium_axe.name=Osmium Топор +item.modernmetals.osmium_bars.name=Osmium Прутья +item.modernmetals.osmium_blend.name=Osmium Смеси +item.modernmetals.osmium_block.name=Блок Osmium +item.modernmetals.osmium_boots.name=Osmium Ботинки +item.modernmetals.osmium_bolt.name=Osmium Арбалетная Стрела +item.modernmetals.osmium_bow.name=Osmium Лук +item.modernmetals.osmium_chestplate.name=Osmium Нагрудник +item.modernmetals.osmium_crackhammer.name=Osmium Измельчающий Молот +item.modernmetals.osmium_crossbow.name=Osmium Арбалет +item.modernmetals.osmium_door.name=Osmium Двери +item.modernmetals.osmium_fishing_rod.name=Osmium Удочка +item.modernmetals.osmium_gear.name=Osmium Шестерня +item.modernmetals.osmium_helmet.name=Osmium Шлем +item.modernmetals.osmium_hoe.name=Osmium Мотыга +item.modernmetals.osmium_horse_armor.name=Osmium Конская Броня +item.modernmetals.osmium_ingot.name=Osmium Слиток +item.modernmetals.osmium_leggings.name=Osmium Поножки +item.modernmetals.osmium_nugget.name=Osmium Самородок +item.modernmetals.osmium_ore.name=Osmium Руда +item.modernmetals.osmium_pickaxe.name=Osmium Кирка +item.modernmetals.osmium_powder.name=Измельчённый Osmium +item.modernmetals.osmium_rod.name=Osmium Стержень +item.modernmetals.osmium_shears.name=Osmium Ножницы +item.modernmetals.osmium_shield.name=Osmium Щит +item.modernmetals.osmium_shovel.name=Osmium Лопата +item.modernmetals.osmium_smallblend.name=Маленькая Кучка Osmium Смеси +item.modernmetals.osmium_smallpowder.name=Маленькая Кучка Измельчённого Osmium +item.modernmetals.osmium_sword.name=Osmium Меч +item.modernmetals.osmium_trapdoor.name=Osmium Люк + +material.pewter.name=Pewter +item.modernmetals.pewter_arrow.name=Pewter Стрела +item.modernmetals.pewter_axe.name=Pewter Топор +item.modernmetals.pewter_bars.name=Pewter Прутья +item.modernmetals.pewter_blend.name=Pewter Смеси +item.modernmetals.pewter_block.name=Блок Pewter +item.modernmetals.pewter_boots.name=Pewter Ботинки +item.modernmetals.pewter_chestplate.name=Pewter Нагрудник +item.modernmetals.pewter_crackhammer.name=Pewter Измельчающий Молот +item.modernmetals.pewter_door.name=Pewter Двери +item.modernmetals.pewter_helmet.name=Pewter Шлем +item.modernmetals.pewter_hoe.name=Pewter Мотыга +item.modernmetals.pewter_ingot.name=Pewter Слиток +item.modernmetals.pewter_leggings.name=Pewter Поножки +item.modernmetals.pewter_nugget.name=Pewter Самородок +item.modernmetals.pewter_ore.name=Pewter Руда +item.modernmetals.pewter_pickaxe.name=Pewter Кирка +item.modernmetals.pewter_powder.name=Измельчённый Pewter +item.modernmetals.pewter_shears.name=Pewter Ножницы +item.modernmetals.pewter_shield.name=Pewter Щит +item.modernmetals.pewter_shovel.name=Pewter Лопата +item.modernmetals.pewter_smallpowder.name=Маленькая Кучка Измельчённого Pewter +item.modernmetals.pewter_sword.name=Pewter Меч +item.modernmetals.pewter_trapdoor.name=Pewter Люк + +material.plutonium.name=Plutonium +item.modernmetals.plutonium_arrow.name=Plutonium Стрела +item.modernmetals.plutonium_axe.name=Plutonium Топор +item.modernmetals.plutonium_bars.name=Plutonium Прутья +item.modernmetals.plutonium_blend.name=Plutonium Смеси +item.modernmetals.plutonium_block.name=Блок Plutonium +item.modernmetals.plutonium_bolt.name=Plutonium Арбалетная Стрела +item.modernmetals.plutonium_boots.name=Plutonium Ботинки +item.modernmetals.plutonium_bow.name=Plutonium Лук +item.modernmetals.plutonium_chestplate.name=Plutonium Нагрудник +item.modernmetals.plutonium_crackhammer.name=Plutonium Измельчающий Молот +item.modernmetals.plutonium_crossbow.name=Plutonium Арбалет +item.modernmetals.plutonium_door.name=Plutonium Двери +item.modernmetals.plutonium_fishing_rod.name=Plutonium Удочка +item.modernmetals.plutonium_gear.name=Plutonium Шестерня +item.modernmetals.plutonium_helmet.name=Plutonium Шлем +item.modernmetals.plutonium_hoe.name=Plutonium Мотыга +item.modernmetals.plutonium_horse_armor.name=Plutonium Конская Броня +item.modernmetals.plutonium_ingot.name=Plutonium Слиток +item.modernmetals.plutonium_leggings.name=Plutonium Поножки +item.modernmetals.plutonium_nugget.name=Plutonium Самородок +item.modernmetals.plutonium_ore.name=Plutonium Руда +item.modernmetals.plutonium_pickaxe.name=Plutonium Кирка +item.modernmetals.plutonium_powder.name=Измельчённый Plutonium +item.modernmetals.plutonium_rod.name=Plutonium Стержень +item.modernmetals.plutonium_shears.name=Plutonium Ножницы +item.modernmetals.plutonium_shield.name=Plutonium Щит +item.modernmetals.plutonium_shovel.name=Plutonium Лопата +item.modernmetals.plutonium_smallblend.name=Маленькая Кучка Plutonium Смеси +item.modernmetals.plutonium_smallpowder.name=Маленькая Кучка Измельчённого Plutonium +item.modernmetals.plutonium_sword.name=Plutonium Меч +item.modernmetals.plutonium_trapdoor.name=Plutonium Люк + +material.rutile.name=Rutile +item.modernmetals.rutile_arrow.name=Rutile Стрела +item.modernmetals.rutile_axe.name=Rutile Топор +item.modernmetals.rutile_bars.name=Rutile Прутья +item.modernmetals.rutile_blend.name=Rutile Смеси +item.modernmetals.rutile_block.name=Блок Rutile +item.modernmetals.rutile_boots.name=Rutile Ботинки +item.modernmetals.rutile_bolt.name=Rutile Арбалетная Стрела +item.modernmetals.rutile_bow.name=Rutile Лук +item.modernmetals.rutile_chestplate.name=Rutile Нагрудник +item.modernmetals.rutile_crackhammer.name=Rutile Измельчающий Молот +item.modernmetals.rutile_crossbow.name=Rutile Арбалет +item.modernmetals.rutile_door.name=Rutile Двери +item.modernmetals.rutile_fishing_rod.name=Rutile Удочка +item.modernmetals.rutile_gear.name=Rutile Шестерня +item.modernmetals.rutile_helmet.name=Rutile Шлем +item.modernmetals.rutile_hoe.name=Rutile Мотыга +item.modernmetals.rutile_horse_armor.name=Rutile Конская Броня +item.modernmetals.rutile_ingot.name=Rutile Слиток +item.modernmetals.rutile_leggings.name=Rutile Поножки +item.modernmetals.rutile_nugget.name=Rutile Самородок +item.modernmetals.rutile_ore.name=Rutile Руда +item.modernmetals.rutile_pickaxe.name=Rutile Кирка +item.modernmetals.rutile_powder.name=Измельчённый Rutile +item.modernmetals.rutile_rod.name=Rutile Стержень +item.modernmetals.rutile_shears.name=Rutile Ножницы +item.modernmetals.rutile_shield.name=Rutile Щит +item.modernmetals.rutile_shovel.name=Rutile Лопата +item.modernmetals.rutile_smallblend.name=Маленькая Кучка Rutile Смеси +item.modernmetals.rutile_smallpowder.name=Маленькая Кучка Измельчённого Rutile +item.modernmetals.rutile_sword.name=Rutile Меч +item.modernmetals.rutile_trapdoor.name=Rutile Люк + +material.stainlesssteel.name=Stainless Steel +item.modernmetals.stainlesssteel_arrow.name=Stainless Steel Стрела +item.modernmetals.stainlesssteel_axe.name=Stainless Steel Топор +item.modernmetals.stainlesssteel_bars.name=Stainless Steel Прутья +item.modernmetals.stainlesssteel_blend.name=Stainless Steel Смеси +item.modernmetals.stainlesssteel_block.name=Блок Stainless Steel +item.modernmetals.stainlesssteel_bolt.name=Stainless Steel Арбалетная Стрела +item.modernmetals.stainlesssteel_boots.name=Stainless Steel Ботинки +item.modernmetals.stainlesssteel_bow.name=Stainless Steel Лук +item.modernmetals.stainlesssteel_chestplate.name=Stainless Steel Нагрудник +item.modernmetals.stainlesssteel_crackhammer.name=Stainless Steel Измельчающий Молот +item.modernmetals.stainlesssteel_crossbow.name=Stainless Steel Арбалет +item.modernmetals.stainlesssteel_door.name=Stainless Steel Двери +item.modernmetals.stainlesssteel_fishing_rod.name=Stainless Steel Удочка +item.modernmetals.stainlesssteel_gear.name=Stainless Steel Шестерня +item.modernmetals.stainlesssteel_helmet.name=Stainless Steel Шлем +item.modernmetals.stainlesssteel_hoe.name=Stainless Steel Мотыга +item.modernmetals.stainlesssteel_horse_armor.name=Stainless Steel Конская Броня +item.modernmetals.stainlesssteel_ingot.name=Stainless Steel Слиток +item.modernmetals.stainlesssteel_leggings.name=Stainless Steel Поножки +item.modernmetals.stainlesssteel_nugget.name=Stainless Steel Самородок +item.modernmetals.stainlesssteel_ore.name=Stainless Steel Руда +item.modernmetals.stainlesssteel_pickaxe.name=Stainless Steel Кирка +item.modernmetals.stainlesssteel_powder.name=Измельчённый Stainless Steel +item.modernmetals.stainlesssteel_rod.name=Stainless Steel Стержень +item.modernmetals.stainlesssteel_shears.name=Stainless Steel Ножницы +item.modernmetals.stainlesssteel_shield.name=Stainless Steel Щит +item.modernmetals.stainlesssteel_shovel.name=Stainless Steel Лопата +item.modernmetals.stainlesssteel_smallblend.name=Маленькая Кучка Stainless Steel Смеси +item.modernmetals.stainlesssteel_smallpowder.name=Маленькая Кучка Измельчённого Stainless Steel +item.modernmetals.stainlesssteel_sword.name=Stainless Steel Меч +item.modernmetals.stainlesssteel_trapdoor.name=Stainless Steel Люк + +material.tantalum.name=Tantalum +item.modernmetals.tantalum_arrow.name=Tantalum Стрела +item.modernmetals.tantalum_axe.name=Tantalum Топор +item.modernmetals.tantalum_bars.name=Tantalum Прутья +item.modernmetals.tantalum_blend.name=Tantalum Смеси +item.modernmetals.tantalum_block.name=Блок Tantalum +item.modernmetals.tantalum_bolt.name=Tantalum Арбалетная Стрела +item.modernmetals.tantalum_boots.name=Tantalum Ботинки +item.modernmetals.tantalum_bow.name=Tantalum Лук +item.modernmetals.tantalum_chestplate.name=Tantalum Нагрудник +item.modernmetals.tantalum_crackhammer.name=Tantalum Измельчающий Молот +item.modernmetals.tantalum_crossbow.name=Tantalum Арбалет +item.modernmetals.tantalum_door.name=Tantalum Двери +item.modernmetals.tantalum_fishing_rod.name=Tantalum Удочка +item.modernmetals.tantalum_gear.name=Tantalum Шестерня +item.modernmetals.tantalum_helmet.name=Tantalum Шлем +item.modernmetals.tantalum_hoe.name=Tantalum Мотыга +item.modernmetals.tantalum_horse_armor.name=Tantalum Конская Броня +item.modernmetals.tantalum_ingot.name=Tantalum Слиток +item.modernmetals.tantalum_leggings.name=Tantalum Поножки +item.modernmetals.tantalum_nugget.name=Tantalum Самородок +item.modernmetals.tantalum_ore.name=Tantalum Руда +item.modernmetals.tantalum_pickaxe.name=Tantalum Кирка +item.modernmetals.tantalum_powder.name=Измельчённый Tantalum +item.modernmetals.tantalum_rod.name=Tantalum Стержень +item.modernmetals.tantalum_shears.name=Tantalum Ножницы +item.modernmetals.tantalum_shield.name=Tantalum Щит +item.modernmetals.tantalum_shovel.name=Tantalum Лопата +item.modernmetals.tantalum_smallblend.name=Маленькая Кучка Tantalum Смеси +item.modernmetals.tantalum_smallpowder.name=Маленькая Кучка Измельчённого Tantalum +item.modernmetals.tantalum_sword.name=Tantalum Меч +item.modernmetals.tantalum_trapdoor.name=Tantalum Люк + +material.titanium.name=Titanium +item.modernmetals.titanium_arrow.name=Titanium Стрела +item.modernmetals.titanium_axe.name=Titanium Топор +item.modernmetals.titanium_bars.name=Titanium Прутья +item.modernmetals.titanium_blend.name=Titanium Смеси +item.modernmetals.titanium_block.name=Блок Titanium +item.modernmetals.titanium_boots.name=Titanium Ботинки +item.modernmetals.titanium_bolt.name=Titanium Арбалетная Стрела +item.modernmetals.titanium_bow.name=Titanium Лук +item.modernmetals.titanium_chestplate.name=Titanium Нагрудник +item.modernmetals.titanium_crackhammer.name=Titanium Измельчающий Молот +item.modernmetals.titanium_crossbow.name=Titanium Арбалет +item.modernmetals.titanium_door.name=Titanium Двери +item.modernmetals.titanium_fishing_rod.name=Titanium Удочка +item.modernmetals.titanium_gear.name=Titanium Шестерня +item.modernmetals.titanium_helmet.name=Titanium Шлем +item.modernmetals.titanium_hoe.name=Titanium Мотыга +item.modernmetals.titanium_horse_armor.name=Titanium Конская Броня +item.modernmetals.titanium_ingot.name=Titanium Слиток +item.modernmetals.titanium_leggings.name=Titanium Поножки +item.modernmetals.titanium_nugget.name=Titanium Самородок +item.modernmetals.titanium_ore.name=Titanium Руда +item.modernmetals.titanium_pickaxe.name=Titanium Кирка +item.modernmetals.titanium_powder.name=Измельчённый Titanium +item.modernmetals.titanium_rod.name=Titanium Стержень +item.modernmetals.titanium_shears.name=Titanium Ножницы +item.modernmetals.titanium_shield.name=Titanium Щит +item.modernmetals.titanium_shovel.name=Titanium Лопата +item.modernmetals.titanium_smallblend.name=Маленькая Кучка Titanium Смеси +item.modernmetals.titanium_smallpowder.name=Маленькая Кучка Измельчённого Titanium +item.modernmetals.titanium_sword.name=Titanium Меч +item.modernmetals.titanium_trapdoor.name=Titanium Люк + +material.tungsten.name=Tungsten +item.modernmetals.tungsten_arrow.name=Tungsten Стрела +item.modernmetals.tungsten_axe.name=Tungsten Топор +item.modernmetals.tungsten_bars.name=Tungsten Прутья +item.modernmetals.tungsten_blend.name=Tungsten Смеси +item.modernmetals.tungsten_block.name=Блок Tungsten +item.modernmetals.tungsten_boots.name=Tungsten Ботинки +item.modernmetals.tungsten_bolt.name=Tungsten Арбалетная Стрела +item.modernmetals.tungsten_bow.name=Tungsten Лук +item.modernmetals.tungsten_chestplate.name=Tungsten Нагрудник +item.modernmetals.tungsten_crackhammer.name=Tungsten Измельчающий Молот +item.modernmetals.tungsten_crossbow.name=Tungsten Арбалет +item.modernmetals.tungsten_door.name=Tungsten Двери +item.modernmetals.tungsten_fishing_rod.name=Tungsten Удочка +item.modernmetals.tungsten_gear.name=Tungsten Шестерня +item.modernmetals.tungsten_helmet.name=Tungsten Шлем +item.modernmetals.tungsten_hoe.name=Tungsten Мотыга +item.modernmetals.tungsten_horse_armor.name=Tungsten Конская Броня +item.modernmetals.tungsten_ingot.name=Tungsten Слиток +item.modernmetals.tungsten_leggings.name=Tungsten Поножки +item.modernmetals.tungsten_nugget.name=Tungsten Самородок +item.modernmetals.tungsten_ore.name=Tungsten Руда +item.modernmetals.tungsten_pickaxe.name=Tungsten Кирка +item.modernmetals.tungsten_powder.name=Измельчённый Tungsten +item.modernmetals.tungsten_rod.name=Tungsten Стержень +item.modernmetals.tungsten_shears.name=Tungsten Ножницы +item.modernmetals.tungsten_shield.name=Tungsten Щит +item.modernmetals.tungsten_shovel.name=Tungsten Лопата +item.modernmetals.tungsten_smallblend.name=Маленькая Кучка Tungsten Смеси +item.modernmetals.tungsten_smallpowder.name=Маленькая Кучка Измельчённого Tungsten +item.modernmetals.tungsten_sword.name=Tungsten Меч +item.modernmetals.tungsten_trapdoor.name=Tungsten Люк + +material.uranium.name=Uranium +item.modernmetals.uranium_arrow.name=Uranium Стрела +item.modernmetals.uranium_axe.name=Uranium Топор +item.modernmetals.uranium_bars.name=Uranium Прутья +item.modernmetals.uranium_blend.name=Uranium Смеси +item.modernmetals.uranium_block.name=Блок Uranium +item.modernmetals.uranium_boots.name=Uranium Ботинки +item.modernmetals.uranium_bolt.name=Uranium Арбалетная Стрела +item.modernmetals.uranium_bow.name=Uranium Лук +item.modernmetals.uranium_chestplate.name=Uranium Нагрудник +item.modernmetals.uranium_crackhammer.name=Uranium Измельчающий Молот +item.modernmetals.uranium_crossbow.name=Uranium Арбалет +item.modernmetals.uranium_door.name=Uranium Двери +item.modernmetals.uranium_fishing_rod.name=Uranium Удочка +item.modernmetals.uranium_gear.name=Uranium Шестерня +item.modernmetals.uranium_helmet.name=Uranium Шлем +item.modernmetals.uranium_hoe.name=Uranium Мотыга +item.modernmetals.uranium_horse_armor.name=Uranium Конская Броня +item.modernmetals.uranium_ingot.name=Uranium Слиток +item.modernmetals.uranium_leggings.name=Uranium Поножки +item.modernmetals.uranium_nugget.name=Uranium Самородок +item.modernmetals.uranium_ore.name=Uranium Руда +item.modernmetals.uranium_pickaxe.name=Uranium Кирка +item.modernmetals.uranium_powder.name=Измельчённый Uranium +item.modernmetals.uranium_rod.name=Uranium Стержень +item.modernmetals.uranium_shears.name=Uranium Ножницы +item.modernmetals.uranium_shield.name=Uranium Щит +item.modernmetals.uranium_shovel.name=Uranium Лопата +item.modernmetals.uranium_smallblend.name=Маленькая Кучка Uranium Смеси +item.modernmetals.uranium_smallpowder.name=Маленькая Кучка Измельчённого Uranium +item.modernmetals.uranium_sword.name=Uranium Меч +item.modernmetals.uranium_trapdoor.name=Uranium Люк + +material.zirconium.name=Zirconium +item.modernmetals.zirconium_arrow.name=Zirconium Стрела +item.modernmetals.zirconium_axe.name=Zirconium Топор +item.modernmetals.zirconium_bars.name=Zirconium Прутья +item.modernmetals.zirconium_blend.name=Zirconium Смеси +item.modernmetals.zirconium_block.name=Блок Zirconium +item.modernmetals.zirconium_bolt.name=Zirconium Арбалетная Стрела +item.modernmetals.zirconium_boots.name=Zirconium Ботинки +item.modernmetals.zirconium_bow.name=Zirconium Лук +item.modernmetals.zirconium_chestplate.name=Zirconium Нагрудник +item.modernmetals.zirconium_crackhammer.name=Zirconium Измельчающий Молот +item.modernmetals.zirconium_crossbow.name=Zirconium Арбалет +item.modernmetals.zirconium_door.name=Zirconium Двери +item.modernmetals.zirconium_fishing_rod.name=Zirconium Удочка +item.modernmetals.zirconium_gear.name=Zirconium Шестерня +item.modernmetals.zirconium_helmet.name=Zirconium Шлем +item.modernmetals.zirconium_hoe.name=Zirconium Мотыга +item.modernmetals.zirconium_horse_armor.name=Zirconium Конская Броня +item.modernmetals.zirconium_ingot.name=Zirconium Слиток +item.modernmetals.zirconium_leggings.name=Zirconium Поножки +item.modernmetals.zirconium_nugget.name=Zirconium Самородок +item.modernmetals.zirconium_ore.name=Zirconium Руда +item.modernmetals.zirconium_pickaxe.name=Zirconium Кирка +item.modernmetals.zirconium_powder.name=Измельчённый Zirconium +item.modernmetals.zirconium_rod.name=Zirconium Стержень +item.modernmetals.zirconium_shears.name=Zirconium Ножницы +item.modernmetals.zirconium_shield.name=Zirconium Щит +item.modernmetals.zirconium_shovel.name=Zirconium Лопата +item.modernmetals.zirconium_smallblend.name=Маленькая Кучка Zirconium Смеси +item.modernmetals.zirconium_smallpowder.name=Маленькая Кучка Измельчённого Zirconium +item.modernmetals.zirconium_sword.name=Zirconium Меч +item.modernmetals.zirconium_trapdoor.name=Zirconium Люк + +tile.modernmetals.aluminum_bars.name=Алюминиевый Прутья +tile.modernmetals.aluminum_block.name=Блок Алюминиевый +tile.modernmetals.aluminum_door.name=Алюминиевый Двери +tile.modernmetals.aluminum_ore.name=Алюминиевый Руда +tile.modernmetals.aluminum_plate.name=Алюминиевый Plate +tile.modernmetals.aluminum_trapdoor.name=Алюминиевый Люк + +tile.modernmetals.aluminumbrass_bars.name=Алюминиево-Латунный Прутья +tile.modernmetals.aluminumbrass_block.name=Блок Алюминиево-Латунный +tile.modernmetals.aluminumbrass_door.name=Алюминиево-Латунный Двери +tile.modernmetals.aluminumbrass_ore.name=Алюминиево-Латунный Руда +tile.modernmetals.aluminumbrass_plate.name=Алюминиево-Латунный Plate +tile.modernmetals.aluminumbrass_trapdoor.name=Алюминиево-Латунный Люк + +tile.modernmetals.boron_bars.name=Boron Прутья +tile.modernmetals.boron_block.name=Блок Boron +tile.modernmetals.boron_door.name=Boron Двери +tile.modernmetals.boron_ore.name=Boron Руда +tile.modernmetals.boron_plate.name=Boron Plate +tile.modernmetals.boron_trapdoor.name=Boron Люк + +tile.modernmetals.cadmium_bars.name=Cadmium Прутья +tile.modernmetals.cadmium_block.name=Блок Cadmium +tile.modernmetals.cadmium_door.name=Cadmium Двери +tile.modernmetals.cadmium_ore.name=Cadmium Руда +tile.modernmetals.cadmium_plate.name=Cadmium Plate +tile.modernmetals.cadmium_trapdoor.name=Cadmium Люк + +tile.modernmetals.chromium_bars.name=Chromium Прутья +tile.modernmetals.chromium_block.name=Блок Chromium +tile.modernmetals.chromium_door.name=Chromium Двери +tile.modernmetals.chromium_ore.name=Chromium Руда +tile.modernmetals.chromium_plate.name=Chromium Plate +tile.modernmetals.chromium_trapdoor.name=Chromium Люк + +tile.modernmetals.galvanizedsteel_bars.name=Galvanized Steel Прутья +tile.modernmetals.galvanizedsteel_block.name=Блок Galvanized Steel +tile.modernmetals.galvanizedsteel_door.name=Galvanized Steel Двери +tile.modernmetals.galvanizedsteel_ore.name=Galvanized Steel Руда +tile.modernmetals.galvanizedsteel_plate.name=Galvanized Steel Plate +tile.modernmetals.galvanizedsteel_trapdoor.name=Galvanized Steel Люк + +tile.modernmetals.iridium_bars.name=Iridium Прутья +tile.modernmetals.iridium_block.name=Блок Iridium +tile.modernmetals.iridium_door.name=Iridium Двери +tile.modernmetals.iridium_ore.name=Iridium Руда +tile.modernmetals.iridium_plate.name=Iridium Plate +tile.modernmetals.iridium_trapdoor.name=Iridium Люк + +tile.modernmetals.magnesium_bars.name=Magnesium Прутья +tile.modernmetals.magnesium_block.name=Блок Magnesium +tile.modernmetals.magnesium_door.name=Magnesium Двери +tile.modernmetals.magnesium_ore.name=Magnesium Руда +tile.modernmetals.magnesium_plate.name=Magnesium Plate +tile.modernmetals.magnesium_trapdoor.name=Magnesium Люк + +tile.modernmetals.manganese_bars.name=Manganese Прутья +tile.modernmetals.manganese_block.name=Блок Manganese +tile.modernmetals.manganese_door.name=Manganese Двери +tile.modernmetals.manganese_ore.name=Manganese Руда +tile.modernmetals.manganese_plate.name=Manganese Plate +tile.modernmetals.manganese_trapdoor.name=Manganese Люк + +tile.modernmetals.nichrome_bars.name=Nichrome Прутья +tile.modernmetals.nichrome_block.name=Блок Nichrome +tile.modernmetals.nichrome_door.name=Nichrome Двери +tile.modernmetals.nichrome_ore.name=Nichrome Руда +tile.modernmetals.nichrome_plate.name=Nichrome Plate +tile.modernmetals.nichrome_trapdoor.name=Nichrome Люк + +tile.modernmetals.osmium_bars.name=Osmium Прутья +tile.modernmetals.osmium_block.name=Блок Osmium +tile.modernmetals.osmium_door.name=Osmium Двери +tile.modernmetals.osmium_ore.name=Osmium Руда +tile.modernmetals.osmium_plate.name=Osmium Plate +tile.modernmetals.osmium_trapdoor.name=Osmium Люк + +tile.modernmetals.plutonium_bars.name=Plutonium Прутья +tile.modernmetals.plutonium_block.name=Блок Plutonium +tile.modernmetals.plutonium_door.name=Plutonium Двери +tile.modernmetals.plutonium_ore.name=Plutonium Руда +tile.modernmetals.plutonium_plate.name=Plutonium Plate +tile.modernmetals.plutonium_trapdoor.name=Plutonium Люк + +tile.modernmetals.rutile_bars.name=Rutile Прутья +tile.modernmetals.rutile_block.name=Блок Rutile +tile.modernmetals.rutile_door.name=Rutile Двери +tile.modernmetals.rutile_ore.name=Rutile Руда +tile.modernmetals.rutile_plate.name=Rutile Plate +tile.modernmetals.rutile_trapdoor.name=Rutile Люк + +tile.modernmetals.stainlesssteel_bars.name=Stainless Steel Прутья +tile.modernmetals.stainlesssteel_block.name=Блок Stainless Steel +tile.modernmetals.stainlesssteel_door.name=Stainless Steel Двери +tile.modernmetals.stainlesssteel_ore.name=Stainless Steel Руда +tile.modernmetals.stainlesssteel_plate.name=Stainless Steel Plate +tile.modernmetals.stainlesssteel_trapdoor.name=Stainless Steel Люк + +tile.modernmetals.tantalum_bars.name=Tantalum Прутья +tile.modernmetals.tantalum_block.name=Блок Tantalum +tile.modernmetals.tantalum_door.name=Tantalum Двери +tile.modernmetals.tantalum_ore.name=Tantalum Руда +tile.modernmetals.tantalum_plate.name=Tantalum Plate +tile.modernmetals.tantalum_trapdoor.name=Tantalum Люк + +tile.modernmetals.titanium_bars.name=Titanium Прутья +tile.modernmetals.titanium_block.name=Блок Titanium +tile.modernmetals.titanium_door.name=Titanium Двери +tile.modernmetals.titanium_ore.name=Titanium Руда +tile.modernmetals.titanium_plate.name=Titanium Plate +tile.modernmetals.titanium_trapdoor.name=Titanium Люк + +tile.modernmetals.tungsten_bars.name=Tungsten Прутья +tile.modernmetals.tungsten_block.name=Блок Tungsten +tile.modernmetals.tungsten_door.name=Tungsten Двери +tile.modernmetals.tungsten_ore.name=Tungsten Руда +tile.modernmetals.tungsten_plate.name=Tungsten Plate +tile.modernmetals.tungsten_trapdoor.name=Tungsten Люк + +tile.modernmetals.uranium_bars.name=Uranium Прутья +tile.modernmetals.uranium_block.name=Блок Uranium +tile.modernmetals.uranium_door.name=Uranium Двери +tile.modernmetals.uranium_ore.name=Uranium Руда +tile.modernmetals.uranium_plate.name=Uranium Plate +tile.modernmetals.uranium_trapdoor.name=Uranium Люк + +tile.modernmetals.zirconium_bars.name=Zirconium Прутья +tile.modernmetals.zirconium_block.name=Блок Zirconium +tile.modernmetals.zirconium_door.name=Zirconium Двери +tile.modernmetals.zirconium_ore.name=Zirconium Руда +tile.modernmetals.zirconium_plate.name=Zirconium Plate +tile.modernmetals.zirconium_trapdoor.name=Zirconium Люк + +tooltip.aluminum.tool=Nothing Special +tooltip.aluminumbrass.tool=Nothing Special +tooltip.cadmium.tool=Nothing Special +tooltip.chromium.tool=Nothing Special +tooltip.galvanized_steel.tool=Nothing Special +tooltip.iridium.tool=Nothing Special +tooltip.magnesium.tool=Nothing Special +tooltip.manganese.tool=Nothing Special +tooltip.nichrome.tool=Nothing Special +tooltip.osmium.tool=Nothing Special +tooltip.plutonium.tool=Nothing Special +tooltip.rutile.tool=Nothing Special +tooltip.stainless_steel.tool=Nothing Special +tooltip.tantalum.tool=Nothing Special +tooltip.titanium.tool=Nothing Special +tooltip.tungsten.tool=Nothing Special +tooltip.uranium.tool=Nothing Special +tooltip.zirconium.tool=Nothing Special + +tooltip.aluminum.armor=Nothing Special +tooltip.aluminumbrass.armor=Nothing Special +tooltip.cadmium.armor=Nothing Special +tooltip.chromium.armor=Nothing Special +tooltip.galvanized_steel.armor=Nothing Special +tooltip.iridium.armor=Nothing Special +tooltip.magnesium.armor=Nothing Special +tooltip.manganese.armor=Nothing Special +tooltip.nichrome.armor=Nothing Special +tooltip.osmium.armor=Nothing Special +tooltip.plutonium.armor=Nothing Special +tooltip.rutile.armor=Nothing Special +tooltip.stainless_steel.armor=Nothing Special +tooltip.tantalum.armor=Nothing Special +tooltip.titanium.armor=Nothing Special +tooltip.tungsten.armor=Nothing Special +tooltip.uranium.armor=Nothing Special +tooltip.zirconium.armor=Nothing Special + +achievement.aluminumbrass_maker=Алюминиево-Латунный +achievement.galvanized_steel_maker=Galvanized Steel +achievement.nichrome_maker=Nichrome +achievement.stainless_steel_maker=Stainless Steel +achievement.titanium_maker=Titanium +achievement.aluminumbrass_maker.desc=Combine powdered Алюминиевый and Brass to make Алюминиево-Латунный +achievement.galvanized_steel_maker.desc=Combine powdered Steel and Zinc to make Galvanized Steel +achievement.nichrome_maker.desc=Combine powdered Nickel and Chrome to make Nichrome +achievement.stainless_steel_maker.desc=Combine powdered Steel and Chrome to make Stainless Steel +achievement.titanium_maker.desc=Combine powdered Rutile and Magnesium to make Titanium + +fluid.modernmetals.aluminum=Molten Алюминиевый +fluid.modernmetals.aluminumbrass=Molten Алюминиево-Латунный +fluid.modernmetals.boron=Molten Boron +fluid.modernmetals.beryllium=Molten Beryllium +fluid.modernmetals.cadmium=Molten Cadmium +fluid.modernmetals.chromium=Molten Chromium +fluid.modernmetals.galvanizedsteel=Molten Galvanized Steel +fluid.modernmetals.iridium=Molten Iridium +fluid.modernmetals.magnesium=Molten Magnesium +fluid.modernmetals.manganese=Molten Manganese +fluid.modernmetals.nichrome=Molten Nichrome +fluid.modernmetals.osmium=Molten Osmium +fluid.modernmetals.plutonium=Molten Plutonium +fluid.modernmetals.rutile=Molten Rutile +fluid.modernmetals.stainlesssteel=Molten Stainless Steel +fluid.modernmetals.tantalum=Molten Tantalum +fluid.modernmetals.titanium=Molten Titanium +fluid.modernmetals.tungsten=Molten Tungsten +fluid.modernmetals.uranium=Molten Uranium +fluid.modernmetals.zirconium=Molten Zirconium + +fluid.modernmetals.aluminum.name=Molten Алюминиевый +fluid.modernmetals.aluminumbrass.name=Molten Алюминиево-Латунный +fluid.modernmetals.boron.name=Molten Boron +fluid.modernmetals.beryllium.name=Molten Beryllium +fluid.modernmetals.cadmium.name=Molten Cadmium +fluid.modernmetals.chromium.name=Molten Chromium +fluid.modernmetals.galvanizedsteel.name=Molten Galvanized Steel +fluid.modernmetals.iridium.name=Molten Iridium +fluid.modernmetals.magnesium.name=Molten Magnesium +fluid.modernmetals.manganese.name=Molten Manganese +fluid.modernmetals.nichrome.name=Molten Nichrome +fluid.modernmetals.osmium.name=Molten Osmium +fluid.modernmetals.plutonium.name=Molten Plutonium +fluid.modernmetals.rutile.name=Molten Rutile +fluid.modernmetals.stainlesssteel.name=Molten Stainless Steel +fluid.modernmetals.tantalum.name=Molten Tantalum +fluid.modernmetals.titanium.name=Molten Titanium +fluid.modernmetals.tungsten.name=Molten Tungsten +fluid.modernmetals.uranium.name=Molten Uranium +fluid.modernmetals.zirconium.name=Molten Zirconium + +tile.modernmetals.aluminum.name=Molten Алюминиевый +tile.modernmetals.aluminumbrass.name=Molten Алюминиево-Латунный +tile.modernmetals.boron.name=Molten Boron +tile.modernmetals.cadmium.name=Molten Cadmium +tile.modernmetals.chromium.name=Molten Chromium +tile.modernmetals.galvanizedsteel.name=Molten Galvanized Steel +tile.modernmetals.iridium.name=Molten Iridium +tile.modernmetals.magnesium.name=Molten Magnesium +tile.modernmetals.manganese.name=Molten Manganese +tile.modernmetals.nichrome.name=Molten Nichrome +tile.modernmetals.osmium.name=Molten Osmium +tile.modernmetals.plutonium.name=Molten Plutonium +tile.modernmetals.rutile.name=Molten Rutile +tile.modernmetals.stainlesssteel.name=Molten Stainless Steel +tile.modernmetals.tantalum.name=Molten Tantalum +tile.modernmetals.titanium.name=Molten Titanium +tile.modernmetals.tungsten.name=Molten Tungsten +tile.modernmetals.uranium.name=Molten Uranium +tile.modernmetals.zirconium.name=Molten Zirconium + +item.modernmetals.aluminum_clump.name=Алюминиевый Clump +item.modernmetals.aluminum_crushed.name=Crushed Алюминиевый Руда +item.modernmetals.aluminum_crushed_purified.name=Purified Crushed Алюминиевый Руда +item.modernmetals.aluminum_crystal.name=Алюминиевый Crystal +item.modernmetals.aluminum_dense_plate.name=Dense Алюминиевый Plate +item.modernmetals.aluminum_powder_dirty.name=Dirty Алюминиевый Dust +item.modernmetals.aluminum_shard.name=Алюминиевый Shard + +item.modernmetals.aluminum_button.name=Алюминиевый Button +item.modernmetals.aluminum_lever.name=Алюминиевый Lever +item.modernmetals.aluminum_pressure_plate.name=Алюминиевый Pressure Plate +item.modernmetals.aluminum_slab.name=Алюминиевый Slab +item.modernmetals.aluminum_stairs.name=Алюминиевый Stairs +item.modernmetals.aluminum_wall.name=Алюминиевый Wall + +tile.modernmetals.aluminum_button.name=Алюминиевый Button +tile.modernmetals.aluminum_lever.name=Алюминиевый Lever +tile.modernmetals.aluminum_pressure_plate.name=Алюминиевый Pressure Plate +tile.modernmetals.aluminum_slab.name=Алюминиевый Slab +tile.modernmetals.aluminum_stairs.name=Алюминиевый Stairs +tile.modernmetals.aluminum_wall.name=Алюминиевый Wall + + +item.modernmetals.aluminumbrass_clump.name=Алюминиево-Латунный Clump +item.modernmetals.aluminumbrass_crushed.name=Crushed Алюминиево-Латунный Руда +item.modernmetals.aluminumbrass_crushed_purified.name=Purified Crushed Алюминиево-Латунный Руда +item.modernmetals.aluminumbrass_crystal.name=Алюминиево-Латунный Crystal +item.modernmetals.aluminumbrass_dense_plate.name=Dense Алюминиево-Латунный Plate +item.modernmetals.aluminumbrass_powder_dirty.name=Dirty Алюминиево-Латунный Dust +item.modernmetals.aluminumbrass_shard.name=Алюминиево-Латунный Shard + +item.modernmetals.aluminumbrass_button.name=Алюминиево-Латунный Button +item.modernmetals.aluminumbrass_lever.name=Алюминиево-Латунный Lever +item.modernmetals.aluminumbrass_pressure_plate.name=Алюминиево-Латунный Pressure Plate +item.modernmetals.aluminumbrass_slab.name=Алюминиево-Латунный Slab +item.modernmetals.aluminumbrass_stairs.name=Алюминиево-Латунный Stairs +item.modernmetals.aluminumbrass_wall.name=Алюминиево-Латунный Wall + +tile.modernmetals.aluminumbrass_button.name=Алюминиево-Латунный Button +tile.modernmetals.aluminumbrass_lever.name=Алюминиево-Латунный Lever +tile.modernmetals.aluminumbrass_pressure_plate.name=Алюминиево-Латунный Pressure Plate +tile.modernmetals.aluminumbrass_slab.name=Алюминиево-Латунный Slab +tile.modernmetals.aluminumbrass_stairs.name=Алюминиево-Латунный Stairs +tile.modernmetals.aluminumbrass_wall.name=Алюминиево-Латунный Wall + + +tile.modernmetals.beryllium_bars.name=Beryllium Прутья +tile.modernmetals.beryllium_block.name=Блок Beryllium +tile.modernmetals.beryllium_door.name=Beryllium Двери +tile.modernmetals.beryllium_ore.name=Beryllium Руда +tile.modernmetals.beryllium_plate.name=Beryllium Plate +tile.modernmetals.beryllium_trapdoor.name=Beryllium Люк + +item.modernmetals.beryllium_clump.name=Beryllium Clump +item.modernmetals.beryllium_crushed.name=Crushed Beryllium Руда +item.modernmetals.beryllium_crushed_purified.name=Purified Crushed Beryllium Руда +item.modernmetals.beryllium_crystal.name=Beryllium Crystal +item.modernmetals.beryllium_dense_plate.name=Dense Beryllium Plate +item.modernmetals.beryllium_powder_dirty.name=Dirty Beryllium Dust +item.modernmetals.beryllium_shard.name=Beryllium Shard +item.modernmetals.beryllium_button.name=Beryllium Button +item.modernmetals.beryllium_lever.name=Beryllium Lever +item.modernmetals.beryllium_pressure_plate.name=Beryllium Pressure Plate +item.modernmetals.beryllium_slab.name=Beryllium Slab +item.modernmetals.beryllium_stairs.name=Beryllium Stairs +item.modernmetals.beryllium_wall.name=Beryllium Wall + + +tile.modernmetals.beryllium.name=Molten Beryllium +tile.modernmetals.beryllium_button.name=Beryllium Button +tile.modernmetals.beryllium_lever.name=Beryllium Lever +tile.modernmetals.beryllium_pressure_plate.name=Beryllium Pressure Plate +tile.modernmetals.beryllium_slab.name=Beryllium Slab +tile.modernmetals.beryllium_stairs.name=Beryllium Stairs +tile.modernmetals.beryllium_wall.name=Beryllium Wall + + +item.modernmetals.boron_clump.name=Boron Clump +item.modernmetals.boron_crushed.name=Crushed Boron Руда +item.modernmetals.boron_crushed_purified.name=Purified Crushed Boron Руда +item.modernmetals.boron_crystal.name=Boron Crystal +item.modernmetals.boron_dense_plate.name=Dense Boron Plate +item.modernmetals.boron_powder_dirty.name=Dirty Boron Dust +item.modernmetals.boron_shard.name=Boron Shard + +item.modernmetals.boron_button.name=Boron Button +item.modernmetals.boron_lever.name=Boron Lever +item.modernmetals.boron_pressure_plate.name=Boron Pressure Plate +item.modernmetals.boron_slab.name=Boron Slab +item.modernmetals.boron_stairs.name=Boron Stairs +item.modernmetals.boron_wall.name=Boron Wall + +tile.modernmetals.boron_button.name=Boron Button +tile.modernmetals.boron_lever.name=Boron Lever +tile.modernmetals.boron_pressure_plate.name=Boron Pressure Plate +tile.modernmetals.boron_slab.name=Boron Slab +tile.modernmetals.boron_stairs.name=Boron Stairs +tile.modernmetals.boron_wall.name=Boron Wall + + +item.modernmetals.cadmium_clump.name=Cadmium Clump +item.modernmetals.cadmium_crushed.name=Crushed Cadmium Руда +item.modernmetals.cadmium_crushed_purified.name=Purified Crushed Cadmium Руда +item.modernmetals.cadmium_crystal.name=Cadmium Crystal +item.modernmetals.cadmium_dense_plate.name=Dense Cadmium Plate +item.modernmetals.cadmium_powder_dirty.name=Dirty Cadmium Dust +item.modernmetals.cadmium_shard.name=Cadmium Shard + +item.modernmetals.cadmium_button.name=Cadmium Button +item.modernmetals.cadmium_lever.name=Cadmium Lever +item.modernmetals.cadmium_pressure_plate.name=Cadmium Pressure Plate +item.modernmetals.cadmium_slab.name=Cadmium Slab +item.modernmetals.cadmium_stairs.name=Cadmium Stairs +item.modernmetals.cadmium_wall.name=Cadmium Wall + +tile.modernmetals.cadmium_button.name=Cadmium Button +tile.modernmetals.cadmium_lever.name=Cadmium Lever +tile.modernmetals.cadmium_pressure_plate.name=Cadmium Pressure Plate +tile.modernmetals.cadmium_slab.name=Cadmium Slab +tile.modernmetals.cadmium_stairs.name=Cadmium Stairs +tile.modernmetals.cadmium_wall.name=Cadmium Wall + + +item.modernmetals.chromium_clump.name=Chromium Clump +item.modernmetals.chromium_crushed.name=Crushed Chromium Руда +item.modernmetals.chromium_crushed_purified.name=Purified Crushed Chromium Руда +item.modernmetals.chromium_crystal.name=Chromium Crystal +item.modernmetals.chromium_dense_plate.name=Dense Chromium Plate +item.modernmetals.chromium_powder_dirty.name=Dirty Chromium Dust +item.modernmetals.chromium_shard.name=Chromium Shard + +item.modernmetals.chromium_button.name=Chromium Button +item.modernmetals.chromium_lever.name=Chromium Lever +item.modernmetals.chromium_pressure_plate.name=Chromium Pressure Plate +item.modernmetals.chromium_slab.name=Chromium Slab +item.modernmetals.chromium_stairs.name=Chromium Stairs +item.modernmetals.chromium_wall.name=Chromium Wall + +tile.modernmetals.chromium_button.name=Chromium Button +tile.modernmetals.chromium_lever.name=Chromium Lever +tile.modernmetals.chromium_pressure_plate.name=Chromium Pressure Plate +tile.modernmetals.chromium_slab.name=Chromium Slab +tile.modernmetals.chromium_stairs.name=Chromium Stairs +tile.modernmetals.chromium_wall.name=Chromium Wall + + +item.modernmetals.galvanizedsteel_clump.name=Galvanized Steel Clump +item.modernmetals.galvanizedsteel_crushed.name=Crushed Galvanized Steel Руда +item.modernmetals.galvanizedsteel_crushed_purified.name=Purified Crushed Galvanized Steel Руда +item.modernmetals.galvanizedsteel_crystal.name=Galvanized Steel Crystal +item.modernmetals.galvanizedsteel_dense_plate.name=Dense Galvanized Steel Plate +item.modernmetals.galvanizedsteel_powder_dirty.name=Dirty Galvanized Steel Dust +item.modernmetals.galvanizedsteel_shard.name=Galvanized Steel Shard + +item.modernmetals.galvanizedsteel_button.name=Galvanized Steel Button +item.modernmetals.galvanizedsteel_lever.name=Galvanized Steel Lever +item.modernmetals.galvanizedsteel_pressure_plate.name=Galvanized Steel Pressure Plate +item.modernmetals.galvanizedsteel_slab.name=Galvanized Steel Slab +item.modernmetals.galvanizedsteel_stairs.name=Galvanized Steel Stairs +item.modernmetals.galvanizedsteel_wall.name=Galvanized Steel Wall + +tile.modernmetals.galvanizedsteel_button.name=Galvanized Steel Button +tile.modernmetals.galvanizedsteel_lever.name=Galvanized Steel Lever +tile.modernmetals.galvanizedsteel_pressure_plate.name=Galvanized Steel Pressure Plate +tile.modernmetals.galvanizedsteel_slab.name=Galvanized Steel Slab +tile.modernmetals.galvanizedsteel_stairs.name=Galvanized Steel Stairs +tile.modernmetals.galvanizedsteel_wall.name=Galvanized Steel Wall + + +item.modernmetals.iridium_clump.name=Iridium Clump +item.modernmetals.iridium_crushed.name=Crushed Iridium Руда +item.modernmetals.iridium_crushed_purified.name=Purified Crushed Iridium Руда +item.modernmetals.iridium_crystal.name=Iridium Crystal +item.modernmetals.iridium_dense_plate.name=Dense Iridium Plate +item.modernmetals.iridium_powder_dirty.name=Dirty Iridium Dust +item.modernmetals.iridium_shard.name=Iridium Shard + +item.modernmetals.iridium_button.name=Iridium Button +item.modernmetals.iridium_lever.name=Iridium Lever +item.modernmetals.iridium_pressure_plate.name=Iridium Pressure Plate +item.modernmetals.iridium_slab.name=Iridium Slab +item.modernmetals.iridium_stairs.name=Iridium Stairs +item.modernmetals.iridium_wall.name=Iridium Wall + +tile.modernmetals.iridium_button.name=Iridium Button +tile.modernmetals.iridium_lever.name=Iridium Lever +tile.modernmetals.iridium_pressure_plate.name=Iridium Pressure Plate +tile.modernmetals.iridium_slab.name=Iridium Slab +tile.modernmetals.iridium_stairs.name=Iridium Stairs +tile.modernmetals.iridium_wall.name=Iridium Wall + + +item.modernmetals.magnesium_clump.name=Magnesium Clump +item.modernmetals.magnesium_crushed.name=Crushed Magnesium Руда +item.modernmetals.magnesium_crushed_purified.name=Purified Crushed Magnesium Руда +item.modernmetals.magnesium_crystal.name=Magnesium Crystal +item.modernmetals.magnesium_dense_plate.name=Dense Magnesium Plate +item.modernmetals.magnesium_powder_dirty.name=Dirty Magnesium Dust +item.modernmetals.magnesium_shard.name=Magnesium Shard + +item.modernmetals.magnesium_button.name=Magnesium Button +item.modernmetals.magnesium_lever.name=Magnesium Lever +item.modernmetals.magnesium_pressure_plate.name=Magnesium Pressure Plate +item.modernmetals.magnesium_slab.name=Magnesium Slab +item.modernmetals.magnesium_stairs.name=Magnesium Stairs +item.modernmetals.magnesium_wall.name=Magnesium Wall + +tile.modernmetals.magnesium_button.name=Magnesium Button +tile.modernmetals.magnesium_lever.name=Magnesium Lever +tile.modernmetals.magnesium_pressure_plate.name=Magnesium Pressure Plate +tile.modernmetals.magnesium_slab.name=Magnesium Slab +tile.modernmetals.magnesium_stairs.name=Magnesium Stairs +tile.modernmetals.magnesium_wall.name=Magnesium Wall + + +item.modernmetals.manganese_clump.name=Manganese Clump +item.modernmetals.manganese_crushed.name=Crushed Manganese Руда +item.modernmetals.manganese_crushed_purified.name=Purified Crushed Manganese Руда +item.modernmetals.manganese_crystal.name=Manganese Crystal +item.modernmetals.manganese_dense_plate.name=Dense Manganese Plate +item.modernmetals.manganese_powder_dirty.name=Dirty Manganese Dust +item.modernmetals.manganese_shard.name=Manganese Shard + +item.modernmetals.manganese_button.name=Manganese Button +item.modernmetals.manganese_lever.name=Manganese Lever +item.modernmetals.manganese_pressure_plate.name=Manganese Pressure Plate +item.modernmetals.manganese_slab.name=Manganese Slab +item.modernmetals.manganese_stairs.name=Manganese Stairs +item.modernmetals.manganese_wall.name=Manganese Wall + +tile.modernmetals.manganese_button.name=Manganese Button +tile.modernmetals.manganese_lever.name=Manganese Lever +tile.modernmetals.manganese_pressure_plate.name=Manganese Pressure Plate +tile.modernmetals.manganese_slab.name=Manganese Slab +tile.modernmetals.manganese_stairs.name=Manganese Stairs +tile.modernmetals.manganese_wall.name=Manganese Wall + + +item.modernmetals.nichrome_clump.name=Nichrome Clump +item.modernmetals.nichrome_crushed.name=Crushed Nichrome Руда +item.modernmetals.nichrome_crushed_purified.name=Purified Crushed Nichrome Руда +item.modernmetals.nichrome_crystal.name=Nichrome Crystal +item.modernmetals.nichrome_dense_plate.name=Dense Nichrome Plate +item.modernmetals.nichrome_powder_dirty.name=Dirty Nichrome Dust +item.modernmetals.nichrome_shard.name=Nichrome Shard + +item.modernmetals.nichrome_button.name=Nichrome Button +item.modernmetals.nichrome_lever.name=Nichrome Lever +item.modernmetals.nichrome_pressure_plate.name=Nichrome Pressure Plate +item.modernmetals.nichrome_slab.name=Nichrome Slab +item.modernmetals.nichrome_stairs.name=Nichrome Stairs +item.modernmetals.nichrome_wall.name=Nichrome Wall + +tile.modernmetals.nichrome_button.name=Nichrome Button +tile.modernmetals.nichrome_lever.name=Nichrome Lever +tile.modernmetals.nichrome_pressure_plate.name=Nichrome Pressure Plate +tile.modernmetals.nichrome_slab.name=Nichrome Slab +tile.modernmetals.nichrome_stairs.name=Nichrome Stairs +tile.modernmetals.nichrome_wall.name=Nichrome Wall + + +item.modernmetals.osmium_clump.name=Osmium Clump +item.modernmetals.osmium_crushed.name=Crushed Osmium Руда +item.modernmetals.osmium_crushed_purified.name=Purified Crushed Osmium Руда +item.modernmetals.osmium_crystal.name=Osmium Crystal +item.modernmetals.osmium_dense_plate.name=Dense Osmium Plate +item.modernmetals.osmium_powder_dirty.name=Dirty Osmium Dust +item.modernmetals.osmium_shard.name=Osmium Shard + +item.modernmetals.osmium_button.name=Osmium Button +item.modernmetals.osmium_lever.name=Osmium Lever +item.modernmetals.osmium_pressure_plate.name=Osmium Pressure Plate +item.modernmetals.osmium_slab.name=Osmium Slab +item.modernmetals.osmium_stairs.name=Osmium Stairs +item.modernmetals.osmium_wall.name=Osmium Wall + +tile.modernmetals.osmium_button.name=Osmium Button +tile.modernmetals.osmium_lever.name=Osmium Lever +tile.modernmetals.osmium_pressure_plate.name=Osmium Pressure Plate +tile.modernmetals.osmium_slab.name=Osmium Slab +tile.modernmetals.osmium_stairs.name=Osmium Stairs +tile.modernmetals.osmium_wall.name=Osmium Wall + + +item.modernmetals.plutonium_clump.name=Plutonium Clump +item.modernmetals.plutonium_crushed.name=Crushed Plutonium Руда +item.modernmetals.plutonium_crushed_purified.name=Purified Crushed Plutonium Руда +item.modernmetals.plutonium_crystal.name=Plutonium Crystal +item.modernmetals.plutonium_dense_plate.name=Dense Plutonium Plate +item.modernmetals.plutonium_powder_dirty.name=Dirty Plutonium Dust +item.modernmetals.plutonium_shard.name=Plutonium Shard + +item.modernmetals.plutonium_button.name=Plutonium Button +item.modernmetals.plutonium_lever.name=Plutonium Lever +item.modernmetals.plutonium_pressure_plate.name=Plutonium Pressure Plate +item.modernmetals.plutonium_slab.name=Plutonium Slab +item.modernmetals.plutonium_stairs.name=Plutonium Stairs +item.modernmetals.plutonium_wall.name=Plutonium Wall + +tile.modernmetals.plutonium_button.name=Plutonium Button +tile.modernmetals.plutonium_lever.name=Plutonium Lever +tile.modernmetals.plutonium_pressure_plate.name=Plutonium Pressure Plate +tile.modernmetals.plutonium_slab.name=Plutonium Slab +tile.modernmetals.plutonium_stairs.name=Plutonium Stairs +tile.modernmetals.plutonium_wall.name=Plutonium Wall + + +item.modernmetals.rutile_clump.name=Rutile Clump +item.modernmetals.rutile_crushed.name=Crushed Rutile Руда +item.modernmetals.rutile_crushed_purified.name=Purified Crushed Rutile Руда +item.modernmetals.rutile_crystal.name=Rutile Crystal +item.modernmetals.rutile_dense_plate.name=Dense Rutile Plate +item.modernmetals.rutile_powder_dirty.name=Dirty Rutile Dust +item.modernmetals.rutile_shard.name=Rutile Shard + +item.modernmetals.rutile_button.name=Rutile Button +item.modernmetals.rutile_lever.name=Rutile Lever +item.modernmetals.rutile_pressure_plate.name=Rutile Pressure Plate +item.modernmetals.rutile_slab.name=Rutile Slab +item.modernmetals.rutile_stairs.name=Rutile Stairs +item.modernmetals.rutile_wall.name=Rutile Wall + +tile.modernmetals.rutile_button.name=Rutile Button +tile.modernmetals.rutile_lever.name=Rutile Lever +tile.modernmetals.rutile_pressure_plate.name=Rutile Pressure Plate +tile.modernmetals.rutile_slab.name=Rutile Slab +tile.modernmetals.rutile_stairs.name=Rutile Stairs +tile.modernmetals.rutile_wall.name=Rutile Wall + + +item.modernmetals.stainlesssteel_clump.name=Stainless Steel Clump +item.modernmetals.stainlesssteel_crushed.name=Crushed Stainless Steel Руда +item.modernmetals.stainlesssteel_crushed_purified.name=Purified Crushed Stainless Steel Руда +item.modernmetals.stainlesssteel_crystal.name=Stainless Steel Crystal +item.modernmetals.stainlesssteel_dense_plate.name=Dense Stainless Steel Plate +item.modernmetals.stainlesssteel_powder_dirty.name=Dirty Stainless Steel Dust +item.modernmetals.stainlesssteel_shard.name=Stainless Steel Shard + +item.modernmetals.stainlesssteel_button.name=Stainless Steel Button +item.modernmetals.stainlesssteel_lever.name=Stainless Steel Lever +item.modernmetals.stainlesssteel_pressure_plate.name=Stainless Steel Pressure Plate +item.modernmetals.stainlesssteel_slab.name=Stainless Steel Slab +item.modernmetals.stainlesssteel_stairs.name=Stainless Steel Stairs +item.modernmetals.stainlesssteel_wall.name=Stainless Steel Wall + +tile.modernmetals.stainlesssteel_button.name=Stainless Steel Button +tile.modernmetals.stainlesssteel_lever.name=Stainless Steel Lever +tile.modernmetals.stainlesssteel_pressure_plate.name=Stainless Steel Pressure Plate +tile.modernmetals.stainlesssteel_slab.name=Stainless Steel Slab +tile.modernmetals.stainlesssteel_stairs.name=Stainless Steel Stairs +tile.modernmetals.stainlesssteel_wall.name=Stainless Steel Wall + + +item.modernmetals.tantalum_clump.name=Tantalum Clump +item.modernmetals.tantalum_crushed.name=Crushed Tantalum Руда +item.modernmetals.tantalum_crushed_purified.name=Purified Crushed Tantalum Руда +item.modernmetals.tantalum_crystal.name=Tantalum Crystal +item.modernmetals.tantalum_dense_plate.name=Dense Tantalum Plate +item.modernmetals.tantalum_powder_dirty.name=Dirty Tantalum Dust +item.modernmetals.tantalum_shard.name=Tantalum Shard + +item.modernmetals.tantalum_button.name=Tantalum Button +item.modernmetals.tantalum_lever.name=Tantalum Lever +item.modernmetals.tantalum_pressure_plate.name=Tantalum Pressure Plate +item.modernmetals.tantalum_slab.name=Tantalum Slab +item.modernmetals.tantalum_stairs.name=Tantalum Stairs +item.modernmetals.tantalum_wall.name=Tantalum Wall + +tile.modernmetals.tantalum_button.name=Tantalum Button +tile.modernmetals.tantalum_lever.name=Tantalum Lever +tile.modernmetals.tantalum_pressure_plate.name=Tantalum Pressure Plate +tile.modernmetals.tantalum_slab.name=Tantalum Slab +tile.modernmetals.tantalum_stairs.name=Tantalum Stairs +tile.modernmetals.tantalum_wall.name=Tantalum Wall + + +item.modernmetals.titanium_clump.name=Titanium Clump +item.modernmetals.titanium_crushed.name=Crushed Titanium Руда +item.modernmetals.titanium_crushed_purified.name=Purified Crushed Titanium Руда +item.modernmetals.titanium_crystal.name=Titanium Crystal +item.modernmetals.titanium_dense_plate.name=Dense Titanium Plate +item.modernmetals.titanium_powder_dirty.name=Dirty Titanium Dust +item.modernmetals.titanium_shard.name=Titanium Shard + +item.modernmetals.titanium_button.name=Titanium Button +item.modernmetals.titanium_lever.name=Titanium Lever +item.modernmetals.titanium_pressure_plate.name=Titanium Pressure Plate +item.modernmetals.titanium_slab.name=Titanium Slab +item.modernmetals.titanium_stairs.name=Titanium Stairs +item.modernmetals.titanium_wall.name=Titanium Wall + +tile.modernmetals.titanium_button.name=Titanium Button +tile.modernmetals.titanium_lever.name=Titanium Lever +tile.modernmetals.titanium_pressure_plate.name=Titanium Pressure Plate +tile.modernmetals.titanium_slab.name=Titanium Slab +tile.modernmetals.titanium_stairs.name=Titanium Stairs +tile.modernmetals.titanium_wall.name=Titanium Wall + + +item.modernmetals.tungsten_clump.name=Tungsten Clump +item.modernmetals.tungsten_crushed.name=Crushed Tungsten Руда +item.modernmetals.tungsten_crushed_purified.name=Purified Crushed Tungsten Руда +item.modernmetals.tungsten_crystal.name=Tungsten Crystal +item.modernmetals.tungsten_dense_plate.name=Dense Tungsten Plate +item.modernmetals.tungsten_powder_dirty.name=Dirty Tungsten Dust +item.modernmetals.tungsten_shard.name=Tungsten Shard + +item.modernmetals.tungsten_button.name=Tungsten Button +item.modernmetals.tungsten_lever.name=Tungsten Lever +item.modernmetals.tungsten_pressure_plate.name=Tungsten Pressure Plate +item.modernmetals.tungsten_slab.name=Tungsten Slab +item.modernmetals.tungsten_stairs.name=Tungsten Stairs +item.modernmetals.tungsten_wall.name=Tungsten Wall + +tile.modernmetals.tungsten_button.name=Tungsten Button +tile.modernmetals.tungsten_lever.name=Tungsten Lever +tile.modernmetals.tungsten_pressure_plate.name=Tungsten Pressure Plate +tile.modernmetals.tungsten_slab.name=Tungsten Slab +tile.modernmetals.tungsten_stairs.name=Tungsten Stairs +tile.modernmetals.tungsten_wall.name=Tungsten Wall + + +item.modernmetals.uranium_clump.name=Uranium Clump +item.modernmetals.uranium_crushed.name=Crushed Uranium Руда +item.modernmetals.uranium_crushed_purified.name=Purified Crushed Uranium Руда +item.modernmetals.uranium_crystal.name=Uranium Crystal +item.modernmetals.uranium_dense_plate.name=Dense Uranium Plate +item.modernmetals.uranium_powder_dirty.name=Dirty Uranium Dust +item.modernmetals.uranium_shard.name=Uranium Shard + +item.modernmetals.uranium_button.name=Uranium Button +item.modernmetals.uranium_lever.name=Uranium Lever +item.modernmetals.uranium_pressure_plate.name=Uranium Pressure Plate +item.modernmetals.uranium_slab.name=Uranium Slab +item.modernmetals.uranium_stairs.name=Uranium Stairs +item.modernmetals.uranium_wall.name=Uranium Wall + +tile.modernmetals.uranium_button.name=Uranium Button +tile.modernmetals.uranium_lever.name=Uranium Lever +tile.modernmetals.uranium_pressure_plate.name=Uranium Pressure Plate +tile.modernmetals.uranium_slab.name=Uranium Slab +tile.modernmetals.uranium_stairs.name=Uranium Stairs +tile.modernmetals.uranium_wall.name=Uranium Wall + + +item.modernmetals.zirconium_clump.name=Zirconium Clump +item.modernmetals.zirconium_crushed.name=Crushed Zirconium Руда +item.modernmetals.zirconium_crushed_purified.name=Purified Crushed Zirconium Руда +item.modernmetals.zirconium_crystal.name=Zirconium Crystal +item.modernmetals.zirconium_dense_plate.name=Dense Zirconium Plate +item.modernmetals.zirconium_powder_dirty.name=Dirty Zirconium Dust +item.modernmetals.zirconium_shard.name=Zirconium Shard + +item.modernmetals.zirconium_button.name=Zirconium Button +item.modernmetals.zirconium_lever.name=Zirconium Lever +item.modernmetals.zirconium_pressure_plate.name=Zirconium Pressure Plate +item.modernmetals.zirconium_slab.name=Zirconium Slab +item.modernmetals.zirconium_stairs.name=Zirconium Stairs +item.modernmetals.zirconium_wall.name=Zirconium Wall + +tile.modernmetals.zirconium_button.name=Zirconium Button +tile.modernmetals.zirconium_lever.name=Zirconium Lever +tile.modernmetals.zirconium_pressure_plate.name=Zirconium Pressure Plate +tile.modernmetals.zirconium_slab.name=Zirconium Slab +tile.modernmetals.zirconium_stairs.name=Zirconium Stairs +tile.modernmetals.zirconium_wall.name=Zirconium Wall + + +material.thorium.name=Thorium +item.modernmetals.thorium_arrow.name=Thorium Стрела +item.modernmetals.thorium_axe.name=Thorium Топор +item.modernmetals.thorium_bars.name=Thorium Прутья +item.modernmetals.thorium_blend.name=Thorium Смеси +item.modernmetals.thorium_block.name=Блок Thorium +item.modernmetals.thorium_boots.name=Thorium Ботинки +item.modernmetals.thorium_bolt.name=Thorium Арбалетная Стрела +item.modernmetals.thorium_bow.name=Thorium Лук +item.modernmetals.thorium_chestplate.name=Thorium Нагрудник +item.modernmetals.thorium_crackhammer.name=Thorium Измельчающий Молот +item.modernmetals.thorium_crossbow.name=Thorium Арбалет +item.modernmetals.thorium_door.name=Thorium Двери +item.modernmetals.thorium_fishing_rod.name=Thorium Удочка +item.modernmetals.thorium_gear.name=Thorium Шестерня +item.modernmetals.thorium_helmet.name=Thorium Шлем +item.modernmetals.thorium_hoe.name=Thorium Мотыга +item.modernmetals.thorium_horse_armor.name=Thorium Конская Броня +item.modernmetals.thorium_ingot.name=Thorium Слиток +item.modernmetals.thorium_leggings.name=Thorium Поножки +item.modernmetals.thorium_nugget.name=Thorium Самородок +item.modernmetals.thorium_ore.name=Thorium Руда +item.modernmetals.thorium_pickaxe.name=Thorium Кирка +item.modernmetals.thorium_powder.name=Измельчённый Thorium +item.modernmetals.thorium_rod.name=Thorium Стержень +item.modernmetals.thorium_shears.name=Thorium Ножницы +item.modernmetals.thorium_shield.name=Thorium Щит +item.modernmetals.thorium_shovel.name=Thorium Лопата +item.modernmetals.thorium_smallblend.name=Маленькая Кучка Thorium Смеси +item.modernmetals.thorium_smallpowder.name=Маленькая Кучка Измельчённого Thorium +item.modernmetals.thorium_sword.name=Thorium Меч +item.modernmetals.thorium_trapdoor.name=Thorium Люк +tile.modernmetals.thorium_bars.name=Thorium Прутья +tile.modernmetals.thorium_block.name=Блок Thorium +tile.modernmetals.thorium_door.name=Thorium Двери +tile.modernmetals.thorium_ore.name=Thorium Руда +tile.modernmetals.thorium_plate.name=Thorium Plate +tile.modernmetals.thorium_trapdoor.name=Thorium Люк +fluid.modernmetals.thorium=Molten Thorium +fluid.modernmetals.thorium.name=Molten Thorium +tile.modernmetals.thorium.name=Molten Thorium +item.modernmetals.thorium_clump.name=Thorium Clump +item.modernmetals.thorium_crushed.name=Crushed Thorium Руда +item.modernmetals.thorium_crushed_purified.name=Purified Crushed Thorium Руда +item.modernmetals.thorium_crystal.name=Thorium Crystal +item.modernmetals.thorium_dense_plate.name=Dense Thorium Plate +item.modernmetals.thorium_powder_dirty.name=Dirty Thorium Dust +item.modernmetals.thorium_shard.name=Thorium Shard +item.modernmetals.thorium_button.name=Thorium Button +item.modernmetals.thorium_lever.name=Thorium Lever +item.modernmetals.thorium_pressure_plate.name=Thorium Pressure Plate +item.modernmetals.thorium_slab.name=Thorium Slab +item.modernmetals.thorium_stairs.name=Thorium Stairs +item.modernmetals.thorium_wall.name=Thorium Wall +tile.modernmetals.thorium_button.name=Thorium Button +tile.modernmetals.thorium_lever.name=Thorium Lever +tile.modernmetals.thorium_pressure_plate.name=Thorium Pressure Plate +tile.modernmetals.thorium_slab.name=Thorium Slab +tile.modernmetals.thorium_stairs.name=Thorium Stairs +tile.modernmetals.thorium_wall.name=Thorium Wall + +# Modern Metals settings and restored content +config.modernmetals.material.tooltip=Включает блоки, предметы и рецепты этого материала. Требуется перезапуск игры. +config.modernmetals.title=Modern Metals +config.modernmetals.description=Для применения нужен перезапуск. Руды настраиваются в OreSpawn. +config.modernmetals.content_mode=Уровень фэнтези +config.modernmetals.content_mode.tooltip=Высокий уровень сохраняет все рецепты. Низкий ограничивает нереалистичное снаряжение и строительные блоки. Требуется перезапуск игры. +high_fantasy=Высокий уровень фэнтези +low_fantasy=Низкий уровень фэнтези +config.modernmetals.villager_trades=Торговля с жителями +config.modernmetals.villager_trades.tooltip=Кузнецы могут торговать слитками и снаряжением Modern Metals. Требуется перезапуск игры. +config.modernmetals.connection.mismatch=Уровень на сервере — %s, в вашей игре — %s. Выберите тот же уровень в меню «Моды > Modern Metals > Настройки» и перезапустите игру. +config.modernmetals.connection.protocol=Не удалось проверить уровень фэнтези Modern Metals. Установите одну и ту же сборку Modern Metals на клиенте и сервере. +itemGroup.modernmetals.combat=Modern Metals: боевое снаряжение +item.modernmetals.titanium_casing.name=Корпус из материала «Титан» +item.modernmetals.beryllium_scythe.name=Серп из материала «Бериллий» +item.modernmetals.boron_scythe.name=Серп из материала «Бор» +item.modernmetals.thorium_scythe.name=Серп из материала «Торий» +item.modernmetals.titanium_scythe.name=Серп из материала «Титан» +config.modernmetals.enable_thorium=Включить торий +config.modernmetals.enable_boron=Включить бор +config.modernmetals.enable_beryllium=Включить бериллий +config.modernmetals.enable_cadmium=Включить кадмий +config.modernmetals.enable_nichrome=Включить нихром +config.modernmetals.enable_aluminum=Включить алюминий +config.modernmetals.enable_aluminumbrass=Включить алюминиевую латунь +config.modernmetals.enable_chromium=Включить хром +config.modernmetals.enable_galvanizedsteel=Включить оцинкованную сталь +config.modernmetals.enable_iridium=Включить иридий +config.modernmetals.enable_magnesium=Включить магний +config.modernmetals.enable_manganese=Включить марганец +config.modernmetals.enable_osmium=Включить осмий +config.modernmetals.enable_plutonium=Включить плутоний +config.modernmetals.enable_rutile=Включить рутил +config.modernmetals.enable_stainlesssteel=Включить нержавеющую сталь +config.modernmetals.enable_tantalum=Включить тантал +config.modernmetals.enable_titanium=Включить титан +config.modernmetals.enable_tungsten=Включить вольфрам +config.modernmetals.enable_uranium=Включить уран +config.modernmetals.enable_zirconium=Включить цирконий + +achievement.aluminum_brass_maker=Алюминиево-Латунный +achievement.aluminum_brass_maker.desc=Combine powdered Алюминиевый and Brass to make Алюминиево-Латунный diff --git a/src/main/resources/assets/modernmetals/lang/zh_CN.lang b/src/main/resources/assets/modernmetals/lang/zh_CN.lang index 57e1b4f6..2836870f 100644 --- a/src/main/resources/assets/modernmetals/lang/zh_CN.lang +++ b/src/main/resources/assets/modernmetals/lang/zh_CN.lang @@ -3,7 +3,7 @@ itemGroup.modernmetals.tools=Modern Metals itemGroup.modernmetals.blocks=Modern Metals nei.modernmetals.recipehandler.crusher.name=粉碎的方块/物品 -material.aluminum.name=Aluminum +material.aluminum.name=铝 item.modernmetals.aluminum_arrow.name=铝箭 item.modernmetals.aluminum_axe.name=铝斧 item.modernmetals.aluminum_bars.name=铝栏杆 @@ -1513,3 +1513,46 @@ tile.modernmetals.thorium_stairs.name=钍楼梯 tile.modernmetals.thorium_wall.name=钍墙 + +# Modern Metals settings and restored content +config.modernmetals.material.tooltip=控制是否启用此材料的方块、物品和配方。需要重启游戏。 +config.modernmetals.title=Modern Metals +config.modernmetals.description=重启游戏后应用更改。请在 OreSpawn 中设置矿石生成。 +config.modernmetals.content_mode=奇幻等级 +config.modernmetals.content_mode.tooltip=高奇幻保留全部配方。低奇幻限制不符合现实的装备和建筑部件。需要重启游戏。 +high_fantasy=高奇幻 +low_fantasy=低奇幻 +config.modernmetals.villager_trades=村民交易 +config.modernmetals.villager_trades.tooltip=允许铁匠交易 Modern Metals 的金属锭和装备。需要重启游戏。 +config.modernmetals.connection.mismatch=服务器使用%s,你的游戏使用%s。请在模组 > Modern Metals > 配置中选择与服务器一致的奇幻等级,然后重启游戏。 +config.modernmetals.connection.protocol=Modern Metals 无法核对奇幻等级。请在服务器和客户端使用相同构建版本的 Modern Metals。 +itemGroup.modernmetals.combat=Modern Metals 战斗用品 +item.modernmetals.titanium_casing.name=钛外壳 +item.modernmetals.beryllium_scythe.name=铍镰刀 +item.modernmetals.boron_scythe.name=硼镰刀 +item.modernmetals.thorium_scythe.name=钍镰刀 +item.modernmetals.titanium_scythe.name=钛镰刀 +config.modernmetals.enable_thorium=启用钍 +config.modernmetals.enable_boron=启用硼 +config.modernmetals.enable_beryllium=启用铍 +config.modernmetals.enable_cadmium=启用镉 +config.modernmetals.enable_nichrome=启用镍铬合金 +config.modernmetals.enable_aluminum=启用铝 +config.modernmetals.enable_aluminumbrass=启用铝黄铜 +config.modernmetals.enable_chromium=启用铬 +config.modernmetals.enable_galvanizedsteel=启用镀锌钢 +config.modernmetals.enable_iridium=启用铱 +config.modernmetals.enable_magnesium=启用镁 +config.modernmetals.enable_manganese=启用锰 +config.modernmetals.enable_osmium=启用锇 +config.modernmetals.enable_plutonium=启用钚 +config.modernmetals.enable_rutile=启用金红石 +config.modernmetals.enable_stainlesssteel=启用不锈钢 +config.modernmetals.enable_tantalum=启用钽 +config.modernmetals.enable_titanium=启用钛 +config.modernmetals.enable_tungsten=启用钨 +config.modernmetals.enable_uranium=启用铀 +config.modernmetals.enable_zirconium=启用锆 + +achievement.aluminum_brass_maker=铝黄铜 +achievement.aluminum_brass_maker.desc=混合铝粉和黄铜粉,并烧制铝黄铜 diff --git a/src/main/resources/assets/modernmetals/models/block/aluminum_ore.json b/src/main/resources/assets/modernmetals/models/block/aluminum_ore.json deleted file mode 100644 index 4616b535..00000000 --- a/src/main/resources/assets/modernmetals/models/block/aluminum_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/aluminum_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/aluminumbrass_ore.json b/src/main/resources/assets/modernmetals/models/block/aluminumbrass_ore.json deleted file mode 100644 index ed015015..00000000 --- a/src/main/resources/assets/modernmetals/models/block/aluminumbrass_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/aluminumbrass_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/beryllium_ore.json b/src/main/resources/assets/modernmetals/models/block/beryllium_ore.json deleted file mode 100644 index 6e531aa2..00000000 --- a/src/main/resources/assets/modernmetals/models/block/beryllium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/beryllium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/boron_ore.json b/src/main/resources/assets/modernmetals/models/block/boron_ore.json deleted file mode 100644 index 13dfb423..00000000 --- a/src/main/resources/assets/modernmetals/models/block/boron_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/boron_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/cadmium_ore.json b/src/main/resources/assets/modernmetals/models/block/cadmium_ore.json deleted file mode 100644 index cf3636e0..00000000 --- a/src/main/resources/assets/modernmetals/models/block/cadmium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/cadmium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/chromium_ore.json b/src/main/resources/assets/modernmetals/models/block/chromium_ore.json deleted file mode 100644 index f867a0b3..00000000 --- a/src/main/resources/assets/modernmetals/models/block/chromium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/chromium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_ore.json b/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_ore.json deleted file mode 100644 index f7e8487e..00000000 --- a/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/galvanizedsteel_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/iridium_ore.json b/src/main/resources/assets/modernmetals/models/block/iridium_ore.json deleted file mode 100644 index b3bd98cb..00000000 --- a/src/main/resources/assets/modernmetals/models/block/iridium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/iridium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/magnesium_ore.json b/src/main/resources/assets/modernmetals/models/block/magnesium_ore.json deleted file mode 100644 index 9f6fb257..00000000 --- a/src/main/resources/assets/modernmetals/models/block/magnesium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/magnesium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/manganese_ore.json b/src/main/resources/assets/modernmetals/models/block/manganese_ore.json deleted file mode 100644 index 6c79d985..00000000 --- a/src/main/resources/assets/modernmetals/models/block/manganese_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/manganese_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/nichrome_ore.json b/src/main/resources/assets/modernmetals/models/block/nichrome_ore.json deleted file mode 100644 index 88f7cf51..00000000 --- a/src/main/resources/assets/modernmetals/models/block/nichrome_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/nichrome_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/osmium_ore.json b/src/main/resources/assets/modernmetals/models/block/osmium_ore.json deleted file mode 100644 index 9aabe0d0..00000000 --- a/src/main/resources/assets/modernmetals/models/block/osmium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/osmium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/plutonium_ore.json b/src/main/resources/assets/modernmetals/models/block/plutonium_ore.json deleted file mode 100644 index 2e4233ea..00000000 --- a/src/main/resources/assets/modernmetals/models/block/plutonium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/plutonium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/rutile_ore.json b/src/main/resources/assets/modernmetals/models/block/rutile_ore.json deleted file mode 100644 index 29f9be6c..00000000 --- a/src/main/resources/assets/modernmetals/models/block/rutile_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/rutile_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/stainlesssteel_ore.json b/src/main/resources/assets/modernmetals/models/block/stainlesssteel_ore.json deleted file mode 100644 index e757848f..00000000 --- a/src/main/resources/assets/modernmetals/models/block/stainlesssteel_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/stainlesssteel_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/tantalum_ore.json b/src/main/resources/assets/modernmetals/models/block/tantalum_ore.json deleted file mode 100644 index 4aae5e70..00000000 --- a/src/main/resources/assets/modernmetals/models/block/tantalum_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/tantalum_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/titanium_ore.json b/src/main/resources/assets/modernmetals/models/block/titanium_ore.json deleted file mode 100644 index ddfbdce7..00000000 --- a/src/main/resources/assets/modernmetals/models/block/titanium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/titanium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/tungsten_ore.json b/src/main/resources/assets/modernmetals/models/block/tungsten_ore.json deleted file mode 100644 index 369e7432..00000000 --- a/src/main/resources/assets/modernmetals/models/block/tungsten_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/tungsten_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/uranium_ore.json b/src/main/resources/assets/modernmetals/models/block/uranium_ore.json deleted file mode 100644 index b0dadbef..00000000 --- a/src/main/resources/assets/modernmetals/models/block/uranium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/uranium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/zirconium_ore.json b/src/main/resources/assets/modernmetals/models/block/zirconium_ore.json deleted file mode 100644 index c5835215..00000000 --- a/src/main/resources/assets/modernmetals/models/block/zirconium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/zirconium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/textures/blocks/aluminum_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/aluminum_ore.png deleted file mode 100644 index 0b32648a..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/aluminum_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/aluminumbrass_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/aluminumbrass_ore.png deleted file mode 100644 index fcd49f0a..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/aluminumbrass_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/beryllium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/beryllium_ore.png deleted file mode 100644 index 4a4c166f..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/beryllium_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/boron_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/boron_ore.png deleted file mode 100644 index 1f281395..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/boron_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/cadmium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/cadmium_ore.png deleted file mode 100644 index ce83c564..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/cadmium_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/chromium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/chromium_ore.png deleted file mode 100644 index 0a0dc933..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/chromium_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/galvanizedsteel_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/galvanizedsteel_ore.png deleted file mode 100644 index 3727bc75..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/galvanizedsteel_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/iridium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/iridium_ore.png deleted file mode 100644 index 3dbdbeec..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/iridium_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/magnesium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/magnesium_ore.png deleted file mode 100644 index c14ee1fe..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/magnesium_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/manganese_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/manganese_ore.png deleted file mode 100644 index 9cc66d93..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/manganese_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/nichrome_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/nichrome_ore.png deleted file mode 100644 index b281291c..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/nichrome_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminum_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminum_ore.png new file mode 100644 index 00000000..6cdc2b42 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminum_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminumbrass_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminumbrass_ore.png new file mode 100644 index 00000000..6d280430 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminumbrass_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/beryllium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/beryllium_ore.png new file mode 100644 index 00000000..a7accaa8 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/beryllium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/boron_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/boron_ore.png new file mode 100644 index 00000000..e6665f3f Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/boron_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/cadmium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/cadmium_ore.png new file mode 100644 index 00000000..b12af2d7 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/cadmium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/chromium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/chromium_ore.png new file mode 100644 index 00000000..5b5db264 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/chromium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/galvanizedsteel_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/galvanizedsteel_ore.png new file mode 100644 index 00000000..afbbc42b Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/galvanizedsteel_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/iridium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/iridium_ore.png new file mode 100644 index 00000000..f33bce70 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/iridium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/magnesium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/magnesium_ore.png new file mode 100644 index 00000000..bba78399 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/magnesium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/manganese_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/manganese_ore.png new file mode 100644 index 00000000..c15b0575 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/manganese_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/nichrome_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/nichrome_ore.png new file mode 100644 index 00000000..726c1eac Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/nichrome_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/osmium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/osmium_ore.png new file mode 100644 index 00000000..cf803c4a Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/osmium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/plutonium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/plutonium_ore.png new file mode 100644 index 00000000..ab038d65 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/plutonium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/rutile_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/rutile_ore.png new file mode 100644 index 00000000..72639290 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/rutile_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/stainlesssteel_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/stainlesssteel_ore.png new file mode 100644 index 00000000..7683080d Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/stainlesssteel_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tantalum_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tantalum_ore.png new file mode 100644 index 00000000..1767a2ed Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tantalum_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/titanium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/titanium_ore.png new file mode 100644 index 00000000..5ed04a22 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/titanium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tungsten_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tungsten_ore.png new file mode 100644 index 00000000..cbe271a9 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tungsten_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/uranium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/uranium_ore.png new file mode 100644 index 00000000..7fe6c81a Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/uranium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/zirconium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/zirconium_ore.png new file mode 100644 index 00000000..96854591 Binary files /dev/null and b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/zirconium_ore.png differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/osmium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/osmium_ore.png deleted file mode 100644 index 933d3bf4..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/osmium_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/plutonium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/plutonium_ore.png deleted file mode 100644 index acad015a..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/plutonium_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/rutile_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/rutile_ore.png deleted file mode 100644 index 02df93b0..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/rutile_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/stainlesssteel_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/stainlesssteel_ore.png deleted file mode 100644 index e22f4cc4..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/stainlesssteel_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/tantalum_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/tantalum_ore.png deleted file mode 100644 index 0f016a90..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/tantalum_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/titanium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/titanium_ore.png deleted file mode 100644 index 419fd4d4..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/titanium_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/tungsten_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/tungsten_ore.png deleted file mode 100644 index a4a3414e..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/tungsten_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/uranium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/uranium_ore.png deleted file mode 100644 index cf42b145..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/uranium_ore.png and /dev/null differ diff --git a/src/main/resources/assets/modernmetals/textures/blocks/zirconium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/zirconium_ore.png deleted file mode 100644 index f436d7b9..00000000 Binary files a/src/main/resources/assets/modernmetals/textures/blocks/zirconium_ore.png and /dev/null differ diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index b0f2ba20..6932238a 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,6 +1,6 @@ { "pack":{ - "pack_format":2, + "pack_format":3, "description":"Modern Metals" } } \ No newline at end of file diff --git a/src/test/java/zone/moddev/mc/modernmetals/ContentPolicyTests.java b/src/test/java/zone/moddev/mc/modernmetals/ContentPolicyTests.java new file mode 100644 index 00000000..24404f76 --- /dev/null +++ b/src/test/java/zone/moddev/mc/modernmetals/ContentPolicyTests.java @@ -0,0 +1,39 @@ +package zone.moddev.mc.modernmetals; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import zone.moddev.mc.modernmetals.content.ContentPolicy; + +final class ContentPolicyTests { + @Test void oldAndInvalidSettingsPreserveHighFantasy() { + for(String mode:new String[]{null,"","high_fantasy","unknown"}) + assertTrue(ContentPolicy.forMode(mode).allows("plutonium","pickaxe")); + } + @Test void lowFantasyRetainsIndustrialMaterialsAndRestrictsEquipment() { + final ContentPolicy low=ContentPolicy.forMode(" LOW_FANTASY "); + for(String name:new String[]{"plutonium","uranium","thorium","boron","beryllium","cadmium","rutile","chromium","magnesium","tungsten"}) { + assertFalse(low.allows(name,"pickaxe"),name); + assertFalse(low.allows(name,"helmet"),name); + for(String form:new String[]{"ore","ingot","powder","fluid","plate","block"}) assertTrue(low.allows(name,form),name+":"+form); + } + for(String name:new String[]{"titanium","stainlesssteel","galvanizedsteel"}) { + assertTrue(low.allows(name,"pickaxe"));assertTrue(low.allows(name,"helmet")); + assertFalse(low.allows(name,"bow"));assertFalse(low.allows(name,"crossbow"));assertFalse(low.allows(name,"fishing_rod")); + } + } + @Test void allModernMaterialsFollowTheEquipmentAndEngineeringMatrix() { + final ContentPolicy low=ContentPolicy.forMode("low_fantasy"); + final java.util.Set tools=new java.util.HashSet<>(java.util.Arrays.asList("galvanizedsteel","stainlesssteel","titanium")); + final java.util.Set protective=new java.util.HashSet<>(java.util.Arrays.asList("aluminum","aluminumbrass","galvanizedsteel","stainlesssteel","titanium")); + final java.util.Set structural=new java.util.HashSet<>(java.util.Arrays.asList("aluminum","aluminumbrass","galvanizedsteel","nichrome","stainlesssteel","tantalum","titanium","tungsten","zirconium")); + for(String name:new String[]{"aluminum","aluminumbrass","beryllium","boron","cadmium","chromium","galvanizedsteel","iridium","magnesium","manganese","nichrome","osmium","plutonium","rutile","stainlesssteel","tantalum","thorium","titanium","tungsten","uranium","zirconium"}) { + for(String form:new String[]{"pickaxe","axe","sword","shovel","hoe","crackhammer","scythe","shears","arrow","bolt"}) + assertEquals(tools.contains(name),low.allows(name,form),name+":"+form); + for(String form:new String[]{"helmet","chestplate","leggings","boots","horse_armor","shield","gear"}) + assertEquals(protective.contains(name),low.allows(name,form),name+":"+form); + for(String form:new String[]{"door","trapdoor","bars","stairs","slab","pressure_plate","button","fence","fence_gate","wall","bookshelf","ladder","lever","tripwire_hook","flower_pot"}) + assertEquals(structural.contains(name),low.allows(name,form),name+":"+form); + for(String form:new String[]{"bow","crossbow","fishing_rod"}) assertFalse(low.allows(name,form),name+":"+form); + } + } +} diff --git a/src/test/java/zone/moddev/mc/modernmetals/MaterialConfigurationTests.java b/src/test/java/zone/moddev/mc/modernmetals/MaterialConfigurationTests.java new file mode 100644 index 00000000..bec2e26d --- /dev/null +++ b/src/test/java/zone/moddev/mc/modernmetals/MaterialConfigurationTests.java @@ -0,0 +1,17 @@ +package zone.moddev.mc.modernmetals; + +import static org.junit.jupiter.api.Assertions.*; +import java.lang.reflect.Field; +import org.junit.jupiter.api.Test; +import zone.moddev.mc.modernmetals.util.Config; + +class MaterialConfigurationTests { + private void enabled(final String name) throws Exception { + final Field field = Config.Options.class.getField("enable" + name); + assertTrue(field.getBoolean(null), name + " must be enabled by default"); + } + @Test void boronIsConfigurable() throws Exception { enabled("Boron"); } + @Test void berylliumIsConfigurable() throws Exception { enabled("Beryllium"); } + @Test void thoriumIsConfigurable() throws Exception { enabled("Thorium"); } + @Test void titaniumMaterialRemainsEnabled() throws Exception { enabled("Titanium"); } +} diff --git a/src/test/java/zone/moddev/mc/modernmetals/NativeProviderTests.java b/src/test/java/zone/moddev/mc/modernmetals/NativeProviderTests.java new file mode 100644 index 00000000..d0303833 --- /dev/null +++ b/src/test/java/zone/moddev/mc/modernmetals/NativeProviderTests.java @@ -0,0 +1,56 @@ +package zone.moddev.mc.modernmetals; + +import static org.junit.jupiter.api.Assertions.*; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; +import net.minecraft.util.ResourceLocation; +import org.junit.jupiter.api.Test; +import zone.moddev.mc.orespawn.api.WorldgenProvider; + +class NativeProviderTests { + private Optional build(final Predicate available) throws Exception { + final Class provider = Class.forName("zone.moddev.mc.modernmetals.worldgen.ModernMetalsWorldgenProvider"); + return (Optional) provider.getMethod("build", Predicate.class).invoke(null, available); + } + @Test void preservesHistoricalRulesAndAddsOptionalTitanium() throws Exception { + final JsonObject nativeProvider = build(id -> true).get().toJson(); + Files.createDirectories(Paths.get("build/reports")); + Files.write(Paths.get("build/reports/native-provider.json"), (nativeProvider.toString()+"\n").getBytes(StandardCharsets.UTF_8)); + assertEquals(5, nativeProvider.get("schema_version").getAsInt()); + final JsonObject ores = nativeProvider.getAsJsonObject("ores"); + assertEquals(17, ores.entrySet().size()); + final JsonObject old = new JsonParser().parse(new String(Files.readAllBytes(Paths.get( + "tools/validation/legacy111/fixtures/orespawn3-modernmetals.json")), StandardCharsets.UTF_8)).getAsJsonObject().getAsJsonObject("spawns"); + for (final Map.Entry entry : old.entrySet()) { + final JsonObject params = entry.getValue().getAsJsonObject().getAsJsonObject("parameters"); + final JsonObject ore = ores.getAsJsonObject("modernmetals:legacy/"+entry.getKey()); + assertEquals("orespawn:" + entry.getKey().replace("_ore", ""), ore.get("material").getAsString()); + assertTrue(ore.get("enabled").getAsBoolean()); + assertFalse(ore.get("retrogen").getAsBoolean()); + assertFalse(ore.get("native_generation").getAsBoolean()); + final JsonObject placement = ore.getAsJsonObject("dimension_selectors").getAsJsonObject("orespawn:all_except_nether_end"); + assertEquals("orespawn:standard", placement.get("placement_channel").getAsString()); + assertEquals(params.get("frequency").getAsDouble(), placement.get("frequency").getAsDouble()); + assertEquals(params.get("maxHeight").getAsInt()-1, placement.get("max_y").getAsInt()); + assertEquals(Math.max(1,params.get("size").getAsInt()-params.get("variation").getAsInt()), placement.get("min_quantity").getAsInt()); + assertEquals(params.get("size").getAsInt()+params.get("variation").getAsInt()-1, placement.get("max_quantity").getAsInt()); + } + final JsonObject titanium = ores.getAsJsonObject("modernmetals:legacy/titanium_ore"); + assertFalse(titanium.get("enabled").getAsBoolean()); + assertEquals(ores.getAsJsonObject("modernmetals:legacy/rutile_ore").get("dimension_selectors"),titanium.get("dimension_selectors")); + for (final String section : new String[]{"rocks","terrain_dimensions","geomes","fluid_deposits"}) + assertEquals(0,nativeProvider.getAsJsonObject(section).entrySet().size()); + } + @Test void omitsMissingOrDisabledBlocks() throws Exception { + assertFalse(build(id -> false).isPresent()); + final JsonObject ores = build(id -> !id.getResourcePath().equals("boron_ore")).get().toJson().getAsJsonObject("ores"); + assertEquals(16, ores.entrySet().size()); + assertFalse(ores.has("modernmetals:legacy/boron_ore")); + } +} diff --git a/src/test/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformerTests.java b/src/test/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformerTests.java new file mode 100644 index 00000000..bbd8b083 --- /dev/null +++ b/src/test/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformerTests.java @@ -0,0 +1,87 @@ +package zone.moddev.mc.modernmetals.compat; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodNode; + +class LegacyNamespaceTransformerTests { + + @Test + void linksOldTypesFieldsInheritanceAndReflectionToCurrentClasses() { + final byte[] output = new LegacyNamespaceTransformer().transform("example.Consumer", null, fixture()); + final ClassNode type = new ClassNode(); + new ClassReader(output).accept(type, 0); + assertEquals("example/Consumer", type.name); + assertEquals("zone/moddev/mc/modernmetals/proxy/CommonProxy", type.superName); + assertEquals("Lzone/moddev/mc/modernmetals/ModernMetals;", ((FieldNode) type.fields.get(0)).desc); + + final MethodNode method = (MethodNode) type.methods.get(0); + assertEquals("zone/moddev/mc/modernmetals/ModernMetals", ((FieldInsnNode) method.instructions.get(0)).owner); + assertEquals("zone.moddev.mc.modernmetals.init.Materials", ((LdcInsnNode) method.instructions.get(2)).cst); + assertEquals(Type.getObjectType("zone/moddev/mc/modernmetals/util/Config$Options"), + ((LdcInsnNode) method.instructions.get(4)).cst); + assertEquals("other.mod/textures/ore.png", ((LdcInsnNode) method.instructions.get(6)).cst); + } + + @Test + void doesNotRelocateAddonOwnedClassesInsideTheHistoricalPackage() { + final ClassWriter source = new ClassWriter(0); + source.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "com/mcmoddev/modernmetals/addon/Extra", null, + "com/mcmoddev/modernmetals/proxy/CommonProxy", null); + source.visitEnd(); + + final ClassNode type = new ClassNode(); + new ClassReader(new LegacyNamespaceTransformer().transform("com.mcmoddev.modernmetals.addon.Extra", null, + source.toByteArray())).accept(type, 0); + assertEquals("com/mcmoddev/modernmetals/addon/Extra", type.name); + assertEquals("zone/moddev/mc/modernmetals/proxy/CommonProxy", type.superName); + } + + @Test + void leavesCanonicalClassesAndTheCyanoBridgeAlone() { + final LegacyNamespaceTransformer transformer = new LegacyNamespaceTransformer(); + final byte[] bytes = fixture(); + assertSame(bytes, transformer.transform("zone.moddev.mc.modernmetals.ModernMetals", null, bytes)); + assertNull(transformer.transform("missing", "missing", null)); + + final ClassWriter source = new ClassWriter(0); + source.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "example/Golem", null, "java/lang/Object", null); + source.visitField(Opcodes.ACC_PUBLIC, "old", "Lcyano/modernmetals/init/Blocks;", null, null).visitEnd(); + source.visitEnd(); + final byte[] golem = source.toByteArray(); + assertSame(golem, transformer.transform("example.Golem", null, golem)); + } + + private static byte[] fixture() { + final ClassWriter type = new ClassWriter(0); + type.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "example/Consumer", null, + "com/mcmoddev/modernmetals/proxy/CommonProxy", null); + type.visitField(Opcodes.ACC_PUBLIC, "mod", "Lcom/mcmoddev/modernmetals/ModernMetals;", null, null).visitEnd(); + + final MethodVisitor method = type.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "link", "()V", null, null); + method.visitCode(); + method.visitFieldInsn(Opcodes.GETSTATIC, "com/mcmoddev/modernmetals/ModernMetals", "instance", "Lcom/mcmoddev/modernmetals/ModernMetals;"); + method.visitInsn(Opcodes.POP); + method.visitLdcInsn("com.mcmoddev.modernmetals.init.Materials"); + method.visitInsn(Opcodes.POP); + method.visitLdcInsn(Type.getObjectType("com/mcmoddev/modernmetals/util/Config$Options")); + method.visitInsn(Opcodes.POP); + method.visitLdcInsn("other.mod/textures/ore.png"); + method.visitInsn(Opcodes.POP); + method.visitInsn(Opcodes.RETURN); + method.visitMaxs(1, 0); + method.visitEnd(); + type.visitEnd(); + return type.toByteArray(); + } +} diff --git a/src/test/java/zone/moddev/mc/modernmetals/util/ContentModeConfigurationTests.java b/src/test/java/zone/moddev/mc/modernmetals/util/ContentModeConfigurationTests.java new file mode 100644 index 00000000..8af4a5bd --- /dev/null +++ b/src/test/java/zone/moddev/mc/modernmetals/util/ContentModeConfigurationTests.java @@ -0,0 +1,41 @@ +package zone.moddev.mc.modernmetals.util; + +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +final class ContentModeConfigurationTests { + @Test void defaultSelectorIsHighFantasyAndRequiresRestart() { + final Configuration config = new Configuration(); + assertEquals("high_fantasy", Config.contentMode(config)); + final Property selector = config.getCategory("General").get("contentMode"); + assertArrayEquals(new String[]{"high_fantasy", "low_fantasy"}, selector.getValidValues()); + assertTrue(selector.requiresMcRestart()); + assertEquals("config.modernmetals.content_mode", selector.getLanguageKey()); + } + + @Test void selectedLowFantasySurvivesRepeatedReads() { + final Configuration config = new Configuration(); + config.get("General", "contentMode", "high_fantasy").set(" LOW_FANTASY "); + assertEquals("low_fantasy", Config.contentMode(config)); + assertEquals("low_fantasy", Config.contentMode(config)); + } + + @Test void invalidModeFallsBackToHighFantasy() { + final Configuration config = new Configuration(); + config.get("General", "contentMode", "high_fantasy").set("invalid_value"); + assertEquals("high_fantasy", Config.contentMode(config)); + assertEquals("high_fantasy", config.getCategory("General").get("contentMode").getString()); + } + + @Test void tradeSwitchDefaultsToEnabledAndPreservesAnExplicitOptOut() { + final Configuration config = new Configuration(); + assertTrue(Config.villagerTrades(config)); + final Property selector = config.getCategory("General").get("enableVillagerTrades"); + assertTrue(selector.requiresMcRestart()); + assertEquals("config.modernmetals.villager_trades", selector.getLanguageKey()); + selector.set(false); + assertFalse(Config.villagerTrades(config)); + } +} diff --git a/tools/validation/legacy111/EclipseModelCheck.java b/tools/validation/legacy111/EclipseModelCheck.java new file mode 100644 index 00000000..56af1d95 --- /dev/null +++ b/tools/validation/legacy111/EclipseModelCheck.java @@ -0,0 +1,95 @@ +package review.ide; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import java.util.stream.Collectors; +import org.gradle.tooling.GradleConnector; +import org.gradle.tooling.ProjectConnection; +import org.gradle.tooling.model.build.BuildEnvironment; +import org.gradle.tooling.model.eclipse.EclipseProject; +import org.gradle.tooling.model.eclipse.EclipseSourceDirectory; +import org.gradle.tooling.model.gradle.GradleBuild; + +/** Requests the same project models used by Eclipse Buildship's import wizard. */ +public final class EclipseModelCheck { + + public static void main(final String[] args) throws Exception { + final File project = new File(args[0]); + final File cache = new File(args[1]); + final File javaHome = new File(args[2]); + + try (ProjectConnection connection = GradleConnector.newConnector() + .forProjectDirectory(project).useGradleUserHomeDir(cache) + .useBuildDistribution().connect()) { + final BuildEnvironment environment = connection.model(BuildEnvironment.class) + .setJavaHome(javaHome).withArguments("--offline").get(); + final GradleBuild build = connection.model(GradleBuild.class) + .setJavaHome(javaHome).withArguments("--offline").get(); + final EclipseProject eclipse = connection.model(EclipseProject.class) + .setJavaHome(javaHome).withArguments("--offline").get(); + + if (!"9.6.1".equals(environment.getGradle().getGradleVersion()) + || !"ModernMetals".equals(build.getRootProject().getName()) + || !"ModernMetals".equals(eclipse.getName()) + || eclipse.getSourceDirectories().stream().noneMatch(source -> "build/resources/main".equals(source.getPath())) + || !"1.8".equals(eclipse.getJavaSourceSettings().getSourceLanguageLevel().toString())) { + throw new AssertionError("Wrong wrapper, project or Java source level in the Eclipse model"); + } + eclipse.getClasspath().forEach(dependency -> { + if (!dependency.getFile().isFile()) { + throw new AssertionError("Missing Eclipse library: " + dependency.getFile()); + } + }); + if (eclipse.getSourceDirectories().stream().map(source -> source.getPath()).distinct().count() + != eclipse.getSourceDirectories().size()) { + throw new AssertionError("Duplicate Eclipse source directories"); + } + + for (final EclipseSourceDirectory source : eclipse.getSourceDirectories()) { + verifySourcePackages(new File(project, source.getPath()).toPath()); + } + + if (args.length > 3) { + final List libraries = new ArrayList<>(); + eclipse.getClasspath().forEach(dependency -> libraries.add(dependency.getFile().getAbsolutePath())); + Files.write(new File(args[3]).toPath(), libraries, StandardCharsets.UTF_8); + } + + System.out.println("MODERN_METALS_ECLIPSE_MODEL_PASS gradle=" + + environment.getGradle().getGradleVersion() + " source=1.8 libraries=" + + eclipse.getClasspath().size() + " gradleJava=" + + environment.getJava().getJavaHome()); + } + } + + private static void verifySourcePackages(final Path root) throws IOException { + if (!Files.isDirectory(root)) return; + + final Pattern packageDeclaration = Pattern.compile("(?m)^\\s*package\\s+([\\w.]+)\\s*;"); + final List sources; + try (Stream files = Files.walk(root)) { + sources = files.filter(path -> path.toString().endsWith(".java")) + .collect(Collectors.toList()); + } + + for (final Path file : sources) { + final Path parent = root.relativize(file).getParent(); + final String expected = parent == null ? "" : parent.toString().replace(File.separatorChar, '.'); + final String text = new String(Files.readAllBytes(file), StandardCharsets.UTF_8); + final Matcher declaration = packageDeclaration.matcher(text); + final String declared = declaration.find() ? declaration.group(1) : ""; + + if (!expected.equals(declared)) { + throw new AssertionError("Package does not match the Eclipse source folder: " + file); + } + } + } +} diff --git a/tools/validation/legacy111/README.md b/tools/validation/legacy111/README.md new file mode 100644 index 00000000..4a25e67d --- /dev/null +++ b/tools/validation/legacy111/README.md @@ -0,0 +1,105 @@ +# Modern Metals 1.11 qualification + +Tests and probes are isolated from release jars. All game runs use disposable +directories and worlds. The packaged suite fingerprints its source fixture world +and confirms that imports/reloads do not rewrite it. + +## Automated coverage + +- **JUnit.** Checks default-enabled materials, historical ore conversion, missing + blocks, optional titanium, empty geology declarations, namespace linkage and + the Low Fantasy matrix for all 21 materials and product families. +- **Resources.** Parse every JSON/mcmeta/mod-info file, validate provider schema 5 + from the pinned production OreSpawn jar, check material plate assets/GUI labels + and registered material model references/parents, and parse all workflows with + full action pins. Historical unregistered silicon item placeholders are retained + and parsed. +- **Installed server.** Checks materials, items, storage, fluids, buckets and + five alloy chains. Covers aliases, statistics, named stacks, equipment and NBT. + Exercises ore and material switches, optional titanium, dimension boundaries, + legacy imports, saved overrides, later chunks and Low Fantasy with Tinkers. +- **Optional mods.** Tinkers 2.7.1.34, its matching Mantle, IC2 + 2.7.81-ex111 and Mekanism 9.3.5. Tests cover molten + forms, alloys, casting, actual tool construction/repair and processing recipes. +- **Dependency rejection.** missing/old Base Metals and OreSpawn fail with explicit + version requirements before `world/level.dat` exists. Historical fixture + downloads are checksum verified. +- **Client.** Checks the Forge configuration factory and the OreSpawn Mods entry, the High/Low Fantasy selector, translated labels, + scrolling, Escape/Done, saved restart-required switches, unchanged live registries, baked models, + creative tabs, achievement labels and integrated-world fresh/reload checks. + +`packagedForgeRuntimeTest` runs 18 world scenarios plus five dependency rejection +cases. See [BUILDING](../../../docs/BUILDING.md) for setup. The server template +contains Forge 1.11.2-13.20.1.2588, vanilla server and libraries, without mods, +configuration or worlds. Do not point a test at an active installation. + +## Client commands + +`runtimeProbeJar` builds the development probe. `packagedRuntimeProbeJar` builds +the installed probe. They must never be distributed as part of the mod. + +```text +python run_eclipse_profile.py first --probe --production-output +python run_eclipse_profile.py reload --probe --production-output +``` + +Merge `build/classes/java/main` and `build/resources/main` into that disposable +output first. Keep the editor's `bin/main` unchanged during qualification. + +On Windows, `run_packaged_client.py` launches the installed jar against the +unmodified obfuscated Minecraft client and Forge universal jar. Supply `--java`, +`--cache`, `--installer`, `--template`, `--output`, `--mirror` and `--phase first|reload`. +It verifies the Forge installer and Minecraft client hashes, stages verified +Windows natives in the disposable game directory, and isolates the universal jar from the server jar named +in its manifest. Every ordinary installed mod is copied into the disposable +directory with the packaged game classpath. Inspect the generated +client reports and screenshots separately from manual acceptance. +The matching Mojang asset index and every referenced object are checksum verified +in a disposable asset directory. The script copies matching cached objects and +downloads the rest from Mojang. A different branch's shared Gradle asset index is not +overwritten or assumed to match Minecraft 1.11.2. + +For the overlay check, run `--phase first` with vanilla textures, then run +`--phase reload --ore-host-pack` against the same disposable world. The probe +checks the baked block and inventory faces and records an ore display screenshot +in each run. Copy the first screenshot before reloading to compare both. +Use `--language de_DE`, `zh_CN` or `ru_RU` to check the actual localized +settings and selector values. + +## Multiplayer and original saves + +`check_multiplayer_mode.py` uses the same client inputs and a loopback-only +dedicated server. Supply `--server-jar` and `--client-jar` to test the installed +archives. The four default scenarios cover each pair of High and Low Fantasy. +The server log verifies that a rejected client never enters the world. + +`check_original_saves.py --java --template +--public-jars --candidate +--output ` verifies the SHA-256 of the original +public jars, creates a 1.11.2 beta4.58 fixture, and checks a separate +upgraded copy on first load and reload. Pass `--mirror` for the staged dependencies. It checks placed blocks, filled +buckets and item stacks with non-default count or damage. It stops on any Forge +prompt to remove missing blocks or items. The source worlds are fingerprinted. + +## Limits and separately owned defects + +Base Metals 2.5.1.111021 and OreSpawn 4.1.5.111021 are staged locally. +The suite tests OS1 and OS3 import and checks that existing native overrides +and saved world profiles survive migration. + +Modern Metals 1.11 owns its `General.contentMode` in `ModernMetals.cfg` and the +Forge configuration screen. `check_content_modes.py --java --template --output ` checks defaults, invalid +values, conflicting Base Metals settings, saved-world mode changes and Low Fantasy +with Tinkers, including fresh/reload behavior. It preserves its source fixture. +Core-mod inheritance is reserved for 1.12. See [CONTENT_MODE](../../../docs/CONTENT_MODE.md). + +The 1.10 regression matrix supplies the corresponding checks for 1.11. +The optional runtime tests cover Tinkers, Mantle, Mekanism and IC2. Other +integrations have source and API coverage only. The local issue matrix +records these limits and reports lacking exact reproduction evidence. Radiation, +new alloys/abilities and Construct's Armory are outside this modernization. + +Hosted CI/CodeQL, manual graphical acceptance, real-world migration, multiplayer +pack qualification and publication remain separate steps. No performance budget +is claimed from successful bounded generation alone. diff --git a/tools/validation/legacy111/audit_resources.py b/tools/validation/legacy111/audit_resources.py new file mode 100644 index 00000000..443aef4c --- /dev/null +++ b/tools/validation/legacy111/audit_resources.py @@ -0,0 +1,119 @@ +"""Validate resources, native provider schema, workflow pins and material assets.""" +import argparse +import json +import re +import zipfile +from pathlib import Path +from referencing import Registry, Resource +from jsonschema import Draft202012Validator +import yaml +from run_runtime_checks import ROOT, dependency + + +def language_entries(path): + entries = {} + for line in path.read_text(encoding='utf-8-sig').splitlines(): + if '=' not in line or line.startswith('#'): + continue + key, value = line.split('=', 1) + assert key not in entries, f'{path.name}: duplicate key {key}' + entries[key] = value + return entries + + +def audit_languages(directory, materials): + languages = {path.stem: language_entries(path) for path in directory.glob('*.lang')} + settings = { + 'config.modernmetals.title', 'config.modernmetals.description', + 'config.modernmetals.content_mode', 'config.modernmetals.content_mode.tooltip', + 'config.modernmetals.villager_trades', 'config.modernmetals.villager_trades.tooltip', + 'config.modernmetals.material.tooltip', + 'config.modernmetals.connection.mismatch', 'config.modernmetals.connection.protocol', + 'high_fantasy', 'low_fantasy', 'itemGroup.modernmetals.combat', + } + settings.update('config.modernmetals.enable_' + name for name in materials) + english = languages['en_us'] + restored_material = re.compile(r'\b(?:boron|beryllium|thorium|titanium)(?=\b|_)') + content = {key for key in english if restored_material.search(key)} + for locale in ('en_us', 'de_de', 'ru_ru', 'zh_cn'): + entries = languages.get(locale, {}) + missing = {key for key in settings | content if not entries.get(key)} + assert not missing, f'{locale}: missing translations {sorted(missing)}' + assert entries['config.modernmetals.connection.mismatch'].count('%s') == 2, locale + for locale in ('en_au', 'en_ca', 'en_gb', 'en_nz', 'en_pt'): + assert locale in languages, f'{locale}: missing regional English file' + +def main(): + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('--mirror',type=Path) + args=parser.parse_args() + checked=0 + resources=ROOT/'build/resources/main' + for path in resources.rglob('*'): + if path.is_file() and (path.suffix in ('.json','.mcmeta') or path.name=='mcmod.info'): + json.loads(path.read_text(encoding='utf-8-sig')); checked+=1 + def references(value,key): + if isinstance(value,dict): + for name,child in value.items(): + if name==key and isinstance(child,str) and child.startswith('modernmetals:'):yield child.split(':',1)[1] + else:yield from references(child,key) + elif isinstance(value,list): + for child in value:yield from references(child,key) + assets=resources/'assets/modernmetals' + materials=re.findall(r'material\("([a-z]+)",', (ROOT/'src/main/java/zone/moddev/mc/modernmetals/util/Config.java').read_text()) + audit_languages(assets/'lang', materials) + for path in (assets/'blockstates').glob('*.json'): + for model in references(json.loads(path.read_text()),'model'): + assert (assets/'models/block'/(model+'.json')).is_file(),f'{path.name}: missing block model {model}' + for path in (assets/'models').rglob('*.json'): + # Historical silicon item placeholders belong to no registered material + # in this branch. Preserve those asset names without inventing content. + if path.parent.name=='item' and path.stem.startswith('silicon_') and 'silicon' not in materials:continue + for parent in references(json.loads(path.read_text()),'parent'): + assert (assets/'models'/(parent+'.json')).is_file(),f'{path.name}: missing parent {parent}' + ore_states=sorted((assets/'blockstates').glob('*_ore.json')) + assert ore_states, 'Processed ore assets are missing; run processResources first' + for state in ore_states: + name=state.stem + model=json.loads((assets/'models/block'/state.name).read_text()) + for value in model['textures'].values(): + if value.startswith('modernmetals:'): + texture=value.split(':',1)[1] + assert (assets/'textures'/(texture+'.png')).is_file(),f'{name}: missing texture {texture}' + if name!='thorium_ore': + assert model['parent']=='modernmetals:block/ore_overlay',name + assert model['textures']['base']=='minecraft:blocks/stone',name + assert model['textures']['overlay']==f'modernmetals:blocks/ore_overlays/{name}',name + assert not (assets/'textures/blocks'/(name+'.png')).exists(),f'{name}: stale flat texture' + specs=json.loads((ROOT/'gradle/dependencies.json').read_text()) + with zipfile.ZipFile(dependency(next(s for s in specs if s['id']=='orespawn'),args.mirror)) as jar: + schemas=[json.loads(jar.read(n)) for n in jar.namelist() if n.startswith('META-INF/orespawn/docs/schemas/') and n.endswith('.json')] + registry=Registry().with_resources((s['$id'],Resource.from_contents(s)) for s in schemas) + schema=next(s for s in schemas if s['$id'].endswith('/orespawn-provider.schema.json')) + provider=json.loads((ROOT/'build/reports/native-provider.json').read_text()) + Draft202012Validator(schema,registry=registry).validate(provider) + assert len(provider['ores'])==17 and not provider['ores']['modernmetals:legacy/titanium_ore']['enabled'] + for name in ('boron','beryllium','thorium','titanium'): + for relative in (f'blockstates/{name}_ore.json',f'models/item/{name}_ingot.json', + f'models/item/{name}_plate.json',f'models/block/{name}_plate.json',f'textures/blocks/{name}_plate.png'): + assert (ROOT/'src/main/resources/assets/modernmetals'/relative).is_file(),relative + language=dict(line.split('=',1) for line in (ROOT/'src/main/resources/assets/modernmetals/lang/en_US.lang').read_text().splitlines() if '=' in line and not line.startswith('#')) + assert len(materials)==21 + for key in ('config.modernmetals.content_mode','config.modernmetals.content_mode.tooltip','high_fantasy','low_fantasy'): + assert key in language,key + for name in materials: + assert 'config.modernmetals.enable_'+name in language,name + for relative in (f'models/item/{name}_plate.json',f'models/block/{name}_plate.json',f'textures/blocks/{name}_plate.png'): + assert (ROOT/'src/main/resources/assets/modernmetals'/relative).is_file(),relative + for path in (ROOT/'.github/workflows').glob('*.yml'): + workflow=yaml.load(path.read_text(),Loader=yaml.BaseLoader) + assert workflow.get('on') and workflow.get('jobs'),path + for job in workflow['jobs'].values(): + for step in job.get('steps',[]): + if 'uses' in step: assert re.fullmatch(r'[^@]+@[0-9a-f]{40}',step['uses']),step['uses'] + release=(ROOT/'.github/workflows/deploy-release.yml').read_text() + assert 'confirm_live_publication' in release and '240967(required)' in release and '245586(required)' in release + assert '243157(optional)' in release and '244357' in release + print(f'MODERN_METALS_RESOURCES_PASS json={checked} provider_schema=5 workflows=5') + +if __name__=='__main__':main() diff --git a/tools/validation/legacy111/check_content_modes.py b/tools/validation/legacy111/check_content_modes.py new file mode 100644 index 00000000..ab47b119 --- /dev/null +++ b/tools/validation/legacy111/check_content_modes.py @@ -0,0 +1,59 @@ +"""Check Modern Metals-owned fantasy configuration using installed release jars.""" +import argparse,json,shutil +from pathlib import Path +from run_runtime_checks import ROOT,dependency,prepare,run + +def main(): + parser=argparse.ArgumentParser(description=__doc__) + for name in ('java','template','output'):parser.add_argument('--'+name,type=Path,required=True) + parser.add_argument('--mirror', type=Path) + parser.add_argument('--scenario',choices=['modern-low']) + args=parser.parse_args() + args.output.mkdir(parents=True,exist_ok=False) + specs=json.loads((ROOT/'gradle/dependencies.json').read_text()) + jars=[dependency(s, args.mirror) for s in specs if s.get('runtime')] + jars += [ROOT/'build/libs/ModernMetals-2.5.1.111021.jar',ROOT/'build/test-mods/modernmetals-probe-packaged.jar'] + cases=[('default-with-base-low',None,'low_fantasy',False), + ('modern-low','low_fantasy','high_fantasy',True), + ('modern-high-with-base-low','high_fantasy','low_fantasy',False), + ('invalid-mode','invalid_value','low_fantasy',False)] + completed=[] + for name,modern,base,low in cases: + if args.scenario and name!=args.scenario:continue + target=args.output/name + prepare(target,args.template,jars) + (target/'config').mkdir() + (target/'config/BaseMetals.cfg').write_text('general {\n S:contentMode='+base+'\n}\n') + if modern is not None: + (target/'config/ModernMetals.cfg').write_text('general {\n S:contentMode='+modern+'\n}\n') + for phase in ('first','reload'): + run(args.java,target,phase,{'modern.lowFantasy':str(low).lower(),'modern.saved':'true'}) + text=(target/'config/ModernMetals.cfg').read_text() + assert 'S:contentMode='+('low_fantasy' if low else 'high_fantasy') in text + completed.append(name) + if not args.scenario: + source=args.output/'default-with-base-low'/'world' + # Copy only the disposable fixture; never run against its source directory. + from run_packaged_suite import fingerprint + before=fingerprint(source) + switched=args.output/'high-to-low-existing-world' + prepare(switched,args.template,jars,source) + (switched/'config').mkdir() + (switched/'config/ModernMetals.cfg').write_text('general {\n S:contentMode=low_fantasy\n}\n') + run(args.java,switched,'reload',{'modern.lowFantasy':'true','modern.saved':'true'}) + assert fingerprint(source)==before + completed.append(switched.name) + optional=args.output/'modern-low-tinkers' + prepare(optional,args.template,jars) + (optional/'config').mkdir() + (optional/'config/ModernMetals.cfg').write_text('general {\n S:contentMode=low_fantasy\n}\n') + for spec in specs: + if spec['id'] in ('tconstruct','mantle'): + jar=dependency(spec, args.mirror);shutil.copy2(jar,optional/'mods'/jar.name) + for phase in ('first','reload'): + run(args.java,optional,phase,{'modern.lowFantasy':'true','modern.saved':'true'}) + completed.append(optional.name) + (args.output/'qualification.json').write_text(json.dumps({'passed':True,'scenarios':completed},indent=2)+'\n') + print('MODERN_METALS_OWNED_CONTENT_MODE_PASS',len(completed),flush=True) + +if __name__=='__main__':main() diff --git a/tools/validation/legacy111/check_multiplayer_mode.py b/tools/validation/legacy111/check_multiplayer_mode.py new file mode 100644 index 00000000..0918d2a4 --- /dev/null +++ b/tools/validation/legacy111/check_multiplayer_mode.py @@ -0,0 +1,84 @@ +"""Exercise the fantasy mode handshake over a real loopback Forge connection.""" +import argparse +import json +import socket +import subprocess +import sys +import time +from pathlib import Path + +from run_runtime_checks import FORGE, ROOT, dependency, digest, prepare, write_json + + +def check(args, server_mode, client_mode): + output = args.output / f"{server_mode}-{client_mode}" + server = output / "server" + specs = json.loads((ROOT / "gradle/dependencies.json").read_text()) + jars = [dependency(spec, args.mirror) for spec in specs if spec.get("runtime")] + prepare(server, args.template, jars + [args.server_jar]) + with socket.socket() as reservation: + reservation.bind(("127.0.0.1", 0)) + port = reservation.getsockname()[1] + properties = server / "server.properties" + properties.write_text(properties.read_text().replace("server-port=0", f"server-port={port}")) + config = server / "config/ModernMetals.cfg" + config.parent.mkdir() + config.write_text(f"general {{\n S:contentMode={server_mode}_fantasy\n}}\n") + console = server / "console.log" + mismatch = server_mode != client_mode + rejection = args.incompatible_build or mismatch + with console.open("wb") as log, subprocess.Popen( + [str(args.java), "-Xmx2G", "-jar", FORGE, "nogui"], cwd=server, + stdin=subprocess.PIPE, stdout=log, stderr=subprocess.STDOUT + ) as process: + try: + deadline = time.monotonic() + 90 + while 'For help, type "help"' not in console.read_text(errors="replace"): + if process.poll() is not None or time.monotonic() >= deadline: + raise AssertionError(f"Dedicated server did not start: {console}") + time.sleep(0.25) + command = [sys.executable, str(Path(__file__).with_name("run_packaged_client.py")), + "--java", str(args.java), "--cache", str(args.cache), "--installer", str(args.installer), + "--template", str(args.template), "--output", str(output / "client"), + "--mod-jar", str(args.client_jar), "--remote-port", str(port)] + if args.mirror: + command += ["--mirror", str(args.mirror)] + if client_mode == "low": + command.append("--low-fantasy") + if args.incompatible_build: + command += ["--expected-disconnect", "Use the same Modern Metals build"] + elif mismatch: + command += ["--expected-disconnect", f"The server uses {server_mode.title()} Fantasy, " + f"but your game uses {client_mode.title()} Fantasy"] + subprocess.run(command, check=True, timeout=360) + finally: + if process.poll() is None: + try: + process.communicate(b"stop\n", timeout=45) + except subprocess.TimeoutExpired: + process.kill() + process.wait() + raise + assert process.returncode == 0, console + assert ("ModernCheck joined the game" in console.read_text(errors="replace")) == (not rejection), console + write_json(output / "receipt.json", { + "passed": True, "server_mode": server_mode, "client_mode": client_mode, + "server_sha256": digest(args.server_jar), "client_sha256": digest(args.client_jar), + "joined": not rejection, + }) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + for name in ("java", "cache", "installer", "template", "output", "server-jar", "client-jar"): + parser.add_argument("--" + name, required=True, type=lambda path: Path(path).resolve()) + parser.add_argument("--mirror", type=lambda path: Path(path).resolve()) + parser.add_argument("--scenario", action="append", choices=["high-high", "high-low", "low-high", "low-low"]) + parser.add_argument("--incompatible-build", action="store_true", help="Expect a missing mode advertisement.") + args = parser.parse_args() + for scenario in args.scenario or ["high-low", "low-high", "high-high", "low-low"]: + check(args, *scenario.split("-")) + + +if __name__ == "__main__": + main() diff --git a/tools/validation/legacy111/check_original_saves.py b/tools/validation/legacy111/check_original_saves.py new file mode 100644 index 00000000..febdd1d0 --- /dev/null +++ b/tools/validation/legacy111/check_original_saves.py @@ -0,0 +1,111 @@ +"""Create and upgrade disposable worlds using the public 1.11.2 beta4.58 release.""" +import argparse +import json +import subprocess +import time +from pathlib import Path + +from run_packaged_suite import fingerprint +from run_runtime_checks import FORGE, ROOT, dependency, digest, prepare, write_json + + +PUBLIC_JARS = { + "modern-metals-244357-2534044": "8F5011542650A03A7B25E4BCB410D676A5FB6AD705B91552E494A08A6C43087A", + "base-metals-240967-2533178": "ABDF704023100AD6BB70351CE72C31A07943AD5BCE8308155564194A25089CCC", + "mmd-orespawn-245586-2533159": "05CC48E59C7B20ED9B7F29AE014F5E2E5B2F16F289EF7D6F274F017B5E2A6F77", +} +BLOCKS = [ + ("0 100 0", "aluminum_ore"), + ("1 100 0", "chromium_ore"), + ("2 100 0", "tungsten_block"), + ("7 100 7", "aluminumbrass"), + ("15 100 7", "titanium"), +] +STACKS = [ + ("aluminum_ingot", 7, 0), + ("titanium_pickaxe", 1, 17), + ("plutonium_pickaxe", 1, 3), + ("aluminum_door", 2, 0), + ("tungsten_fishing_rod", 1, 4), + ("titanium_horse_armor", 1, 0), +] + + +def chest(): + items = [f'{{Slot:{slot}b,id:"modernmetals:{item}",Count:{count}b,Damage:{damage}s}}' + for slot, (item, count, damage) in enumerate(STACKS)] + items.append('{Slot:6b,id:"forge:bucketfilled",Count:1b,Damage:0s,tag:{FluidName:"aluminumbrass",Amount:1000}}') + return "{Items:[" + ",".join(items) + "]}" + + +def place(): + containers = ["fill 5 99 5 9 102 9 minecraft:stone 0 hollow", "fill 13 99 5 17 102 9 minecraft:stone 0 hollow"] + blocks = [f"setblock {pos} modernmetals:{block}" for pos, block in BLOCKS] + return containers + blocks + [f"setblock 0 100 2 minecraft:chest 0 replace {chest()}"] + + +def verify(): + blocks = [f"testforblock {pos} modernmetals:{block}" for pos, block in BLOCKS] + return blocks + [f"testforblock 0 100 2 minecraft:chest -1 {chest()}"] + + +def run(java, directory, phase, setup, checks): + console = directory / (phase + "-console.log") + with console.open("wb") as log, subprocess.Popen( + [str(java), "-Xmx2G", "-jar", FORGE, "nogui"], + cwd=directory, stdin=subprocess.PIPE, stdout=log, stderr=subprocess.STDOUT + ) as process: + try: + deadline = time.monotonic() + 100 + while 'For help, type "help"' not in (output := console.read_text(errors="replace")): + if "Run the command /fml confirm" in output: + raise AssertionError(f"Forge requested permission to remove old entries: {console}") + if process.poll() is not None or time.monotonic() >= deadline: + raise AssertionError(f"Server did not reach the world: {console}") + time.sleep(0.25) + commands = setup + checks + ["save-all", "stop"] + process.communicate(("\n".join(commands) + "\n").encode("ascii"), timeout=60) + assert process.returncode == 0, console + finally: + if process.poll() is None: + process.kill() + process.wait() + assert console.read_text(errors="replace").count("Successfully found the block") == len(checks), console + print(f"{directory.name} {phase} passed {len(checks)} block and chest checks", flush=True) + + +def check(args): + jars = [] + for name, expected in PUBLIC_JARS.items(): + jar = args.public_jars / (name + ".jar") + assert digest(jar).upper() == expected, f"Public jar checksum mismatch: {jar}" + jars.append(jar) + source = args.output / "beta4.58-source" + prepare(source, args.template, jars) + run(args.java, source, "capture", place(), verify()) + world = source / "world" + before = fingerprint(world) + specs = json.loads((ROOT / "gradle/dependencies.json").read_text()) + jars = [dependency(spec, args.mirror) for spec in specs if spec.get("runtime")] + target = args.output / "beta4.58-upgraded" + prepare(target, args.template, jars + [args.candidate], world) + try: + for phase in ("first", "reload"): + run(args.java, target, phase, [], verify()) + finally: + assert fingerprint(world) == before, "Original fixture was modified" + write_json(target / "receipt.json", {"passed": True, "candidate_sha256": digest(args.candidate), + "source_world_unchanged": True, "public_jars": PUBLIC_JARS}) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + for name in ("java", "template", "output", "public-jars", "candidate"): + parser.add_argument("--" + name, required=True, type=lambda path: Path(path).resolve()) + parser.add_argument("--mirror", type=lambda path: Path(path).resolve()) + args = parser.parse_args() + check(args) + + +if __name__ == "__main__": + main() diff --git a/tools/validation/legacy111/dependency_requirements.py b/tools/validation/legacy111/dependency_requirements.py new file mode 100644 index 00000000..2ecd4ec3 --- /dev/null +++ b/tools/validation/legacy111/dependency_requirements.py @@ -0,0 +1,44 @@ +"""Reject missing/old public dependencies before any disposable world is loaded.""" +import hashlib +import json +import subprocess +import urllib.request +from run_runtime_checks import FORGE, prepare, write_json + +OLD = { + 'base':('base-metals-240967','2533178','abdf704023100ad6bb70351ce72c31a07943ad5bce8308155564194a25089ccc'), + 'os':('mmd-orespawn-245586','2533159','05cc48e59c7b20ed9b7f29ae014f5e2e5b2f16f289ef7d6f274f017b5e2a6f77'), + 'os4':('mmd-orespawn-245586','8840593','51ff282d41e3c566db2541ccad53968030ea12e985a5cee7307162bbf560c99a') +} + +def verify(java,template,output,base,orespawn,modern): + fixtures=output/'dependency-fixtures';fixtures.mkdir() + old={} + for key,(artifact,version,expected) in OLD.items(): + target=fixtures/f'{artifact}-{version}.jar' + request=urllib.request.Request(f'https://www.cursemaven.com/curse/maven/{artifact}/{version}/{target.name}',headers={'User-Agent':'Mozilla/5.0'}) + with urllib.request.urlopen(request,timeout=45) as response:target.write_bytes(response.read()) + assert hashlib.sha256(target.read_bytes()).hexdigest()==expected,'Public fixture checksum mismatch' + old[key]=target + scenarios={ + 'missing-base':([orespawn,modern],'basemetals','2.5.1.111021'), + 'old-base':([old['base'],orespawn,modern],'basemetals','2.5.1.111021'), + 'missing-orespawn':([base,modern],'orespawn','4.1.5.111021'), + 'old-orespawn':([base,old['os'],modern],'orespawn','4.1.5.111021'), + 'old-orespawn4':([base,old['os4'],modern],'orespawn','4.1.5.111021') + } + result={} + for name,(jars,modid,version) in scenarios.items(): + target=output/name;prepare(target,template,jars) + log=target/'rejection-console.log' + with log.open('wb') as stream: + subprocess.run([str(java),'-Xmx1G','-jar',FORGE,'nogui'],cwd=target,stdout=stream,stderr=subprocess.STDOUT,timeout=90) + text=log.read_text(errors='replace') + requirement = f'Mod modernmetals (Modern Metals) requires [{modid}@[{version},' + assert requirement in text, log + assert 'Preparing level' not in text and not (target/'world/level.dat').exists(), ( + 'Incompatible dependencies reached world loading' + ) + result[name]={'rejected_before_world':True,'required_mod':modid,'minimum':version} + print(name,'PASS',flush=True) + write_json(output/'dependency-requirements.json',result) diff --git a/src/main/resources/assets/modernmetals/orespawn/modernmetals.json b/tools/validation/legacy111/fixtures/orespawn3-modernmetals.json old mode 100755 new mode 100644 similarity index 100% rename from src/main/resources/assets/modernmetals/orespawn/modernmetals.json rename to tools/validation/legacy111/fixtures/orespawn3-modernmetals.json diff --git a/tools/validation/legacy111/run_eclipse_profile.py b/tools/validation/legacy111/run_eclipse_profile.py new file mode 100644 index 00000000..887cf8ca --- /dev/null +++ b/tools/validation/legacy111/run_eclipse_profile.py @@ -0,0 +1,72 @@ +"""Run a generated Eclipse profile in an isolated directory, with a test-only probe.""" + +import argparse +import json +import os +import shlex +import shutil +import subprocess +import time +import xml.etree.ElementTree as ET +from pathlib import Path + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("profile", type=Path) + parser.add_argument("java", type=Path) + parser.add_argument("directory", type=Path) + parser.add_argument("phase", choices=["first", "reload"]) + parser.add_argument("--probe", type=Path, required=True) + parser.add_argument("--production-output", type=Path, + help="Use a disposable Eclipse build without touching the editor's bin directory") + args = parser.parse_args() + profile = args.profile.resolve() + root = ET.parse(profile).getroot() + strings = {node.get("key"): node.get("value") for node in root.findall("stringAttribute")} + environment = os.environ.copy() + for node in root.findall("mapAttribute/mapEntry"): + environment[node.get("key")] = node.get("value") + production_output = (args.production_output or profile.parent / "bin/main").resolve() + environment["MOD_CLASSES"] = str(production_output) + + archives = [] + for node in root.findall("listAttribute[@key='org.eclipse.jdt.launching.CLASSPATH']/listEntry"): + entry = ET.fromstring(node.get("value")) + path = entry.get("externalArchive") + if path: + archives.append(path) + elif entry.get("projectName"): + archives.append(str(production_output)) + + directory = args.directory.resolve() + (directory / "mods").mkdir(parents=True, exist_ok=True) + shutil.copy2(args.probe, directory / "mods" / args.probe.name) + (directory / "eula.txt").write_text("eula=true\n", encoding="utf-8") + if not (directory / "server.properties").exists(): + (directory / "server.properties").write_text("server-ip=127.0.0.1\nserver-port=0\nonline-mode=false\nlevel-type=FLAT\nview-distance=3\n", encoding="utf-8") + + command = [str(args.java), "-Xmx3G", "-Dmodern.phase=" + args.phase, "-Dmodern.saved=true"] + client = "Client" in profile.stem + if client: + command += ["-Dmodern.client=true", "-Dmodern.lowFantasy="+str(args.phase=="reload").lower()] + command += shlex.split(strings.get("org.eclipse.jdt.launching.VM_ARGUMENTS", "")) + command += ["-cp", os.pathsep.join(dict.fromkeys(archives)), strings["org.eclipse.jdt.launching.MAIN_TYPE"]] + command += shlex.split(strings["org.eclipse.jdt.launching.PROGRAM_ARGUMENTS"]) + started = time.time_ns() + with (directory / (args.phase + "-console.log")).open("w", encoding="utf-8") as log: + process = subprocess.run(command, cwd=directory, env=environment, stdout=log, + stderr=subprocess.STDOUT, timeout=300) + + report = directory / (args.phase + ("-client.json" if client else "-modern.json")) + if (process.returncode or not report.exists() or report.stat().st_mtime_ns < started + or not json.loads(report.read_text())["passed"]): + raise SystemExit(f"Launch failed; inspect {directory / (args.phase + '-console.log')}") + print(report.read_text(), flush=True) + if client: + world_report=json.loads((directory/(args.phase+'-modern.json')).read_text()) + if not world_report['passed']:raise SystemExit(str(world_report['failures'])) + + +if __name__ == "__main__": + main() diff --git a/tools/validation/legacy111/run_packaged_client.py b/tools/validation/legacy111/run_packaged_client.py new file mode 100644 index 00000000..9d53e6b2 --- /dev/null +++ b/tools/validation/legacy111/run_packaged_client.py @@ -0,0 +1,170 @@ +"""Run installed release jars on the obfuscated Forge client, in a disposable game directory.""" +import argparse,hashlib,json,os,shutil,struct,subprocess,time,zipfile,zlib +from concurrent.futures import ThreadPoolExecutor +from pathlib import Path +from urllib.request import urlopen +from run_runtime_checks import ROOT,dependency,FORGE + +def install_ore_host_pack(game): + # A solid test colour makes the host/ore separation easy to see in a screenshot. + pack=game/'resourcepacks/ore-host-probe' + target=pack/'assets/minecraft/textures/blocks/stone.png' + target.parent.mkdir(parents=True,exist_ok=True) + (pack/'pack.mcmeta').write_text(json.dumps({'pack':{'pack_format':3,'description':'Ore host probe'}}),encoding='utf-8') + def chunk(name,data): + return struct.pack('>I',len(data))+name+data+struct.pack('>I',zlib.crc32(name+data)) + target.write_bytes(b'\x89PNG\r\n\x1a\n'+chunk(b'IHDR',struct.pack('>IIBBBBB',16,16,8,2,0,0,0)) + +chunk(b'IDAT',zlib.compress((b'\0'+bytes((57,180,142))*16)*16))+chunk(b'IEND',b'')) + +def configure_client(game,host_pack): + options=game/'options.txt' + lines=options.read_text(encoding='utf-8').splitlines() if options.is_file() else [] + # Do not let losing window focus pause the disposable world during screenshots. + if host_pack:install_ore_host_pack(game) + packs=['ore-host-probe'] if host_pack else [] + settings=['pauseOnLostFocus:false','resourcePacks:'+json.dumps(packs),'incompatibleResourcePacks:[]'] + keys=tuple(line.split(':',1)[0]+':' for line in settings) + options.write_text('\n'.join([line for line in lines if not line.startswith(keys)]+settings)+'\n',encoding='utf-8') + +def prepare_assets(output,cache,metadata): + """Use this version's verified index without overwriting a shared Gradle index.""" + assets=output/'assets' + index=assets/'indexes'/(metadata['id']+'.json') + index.parent.mkdir(parents=True,exist_ok=True) + if not index.is_file(): + with urlopen(metadata['url'],timeout=60) as response:index.write_bytes(response.read()) + assert hashlib.sha1(index.read_bytes()).hexdigest()==metadata['sha1'],'Minecraft asset index checksum mismatch' + objects=json.loads(index.read_text())['objects'] + def stage(spec): + sha=spec['hash'];relative=Path('objects')/sha[:2]/sha + target=assets/relative + if not target.is_file(): + target.parent.mkdir(parents=True,exist_ok=True) + cached=cache/'caches/minecraft/assets'/relative + if cached.is_file() and hashlib.sha1(cached.read_bytes()).hexdigest()==sha: + shutil.copy2(cached,target) + else: + with urlopen(f'https://resources.download.minecraft.net/{sha[:2]}/{sha}',timeout=60) as response: + target.write_bytes(response.read()) + assert target.stat().st_size==spec['size'] and hashlib.sha1(target.read_bytes()).hexdigest()==sha,sha + # Multiple names may share one content-addressed object. + unique={spec['hash']:spec for spec in objects.values()} + with ThreadPoolExecutor(max_workers=8) as pool:list(pool.map(stage,unique.values())) + print('MODERN_METALS_CLIENT_ASSETS_VERIFIED',metadata['id'],len(unique),flush=True) + return assets + +def windows_allowed(library): + allowed='rules' not in library + for rule in library.get('rules',[]): + if not rule.get('os') or rule['os'].get('name')=='windows': + allowed=rule['action']=='allow' + return allowed + +def prepare_natives(output,cache,libraries): + # An Eclipse launch is not required to populate a shared natives directory. + natives=output/'natives' + natives.mkdir(exist_ok=True) + for library in libraries: + classifier=library.get('natives',{}).get('windows') + if not classifier or not windows_allowed(library):continue + spec=library['downloads']['classifiers'][classifier.replace('${arch}','64')] + group,artifact,version=library['name'].split(':') + name=Path(spec['path']).name + archive=output/'engine'/name + if not archive.is_file(): + cached=next((cache/'caches/modules-2/files-2.1'/group/artifact/version).glob(f'*/{name}'),None) + if cached and hashlib.sha1(cached.read_bytes()).hexdigest()==spec['sha1']: + shutil.copy2(cached,archive) + else: + with urlopen(spec['url'],timeout=60) as response:archive.write_bytes(response.read()) + assert hashlib.sha1(archive.read_bytes()).hexdigest()==spec['sha1'],name + with zipfile.ZipFile(archive) as jar: + for member in jar.namelist(): + if Path(member).name==member and member.endswith('.dll'): + (natives/member).write_bytes(jar.read(member)) + assert (natives/'lwjgl64.dll').is_file() + return natives + +def main(): + parser=argparse.ArgumentParser(description=__doc__) + for name in ('java','cache','installer','template','output'):parser.add_argument('--'+name,type=Path,required=True) + parser.add_argument('--phase',choices=['first','reload'],default='first') + parser.add_argument('--ore-host-pack',action='store_true',help='Replace vanilla stone with a test texture in this disposable client.') + parser.add_argument('--language',choices=['de_DE','en_US','ru_RU','zh_CN']) + parser.add_argument('--remote-port',type=int,help='Connect to a disposable server on loopback.') + parser.add_argument('--expected-disconnect',help='Expected text in a pre-world disconnect.') + parser.add_argument('--low-fantasy',action='store_true',help='Start the remote client in Low Fantasy.') + parser.add_argument('--mod-jar',type=Path,default=ROOT/'build/libs/ModernMetals-2.5.1.111021.jar') + parser.add_argument('--mirror', type=Path) + parser.add_argument('--probe-jar', type=Path, default=ROOT/'build/test-mods/modernmetals-probe-packaged.jar') + args=parser.parse_args() + if args.phase=='first':args.output.mkdir(parents=True,exist_ok=False) + assert hashlib.sha256(args.installer.read_bytes()).hexdigest().upper()=='00AEA3DDF1E78BBB4D5D05E8C22FB3E849DE09B47D828E57DAD373A16BB8A26F' + with zipfile.ZipFile(args.installer) as jar:forge=json.loads(jar.read('install_profile.json'))['versionInfo'] + vanilla_root=args.cache/'caches/minecraftforge/forgegradle/mavenizer/caches/minecraft_tasks/1.11.2' + vanilla=json.loads((vanilla_root/'version.json').read_text()) + assert hashlib.sha1((vanilla_root/'client.jar').read_bytes()).hexdigest()==vanilla['downloads']['client']['sha1'] + libraries={lib['name']:lib for lib in vanilla['libraries']} + libraries.update({lib['name']:lib for lib in forge['libraries']}) + classpath=[] + for coordinate,lib in libraries.items(): + if lib.get('clientreq') is False or not windows_allowed(lib):continue + group,artifact,version,*classifier=coordinate.split(':') + if group=='net.minecraftforge' and artifact=='forge': + # The server distribution's manifest names its neighboring server jar. + # Isolate the unchanged universal jar so it cannot shadow client classes. + engine=args.output/'engine';engine.mkdir(exist_ok=True) + shutil.copy2(args.template/FORGE,engine/FORGE) + classpath.append(engine/FORGE);continue + if 'natives' in lib and not lib.get('downloads',{}).get('artifact'):continue + filename=artifact+'-'+version+('-'+classifier[0] if classifier else '')+'.jar' + paths=list((args.cache/'caches/modules-2/files-2.1'/group/artifact/version).glob('*/'+filename)) + if not paths: + path=args.template/'libraries'/group.replace('.','/')/artifact/version/filename + if path.is_file():paths=[path] + if not paths:raise FileNotFoundError(coordinate) + classpath.append(paths[0]) + classpath.append(vanilla_root/'client.jar') + if args.phase=='first': + (args.output/'mods').mkdir() + specs=json.loads((ROOT/'gradle/dependencies.json').read_text()) + jars=[dependency(s, args.mirror) for s in specs if s.get('runtime')] + jars+=[args.mod_jar,args.probe_jar] + for jar in jars:shutil.copy2(jar,args.output/'mods'/jar.name) + natives=prepare_natives(args.output,args.cache,vanilla['libraries']) + assets=prepare_assets(args.output,args.cache,vanilla['assetIndex']) + configure_client(args.output,args.ore_host_pack) + if args.language: + options=args.output/'options.txt' + lines=[line for line in options.read_text(encoding='utf-8').splitlines() if not line.startswith('lang:')] + options.write_text('\n'.join(lines+[f'lang:{args.language.lower()}'])+'\n',encoding='utf-8') + if args.remote_port and args.low_fantasy: + config=args.output/'config/ModernMetals.cfg' + config.parent.mkdir(exist_ok=True) + config.write_text('general {\n S:contentMode=low_fantasy\n}\n',encoding='utf-8') + remote=[] + if args.remote_port: + remote=[f'-Dmodern.remoteServer=127.0.0.1:{args.remote_port}', + f'-Dmodern.expectedDisconnect={args.expected_disconnect or ""}'] + command=[str(args.java),'-Xmx3G',f'-Djava.library.path={natives}',f'-Dmodern.phase={args.phase}', + '-Dmodern.saved=true','-Dmodern.client=true','-Dmodern.lowFantasy='+str(args.phase=='reload').lower(), + '-Dmodern.oreHostPack='+str(args.ore_host_pack).lower(), + '-Dmodern.language='+(args.language or 'en_US').lower(), + *remote, + '-cp',os.pathsep.join(map(str,classpath)),forge['mainClass'], + '--username','ModernCheck','--uuid','00000000000000000000000000000001','--accessToken','0', + '--version',forge['id'],'--gameDir',str(args.output),'--assetsDir',str(assets),'--assetIndex',vanilla['assetIndex']['id'], + '--userType','legacy','--tweakClass','net.minecraftforge.fml.common.launcher.FMLTweaker','--versionType','Forge'] + started=time.time_ns() + with (args.output/(args.phase+'-console.log')).open('wb') as log: + result=subprocess.run(command,cwd=args.output,stdout=log,stderr=subprocess.STDOUT,timeout=300) + for suffix in (('client',) if args.remote_port else ('client','modern')): + report=args.output/(args.phase+'-'+suffix+'.json') + assert report.is_file() and report.stat().st_mtime_ns>started,report + assert json.loads(report.read_text())['passed'],report + assert result.returncode==0 + console=(args.output/(args.phase+'-console.log')).read_text(errors='replace') + assert "Can't find the resource index file" not in console,'Minecraft assets were not loaded' + print('MODERN_METALS_PACKAGED_CLIENT_PASS',args.phase,flush=True) + +if __name__=='__main__':main() diff --git a/tools/validation/legacy111/run_packaged_suite.py b/tools/validation/legacy111/run_packaged_suite.py new file mode 100644 index 00000000..4e87ebf2 --- /dev/null +++ b/tools/validation/legacy111/run_packaged_suite.py @@ -0,0 +1,192 @@ +"""Qualify native generation, migrations, switches and saved worlds in disposable servers.""" +import argparse +import copy +import json +from pathlib import Path +from run_runtime_checks import ROOT, dependency, digest, prepare, run, write_json + +RULE='modernmetals:legacy/' +SELECTOR='orespawn:all_except_nether_end' +FIELDS=['Aluminum','AluminumBrass','Beryllium','Boron','Cadmium','Chromium','GalvanizedSteel', + 'Iridium','Magnesium','Manganese','Nichrome','Osmium','Plutonium','Rutile','StainlessSteel', + 'Tantalum','Thorium','Titanium','Tungsten','Uranium','Zirconium'] + +def fingerprint(directory): + return {str(p.relative_to(directory)):digest(p) for p in directory.rglob('*') if p.is_file()} + +def main(): + parser=argparse.ArgumentParser(description=__doc__) + for name in ('java','template','output'):parser.add_argument('--'+name,type=Path,required=True) + parser.add_argument('--mirror',type=Path) + args=parser.parse_args() + args.output.mkdir(parents=True,exist_ok=False) + specs=json.loads((ROOT/'gradle/dependencies.json').read_text()) + jars=[dependency(s,args.mirror) for s in specs if s.get('runtime')] + jars += [ROOT/'build/libs/ModernMetals-2.5.1.111021.jar',ROOT/'build/test-mods/modernmetals-probe-packaged.jar'] + native=json.loads((ROOT/'build/reports/native-provider.json').read_text()) + stock=json.loads((ROOT/'tools/validation/legacy111/fixtures/orespawn3-modernmetals.json').read_text()) + + def setup(name,world=None): + target=args.output/name + prepare(target,args.template,jars,world) + return target + def check(target,expected=None,properties=None,sample=False,saved=False): + properties=dict(properties or {}) + if expected: + write_json(target/'expected.json',expected) + properties['modern.expected']=str((target/'expected.json').resolve()) + if saved:properties['modern.saved']='true' + first=run(args.java,target,'first',{**properties,**({'modern.sample':'true'} if sample else {})}) + run(args.java,target,'reload',properties) + assert not first['profile'].get('terrain_dimensions'), 'Ore-only mods enabled terrain replacement' + return first + + fresh=setup('native-fresh') + (fresh/'config').mkdir(exist_ok=True) + (fresh/'config/BaseMetals.cfg').write_text('general {\n S:contentMode=low_fantasy\n}\n',encoding='utf-8') + report=check(fresh,{'ores':native['ores']},properties={'modern.lowFantasy':'false','modern.trades':'true'},sample=True,saved=True) + base_profile=report['profile'] + assert len([key for key in base_profile['ores'] if key.startswith('modernmetals:')])==17 + aluminum_group = base_profile['ore_material_groups']['orespawn:aluminum'] + assert {'oreAluminum', 'oreAluminium', 'oreBauxite'} <= set(aluminum_group['ore_dictionary_entries']) + aluminum_policy = base_profile['ore_source_policies'][f'orespawn:aluminum|{SELECTOR}'] + aluminum_source = RULE + 'aluminum_ore' + assert aluminum_policy['placement_sources']['orespawn:standard'] == aluminum_source + assert any(candidate['source_id'] == aluminum_source and candidate['loaded'] and candidate['material_declared'] + for candidate in aluminum_policy['candidates']) + before=fingerprint(fresh/'world') + + disabled=setup('materials-disabled') + config=disabled/'config/ModernMetals.cfg';config.parent.mkdir(parents=True,exist_ok=True) + config.write_text('metals {\n'+''.join(f' B:Enable{name}=false\n' for name in ('Boron','Beryllium','Thorium','Titanium'))+'}\n',encoding='utf-8') + expected={k:v for k,v in native['ores'].items() if not any(k.endswith('/'+x+'_ore') for x in ('boron','beryllium','thorium','titanium'))} + result=check(disabled,{'ores':expected},{'modern.disabled':'boron,beryllium,thorium,titanium'}) + assert len([k for k in result['profile']['ores'] if k.startswith('modernmetals:')])==13 + + all_disabled=setup('all-materials-disabled') + config=all_disabled/'config/ModernMetals.cfg';config.parent.mkdir(parents=True,exist_ok=True) + config.write_text('metals {\n'+''.join(f' B:Enable{name}=false\n' for name in FIELDS)+'}\n',encoding='utf-8') + check(all_disabled,properties={'modern.disabled':','.join(n.lower() for n in FIELDS),'modern.allDisabled':'true'}) + + titanium=setup('titanium-enabled') + override=copy.deepcopy(native);override['ores'][RULE+'titanium_ore']['enabled']=True + write_json(titanium/'config/modernmetals-orespawn.json',override) + check(titanium,{'ores':override['ores']},{'modern.titanium':'true'},sample=True,saved=True) + + no_ores=setup('ores-disabled') + override=copy.deepcopy(native) + for ore in override['ores'].values():ore['enabled']=False + write_json(no_ores/'config/modernmetals-orespawn.json',override) + result=check(no_ores,{'ores':override['ores']},{'modern.noOres':'true'},sample=True) + assert all(not counts for counts in result['oreSamples'].values()) + + custom=copy.deepcopy(stock) + custom['spawns']['aluminum_ore']['parameters'].update(size=5,variation=0,frequency=2.75,minHeight=10,maxHeight=50) + custom['spawns']['thorium_ore']['enabled']=False + custom['spawns']['boron_ore']['dimensions']=[0] + target=setup('os3-import',fresh/'world') + # An existing OS4 profile is authoritative; remove only this disposable copy's snapshot + # to exercise an old world which has not yet been imported by OreSpawn 4. + snapshot=target/'world/serverconfig/orespawn-worldgen.json' + assert snapshot.resolve().is_relative_to(args.output.resolve()) + snapshot.unlink() + legacy=target/'config/orespawn3/modernmetals.json';write_json(legacy,custom) + legacy_hash=digest(legacy) + expected={'ores':{RULE+'aluminum_ore':{'dimension_selectors':{SELECTOR:{'frequency':2.75,'min_y':10,'max_y':49,'quantity':5}}}, + RULE+'thorium_ore':{'enabled':False},RULE+'boron_ore':{'dimensions':{'minecraft:overworld':{'enabled':True}}}}} + check(target,expected,saved=True) + assert digest(legacy)==legacy_hash + override_path=target/'config/modernmetals-orespawn.json' + assert override_path.is_file() + stable={p:digest(p) for p in (legacy,override_path,target/'world/serverconfig/orespawn-worldgen.json')} + run(args.java,target,'later',{'modern.expected':str((target/'expected.json').resolve()),'modern.saved':'true'}) + assert all(digest(p)==value for p,value in stable.items()) + + os1=setup('os1-import') + legacy=os1/'config/orespawn/modernmetals.json' + write_json(legacy,{'dimensions':[{'dimension':'0','ores':[{'blockID':'modernmetals:aluminum_ore','size':4,'variation':0,'frequency':1.5,'minHeight':8,'maxHeight':40}]}]}) + legacy_hash=digest(legacy) + result=check(os1) + imported=[v for v in result['profile']['ores'].values() if v.get('block')=='modernmetals:aluminum_ore'] + frequencies = [ + placement.get('frequency') + for ore in imported + for section in ('dimensions', 'dimension_selectors') + for placement in ore.get(section, {}).values() + ] + assert len(imported) == 1 and 1.5 in frequencies, ( + f'Expected one OS1 aluminum rule at frequency 1.5, found {len(imported)} rules with {frequencies}' + ) + assert digest(legacy)==legacy_hash + + existing=setup('existing-os4',fresh/'world') + profile_path=existing/'world/serverconfig/orespawn-worldgen.json' + profile=json.loads(profile_path.read_text()) + profile['ores'][RULE+'aluminum_ore']['dimension_selectors'][SELECTOR]['frequency']=6.25 + write_json(profile_path,profile) + override=copy.deepcopy(native) + override['ores'][RULE+'aluminum_ore']['dimension_selectors'][SELECTOR]['frequency']=9.0 + write_json(existing/'config/modernmetals-orespawn.json',override) + write_json(existing/'config/orespawn3/modernmetals.json',custom) + hashes={p:digest(p) for p in (profile_path,existing/'config/modernmetals-orespawn.json')} + check(existing,{'ores':{RULE+'aluminum_ore':{'dimension_selectors':{SELECTOR:{'frequency':6.25}}}}},saved=True) + assert all(digest(p)==value for p,value in hashes.items()) + future=setup('existing-override-new-world') + write_json(future/'config/modernmetals-orespawn.json',override) + write_json(future/'config/orespawn3/modernmetals.json',custom) + check(future,{'ores':override['ores']}) + assert fingerprint(fresh/'world')==before,'A source fixture world changed' + + optional=setup('optional-integrations') + for spec in specs: + if spec['id'] in ('tconstruct','mantle','mekanism','mcmultipart','ic2'): + import shutil + jar=dependency(spec,args.mirror);shutil.copy2(jar,optional/'mods'/jar.name) + check(optional,saved=True) + low=setup('low-fantasy') + (low/'config').mkdir(exist_ok=True) + (low/'config/ModernMetals.cfg').write_text('general {\n S:contentMode=low_fantasy\n}\n',encoding='utf-8') + (low/'config/BaseMetals.cfg').write_text('general {\n S:contentMode=high_fantasy\n}\n',encoding='utf-8') + check(low,properties={'modern.lowFantasy':'true','modern.trades':'true'},saved=True) + for mode in ('high','low'): + trade_off=setup(f'{mode}-fantasy-trades-disabled') + config=trade_off/'config/ModernMetals.cfg' + config.parent.mkdir() + config.write_text(f'general {{\n S:contentMode={mode}_fantasy\n B:enableVillagerTrades=false\n}}\n',encoding='utf-8') + run(args.java,trade_off,'first',{'modern.lowFantasy':str(mode=='low').lower(),'modern.trades':'false'}) + switched=setup('high-to-low-existing-world',fresh/'world') + (switched/'config').mkdir(exist_ok=True) + (switched/'config/ModernMetals.cfg').write_text('general {\n S:contentMode=low_fantasy\n}\n',encoding='utf-8') + run(args.java,switched,'reload',{'modern.lowFantasy':'true','modern.saved':'true'}) + custom_dimension=setup('ordinary-custom-dimension') + check(custom_dimension,properties={'modern.customDimension':'true'},sample=True) + low_optional=setup('low-fantasy-tinkers') + (low_optional/'config').mkdir(exist_ok=True) + (low_optional/'config/ModernMetals.cfg').write_text('general {\n S:contentMode=low_fantasy\n}\n',encoding='utf-8') + for spec in specs: + if spec['id'] in ('tconstruct','mantle'): + jar=dependency(spec,args.mirror);shutil.copy2(jar,low_optional/'mods'/jar.name) + check(low_optional,properties={'modern.lowFantasy':'true'},saved=True) + disabled_integration=setup('tinkers-disabled') + (disabled_integration/'config').mkdir(exist_ok=True) + (disabled_integration/'config/ModernMetals.cfg').write_text('general {\n S:contentMode=low_fantasy\n}\n',encoding='utf-8') + (disabled_integration/'config/BaseMetals.cfg').write_text('"mod integration" {\n B:tinkers_construct_integration=false\n}\n',encoding='utf-8') + for spec in specs: + if spec['id'] in ('tconstruct','mantle'): + jar=dependency(spec,args.mirror);shutil.copy2(jar,disabled_integration/'mods'/jar.name) + check(disabled_integration,properties={'modern.lowFantasy':'true','modern.disabledIntegrationTest':'true'},saved=True) + disabled_materials_optional=setup('materials-disabled-with-tinkers') + (disabled_materials_optional/'config').mkdir(exist_ok=True) + (disabled_materials_optional/'config/ModernMetals.cfg').write_text('metals {\n'+''.join(f' B:Enable{name}=false\n' for name in ('Boron','Beryllium','Thorium','Titanium'))+'}\n',encoding='utf-8') + for spec in specs: + if spec['id'] in ('tconstruct','mantle'): + jar=dependency(spec,args.mirror);shutil.copy2(jar,disabled_materials_optional/'mods'/jar.name) + check(disabled_materials_optional,properties={'modern.disabled':'boron,beryllium,thorium,titanium'}) + from dependency_requirements import verify + verify(args.java,args.template,args.output,dependency(next(s for s in specs if s['id']=='basemetals'),args.mirror), + dependency(next(s for s in specs if s['id']=='orespawn'),args.mirror),ROOT/'build/libs/ModernMetals-2.5.1.111021.jar') + assert fingerprint(fresh/'world')==before,'A source fixture world changed' + write_json(args.output/'qualification.json',{'passed':True,'scenarios':18,'source_world_unchanged':True}) + print('MODERN_METALS_PACKAGED_SUITE_PASS',flush=True) + +if __name__=='__main__':main() diff --git a/tools/validation/legacy111/run_runtime_checks.py b/tools/validation/legacy111/run_runtime_checks.py new file mode 100644 index 00000000..ddf01336 --- /dev/null +++ b/tools/validation/legacy111/run_runtime_checks.py @@ -0,0 +1,94 @@ +"""Run the installed Modern Metals jar in disposable packaged Forge servers.""" +import argparse +import hashlib +import json +import re +import shutil +import subprocess +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[3] +FORGE = 'forge-1.11.2-13.20.1.2588-universal.jar' + +def write_json(path, value): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(value, indent=2)+'\n', encoding='utf-8') + +def digest(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + +def dependency(spec, mirror=None): + group, artifact, version, *classifier = spec['coordinate'].split(':') + filename = f'{artifact}-{version}'+('-'+classifier[0] if classifier else '')+'.jar' + path = (mirror or ROOT/'.gradle/legacy-dependencies')/group.replace('.', '/')/artifact/version/filename + if digest(path).upper() != spec['sha256']: + raise AssertionError(f'Dependency checksum mismatch: {spec["id"]}') + return path + +def prepare(directory, template, jars, world=None): + directory.mkdir(parents=True, exist_ok=False) + for name in (FORGE, 'minecraft_server.1.11.2.jar'): + shutil.copy2(template/name, directory/name) + shutil.copytree(template/'libraries', directory/'libraries') + (directory/'mods').mkdir() + for jar in jars: + shutil.copy2(jar, directory/'mods'/jar.name) + (directory/'eula.txt').write_text('eula=true\n', encoding='utf-8') + (directory/'server.properties').write_text( + 'level-name=world\nserver-ip=127.0.0.1\nserver-port=0\nonline-mode=false\n' + 'level-seed=8675309\nview-distance=2\nmax-tick-time=-1\nspawn-protection=0\n' + 'generate-structures=false\nlevel-type=DEFAULT\n', encoding='utf-8') + if world: + if directory.resolve().is_relative_to(world.resolve()): + raise ValueError('Disposable target is inside the source world') + shutil.copytree(world, directory/'world') + +def run(java, directory, phase, properties=None, expect_failure=False): + properties = {'modern.phase':phase, **(properties or {})} + command = [str(java), '-Xms256M', '-Xmx2G']+[f'-D{k}={v}' for k,v in properties.items()] + command += ['-jar', FORGE, 'nogui'] + log = directory/f'{phase}-console.log' + started = time.time_ns() + with log.open('wb') as stream: + result = subprocess.run(command, cwd=directory, stdout=stream, stderr=subprocess.STDOUT, timeout=240) + report_path = directory/f'{phase}-modern.json' + if not report_path.is_file() or report_path.stat().st_mtime_ns < started: + raise AssertionError(f'No fresh runtime report: {log}') + report = json.loads(report_path.read_text(encoding='utf-8')) + if expect_failure: + if report['passed'] or not any('Missing material: boron' == x for x in report['failures']): + raise AssertionError(f'Missing-material defect was not reproduced: {report_path}') + elif result.returncode or not report['passed']: + raise AssertionError(f'Packaged checks failed: {report_path}; {log}') + text = log.read_text(errors='replace') + if not expect_failure and re.search(r"Couldn't load[^\r\n]*for modernmetals", text): + raise AssertionError(f'An optional integration failed: {log}') + print(directory.name, phase, 'expected failure' if expect_failure else 'PASS', flush=True) + return report + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + for name in ('java','template','output'): + parser.add_argument('--'+name, type=Path, required=True) + parser.add_argument('--baseline', action='store_true') + parser.add_argument('--optional', action='append', default=[]) + parser.add_argument('--mirror', type=Path) + args = parser.parse_args() + specs = json.loads((ROOT/'gradle/dependencies.json').read_text()) + selected = set(args.optional) + if 'tconstruct' in selected: + selected.add('mantle') + if 'mekanism' in selected: + selected.add('mcmultipart') + jars = [dependency(s,args.mirror) for s in specs if s.get('runtime') or s['id'] in selected] + jars += [ROOT/'build/libs/ModernMetals-2.5.1.111021.jar', ROOT/'build/test-mods/modernmetals-probe-packaged.jar'] + prepare(args.output, args.template, jars) + if args.baseline: + run(args.java,args.output,'first',{'modern.baseline':'true'},expect_failure=True) + else: + run(args.java,args.output,'first',{'modern.saved':'true'}) + run(args.java,args.output,'reload',{'modern.saved':'true'}) + +if __name__ == '__main__': + main() diff --git a/tools/validation/legacy111/runtime-probe/review/modern/ClientChecks.java b/tools/validation/legacy111/runtime-probe/review/modern/ClientChecks.java new file mode 100644 index 00000000..227a5cea --- /dev/null +++ b/tools/validation/legacy111/runtime-probe/review/modern/ClientChecks.java @@ -0,0 +1,280 @@ +package review.modern; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.init.Materials; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.concurrent.Future; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiDisconnected; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.multiplayer.GuiConnecting; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.item.ItemStack; +import net.minecraft.world.GameType; +import net.minecraft.world.WorldSettings; +import net.minecraft.world.WorldType; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.client.FMLClientHandler; +import net.minecraftforge.fml.client.IModGuiFactory; +import net.minecraftforge.fml.client.config.GuiConfig; +import net.minecraftforge.fml.client.config.GuiMessageDialog; +import net.minecraftforge.fml.client.config.GuiConfigEntries.BooleanEntry; +import net.minecraftforge.fml.client.config.GuiConfigEntries.CycleValueEntry; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import zone.moddev.mc.modernmetals.util.Config; +import zone.moddev.mc.modernmetals.content.ContentPolicy; + +/** Automated graphical acceptance probes, isolated from all release archives. */ +@Mod(modid="modernmetalsclientprobe",name="Modern Metals client checks",version="1", + acceptableRemoteVersions="*",dependencies="required-after:modernmetalsprobe") +public final class ClientChecks { + @Mod.EventHandler public void init(final FMLInitializationEvent event) { + if (!Boolean.getBoolean("modern.client")) return; + final String server = System.getProperty("modern.remoteServer"); + MinecraftForge.EVENT_BUS.register(server == null ? new Checks() : new RemoteCheck(server)); + } + private static void require(final boolean pass, final String detail) { + if (!pass) throw new AssertionError(detail); + } + + public static final class RemoteCheck { + private final String address; + private final long deadline = System.currentTimeMillis() + 90000; + private boolean connecting; + private boolean finished; + + RemoteCheck(final String address) { this.address = address; } + + @SubscribeEvent public void tick(final TickEvent.ClientTickEvent event) throws Exception { + if (event.phase != TickEvent.Phase.END || finished) return; + final Minecraft mc = Minecraft.getMinecraft(); + if (!connecting && mc.currentScreen instanceof GuiMainMenu) { + mc.displayGuiScreen(new GuiConnecting(mc.currentScreen, mc, new ServerData("Disposable Forge", address, false))); + connecting = true; + return; + } + final String expectedDisconnect = System.getProperty("modern.expectedDisconnect", ""); + final boolean expectRejection = !expectedDisconnect.isEmpty(); + if (mc.world != null && mc.player != null) { + finish(mc, !expectRejection, "Dedicated server world entered"); + } else if (connecting && mc.currentScreen instanceof GuiDisconnected) { + final String reason = disconnectReason((GuiDisconnected) mc.currentScreen); + finish(mc, expectRejection && reason.contains(expectedDisconnect), reason); + } else if (System.currentTimeMillis() >= deadline) { + finish(mc, false, "Dedicated server login timed out"); + } + } + + private static String disconnectReason(final GuiDisconnected screen) throws Exception { + for (final java.lang.reflect.Field field : GuiDisconnected.class.getDeclaredFields()) { + if (field.getType() == net.minecraft.util.text.ITextComponent.class) { + field.setAccessible(true); + return ((net.minecraft.util.text.ITextComponent) field.get(screen)).getUnformattedText(); + } + } + throw new AssertionError("Disconnect reason field missing"); + } + + private void finish(final Minecraft mc, final boolean pass, final String detail) throws Exception { + Checks.screenshot(mc, "dedicated-login.png"); + Checks.report(pass, detail); + finished = true; + mc.shutdown(); + } + } + public static final class Checks { + // Exercise the actual directory entry and the same action as its cog button. + private static GuiScreen openOreSpawnConfig(final Minecraft mc, final GuiScreen parent) + throws ReflectiveOperationException { + final Class directoryType = Class.forName("zone.moddev.mc.orespawn.client.OreSpawnModsScreen"); + final Constructor constructor = directoryType.getDeclaredConstructor(GuiScreen.class); + constructor.setAccessible(true); + final GuiScreen directory = (GuiScreen) constructor.newInstance(parent); + final Field entriesField = directoryType.getDeclaredField("entries"); + entriesField.setAccessible(true); + for (final Object entry : (Iterable) entriesField.get(directory)) { + final Field modId = entry.getClass().getDeclaredField("modId"); + modId.setAccessible(true); + if ("modernmetals".equals(modId.get(entry))) { + mc.displayGuiScreen(directory); + final Method open = directoryType.getDeclaredMethod("open", entry.getClass()); + open.setAccessible(true); + open.invoke(directory, entry); + return directory; + } + } + throw new AssertionError("Modern Metals is missing from the OreSpawn Mods directory"); + } + + private long started=System.currentTimeMillis(); + private int ticks; + private int state; + private GuiScreen parent; + private GuiScreen oreSpawnMods; + private GuiConfig screen; + private Future check; + private boolean finished; + @SubscribeEvent public void tick(final TickEvent.ClientTickEvent event) throws Exception { + if (event.phase!=TickEvent.Phase.END || finished) return; + final Minecraft mc=Minecraft.getMinecraft(); + try { + if (state==0 && mc.currentScreen instanceof GuiMainMenu && ++ticks>=20) { + parent=mc.currentScreen; + final IModGuiFactory factory=FMLClientHandler.instance().getGuiFactoryFor(Loader.instance().getIndexedModList().get("modernmetals")); + require(factory!=null && factory.hasConfigGui(),"Forge Mods config factory missing"); + screen=(GuiConfig)factory.createConfigGui(parent); + mc.displayGuiScreen(screen); state=1;ticks=0; + } else if(state==1 && ++ticks>=20) { + require(screen.parentScreen==parent && screen.configElements.size()==23,"Forge config parent or settings incorrect"); + require(screen.allRequireMcRestart,"Registry switches must require a restart"); + require(!screen.title.startsWith("config."),"Untranslated config title"); + final String locale = System.getProperty("modern.language", "en_us"); + require(locale.equals(mc.gameSettings.language), "Minecraft did not load language " + locale); + if (!"en_us".equals(locale)) { + require(!"Fantasy Level".equals(net.minecraft.client.resources.I18n.format("config.modernmetals.content_mode")), + "Fantasy selector fell back to English in " + locale); + require(!"High Fantasy".equals(net.minecraft.client.resources.I18n.format("high_fantasy")), + "Fantasy value fell back to English in " + locale); + } + for(final net.minecraftforge.fml.client.config.IConfigElement element:screen.configElements) + require(net.minecraft.client.resources.I18n.hasKey(element.getLanguageKey()),"Untranslated material label: "+element.getName()); + final String startupMode=Boolean.getBoolean("modern.lowFantasy")?"low_fantasy":"high_fantasy"; + require(mode(screen).getCurrentValue().equals(startupMode),"Wrong startup fantasy selection"); + require(ContentPolicy.active().lowFantasy()==Boolean.getBoolean("modern.lowFantasy"),"Wrong active fantasy mode"); + screenshot(mc,"forge-mods-config.png"); + mode(screen).valueButtonPressed(0); + final BooleanEntry entry=entry(screen,"EnableBoron"); + entry.valueButtonPressed(0); + entry(screen,"enableVillagerTrades").valueButtonPressed(0); + invoke(screen,"keyTyped",new Class[]{char.class,int.class},'\0',1); + require(mc.currentScreen==parent,"Escape did not return to Forge parent"); + require(Config.getConfiguration().get("Metals","EnableBoron",true).getBoolean(),"Escape saved a discarded switch"); + require(Config.getConfiguration().get("General","enableVillagerTrades",true).getBoolean(),"Escape saved discarded trade settings"); + require(Config.getConfiguration().getCategory("General").get("contentMode").getString().equals(startupMode),"Escape saved a discarded fantasy selection"); + oreSpawnMods = openOreSpawnConfig(mc, parent); + require(mc.currentScreen instanceof GuiConfig, "OreSpawn cog did not open the Modern Metals config"); + screen = (GuiConfig) mc.currentScreen; + state=2;ticks=0; + } else if(state==2 && ++ticks>=20) { + require(screen.parentScreen==oreSpawnMods && screen.configElements.size()==23,"OreSpawn config parent or options incorrect"); + screenshot(mc,"orespawn-child-mods-config.png"); + mode(screen).valueButtonPressed(0); + screen.entryList.scrollBy(10000); + require(screen.entryList.getAmountScrolled()>0,"Material list cannot scroll"); + entry(screen,"EnableBoron").valueButtonPressed(0); + entry(screen,"enableVillagerTrades").valueButtonPressed(0); + invoke(screen,"actionPerformed",new Class[]{GuiButton.class},new GuiButton(2000,0,0,"Done")); + require(mc.currentScreen instanceof GuiMessageDialog, "Missing Forge restart confirmation"); + invoke(mc.currentScreen, "actionPerformed", new Class[]{GuiButton.class}, new GuiButton(0, 0, 0, "OK")); + require(mc.currentScreen == oreSpawnMods, "Restart confirmation did not return to OreSpawn Mods"); + require(!Config.getConfiguration().get("Metals","EnableBoron",true).getBoolean(),"Done did not save the switch"); + require(Config.Options.enableBoron && Materials.hasMaterial("boron"),"GUI rebuilt live registries"); + require(!Config.getConfiguration().get("General","enableVillagerTrades",true).getBoolean() + && Config.Options.enableVillagerTrades,"Trade settings did not save for the next restart"); + final String pendingMode=Boolean.getBoolean("modern.lowFantasy")?"high_fantasy":"low_fantasy"; + require(Config.getConfiguration().getCategory("General").get("contentMode").getString().equals(pendingMode),"Done did not save the fantasy selection"); + require(ContentPolicy.active().lowFantasy()==Boolean.getBoolean("modern.lowFantasy"),"GUI applied fantasy recipes without a restart"); + require(new String(Files.readAllBytes(Paths.get("config/ModernMetals.cfg")),StandardCharsets.UTF_8).contains("B:EnableBoron=false"),"Done did not persist the file"); + require(new String(Files.readAllBytes(Paths.get("config/ModernMetals.cfg")),StandardCharsets.UTF_8).contains("S:contentMode="+pendingMode),"Done did not persist the fantasy setting"); + // Restore the fixture through the same save path before the reload test. + screen=new zone.moddev.mc.modernmetals.client.ModernMetalsConfigScreen(parent); + mc.displayGuiScreen(screen); + entry(screen,"EnableBoron").valueButtonPressed(0); + entry(screen,"enableVillagerTrades").valueButtonPressed(0); + invoke(screen,"actionPerformed",new Class[]{GuiButton.class},new GuiButton(2000,0,0,"Done")); + mc.displayGuiScreen(parent); + mc.launchIntegratedServer("modern-check","Modern Metals disposable checks",new WorldSettings(8675309L,GameType.CREATIVE,false,false,WorldType.DEFAULT)); + state=3; + } else if(state==3 && mc.world!=null && mc.player!=null && check==null) { + OreOverlayChecks.verify(mc); + for(final net.minecraft.block.Block block:net.minecraftforge.fml.common.registry.ForgeRegistries.BLOCKS.getValues()) { + if(!"modernmetals".equals(block.getRegistryName().getResourceDomain())) continue; + for(final net.minecraft.block.state.IBlockState variant:block.getBlockState().getValidStates()) + require(mc.getBlockRendererDispatcher().getBlockModelShapes().getModelForState(variant) + !=mc.getRenderItem().getItemModelMesher().getModelManager().getMissingModel(),"Missing baked block variant: "+variant); + } + for(final com.mcmoddev.lib.material.MMDMaterial material:Materials.getMaterialsByMod("modernmetals")) { + boolean tab=false; + for(final net.minecraft.creativetab.CreativeTabs value:material.getItem(Names.INGOT).getCreativeTabs()) + if(value!=null && value.getTabLabel().startsWith("modernmetals.")) tab=true; + require(tab,"Missing Modern Metals creative tab: "+material.getName()); + } + for(final String label:new String[]{"aluminum_brass_maker","galvanized_steel_maker","nichrome_maker","stainless_steel_maker","titanium_maker"}) { + require(net.minecraft.client.resources.I18n.hasKey("achievement."+label),"Missing achievement label: "+label); + require(net.minecraft.client.resources.I18n.hasKey("achievement."+label+".desc"),"Missing achievement description: "+label); + } + for(final com.mcmoddev.lib.material.MMDMaterial material:Materials.getMaterialsByMod("modernmetals")) { + final String name=material.getName(); + for(final Names form:new Names[]{Names.INGOT,Names.PLATE,Names.PICKAXE}) { + final ItemStack stack=Materials.getMaterialByName(name).hasItem(form) + ? Materials.getMaterialByName(name).getItemStack(form) + : Materials.getMaterialByName(name).getBlockItemStack(form); + require(stack!=null,"Missing model input: "+name+":"+form); + final IBakedModel model=mc.getRenderItem().getItemModelMesher().getItemModel(stack); + require(model!=mc.getRenderItem().getItemModelMesher().getModelManager().getMissingModel(),"Missing baked model: "+name+":"+form); + } + } + check=mc.getIntegratedServer().addScheduledTask(()->{ + try { + ModernMetalsProbe.run(mc.getIntegratedServer()); + OreOverlayChecks.placeDisplay(mc.getIntegratedServer()); + } + catch(Exception failure) {throw new RuntimeException(failure);} + }); + } else if(state==3 && check!=null && check.isDone()) { + check.get(); + mc.displayGuiScreen(null); + mc.gameSettings.hideGUI=true; + state=4; + ticks=0; + } else if(state==4 && ++ticks>=40) { + screenshot(mc,"ore-overlay-display.png"); + report(true,"Both config screens, fantasy restart, baked ore block/item overlays, stone host and integrated saved world"); + finished=true;mc.shutdown(); + } + if(System.currentTimeMillis()-started>240000) throw new AssertionError("Client check timed out, state="+state); + } catch(Throwable failure) {report(false,failure.toString());failure.printStackTrace();finished=true;mc.shutdown();} + } + private static BooleanEntry entry(final GuiConfig screen,final String name) { + for(int i=0;i[] types,final Object...args) throws Exception { + final Class owner = screen instanceof GuiMessageDialog ? GuiMessageDialog.class.getSuperclass() : GuiConfig.class; + Method method; + try {method=owner.getDeclaredMethod(name,types);} + catch(final NoSuchMethodException packaged) { + method=owner.getDeclaredMethod("keyTyped".equals(name)?"func_73869_a":"func_146284_a",types); + } + method.setAccessible(true);method.invoke(screen,args); + } + private static void screenshot(final Minecraft mc,final String name) { + net.minecraft.util.ScreenShotHelper.saveScreenshot(mc.mcDataDir,name,mc.displayWidth,mc.displayHeight,mc.getFramebuffer()); + } + private static void report(final boolean passed,final String detail) throws Exception { + final JsonObject report=new JsonObject();report.addProperty("passed",passed);report.addProperty("detail",detail); + Files.write(Paths.get(System.getProperty("modern.phase")+"-client.json"),(new GsonBuilder().setPrettyPrinting().create().toJson(report)+"\n").getBytes(StandardCharsets.UTF_8)); + System.out.println("MODERN_METALS_CLIENT_"+(passed?"PASS":"FAIL")+" "+detail); + } + } +} diff --git a/tools/validation/legacy111/runtime-probe/review/modern/ModernMetalsProbe.java b/tools/validation/legacy111/runtime-probe/review/modern/ModernMetalsProbe.java new file mode 100644 index 00000000..bb73b43e --- /dev/null +++ b/tools/validation/legacy111/runtime-probe/review/modern/ModernMetalsProbe.java @@ -0,0 +1,322 @@ +package review.modern; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.init.Materials; +import com.mcmoddev.lib.material.MMDMaterial; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntityChest; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.WorldServer; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLServerStartedEvent; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import zone.moddev.mc.orespawn.api.OreSpawnApi; + +/** Inspects the installed jar, registered content, recipes, profile and saved data. */ +@Mod(modid = "modernmetalsprobe", name = "Modern Metals runtime checks", version = "1", + acceptableRemoteVersions = "*", dependencies = "required-after:modernmetals") +public final class ModernMetalsProbe { + private static final String[] MATERIALS = { "aluminum", "aluminumbrass", "beryllium", "boron", + "cadmium", "chromium", "galvanizedsteel", "iridium", "magnesium", "manganese", "nichrome", + "osmium", "plutonium", "rutile", "stainlesssteel", "tantalum", "thorium", "titanium", + "tungsten", "uranium", "zirconium" }; + + private static net.minecraft.world.DimensionType testDimension; + @Mod.EventHandler public void preInit(final net.minecraftforge.fml.common.event.FMLPreInitializationEvent event) { + if(Boolean.getBoolean("modern.disabledIntegrationTest")) OptionalIntegrationChecks.seedForeign(); + if(Boolean.getBoolean("modern.customDimension")) { + testDimension=net.minecraft.world.DimensionType.register("modernmetals_check","_modern_check",42,OrdinaryDimension.class,false); + net.minecraftforge.common.DimensionManager.registerDimension(42,testDimension); + } + } + public static final class OrdinaryDimension extends net.minecraft.world.WorldProviderSurface { + @Override public net.minecraft.world.DimensionType getDimensionType() {return testDimension;} + } + + @Mod.EventHandler + public void started(final FMLServerStartedEvent event) throws Exception { + if (System.getProperty("modern.phase") == null) return; + final MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + if (server.isDedicatedServer()) { + run(server); + server.initiateShutdown(); + } + } + + public static void run(final MinecraftServer server) throws Exception { + final String phase = System.getProperty("modern.phase", "first"); + final JsonObject report = new JsonObject(); + final JsonArray failures = new JsonArray(); + final Set disabled = new HashSet<>(Arrays.asList(System.getProperty("modern.disabled", "").split(","))); + final JsonObject catalog = new JsonObject(); + check("2.5.1.111021".equals(Loader.instance().getIndexedModList().get("modernmetals").getVersion()), "mod version", failures); + for (final String name : MATERIALS) { + final MMDMaterial material = Materials.getMaterialByName(name); + if (disabled.contains(name)) { + check(material.isEmpty(), "Disabled material registered: " + name, failures); + continue; + } + check(!material.isEmpty(), "Missing material: " + name, failures); + if (material.isEmpty()) continue; + final JsonObject entry = new JsonObject(); + entry.addProperty("toolDurability", material.getToolDurability()); + entry.addProperty("fluid", material.getFluid() == null ? "" : material.getFluid().getName()); + for (final Names form : new Names[] {Names.INGOT, Names.NUGGET, Names.POWDER, Names.PICKAXE, Names.HELMET}) { + check(material.hasItem(form), "Missing " + name + ":" + form, failures); + } + check(material.hasBlock(Names.BLOCK), "Missing storage block: " + name, failures); + check(material.getFluid() != null && FluidRegistry.getFluid(material.getFluid().getName()) != null, + "Missing registered fluid: " + name, failures); + if(material.getFluid()!=null) { + check(material.getFluid()==FluidRegistry.getFluid(name),"Material retains an unregistered duplicate fluid: "+name,failures); + check(zone.moddev.mc.modernmetals.init.Fluids.getFluidByName(name)==material.getFluid() + && zone.moddev.mc.modernmetals.init.Fluids.getFluidRegistry().get(name)==material.getFluid() + && name.equals(zone.moddev.mc.modernmetals.init.Fluids.getNameOfFluid(material.getFluid())),"Legacy fluid query lost borrowed material: "+name,failures); + } + check(material.getFluidBlock() != null, "Missing fluid block: " + name, failures); + if (material.getFluid() != null) check(FluidRegistry.getBucketFluids().contains(material.getFluid()), "Missing bucket: " + name, failures); + if (material.hasBlock(Names.ORE)) { + final com.mcmoddev.lib.registry.recipe.ICrusherRecipe recipe = + com.mcmoddev.lib.registry.CrusherRecipeRegistry.getInstance() + .getRecipeForInputItem(material.getBlockItemStack(Names.ORE)); + check(recipe != null && ItemStack.areItemsEqual(recipe.getOutput(), material.getItemStack(Names.POWDER)), + "Base Metals crusher output missing or wrong: " + name, failures); + } + catalog.add(name, entry); + } + report.add("materials", catalog); + if (!Boolean.getBoolean("modern.baseline") && Materials.hasMaterial("aluminumbrass") + && !Loader.isModLoaded("tconstruct")) { + final net.minecraft.nbt.NBTTagCompound savedFluid = new net.minecraft.nbt.NBTTagCompound(); + savedFluid.setString("FluidName", "alubrass"); + savedFluid.setInteger("Amount", 1000); + final net.minecraftforge.fluids.FluidStack legacy = net.minecraftforge.fluids.FluidStack.loadFluidStackFromNBT(savedFluid); + check(legacy != null && legacy.amount == 1000 + && legacy.getFluid().getBlock() == Materials.getMaterialByName("aluminumbrass").getFluidBlock(), + "A saved 0.11.0 aluminum brass bucket no longer resolves", failures); + } + for(final String[] alias:new String[][]{{"chromium","Chrome"},{"chromium","Chromium"},{"tungsten","Wolfram"},{"tungsten","Tungsten"},{"aluminum","Aluminium"}}) { + if(!Materials.hasMaterial(alias[0])) continue; + boolean found=false; + for(final ItemStack stack:net.minecraftforge.oredict.OreDictionary.getOres("ingot"+alias[1])) + if(stack.getItem()==Materials.getMaterialByName(alias[0]).getItem(Names.INGOT)) found=true; + check(found,"Missing ingot alias: "+alias[1],failures); + } + final java.util.Set durability=new java.util.HashSet<>(); + for(final MMDMaterial material:Materials.getMaterialsByMod("modernmetals")) durability.add(material.getToolDurability()); + if(catalog.entrySet().size()>4) check(durability.size()>4,"Material statistics unexpectedly uniform",failures); + if (System.getProperty("modern.lowFantasy") != null) { + final boolean low=Boolean.getBoolean("modern.lowFantasy"); + check(zone.moddev.mc.modernmetals.content.ContentPolicy.active().lowFantasy()==low,"Active fantasy mode does not match the startup selection",failures); + for(final String name:new String[]{"plutonium","uranium","thorium","cadmium","rutile","titanium","stainlesssteel","galvanizedsteel"}) { + final MMDMaterial material=Materials.getMaterialByName(name); + if(material.isEmpty()) continue; + final boolean suitable=Arrays.asList("titanium","stainlesssteel","galvanizedsteel").contains(name); + boolean recipe=false; + for(final net.minecraft.item.crafting.IRecipe value:CraftingManager.getInstance().getRecipeList()) { + final ItemStack output=value.getRecipeOutput(); + if(output!=null && output.getItem()==material.getItem(Names.PICKAXE)) recipe=true; + } + check(recipe==(!low || suitable),"Fantasy mode pickaxe recipe incorrect: "+name,failures); + final boolean hasOre = material.hasOre() || name.equals("stainlesssteel") || name.equals("galvanizedsteel"); + check(material.hasItem(Names.PICKAXE) && material.hasBlock(Names.ORE)==hasOre,"Fantasy mode changed registry identities: "+name,failures); + } + if(low) { + for(final net.minecraft.item.crafting.IRecipe recipe:CraftingManager.getInstance().getRecipeList()) { + final ItemStack output=recipe.getRecipeOutput(); + if(output==null || output.getItem()==null || output.getItem().getRegistryName()==null) continue; + final ResourceLocation id=output.getItem().getRegistryName(); + if(!"modernmetals".equals(id.getResourceDomain())) continue; + final String path=id.getResourcePath(); + for(final String material:new String[]{"plutonium","uranium","thorium","boron","beryllium","cadmium","rutile","chromium","iridium","osmium"}) + for(final String form:new String[]{"gear","door","trapdoor","bars","stairs","slab","pressure_plate"}) + check(!path.equals(material+"_"+form),"Low Fantasy unrealistic product remains: "+path,failures); + } + } + } + if (System.getProperty("modern.trades") != null) checkTrades(server.getWorld(0), failures); + OptionalIntegrationChecks.run(failures); + final WorldServer world = server.getWorld(0); + for (final String[] alloy : new String[][] {{"aluminumbrass","aluminum","brass"}, + {"galvanizedsteel","steel","zinc"}, {"nichrome","nickel","chromium"}, + {"stainlesssteel","steel","chromium"}, {"titanium","rutile","magnesium"}}) { + if (!Materials.hasMaterial(alloy[0]) || !Materials.hasMaterial(alloy[1]) || !Materials.hasMaterial(alloy[2])) continue; + final InventoryCrafting grid = new InventoryCrafting(new Container() { + @Override public boolean canInteractWith(final net.minecraft.entity.player.EntityPlayer player) { return true; } + }, 2, 2); + grid.setInventorySlotContents(0, Materials.getMaterialByName(alloy[1]).getItemStack(Names.POWDER)); + grid.setInventorySlotContents(1, Materials.getMaterialByName(alloy[2]).getItemStack(Names.POWDER)); + final ItemStack result = CraftingManager.getInstance().findMatchingRecipe(grid, world); + check(result != null && result.getItem() == Materials.getMaterialByName(alloy[0]).getItem(Names.BLEND) + && result.getCount() == 2, "Unobtainable alloy blend: " + alloy[0], failures); + } + if (OreSpawnApi.getActiveProfile(server).isPresent()) { + final JsonObject profile = OreSpawnApi.getActiveProfile(server).get().toJson(); + report.add("profile", profile); + if (System.getProperty("modern.expected") != null) { + final JsonObject expected = new JsonParser().parse(new String(Files.readAllBytes( + Paths.get(System.getProperty("modern.expected"))), StandardCharsets.UTF_8)).getAsJsonObject(); + subset(expected, profile, "profile", failures); + } + if (Boolean.getBoolean("modern.sample") || "later".equals(phase)) sample(server, profile.getAsJsonObject("ores"), report, failures); + } + if (!Boolean.getBoolean("modern.baseline")) { + check(OreSpawnApi.isOreTakeoverActive("modernmetals") != Boolean.getBoolean("modern.allDisabled"), "Native provider status incorrect", failures); + try { Class.forName("com.mcmoddev.orespawn.ModernMetalsOreSpawn"); failures.add(new JsonPrimitive("Retired OS3 plugin packaged")); } + catch (final ClassNotFoundException expected) { } + } + if (Boolean.getBoolean("modern.saved")) saved(world, phase, failures); + report.add("failures", failures); + report.addProperty("passed", failures.size() == 0); + report.addProperty("phase", phase); + Files.write(Paths.get(phase + "-modern.json"), (new GsonBuilder().setPrettyPrinting().create().toJson(report) + "\n").getBytes(StandardCharsets.UTF_8)); + server.saveAllWorlds(false); + System.out.println("MODERN_METALS_RUNTIME_" + (failures.size() == 0 ? "PASS" : "FAIL") + " " + failures); + } + + private static void sample(final MinecraftServer server, final JsonObject ores, final JsonObject report, final JsonArray failures) { + final JsonObject result = new JsonObject(); + for (final int dimension : Boolean.getBoolean("modern.customDimension") ? new int[]{0,-1,1,42} : new int[]{0,-1,1}) { + if (net.minecraftforge.common.DimensionManager.getWorld(dimension) == null) + net.minecraftforge.common.DimensionManager.initDimension(dimension); + final WorldServer world = server.getWorld(dimension); + final int origin = "later".equals(System.getProperty("modern.phase")) ? 96 : 64; + for (int x=origin; x<=origin+6; x++) for (int z=origin; z<=origin+6; z++) world.getChunkFromChunkCoords(x,z); + final JsonObject counts = new JsonObject(); + final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); + for (int cx=origin+2;cx<=origin+4;cx++) for (int cz=origin+2;cz<=origin+4;cz++) { + final net.minecraft.world.chunk.Chunk chunk = world.getChunkFromChunkCoords(cx,cz); + for(int x=0;x<16;x++) for(int z=0;z<16;z++) for(int y=0;y<128;y++) { + pos.setPos(cx*16+x,y,cz*16+z); + final Block block = chunk.getBlockState(pos).getBlock(); + if (!"modernmetals".equals(block.getRegistryName().getResourceDomain()) || !block.getRegistryName().getResourcePath().endsWith("_ore")) continue; + final String name = block.getRegistryName().getResourcePath(); + check(dimension==0 || dimension==42,"Modern Metals ore generated in excluded dimension: "+dimension+":"+name,failures); + final JsonObject rule = ores.getAsJsonObject("modernmetals:legacy/"+name); + check(rule != null && rule.get("enabled").getAsBoolean(),"Disabled or undeclared ore generated: "+name,failures); + if (rule != null && rule.has("dimension_selectors")) { + final JsonObject placement=rule.getAsJsonObject("dimension_selectors").getAsJsonObject("orespawn:all_except_nether_end"); + if(placement!=null) check(y>=placement.get("min_y").getAsInt() && y<=placement.get("max_y").getAsInt(),"Ore outside height: "+name,failures); + } + counts.addProperty(name,counts.has(name)?counts.get(name).getAsInt()+1:1); + } + } + if((dimension==0 || dimension==42) && !Boolean.getBoolean("modern.noOres")) { + for(final String name:new String[]{"boron_ore","beryllium_ore","thorium_ore","rutile_ore"}) { + final JsonObject rule=ores.getAsJsonObject("modernmetals:legacy/"+name); + if(rule!=null && rule.get("enabled").getAsBoolean()) check(counts.has(name),"No generated sample: "+name,failures); + } + check(counts.has("titanium_ore")==Boolean.getBoolean("modern.titanium"),"Titanium placement does not match its switch",failures); + } + result.add(String.valueOf(dimension),counts); + } + report.add("oreSamples",result); + } + private static void subset(final JsonElement expected, final JsonElement actual, final String path, final JsonArray failures) { + if(actual==null) { check(false,"Missing "+path,failures); return; } + if(expected.isJsonObject()) { + if(!actual.isJsonObject()) { check(false,"Wrong type at "+path,failures); return; } + for(final java.util.Map.Entry entry:expected.getAsJsonObject().entrySet()) + subset(entry.getValue(),actual.getAsJsonObject().get(entry.getKey()),path+"/"+entry.getKey(),failures); + } else check(expected.equals(actual),"Changed "+path+": expected "+expected+", found "+actual,failures); + } + + private static void saved(final WorldServer world, final String phase, final JsonArray failures) { + final BlockPos pos = new BlockPos(8, 200, 8); + if ("first".equals(phase)) { + world.setBlockState(pos, Blocks.CHEST.getDefaultState(), 2); + final TileEntityChest chest = (TileEntityChest) world.getTileEntity(pos); + int slot = 0; + for (final String name : new String[] {"boron","beryllium","thorium","titanium"}) { + final MMDMaterial material = Materials.getMaterialByName(name); + if (material.isEmpty()) continue; + final ItemStack stack = material.getItemStack(Names.INGOT, 3); + stack.setStackDisplayName("Saved " + name); + stack.getTagCompound().setString("ModernMetalsCheck", name); + chest.setInventorySlotContents(slot++, stack); + } + if(Materials.hasMaterial("plutonium")) { + final ItemStack tool=Materials.getMaterialByName("plutonium").getItemStack(Names.PICKAXE); + tool.setItemDamage(13);tool.setStackDisplayName("Saved plutonium pickaxe"); + tool.getTagCompound().setString("ModernMetalsCheck","preserve equipment across content modes"); + chest.setInventorySlotContents(4,tool); + } + } + final TileEntityChest chest = (TileEntityChest) world.getTileEntity(pos); + check(chest != null, "Saved chest missing", failures); + if (chest == null) return; + int slot = 0; + for (final String name : new String[] {"boron","beryllium","thorium","titanium"}) { + final ItemStack stack = chest.getStackInSlot(slot++); + check(stack != null && stack.getCount() == 3 && ("modernmetals:" + name + "_ingot").equals(stack.getItem().getRegistryName().toString()) + && ("Saved " + name).equals(stack.getDisplayName()) && name.equals(stack.getTagCompound().getString("ModernMetalsCheck")), "Saved stack changed: " + name, failures); + } + if(Materials.hasMaterial("plutonium")) { + final ItemStack tool=chest.getStackInSlot(4); + check(tool!=null && tool.getItem()==Materials.getMaterialByName("plutonium").getItem(Names.PICKAXE) + && tool.getItemDamage()==13 && "Saved plutonium pickaxe".equals(tool.getDisplayName()) + && "preserve equipment across content modes".equals(tool.getTagCompound().getString("ModernMetalsCheck")),"Saved equipment changed in content mode",failures); + } + world.getChunkFromBlockCoords(pos).markDirty(); + } + private static void checkTrades(final WorldServer world, final JsonArray failures) { + final net.minecraft.entity.passive.EntityVillager merchant = new net.minecraft.entity.passive.EntityVillager(world); + final net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession smith = + net.minecraftforge.fml.common.registry.VillagerRegistry.instance().getRegistry() + .getValue(new ResourceLocation("minecraft:smith")); + final net.minecraft.village.MerchantRecipeList offers = new net.minecraft.village.MerchantRecipeList(); + final java.util.Random random = new java.util.Random(8675309L); + for (int career = 1; career <= 3; career++) { + for (int level = 0; level < 8; level++) { + final java.util.List trades = + smith.getCareer(career).getTrades(level); + if (trades != null) trades.forEach(trade -> trade.addMerchantRecipe(merchant, offers, random)); + } + } + int modern = 0; + int base = 0; + final zone.moddev.mc.modernmetals.content.ContentPolicy policy = + zone.moddev.mc.modernmetals.content.ContentPolicy.active(); + for (final net.minecraft.village.MerchantRecipe offer : offers) { + for (final ItemStack stack : new ItemStack[] { + offer.getItemToBuy(), offer.getSecondItemToBuy(), offer.getItemToSell() }) { + if (stack == null || stack.getItem().getRegistryName() == null) continue; + final String domain = stack.getItem().getRegistryName().getResourceDomain(); + if ("modernmetals".equals(domain)) { + modern++; + check(policy.allows(stack), "Fantasy mode allowed a restricted trade: " + stack, failures); + } else if ("basemetals".equals(domain)) { + base++; + } + } + } + check((modern > 0) == Boolean.getBoolean("modern.trades"), "Modern Metals trade switch ignored", failures); + check(base > 0, "Modern Metals settings removed Base Metals trades", failures); + } + + private static void check(final boolean pass, final String detail, final JsonArray failures) { if (!pass) failures.add(new JsonPrimitive(detail)); } +} diff --git a/tools/validation/legacy111/runtime-probe/review/modern/OptionalIntegrationChecks.java b/tools/validation/legacy111/runtime-probe/review/modern/OptionalIntegrationChecks.java new file mode 100644 index 00000000..775db3f2 --- /dev/null +++ b/tools/validation/legacy111/runtime-probe/review/modern/OptionalIntegrationChecks.java @@ -0,0 +1,121 @@ +package review.modern; + +import com.google.gson.JsonArray; +import com.google.gson.JsonPrimitive; +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.init.Materials; +import com.mcmoddev.lib.material.MMDMaterial; +import net.minecraftforge.fml.common.Loader; + +/** Optional API linkage stays in nested classes that are loaded only with that mod. */ +public final class OptionalIntegrationChecks { + private OptionalIntegrationChecks() { } + public static void run(final JsonArray failures) { + if (Loader.isModLoaded("tconstruct")) { + if(com.mcmoddev.lib.util.ConfigBase.Options.isModEnabled("tconstruct")) Tinkers.verify(failures); + else if(Boolean.getBoolean("modern.disabledIntegrationTest")) Tinkers.verifyForeign(failures); + } + if (Loader.isModLoaded("ic2")) IndustrialCraft.verify(failures); + if (Loader.isModLoaded("mekanism")) Mekanism.verify(failures); + } + public static void seedForeign() {Tinkers.seedForeign();} + private static void check(final boolean pass, final String message, final JsonArray failures) { + if (!pass) failures.add(new JsonPrimitive(message)); + } + private static final class Tinkers { + static void seedForeign() { + final slimeknights.tconstruct.library.materials.Material foreign=new slimeknights.tconstruct.library.materials.Material("plutonium",0x123456); + slimeknights.tconstruct.library.TinkerRegistry.addMaterial(foreign); + slimeknights.tconstruct.library.TinkerRegistry.addMaterialStats(foreign, + new slimeknights.tconstruct.library.materials.HeadMaterialStats(200,6F,2F,2), + new slimeknights.tconstruct.library.materials.BowMaterialStats(1F,1F,1F)); + } + static void verifyForeign(final JsonArray failures) { + final slimeknights.tconstruct.library.materials.Material foreign=slimeknights.tconstruct.library.TinkerRegistry.getMaterial("plutonium"); + check(foreign.hasStats("head") && foreign.hasStats("bow"),"Disabled integration modified another mod's Tinkers material",failures); + } + static void verify(final JsonArray failures) { + for (final MMDMaterial material : Materials.getMaterialsByMod("modernmetals")) { + final String name = material.getName(); + final slimeknights.tconstruct.library.materials.Material tc = slimeknights.tconstruct.library.TinkerRegistry.getMaterial(name); + check(tc != slimeknights.tconstruct.library.materials.Material.UNKNOWN, "Tinkers material missing: " + name, failures); + check(tc.matches(material.getItemStack(Names.INGOT)) != null, "Tinkers repair ingredient missing: " + name, failures); + final boolean allowed=zone.moddev.mc.modernmetals.content.ContentPolicy.active().allows(name,"pickaxe"); + check(slimeknights.tconstruct.tools.TinkerTools.pickHead.canUseMaterial(tc)==allowed,"Tinkers tool policy incorrect: "+name,failures); + if(allowed) { + final slimeknights.tconstruct.library.materials.Material wood=slimeknights.tconstruct.library.TinkerRegistry.getMaterial("wood"); + final net.minecraft.item.ItemStack tool=slimeknights.tconstruct.library.utils.ToolBuilder.tryBuildTool(stacks( + slimeknights.tconstruct.tools.TinkerTools.toolRod.getItemstackWithMaterial(wood), + slimeknights.tconstruct.tools.TinkerTools.pickHead.getItemstackWithMaterial(tc), + slimeknights.tconstruct.tools.TinkerTools.binding.getItemstackWithMaterial(wood),net.minecraft.item.ItemStack.EMPTY),"Modern Metals regression tool"); + check(tool!=null && !tool.isEmpty(),"Tinkers tool creation failed: "+name,failures); + if(tool!=null && !tool.isEmpty()) { + tool.setItemDamage(5); + final net.minecraft.item.ItemStack repaired=slimeknights.tconstruct.library.utils.ToolBuilder.tryRepairTool( + stacks(material.getItemStack(Names.INGOT)),tool,false); + check(repaired!=null && !repaired.isEmpty() && repaired.getItemDamage()<5,"Tinkers tool repair failed: "+name,failures); + } + } else check(!slimeknights.tconstruct.tools.TinkerTools.toolRod.canUseMaterial(tc),"Tinkers restricted handle bypass: "+name,failures); + if(zone.moddev.mc.modernmetals.content.ContentPolicy.active().lowFantasy()) + check(!slimeknights.tconstruct.tools.TinkerTools.bowLimb.canUseMaterial(tc),"Tinkers ranged policy bypass: "+name,failures); + final slimeknights.tconstruct.library.smeltery.MeltingRecipe melting = slimeknights.tconstruct.library.TinkerRegistry.getMelting(material.getItemStack(Names.INGOT)); + check(melting != null && melting.getResult() != null && melting.getResult().amount == 144, + "Tinkers ingot melting missing: " + name, failures); + final net.minecraftforge.fluids.Fluid fluid = net.minecraftforge.fluids.FluidRegistry.getFluid(name); + check(slimeknights.tconstruct.library.TinkerRegistry.getTableCasting( + slimeknights.tconstruct.smeltery.TinkerSmeltery.castIngot, fluid) != null, "Tinkers ingot casting missing: " + name, failures); + final net.minecraft.item.ItemStack headCast=slimeknights.tconstruct.library.tools.Pattern.setTagForPart( + new net.minecraft.item.ItemStack(slimeknights.tconstruct.smeltery.TinkerSmeltery.cast),slimeknights.tconstruct.tools.TinkerTools.pickHead); + check((slimeknights.tconstruct.library.TinkerRegistry.getTableCasting(headCast,fluid)!=null)==allowed, + "Tinkers part casting policy incorrect: "+name,failures); + if(zone.moddev.mc.modernmetals.content.ContentPolicy.active().lowFantasy() + && !zone.moddev.mc.modernmetals.content.ContentPolicy.active().allows(name,"gear")) + check(slimeknights.tconstruct.library.TinkerRegistry.getTableCasting(slimeknights.tconstruct.smeltery.TinkerSmeltery.castGear,fluid)==null, + "Tinkers restricted gear casting remains: "+name,failures); + } + boolean titanium = false; + for (final slimeknights.tconstruct.library.smeltery.AlloyRecipe recipe : slimeknights.tconstruct.library.TinkerRegistry.getAlloys()) { + check(recipe.getResult() != null && recipe.getResult().getFluid() != null && recipe.getResult().amount > 0, "Invalid alloy output", failures); + for (final net.minecraftforge.fluids.FluidStack input : recipe.getFluids()) + check(input != null && input.getFluid() != null && input.amount > 0, "Invalid alloy input", failures); + if (recipe.getResult() != null && recipe.getResult().getFluid() != null && "titanium".equals(recipe.getResult().getFluid().getName())) { + final java.util.Set names = new java.util.HashSet<>(); + for (final net.minecraftforge.fluids.FluidStack input : recipe.getFluids()) if (input != null && input.getFluid() != null) names.add(input.getFluid().getName()); + int inputs = 0; + for (final net.minecraftforge.fluids.FluidStack input : recipe.getFluids()) if (input != null) inputs += input.amount; + titanium |= names.contains("rutile") && names.contains("magnesium") && recipe.getResult().amount == inputs; + } + } + if(Materials.hasMaterial("titanium") && Materials.hasMaterial("rutile") && Materials.hasMaterial("magnesium")) + check(titanium, "Tinkers titanium alloy chain missing", failures); + } + } + private static final class IndustrialCraft { + static void verify(final JsonArray failures) { + for (final String name : new String[]{"boron","beryllium","thorium","titanium","chromium"}) { + if (!Materials.hasMaterial(name)) continue; + final MMDMaterial material = Materials.getMaterialByName(name); + check(ic2.api.recipe.Recipes.macerator.getOutputFor(material.getBlockItemStack(Names.ORE), false) != null, + "IC2 ore processing missing: " + name, failures); + } + } + } + private static final class Mekanism { + static void verify(final JsonArray failures) { + for (final String name : new String[]{"boron","beryllium","thorium","titanium","chromium"}) { + if (!Materials.hasMaterial(name)) continue; + final MMDMaterial material = Materials.getMaterialByName(name); + check(mekanism.api.gas.GasRegistry.getGas(name) != null, "Mekanism gas missing: " + name, failures); + check(mekanism.api.gas.GasRegistry.getGas("clean" + name) != null, "Mekanism clean gas missing: " + name, failures); + check(mekanism.common.recipe.RecipeHandler.Recipe.ENRICHMENT_CHAMBER.containsRecipe(material.getBlockItemStack(Names.ORE)), + "Mekanism enrichment missing: " + name, failures); + } + } + } + private static net.minecraft.util.NonNullList stacks(final net.minecraft.item.ItemStack... items) { + final net.minecraft.util.NonNullList list = net.minecraft.util.NonNullList.create(); + java.util.Collections.addAll(list, items); + return list; + } + +} diff --git a/tools/validation/legacy111/runtime-probe/review/modern/OreOverlayChecks.java b/tools/validation/legacy111/runtime-probe/review/modern/OreOverlayChecks.java new file mode 100644 index 00000000..ff847176 --- /dev/null +++ b/tools/validation/legacy111/runtime-probe/review/modern/OreOverlayChecks.java @@ -0,0 +1,104 @@ +package review.modern; + +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.init.Materials; +import java.util.List; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.WorldServer; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import net.minecraftforge.oredict.OreDictionary; + +/** Checks the installed client's baked models, including the inventory forms. */ +final class OreOverlayChecks { + private static final String STONE = "minecraft:blocks/stone"; + private static final int TEST_STONE = 0xFF39B48E; + + static void verify(Minecraft minecraft) { + final IBakedModel missing = minecraft.getRenderItem().getItemModelMesher().getModelManager().getMissingModel(); + int overlaid = 0; + int fallback = 0; + for (com.mcmoddev.lib.material.MMDMaterial material : Materials.getMaterialsByMod("modernmetals")) { + if (!material.hasBlock(Names.ORE)) continue; + final Block ore = material.getBlock(Names.ORE); + final IBlockState state = ore.getDefaultState(); + final Item item = Item.getItemFromBlock(ore); + final String name = material.getName() + "_ore"; + require(ore.getRegistryName().equals(new ResourceLocation("modernmetals", name)), "Ore registry ID changed: " + name); + require(ore.getRegistryName().equals(item.getRegistryName()), "Ore item registry ID changed: " + name); + require(OreDictionary.getOres("ore" + material.getCapitalizedName()).stream().anyMatch(s -> s.getItem() == item), + "Ore Dictionary entry missing: " + name); + final boolean thorium = name.equals("thorium_ore"); + require(ore.getBlockLayer() == (thorium ? BlockRenderLayer.SOLID : BlockRenderLayer.CUTOUT_MIPPED), + "Wrong render layer: " + name); + final IBakedModel blockModel = minecraft.getBlockRendererDispatcher().getBlockModelShapes().getModelForState(state); + final IBakedModel itemModel = minecraft.getRenderItem().getItemModelMesher().getItemModel(new ItemStack(item)); + require(blockModel != missing && itemModel != missing, "Missing baked ore block or item: " + name); + verifyModel(blockModel, state, name, thorium); + verifyModel(itemModel, null, name, thorium); + if (thorium) fallback++; else overlaid++; + } + require(overlaid == 19 && fallback == 1, "Unexpected registered ore set: " + overlaid + " overlays, " + fallback + " fallback"); + final TextureAtlasSprite stone = minecraft.getTextureMapBlocks().getAtlasSprite(STONE); + require((stone.getFrameTextureData(0)[0][0] == TEST_STONE) == Boolean.getBoolean("modern.oreHostPack"), + "The expected stone resource pack was not applied"); + System.out.println("MODERN_METALS_ORE_OVERLAYS_PASS ores=" + overlaid + " fallback=" + fallback + + " hostPack=" + Boolean.getBoolean("modern.oreHostPack")); + } + + private static void verifyModel(IBakedModel model, IBlockState state, String name, boolean thorium) { + final String base = thorium ? "modernmetals:blocks/thorium_ore" : STONE; + final String overlay = "modernmetals:blocks/ore_overlays/" + name; + require(base.equals(model.getParticleTexture().getIconName()), "Wrong particles: " + name); + for (EnumFacing face : EnumFacing.values()) { + final List quads = model.getQuads(state, face, 0); + require(quads.size() == (thorium ? 1 : 2), "Wrong face count: " + name + " " + face); + verifyFace(quads, face, base, 0.0F); + if (!thorium) verifyFace(quads, face, overlay, 0.05F / 16.0F); + } + } + + private static void verifyFace(List quads, EnumFacing face, String texture, float offset) { + final BakedQuad quad = quads.stream().filter(q -> texture.equals(q.getSprite().getIconName())) + .findFirst().orElseThrow(() -> new AssertionError("Missing baked sprite: " + texture)); + final int axis = face.getFrontOffsetX() != 0 ? 0 : face.getFrontOffsetY() != 0 ? 1 : 2; + final float expected = face.getAxisDirection() == EnumFacing.AxisDirection.POSITIVE ? 1.0F + offset : -offset; + final int[] data = quad.getVertexData(); + for (int vertex = 0; vertex < 4; vertex++) { + require(Math.abs(Float.intBitsToFloat(data[vertex * (data.length / 4) + axis]) - expected) < 0.0001F, + "Wrong face depth for " + texture + " " + face); + } + } + + static void placeDisplay(MinecraftServer server) { + final WorldServer world = server.getWorld(0); + for (int x = 0; x < 5; x++) { + for (int z = -5; z <= 0; z++) { + world.setBlockState(new BlockPos(x, 199, z), Blocks.STONE.getDefaultState()); + } + } + final String[] ores = {"aluminum_ore", "boron_ore", "rutile_ore", "thorium_ore"}; + for (int x = 0; x < ores.length; x++) { + final Block ore = ForgeRegistries.BLOCKS.getValue(new ResourceLocation("modernmetals", ores[x])); + world.setBlockState(new BlockPos(x, 200, 0), ore.getDefaultState()); + } + world.setBlockState(new BlockPos(4, 200, 0), Blocks.STONE.getDefaultState()); + server.getPlayerList().getPlayers().get(0).connection.setPlayerLocation(2.5, 200, -4, 0, 13); + } + + private static void require(boolean condition, String detail) { + if (!condition) throw new AssertionError(detail); + } +} diff --git a/tools/validation/requirements.txt b/tools/validation/requirements.txt new file mode 100644 index 00000000..ad5d466f --- /dev/null +++ b/tools/validation/requirements.txt @@ -0,0 +1,2 @@ +jsonschema==4.23.0 +PyYAML==6.0.3 diff --git a/update.json b/update.json index 34cccc7c..2db1142b 100644 --- a/update.json +++ b/update.json @@ -1,7 +1,7 @@ { - "homepage": "http://minecraft.curseforge.com/projects/modern-metals", + "homepage": "https://www.curseforge.com/minecraft/mc-mods/modern-metals/files/all?version=1.11.2", "promos": { - "1.11.2-latest": "0.0.0", + "1.11.2-latest": "2.5.0-beta4", "1.11-latest": "0.0.0", "1.10.2-latest": "0.0.0", "1.10-latest": "0.0.0", @@ -13,7 +13,7 @@ "1.7.10-latest": "0.0.0", "1.7.2-latest": "0.0.0", "1.6.4-latest": "0.0.0", - "1.11.2-recommended": "0.0.0", + "1.11.2-recommended": "2.5.0-beta4", "1.11-recommended": "0.0.0", "1.10.2-recommended": "0.0.0", "1.10-recommended": "0.0.0", @@ -27,7 +27,7 @@ "1.6.4-recommended": "0.0.0" }, "1.11.2": { - "0.0.0": "No Release" + "2.5.0-beta4": "Modern Metals 2.5.0-beta4.58 for Minecraft 1.11.2" }, "1.11": { "0.0.0": "No Release"