Compare commits
60 Commits
1.20
...
eol/1.20.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0eb70468a1 | ||
|
|
04aac43db0 | ||
|
|
8d5e66218e | ||
|
|
2ec1000ae8 | ||
|
|
ccc21d76d8 | ||
|
|
bfdd1f913d | ||
|
|
473597a915 | ||
|
|
29d1f88539 | ||
|
|
acb6809459 | ||
|
|
13ee6b3523 | ||
|
|
af8e23f41a | ||
|
|
19ba30280c | ||
|
|
33609d234c | ||
|
|
6e3134161a | ||
|
|
c672948d8c | ||
|
|
d8263c55f8 | ||
|
|
7af609b56f | ||
|
|
894173cf1d | ||
|
|
fc52570951 | ||
|
|
9efe912090 | ||
|
|
95f2dc95d3 | ||
|
|
79751dac21 | ||
|
|
1339fc7af6 | ||
|
|
3b7d2f1e2e | ||
|
|
546a362d9c | ||
|
|
7a2b71381c | ||
|
|
3add97202e | ||
|
|
6d096e8ae0 | ||
|
|
fbacc85f86 | ||
|
|
f6f7badde8 | ||
|
|
2a099d8537 | ||
|
|
348a2bcf3d | ||
|
|
7f80f38862 | ||
|
|
cc54b6d67d | ||
|
|
e8fa483917 | ||
|
|
dc3cb998b0 | ||
|
|
972121fa50 | ||
|
|
93554d1854 | ||
|
|
a00efe62c7 | ||
|
|
1267a25db1 | ||
|
|
0149a113a8 | ||
|
|
97d4f6bbc2 | ||
|
|
dfb0c52fa3 | ||
|
|
02fa2be42b | ||
|
|
45c29216a8 | ||
|
|
5e8e7649e5 | ||
|
|
0a2cda0814 | ||
|
|
5952fa2178 | ||
|
|
9bf0017aeb | ||
|
|
cb286c0bca | ||
|
|
b56a65c192 | ||
|
|
8d1058cc3f | ||
|
|
eb15718023 | ||
|
|
861474d635 | ||
|
|
f3bda91ebf | ||
|
|
84c72f8da8 | ||
|
|
3716912a53 | ||
|
|
42688757a7 | ||
|
|
23b473f85d | ||
|
|
7fcaf716d8 |
78
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
78
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -1,78 +0,0 @@
|
||||||
name: Bug Report
|
|
||||||
description: "For reporting bugs and other defects"
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: >-
|
|
||||||
**Need help?** Ask on [Discord](https://discord.gg/rN9Y7caguP) instead of opening an issue.
|
|
||||||
|
|
||||||
**Issues that do not meet the requirements below (or are otherwise impossible to address with the given info) will be closed without investigation.**
|
|
||||||
- type: checkboxes
|
|
||||||
id: confirmations
|
|
||||||
attributes:
|
|
||||||
label: Checklist
|
|
||||||
options:
|
|
||||||
- label: I am reporting a defect, not asking for help
|
|
||||||
required: true
|
|
||||||
- label: I have searched existing issues and this has not been reported
|
|
||||||
required: true
|
|
||||||
- label: I have reduced my mod list to the minimum required to reproduce this issue (see below)
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Bug Description
|
|
||||||
description: >-
|
|
||||||
Describe the issue in detail. Be sure to include what you expected to happen and what actually happened.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: minimal-mods
|
|
||||||
attributes:
|
|
||||||
label: Minimal Mod List
|
|
||||||
description: >-
|
|
||||||
List ONLY the mods required to reproduce this issue. Maintainers have debugging tools that help them
|
|
||||||
locate problems quickly, but these generally don't work well in modpacks or large mod sets.
|
|
||||||
A minimal list should typically contain fewer than 10 mods.
|
|
||||||
|
|
||||||
Reports with large mod lists will likely be closed without investigation, unless the problem is very clear.
|
|
||||||
|
|
||||||
If you don't know which mods are causing your problem, use binary search:
|
|
||||||
|
|
||||||
1. Remove half your mods
|
|
||||||
|
|
||||||
2. Test if the issue still occurs
|
|
||||||
|
|
||||||
3. If yes, remove half again. If no, restore the last removed half and repeat from step 1.
|
|
||||||
|
|
||||||
4. Repeat until only the necessary mods remain
|
|
||||||
placeholder: "- ModernFix 5.x.x\n- SomeMod 1.2.3"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: description-reproduction-steps
|
|
||||||
attributes:
|
|
||||||
label: Reproduction Steps
|
|
||||||
description: >-
|
|
||||||
Provide clear steps to reproduce the bug. Each step should be a single concrete action.
|
|
||||||
|
|
||||||
Maintainers are busy and need to be able to quickly replicate your problem. Your reproduction steps should be
|
|
||||||
clear enough for someone who is unfamiliar with your mods to follow in 5 minutes or less (not counting time
|
|
||||||
to launch the game).
|
|
||||||
|
|
||||||
Providing vague steps is likely to result in the issue being closed.
|
|
||||||
|
|
||||||
placeholder: "1. \n2. \n3. "
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: diagnostic-info
|
|
||||||
attributes:
|
|
||||||
label: Diagnostic Info
|
|
||||||
description: >-
|
|
||||||
Drag and drop `latest.log` from `.minecraft/logs/` for the session where the issue occurred.
|
|
||||||
Do not paste log text inline. Issues without a valid `latest.log` will be closed.
|
|
||||||
|
|
||||||
If a crash occurred, also attach the relevant file from `.minecraft/crash-reports/`.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,6 +0,0 @@
|
||||||
blank_issues_enabled: true
|
|
||||||
contact_links:
|
|
||||||
- name: For help with other issues, join our Discord community
|
|
||||||
url: https://discord.gg/rN9Y7caguP
|
|
||||||
about: This is the best option for getting help with mod installation, performance issues, and any other support inquiries
|
|
||||||
# Copied from https://github.com/CaffeineMC/sodium-fabric#community
|
|
||||||
128
.github/workflows/gradle.yml
vendored
128
.github/workflows/gradle.yml
vendored
|
|
@ -10,127 +10,35 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
concurrency:
|
|
||||||
group: release-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up JDK 21
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 21
|
java-version: 17
|
||||||
check-latest: true
|
|
||||||
- name: Check if release branch
|
|
||||||
id: check_branch
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref }}" =~ ^refs/heads/[0-9]+\. ]]; then
|
|
||||||
echo "is_release=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "is_release=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
cache-read-only: ${{ steps.check_branch.outputs.is_release != 'true' }}
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/1.') }}
|
||||||
gradle-home-cache-cleanup: true
|
gradle-home-cache-cleanup: true
|
||||||
- name: Remove tags for release on other versions
|
- name: Setup project Loom cache
|
||||||
if: steps.check_branch.outputs.is_release == 'true'
|
uses: actions/cache@v3
|
||||||
run: ./scripts/tagcleaner.sh
|
|
||||||
- name: Build ModernFix using Gradle
|
|
||||||
run: ./gradlew build
|
|
||||||
- name: Run mixin audit
|
|
||||||
run: timeout 60 xvfb-run ./gradlew runAuditClient
|
|
||||||
- name: Publish mod to CurseForge & Modrinth
|
|
||||||
if: steps.check_branch.outputs.is_release == 'true'
|
|
||||||
run: ./gradlew publishMods copyJarToBin
|
|
||||||
env:
|
|
||||||
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
|
|
||||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
|
||||||
- name: Capture mod version
|
|
||||||
if: steps.check_branch.outputs.is_release == 'true'
|
|
||||||
run: |
|
|
||||||
echo "MOD_VERSION=$(./gradlew properties -q | grep '^version:' | awk '{print $2}')" >> $GITHUB_ENV
|
|
||||||
echo "MC_VERSION=$(grep '^minecraft_version=' gradle.properties | cut -d= -f2)" >> $GITHUB_ENV
|
|
||||||
- name: Comment on fixed issues
|
|
||||||
if: steps.check_branch.outputs.is_release == 'true'
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
with:
|
||||||
script: |
|
path: |
|
||||||
const { execSync } = require('child_process');
|
.gradle/loom-cache
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle.properties', '**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
const branch = context.ref.replace('refs/heads/', '');
|
restore-keys: ${{ runner.os }}-gradle-
|
||||||
const { data: runs } = await github.rest.actions.listWorkflowRuns({
|
- name: Build ModernFix
|
||||||
owner: context.repo.owner,
|
run: |
|
||||||
repo: context.repo.repo,
|
chmod +x gradlew
|
||||||
workflow_id: 'gradle.yml',
|
./gradlew build
|
||||||
branch,
|
|
||||||
status: 'success',
|
|
||||||
per_page: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
const logArgs = runs.workflow_runs.length > 0
|
|
||||||
? `${runs.workflow_runs[0].head_sha}..${context.sha}`
|
|
||||||
: `-1 ${context.sha}`;
|
|
||||||
const log = execSync(`git log ${logArgs} --format=%s%n%b`, { encoding: 'utf8' });
|
|
||||||
|
|
||||||
const issueNumbers = new Set();
|
|
||||||
const pattern = /(?:fix(?:es|ed)?|close[sd]?|resolve[sd]?)\s+#(\d+)/gi;
|
|
||||||
let match;
|
|
||||||
while ((match = pattern.exec(log)) !== null) {
|
|
||||||
issueNumbers.add(parseInt(match[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (issueNumbers.size === 0) {
|
|
||||||
console.log('No fixed issues found in commits');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MARKER = '<!-- modernfix-fix-tracker -->';
|
|
||||||
const modVersion = process.env.MOD_VERSION;
|
|
||||||
const mcVersion = process.env.MC_VERSION;
|
|
||||||
const newLine = `- ${modVersion} for Minecraft ${mcVersion}`;
|
|
||||||
|
|
||||||
for (const issueNumber of issueNumbers) {
|
|
||||||
try {
|
|
||||||
const { data: comments } = await github.rest.issues.listComments({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: issueNumber,
|
|
||||||
per_page: 100
|
|
||||||
});
|
|
||||||
|
|
||||||
const existing = comments.find(c => c.body.includes(MARKER));
|
|
||||||
if (existing) {
|
|
||||||
await github.rest.issues.updateComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
comment_id: existing.id,
|
|
||||||
body: existing.body + `\n${newLine}`
|
|
||||||
});
|
|
||||||
console.log(`Updated comment on issue #${issueNumber}`);
|
|
||||||
} else {
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: issueNumber,
|
|
||||||
body: `${MARKER}\nThe fix for this issue has been released in the following versions of ModernFix:\n${newLine}`
|
|
||||||
});
|
|
||||||
console.log(`Created comment on issue #${issueNumber}`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(`Could not comment on #${issueNumber}: ${e.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
- name: Upload Artifacts to GitHub
|
- name: Upload Artifacts to GitHub
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Package
|
name: Package
|
||||||
path: bin
|
path: bin
|
||||||
|
|
|
||||||
42
.github/workflows/release.yml
vendored
Normal file
42
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
name: Release ModernFix Artifacts
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
if: github.repository_owner == 'embeddedt'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: 17
|
||||||
|
- name: Remove tags for release on other versions
|
||||||
|
run: ./scripts/tagcleaner.sh
|
||||||
|
- name: Build and publish mod to CurseForge & Modrinth
|
||||||
|
run: |
|
||||||
|
chmod +x gradlew
|
||||||
|
./gradlew publishToModSites copyJarToBin
|
||||||
|
env:
|
||||||
|
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||||
|
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||||
|
- name: Upload assets to GitHub
|
||||||
|
uses: AButler/upload-release-assets@v2.0
|
||||||
|
with:
|
||||||
|
files: 'bin/*'
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Add changelog to release
|
||||||
|
uses: irongut/EditRelease@v1.2.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
id: ${{ github.event.release.id }}
|
||||||
|
replacebody: true
|
||||||
|
files: "CHANGELOG.md"
|
||||||
4
.github/workflows/wiki_update.yml
vendored
4
.github/workflows/wiki_update.yml
vendored
|
|
@ -8,10 +8,10 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
wikigen:
|
wikigen:
|
||||||
if: github.repository_owner == 'embeddedt'
|
if: github.repository_owner == 'embeddedt'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Generate Markdown Patch-List
|
- name: Generate Markdown Patch-List
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,16 @@ ModernFix is a standard Minecraft-style Gradle project powered by Architectury L
|
||||||
run the `build` task (e.g. via `./gradlew build`). You can also use `./gradlew forge:build` or `./gradlew fabric:build`
|
run the `build` task (e.g. via `./gradlew build`). You can also use `./gradlew forge:build` or `./gradlew fabric:build`
|
||||||
to build for just one loader (e.g. when debugging and wanting to rebuild quickly).
|
to build for just one loader (e.g. when debugging and wanting to rebuild quickly).
|
||||||
|
|
||||||
You must use Java 21 to develop ModernFix as the toolchain requires it. Nonetheless, the built 1.20.1 JAR is still
|
You must use Java 17 to develop ModernFix as the toolchain requires it. Nonetheless, the 1.16 mod JARs will work on
|
||||||
compatible with Java 17.
|
a Minecraft instance with Java 8.
|
||||||
|
|
||||||
## Submitting pull requests
|
## Submitting pull requests
|
||||||
|
|
||||||
Code or documentation contributions are welcome. Please keep the following points in mind:
|
Code or documentation contributions are welcome. Please keep the following points in mind:
|
||||||
|
|
||||||
* This project supports many Minecraft versions. Ideally, contributions should be made to the oldest relevant MC version (currently 1.20)
|
* This project supports many Minecraft versions. Ideally, contributions should be made to the oldest relevant MC version.
|
||||||
and then they will be ported forward.
|
For instance, a PR optimizing new worldgen should be made to 1.18 (not 1.19 or 1.20) while a PR optimizing something
|
||||||
|
like recipes should be made to 1.16 (the oldest supported version).
|
||||||
|
|
||||||
This somewhat unconventional policy ensures that all supported versions are treated equal when it comes to development,
|
This somewhat unconventional policy ensures that all supported versions are treated equal when it comes to development,
|
||||||
rather than the onus being on other modders and players to backport changes that are needed. Changes to older versions are
|
rather than the onus being on other modders and players to backport changes that are needed. Changes to older versions are
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
plugins {
|
|
||||||
id 'com.gradleup.shadow' version '8.3.9'
|
|
||||||
id 'java-library'
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven { url uri("https://maven.fabricmc.net") }
|
|
||||||
maven { url "https://maven.neoforged.net/releases" }
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
|
|
||||||
compileOnly 'com.google.auto.service:auto-service:1.1.1'
|
|
||||||
|
|
||||||
implementation 'com.google.code.gson:gson:2.10.1'
|
|
||||||
shadow 'com.google.code.gson:gson:2.10.1'
|
|
||||||
implementation 'com.google.auto:auto-common:1.2.1'
|
|
||||||
shadow 'com.google.auto:auto-common:1.2.1'
|
|
||||||
implementation 'com.google.guava:guava:21.0'
|
|
||||||
shadow 'com.google.guava:guava:21.0'
|
|
||||||
|
|
||||||
implementation project(":annotations")
|
|
||||||
shadow project(":annotations")
|
|
||||||
// Shadow annotations
|
|
||||||
implementation 'net.fabricmc:sponge-mixin:0.12.5+'
|
|
||||||
implementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
|
||||||
implementation 'net.minecraftforge:mergetool:1.1.7'
|
|
||||||
implementation 'net.neoforged:mergetool:2.0.2'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
options.release = 17
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
dependencies {
|
|
||||||
include(dependency('net.fabricmc:sponge-mixin:'))
|
|
||||||
include(dependency('net.fabricmc:fabric-loader:'))
|
|
||||||
include(dependency(':mergetool:'))
|
|
||||||
}
|
|
||||||
// shadowJar bug
|
|
||||||
include '*.jar'
|
|
||||||
include 'META-INF/services/javax.annotation.processing.Processor'
|
|
||||||
include 'META-INF/gradle/incremental.annotation.processors'
|
|
||||||
include 'org/spongepowered/asm/mixin/Mixin.class'
|
|
||||||
include 'org/fury_phoenix/**/*'
|
|
||||||
include {it.getName() == 'OnlyIn.class'}
|
|
||||||
include {it.getName() == 'Dist.class'}
|
|
||||||
include {it.getName() == 'Environment.class'}
|
|
||||||
include {it.getName() == 'EnvType.class'}
|
|
||||||
}
|
|
||||||
|
|
||||||
version = '1.1.4'
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
package org.fury_phoenix.mixinAp.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import javax.annotation.processing.Messager;
|
|
||||||
import javax.annotation.processing.ProcessingEnvironment;
|
|
||||||
import javax.lang.model.element.AnnotationValue;
|
|
||||||
import javax.lang.model.element.TypeElement;
|
|
||||||
import javax.lang.model.type.TypeMirror;
|
|
||||||
import javax.lang.model.util.Elements;
|
|
||||||
import javax.lang.model.util.Types;
|
|
||||||
import javax.tools.Diagnostic;
|
|
||||||
|
|
||||||
import net.fabricmc.api.Environment;
|
|
||||||
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.annotation.IgnoreMixin;
|
|
||||||
import org.fury_phoenix.mixinAp.util.TypedAccessorMap;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
|
|
||||||
import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
|
|
||||||
import static java.util.AbstractMap.SimpleImmutableEntry;
|
|
||||||
|
|
||||||
public class ClientMixinValidator {
|
|
||||||
|
|
||||||
private final Messager messager;
|
|
||||||
|
|
||||||
private final Elements elemUtils;
|
|
||||||
|
|
||||||
private final Types types;
|
|
||||||
|
|
||||||
private final boolean debug;
|
|
||||||
|
|
||||||
private static final TypedAccessorMap<Annotation> markers = new TypedAccessorMap<>();
|
|
||||||
|
|
||||||
private static final Map.Entry<Class<Environment>, Function<? super Environment, ?>>
|
|
||||||
FabricAccessor = new SimpleImmutableEntry<>(Environment.class, Environment::value);
|
|
||||||
|
|
||||||
private static final Map.Entry<
|
|
||||||
Class<net.minecraftforge.api.distmarker.OnlyIn>,
|
|
||||||
Function<? super net.minecraftforge.api.distmarker.OnlyIn, ?>>
|
|
||||||
ForgeAccessor = new SimpleImmutableEntry<>(
|
|
||||||
net.minecraftforge.api.distmarker.OnlyIn.class,
|
|
||||||
net.minecraftforge.api.distmarker.OnlyIn::value
|
|
||||||
);
|
|
||||||
|
|
||||||
private static final Map.Entry<
|
|
||||||
Class<net.neoforged.api.distmarker.OnlyIn>,
|
|
||||||
Function<? super net.neoforged.api.distmarker.OnlyIn, ?>>
|
|
||||||
NeoForgeAccessor = new SimpleImmutableEntry<>(
|
|
||||||
net.neoforged.api.distmarker.OnlyIn.class,
|
|
||||||
net.neoforged.api.distmarker.OnlyIn::value
|
|
||||||
);
|
|
||||||
|
|
||||||
static {
|
|
||||||
markers.put(FabricAccessor);
|
|
||||||
markers.put(ForgeAccessor);
|
|
||||||
markers.put(NeoForgeAccessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Collection<String> unannotatedClasses = new HashSet<>();
|
|
||||||
|
|
||||||
public ClientMixinValidator(ProcessingEnvironment env) {
|
|
||||||
debug = Boolean.valueOf(env.getOptions().get("org.fury_phoenix.mixinAp.validator.debug"));
|
|
||||||
messager = env.getMessager();
|
|
||||||
elemUtils = env.getElementUtils();
|
|
||||||
types = env.getTypeUtils();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean validateMixin(TypeElement annotatedMixinClass) {
|
|
||||||
return targetsClient(annotatedMixinClass) &&
|
|
||||||
(annotatedMixinClass.getAnnotation(ClientOnlyMixin.class) == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean targetsClient(TypeElement annotatedMixinClass) {
|
|
||||||
return targetsClient(getTargets(annotatedMixinClass)) &&
|
|
||||||
!isIgnored(annotatedMixinClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean targetsClient(Collection<?> classTargets) {
|
|
||||||
return classTargets.stream().anyMatch(this::targetsClient);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean targetsClient(Object classTarget) {
|
|
||||||
if (classTarget instanceof TypeElement te) {
|
|
||||||
return isClientMarked(te);
|
|
||||||
} else if (classTarget instanceof TypeMirror tm) {
|
|
||||||
var el = types.asElement(tm);
|
|
||||||
return el != null ? targetsClient(el) : warn("TypeMirror of " + tm);
|
|
||||||
} else if (classTarget instanceof String s) {
|
|
||||||
var te = elemUtils.getTypeElement(toSourceString(s.split("\\$")[0]));
|
|
||||||
return te != null ? targetsClient(te) : warn(s);
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Unhandled type: "
|
|
||||||
+ classTarget.getClass() + "\n" + "Stringified contents: "
|
|
||||||
+ classTarget.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isClientMarked(TypeElement te) {
|
|
||||||
for (var entry : markers.entrySet()) {
|
|
||||||
var marker = te.getAnnotation(entry.getKey());
|
|
||||||
if(marker == null) continue;
|
|
||||||
|
|
||||||
return entry.getValue().apply(marker).toString().equals("CLIENT");
|
|
||||||
}
|
|
||||||
if(debug && unannotatedClasses.add(te.toString())) {
|
|
||||||
messager.printMessage(Diagnostic.Kind.WARNING,
|
|
||||||
"No marker annotations present on " + te + "!");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isIgnored(TypeElement te) {
|
|
||||||
if(te.getAnnotation(IgnoreMixin.class) != null) {
|
|
||||||
messager.printMessage(Diagnostic.Kind.WARNING,
|
|
||||||
toSourceString(te.toString()) + " is ignored!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean warn(Object o) {
|
|
||||||
messager.printMessage(Diagnostic.Kind.WARNING,
|
|
||||||
toSourceString(o.toString()) + " can't be loaded, so it is skipped!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map.Entry<? extends CharSequence, ? extends CharSequence>
|
|
||||||
getClientMixinEntry(TypeElement annotatedMixinClass) {
|
|
||||||
return new SimpleImmutableEntry<>(
|
|
||||||
annotatedMixinClass.getQualifiedName(),
|
|
||||||
getTargets(annotatedMixinClass)
|
|
||||||
.stream()
|
|
||||||
.filter(this::targetsClient)
|
|
||||||
.map(Object::toString)
|
|
||||||
.map(ClientMixinValidator::toSourceString)
|
|
||||||
.collect(Collectors.joining(", "))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Collection<Object> getTargets(TypeElement annotatedMixinClass) {
|
|
||||||
Collection<? extends TypeMirror> clzsses = Set.of();
|
|
||||||
Collection<? extends String> imaginaries = Set.of();
|
|
||||||
TypeMirror MixinElement = elemUtils.getTypeElement(Mixin.class.getName()).asType();
|
|
||||||
for (var mirror : annotatedMixinClass.getAnnotationMirrors()) {
|
|
||||||
if(!types.isSameType(mirror.getAnnotationType(), MixinElement))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var wrappedClzss = (List<? extends AnnotationValue>)
|
|
||||||
getAnnotationValue(mirror, "value").getValue();
|
|
||||||
|
|
||||||
clzsses = wrappedClzss.stream()
|
|
||||||
.map(AnnotationValue::getValue)
|
|
||||||
.map(TypeMirror.class::cast)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var wrappedStrings = (List<? extends AnnotationValue>)
|
|
||||||
getAnnotationValue(mirror, "targets").getValue();
|
|
||||||
|
|
||||||
imaginaries = wrappedStrings.stream()
|
|
||||||
.map(AnnotationValue::getValue)
|
|
||||||
.map(String.class::cast)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
return Stream.of(clzsses, imaginaries)
|
|
||||||
.flatMap(Collection::stream)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toSourceString(String bytecodeName) {
|
|
||||||
return bytecodeName.replaceAll("\\/", ".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
package org.fury_phoenix.mixinAp.annotation;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import javax.annotation.processing.AbstractProcessor;
|
|
||||||
import javax.annotation.processing.Processor;
|
|
||||||
import javax.annotation.processing.RoundEnvironment;
|
|
||||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
|
||||||
import javax.annotation.processing.SupportedOptions;
|
|
||||||
import javax.lang.model.SourceVersion;
|
|
||||||
import javax.lang.model.element.Element;
|
|
||||||
import javax.lang.model.element.TypeElement;
|
|
||||||
import javax.tools.Diagnostic;
|
|
||||||
|
|
||||||
import org.fury_phoenix.mixinAp.config.MixinConfig;
|
|
||||||
|
|
||||||
@SupportedAnnotationTypes({"org.spongepowered.asm.mixin.Mixin", "org.embeddedt.modernfix.annotation.ClientOnlyMixin"})
|
|
||||||
@SupportedOptions({"rootProject.name", "project.name", "org.fury_phoenix.mixinAp.validator.debug"})
|
|
||||||
@AutoService(Processor.class)
|
|
||||||
public class MixinProcessor extends AbstractProcessor {
|
|
||||||
|
|
||||||
// Remember to call toString when using aliases
|
|
||||||
private static final Map<String, String> aliases = Map.of(
|
|
||||||
"Mixin", "mixins",
|
|
||||||
"ClientOnlyMixin", "client"
|
|
||||||
);
|
|
||||||
|
|
||||||
private final Map<String, List<String>> mixinConfigList = new HashMap<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SourceVersion getSupportedSourceVersion() {
|
|
||||||
return SourceVersion.latest();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
|
||||||
try {
|
|
||||||
if(roundEnv.processingOver()){
|
|
||||||
filterMixinSets();
|
|
||||||
// create record for serialization, compute package name
|
|
||||||
String packageName = Optional.ofNullable(mixinConfigList.get("mixins"))
|
|
||||||
.orElse(mixinConfigList.get("client"))
|
|
||||||
.get(0).split("(?<=mixin)")[0];
|
|
||||||
finalizeMixinConfig();
|
|
||||||
new MixinConfig(packageName,
|
|
||||||
mixinConfigList.get("mixins"),
|
|
||||||
mixinConfigList.get("client")
|
|
||||||
).generateMixinConfig(processingEnv);
|
|
||||||
} else {
|
|
||||||
processMixins(annotations, roundEnv);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Fatal error:" +
|
|
||||||
Throwables.getStackTraceAsString(e));
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
// Halt the AP to prevent nonsense errors
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processMixins(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
|
||||||
for (TypeElement annotation : annotations) {
|
|
||||||
Set<? extends Element> annotatedMixins = roundEnv.getElementsAnnotatedWith(annotation);
|
|
||||||
|
|
||||||
Stream<TypeElement> mixinStream =
|
|
||||||
annotatedMixins.stream()
|
|
||||||
.map(TypeElement.class::cast);
|
|
||||||
|
|
||||||
validateCommonMixins(annotation, mixinStream);
|
|
||||||
|
|
||||||
List<String> mixins =
|
|
||||||
annotatedMixins.stream()
|
|
||||||
.map(TypeElement.class::cast)
|
|
||||||
.map(e -> processingEnv.getElementUtils().getBinaryName(e).toString())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
mixinConfigList.putIfAbsent(aliases.get(annotation.getSimpleName().toString()), mixins);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void filterMixinSets() {
|
|
||||||
List<String> commonSet = mixinConfigList.get("mixins");
|
|
||||||
if(commonSet == null) return;
|
|
||||||
commonSet.removeAll(mixinConfigList.get("client"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateCommonMixins(TypeElement annotation, Stream<TypeElement> mixins) {
|
|
||||||
if(!annotation.getSimpleName().toString().equals("Mixin"))
|
|
||||||
return;
|
|
||||||
ClientMixinValidator validator = new ClientMixinValidator(processingEnv);
|
|
||||||
// The implementation may throw a CME
|
|
||||||
mixins.sequential()
|
|
||||||
.filter(validator::validateMixin)
|
|
||||||
.map(validator::getClientMixinEntry)
|
|
||||||
.forEach(this::logClientClassTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logClientClassTarget(Map.Entry<? extends CharSequence, ? extends CharSequence> mixin) {
|
|
||||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
|
|
||||||
"Mixin " + mixin.getKey() + " targets client-side classes: " + mixin.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finalizeMixinConfig() {
|
|
||||||
// relativize class names
|
|
||||||
for(var list : mixinConfigList.values()) {
|
|
||||||
list.replaceAll(className -> className.split("(?<=mixin.)")[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
package org.fury_phoenix.mixinAp.config;
|
|
||||||
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import javax.annotation.processing.ProcessingEnvironment;
|
|
||||||
import javax.tools.StandardLocation;
|
|
||||||
|
|
||||||
public record MixinConfig(
|
|
||||||
boolean required,
|
|
||||||
String minVersion,
|
|
||||||
@SerializedName("package")
|
|
||||||
String packageName,
|
|
||||||
String plugin,
|
|
||||||
String compatibilityLevel,
|
|
||||||
@SerializedName("mixins")
|
|
||||||
List<String> commonMixins,
|
|
||||||
@SerializedName("client")
|
|
||||||
List<String> clientMixins,
|
|
||||||
InjectorOptions injectors, OverwriteOptions overwrites
|
|
||||||
) {
|
|
||||||
public MixinConfig(String packageName, List<String> commonMixins, List<String> clientMixins) {
|
|
||||||
this(true, "0.8", packageName, "org.embeddedt.modernfix.core.ModernFixMixinPlugin", "JAVA_17",
|
|
||||||
commonMixins, clientMixins, InjectorOptions.DEFAULT, OverwriteOptions.DEFAULT);
|
|
||||||
}
|
|
||||||
public record InjectorOptions(int defaultRequire) {
|
|
||||||
public static final InjectorOptions DEFAULT = new InjectorOptions(1);
|
|
||||||
}
|
|
||||||
public record OverwriteOptions(boolean conformVisibility) {
|
|
||||||
public static final OverwriteOptions DEFAULT = new OverwriteOptions(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generateMixinConfig(ProcessingEnvironment env) throws IOException {
|
|
||||||
try (
|
|
||||||
Writer mixinConfigWriter = env.getFiler()
|
|
||||||
.createResource(StandardLocation.SOURCE_OUTPUT, "",
|
|
||||||
MixinConfig.computeMixinConfigPath(
|
|
||||||
Optional.of(env.getOptions().get("rootProject.name")),
|
|
||||||
Optional.ofNullable(env.getOptions().get("project.name"))
|
|
||||||
)
|
|
||||||
).openWriter()
|
|
||||||
) {
|
|
||||||
String mixinConfig = new GsonBuilder()
|
|
||||||
.setPrettyPrinting()
|
|
||||||
.create()
|
|
||||||
.toJson(this);
|
|
||||||
|
|
||||||
mixinConfigWriter.write(mixinConfig);
|
|
||||||
mixinConfigWriter.write("\n");
|
|
||||||
} catch (IOException e) { throw e; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String computeMixinConfigPath(Optional<String> rootProjectName, Optional<String> projectName) {
|
|
||||||
return "resources/" +
|
|
||||||
rootProjectName.get() +
|
|
||||||
(projectName.isPresent() ? "-" : "") +
|
|
||||||
projectName.orElse("") +
|
|
||||||
".mixins.json";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
package org.fury_phoenix.mixinAp.util;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import static java.util.Map.Entry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type-safe heterogenous map of accessors
|
|
||||||
* @author Fury_Phoenix
|
|
||||||
* @reason Type-safety since K, V of Map are non-identical
|
|
||||||
* @param <SuperType> The supertype of desired types.
|
|
||||||
* This is useful in cases such as <A extends Annotation>.
|
|
||||||
*/
|
|
||||||
public class TypedAccessorMap<SuperType> {
|
|
||||||
private final Map<Class<? extends SuperType>, Function<Object, ?>> typedAccessors = new HashMap<>();
|
|
||||||
|
|
||||||
public <T extends SuperType> void put(Class<T> key, Function<? super T, ?> func) {
|
|
||||||
Objects.requireNonNull(func);
|
|
||||||
typedAccessors.put(Objects.requireNonNull(key), o -> func.apply(key.cast(o)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends SuperType> void put(Entry<Class<T>, Function<? super T, ?>> entry) {
|
|
||||||
put(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends SuperType> Function<Object, ?> get(Class<T> key) {
|
|
||||||
return typedAccessors.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<Entry<Class<? extends SuperType>, Function<Object, ?>>> entrySet() {
|
|
||||||
return typedAccessors.entrySet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
org.fury_phoenix.mixinAp.annotation.MixinProcessor,aggregating
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
plugins {
|
|
||||||
id("java")
|
|
||||||
}
|
|
||||||
|
|
||||||
version = "1.1.0"
|
|
||||||
|
|
||||||
java {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package org.embeddedt.modernfix.annotation;
|
|
||||||
|
|
||||||
public enum FeatureLevel {
|
|
||||||
GA, BETA;
|
|
||||||
|
|
||||||
public boolean isAtLeast(FeatureLevel required) {
|
|
||||||
return this.ordinal() >= required.ordinal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package org.embeddedt.modernfix.annotation;
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
@Target(ElementType.TYPE)
|
|
||||||
public @interface IgnoreMixin {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package org.embeddedt.modernfix.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.CLASS)
|
|
||||||
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
|
||||||
public @interface RequiresFeatureLevel {
|
|
||||||
FeatureLevel value() default FeatureLevel.GA;
|
|
||||||
}
|
|
||||||
67
build.gradle
Normal file
67
build.gradle
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
plugins {
|
||||||
|
id "architectury-plugin" version "3.4-SNAPSHOT"
|
||||||
|
id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false
|
||||||
|
id "maven-publish"
|
||||||
|
id 'com.matthewprenger.cursegradle' version '1.4.0' apply false
|
||||||
|
id 'com.palantir.git-version' version '1.0.0'
|
||||||
|
id 'org.ajoberstar.grgit' version '5.2.0'
|
||||||
|
id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.79.0'
|
||||||
|
id "com.modrinth.minotaur" version "2.+" apply false
|
||||||
|
id("com.diffplug.spotless") version "6.18.0" apply false
|
||||||
|
id 'modernfix.common-conventions' apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
architectury {
|
||||||
|
minecraft = rootProject.minecraft_version
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.archives_base_name = 'modernfix'
|
||||||
|
|
||||||
|
apply plugin: 'modernfix.common-conventions'
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||||
|
// this fixes some edge cases with special characters not displaying correctly
|
||||||
|
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||||
|
// If Javadoc is generated, this must be specified in that task too.
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
|
||||||
|
// The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
|
||||||
|
// JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
|
||||||
|
// We'll use that if it's available, but otherwise we'll use the older option.
|
||||||
|
def targetVersion = 8
|
||||||
|
/*
|
||||||
|
if (JavaVersion.current().isJava9Compatible()) {
|
||||||
|
options.release = targetVersion
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('generateChangelog', se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask) {
|
||||||
|
def details = versionDetails();
|
||||||
|
def theVersionRef
|
||||||
|
if (details.commitDistance > 0) {
|
||||||
|
theVersionRef = details.lastTag;
|
||||||
|
} else {
|
||||||
|
def secondLastTagCmd = "git describe --abbrev=0 " + details.lastTag + "^"
|
||||||
|
def secondLastTag = secondLastTagCmd.execute().text.trim()
|
||||||
|
theVersionRef = secondLastTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
fromRef = theVersionRef
|
||||||
|
|
||||||
|
file = new File("${rootDir}/CHANGELOG.md");
|
||||||
|
templateContent = new File("${rootDir}/gradle/changelog.mustache").getText('UTF-8').replace("[[modernFixVersionRef]]", theVersionRef);
|
||||||
|
toCommit = "HEAD";
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('checkCleanTag') {
|
||||||
|
doLast {
|
||||||
|
def details = versionDetails()
|
||||||
|
if (!details.isCleanTag || versionDetails().commitDistance != 0) {
|
||||||
|
throw new GradleException('Not a clean tree.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println "ModernFix: " + version
|
||||||
210
build.gradle.kts
210
build.gradle.kts
|
|
@ -1,210 +0,0 @@
|
||||||
plugins {
|
|
||||||
id("net.neoforged.moddev.legacyforge") version("2.0.134")
|
|
||||||
id("me.modmuss50.mod-publish-plugin") version("1.1.0")
|
|
||||||
}
|
|
||||||
|
|
||||||
val minecraft_version = rootProject.properties["minecraft_version"].toString()
|
|
||||||
|
|
||||||
group = "org.embeddedt"
|
|
||||||
|
|
||||||
val gitVersion = providers.of(GitVersionSource::class) {
|
|
||||||
parameters {
|
|
||||||
minecraftVersion.set(minecraft_version)
|
|
||||||
projectDir.set(rootProject.layout.projectDirectory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
version = gitVersion.get()
|
|
||||||
|
|
||||||
base.archivesName = "modernfix-forge"
|
|
||||||
|
|
||||||
legacyForge {
|
|
||||||
enable {
|
|
||||||
forgeVersion = rootProject.properties["forge_version"].toString()
|
|
||||||
isDisableRecompilation = System.getenv("CI") == "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
rootProject.properties["parchment_version"]?.let { parchmentVer ->
|
|
||||||
parchment {
|
|
||||||
minecraftVersion = minecraft_version
|
|
||||||
mappingsVersion = parchmentVer.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runs {
|
|
||||||
create("client") {
|
|
||||||
client()
|
|
||||||
}
|
|
||||||
create("server") {
|
|
||||||
server()
|
|
||||||
}
|
|
||||||
create("auditClient") {
|
|
||||||
client()
|
|
||||||
jvmArguments.addAll("-Dmodernfix.auditAndExit=true", "-Djava.awt.headless=true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mods {
|
|
||||||
create("modernfix") {
|
|
||||||
sourceSet(sourceSets.main.get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mixin {
|
|
||||||
add(sourceSets.main.get(), "modernfix.refmap.json")
|
|
||||||
config("modernfix-modernfix.mixins.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named<Jar>("jar") {
|
|
||||||
manifest.attributes(mapOf(
|
|
||||||
"MixinConfigs" to "modernfix-modernfix.mixins.json",
|
|
||||||
"Specification-Version" to "1",
|
|
||||||
"Implementation-Title" to project.name,
|
|
||||||
"Implementation-Version" to version
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
val curSourceCompatLevel = JavaVersion.VERSION_17
|
|
||||||
sourceCompatibility = curSourceCompatLevel
|
|
||||||
targetCompatibility = curSourceCompatLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
exclusiveContent {
|
|
||||||
forRepository {
|
|
||||||
maven {
|
|
||||||
// location of the maven that hosts JEI files
|
|
||||||
name = "Progwml6 maven"
|
|
||||||
url = uri("https://dvs1.progwml6.com/files/maven/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
forRepository {
|
|
||||||
maven {
|
|
||||||
name = "ModMaven"
|
|
||||||
url = uri("https://modmaven.dev")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
filter {
|
|
||||||
includeGroup("mezz.jei")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exclusiveContent {
|
|
||||||
forRepository {
|
|
||||||
maven("https://cursemaven.com")
|
|
||||||
}
|
|
||||||
filter {
|
|
||||||
includeGroup("curse.maven")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val embed by configurations.creating {
|
|
||||||
isCanBeConsumed = false
|
|
||||||
isCanBeResolved = true
|
|
||||||
isTransitive = true
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":annotations"))
|
|
||||||
embed(project(":annotations"))
|
|
||||||
"additionalRuntimeClasspath"(project(":annotations"))
|
|
||||||
annotationProcessor(project(path = ":annotation-processor", configuration = "shadow"))
|
|
||||||
|
|
||||||
val mixinextrasVersion = rootProject.properties["mixinextras_version"].toString()
|
|
||||||
implementation("io.github.llamalad7:mixinextras-common:${mixinextrasVersion}")
|
|
||||||
annotationProcessor("net.fabricmc:sponge-mixin:0.12.5+mixin.0.8.5")
|
|
||||||
annotationProcessor("io.github.llamalad7:mixinextras-common:${mixinextrasVersion}")
|
|
||||||
implementation("io.github.llamalad7:mixinextras-forge:${mixinextrasVersion}")
|
|
||||||
"jarJar"("io.github.llamalad7:mixinextras-forge:${mixinextrasVersion}")
|
|
||||||
|
|
||||||
val jei_version = rootProject.properties["jei_version"].toString()
|
|
||||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
|
|
||||||
modCompileOnly("curse.maven:spark-361579:${rootProject.properties["spark_version"].toString()}")
|
|
||||||
modCompileOnly("curse.maven:ctm-267602:${rootProject.properties["ctm_version"].toString()}")
|
|
||||||
modCompileOnly("curse.maven:ldlib-626676:${rootProject.properties["ldlib_version"].toString()}")
|
|
||||||
modCompileOnly("curse.maven:supermartijncore-454372:4455391")
|
|
||||||
modCompileOnly("curse.maven:patchouli-306770:6164575")
|
|
||||||
modCompileOnly("curse.maven:cofhcore-69162:5374122")
|
|
||||||
modCompileOnly("curse.maven:resourcefullib-570073:5659871")
|
|
||||||
modCompileOnly("curse.maven:kubejs-238086:5853326")
|
|
||||||
modCompileOnly("curse.maven:terrablender-563928:6290448")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named<Jar>("jar") {
|
|
||||||
from(embed.map { if (it.isDirectory) it else zipTree(it) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// For the AP
|
|
||||||
tasks.withType<JavaCompile>().configureEach {
|
|
||||||
if (!name.lowercase().contains("test")) {
|
|
||||||
options.compilerArgs.addAll(
|
|
||||||
listOf(
|
|
||||||
"-ArootProject.name=${rootProject.name}",
|
|
||||||
"-Aproject.name=${project.name}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main {
|
|
||||||
resources.srcDir(
|
|
||||||
layout.buildDirectory.dir("generated/sources/annotationProcessor/java/main/resources")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named<ProcessResources>("processResources") {
|
|
||||||
dependsOn(tasks.named("compileJava"))
|
|
||||||
|
|
||||||
inputs.property("version", project.version)
|
|
||||||
|
|
||||||
filesMatching("META-INF/mods.toml") {
|
|
||||||
expand("version" to project.version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val finalJarTask = "reobfJar"
|
|
||||||
|
|
||||||
tasks.register<Copy>("copyJarNameConsistent") {
|
|
||||||
from(tasks.named<Jar>(finalJarTask).get().outputs.files)
|
|
||||||
into(project.file("build/libs"))
|
|
||||||
rename { _ -> "modernfix-" + project.name + "-latest.jar" }
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register<Copy>("copyJarToBin") {
|
|
||||||
from(tasks.named<Jar>(finalJarTask).get().outputs.files)
|
|
||||||
into(rootProject.file("bin"))
|
|
||||||
mustRunAfter(tasks.named("copyJarNameConsistent"))
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named("build") {
|
|
||||||
dependsOn("copyJarToBin", "copyJarNameConsistent")
|
|
||||||
}
|
|
||||||
|
|
||||||
publishMods {
|
|
||||||
file.set(tasks.named<Jar>(finalJarTask).flatMap { it.archiveFile })
|
|
||||||
displayName.set(tasks.named<Jar>(finalJarTask).flatMap { it.archiveFileName })
|
|
||||||
changelog = "Please check the [GitHub wiki](https://github.com/embeddedt/ModernFix/wiki/Changelog) for major changes."
|
|
||||||
type = STABLE
|
|
||||||
|
|
||||||
modLoaders.add("forge")
|
|
||||||
|
|
||||||
curseforge {
|
|
||||||
projectId = "790626"
|
|
||||||
projectSlug = "modernfix"
|
|
||||||
accessToken = providers.environmentVariable("CURSEFORGE_TOKEN")
|
|
||||||
minecraftVersions.add(minecraft_version)
|
|
||||||
}
|
|
||||||
modrinth {
|
|
||||||
projectId = "nmDcB62a"
|
|
||||||
accessToken = providers.environmentVariable("MODRINTH_TOKEN")
|
|
||||||
minecraftVersions.add(minecraft_version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named("publishMods") {
|
|
||||||
dependsOn(finalJarTask)
|
|
||||||
}
|
|
||||||
3
buildSrc/build.gradle
Normal file
3
buildSrc/build.gradle
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
plugins {
|
||||||
|
id 'groovy-gradle-plugin'
|
||||||
|
}
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
plugins {
|
|
||||||
`kotlin-dsl`
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
0
buildSrc/settings.gradle
Normal file
0
buildSrc/settings.gradle
Normal file
81
buildSrc/src/main/groovy/modernfix.common-conventions.gradle
Normal file
81
buildSrc/src/main/groovy/modernfix.common-conventions.gradle
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'architectury-plugin'
|
||||||
|
id 'maven-publish'
|
||||||
|
id 'com.diffplug.spotless'
|
||||||
|
}
|
||||||
|
|
||||||
|
spotless {
|
||||||
|
java {
|
||||||
|
removeUnusedImports()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
architectury {
|
||||||
|
compileOnly()
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'org.embeddedt'
|
||||||
|
// extract base version from tag, generate other metadata ourselves
|
||||||
|
def details = versionDetails()
|
||||||
|
def plusIndex = details.lastTag.indexOf("+")
|
||||||
|
if(plusIndex == -1) {
|
||||||
|
plusIndex = details.lastTag.length()
|
||||||
|
}
|
||||||
|
def baseVersion = details.lastTag.substring(0, plusIndex)
|
||||||
|
def dirtyMarker = grgit.status().clean ? "" : ".dirty"
|
||||||
|
def commitHashMarker = details.commitDistance > 0 ? ("." + details.gitHash.substring(0, Math.min(4, details.gitHash.length()))) : ""
|
||||||
|
def preMarker = (details.commitDistance > 0 || !details.isCleanTag) ? ("-beta." + details.commitDistance) : ""
|
||||||
|
if(preMarker.length() > 0) {
|
||||||
|
// bump to next patch release
|
||||||
|
def versionParts = baseVersion.tokenize(".")
|
||||||
|
baseVersion = "${versionParts[0]}.${versionParts[1]}.${versionParts[2].toInteger() + 1}"
|
||||||
|
}
|
||||||
|
def versionString = "${baseVersion}${preMarker}+mc${minecraft_version}${commitHashMarker}${dirtyMarker}"
|
||||||
|
version = versionString
|
||||||
|
archivesBaseName = rootProject.archives_base_name + '-' + project.name
|
||||||
|
|
||||||
|
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven { url 'https://modmaven.dev/' }
|
||||||
|
maven {
|
||||||
|
url "https://cursemaven.com"
|
||||||
|
content {
|
||||||
|
includeGroup "curse.maven"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
name = 'ParchmentMC'
|
||||||
|
url = 'https://maven.parchmentmc.org'
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
// Shedaniel's maven (Architectury API)
|
||||||
|
url = "https://maven.architectury.dev"
|
||||||
|
content {
|
||||||
|
includeGroup "me.shedaniel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maven {
|
||||||
|
// saps.dev Maven (KubeJS and Rhino)
|
||||||
|
url = "https://maven.saps.dev/minecraft"
|
||||||
|
content {
|
||||||
|
includeGroup "dev.latvian.mods"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maven { // CTM
|
||||||
|
url "https://maven.tterrag.com/"
|
||||||
|
}
|
||||||
|
maven { url 'https://maven.blamejared.com' }
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = "Fuzs Mod Resources"
|
||||||
|
url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
url 'https://maven.terraformersmc.com/releases'
|
||||||
|
}
|
||||||
|
maven { url = "https://jitpack.io" }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
plugins {
|
||||||
|
id 'modernfix.common-conventions'
|
||||||
|
id 'dev.architectury.loom'
|
||||||
|
}
|
||||||
|
|
||||||
|
loom {
|
||||||
|
silentMojangMappingsLicense()
|
||||||
|
accessWidenerPath = file("${rootDir}/common/src/main/resources/modernfix.accesswidener")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
|
||||||
|
mappings loom.layered() {
|
||||||
|
officialMojangMappings()
|
||||||
|
if(rootProject.hasProperty("parchment_version")) {
|
||||||
|
parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
processResources {
|
||||||
|
def mixinFileList = []
|
||||||
|
def mixinDirectory = file("src/main/java/org/embeddedt/modernfix/" + project.name + "/mixin")
|
||||||
|
fileTree(mixinDirectory).visit { FileVisitDetails details ->
|
||||||
|
if(details.file.isFile()) {
|
||||||
|
def fileName = mixinDirectory.relativePath(details.file).toString().replaceFirst(/\.java$/, "").replace('/', '.')
|
||||||
|
mixinFileList << fileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def mixinClassesStringB = new StringBuilder()
|
||||||
|
for(int i = 0; i < mixinFileList.size(); i++) {
|
||||||
|
mixinClassesStringB.append(" \"")
|
||||||
|
mixinClassesStringB.append(mixinFileList.get(i))
|
||||||
|
mixinClassesStringB.append('"')
|
||||||
|
if(i < (mixinFileList.size() - 1))
|
||||||
|
mixinClassesStringB.append(',')
|
||||||
|
mixinClassesStringB.append('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
def replacements = [
|
||||||
|
mixin_classes: mixinClassesStringB.toString()
|
||||||
|
]
|
||||||
|
|
||||||
|
inputs.properties replacements
|
||||||
|
def filePattern = "modernfix-" + project.name + ".mixins.json"
|
||||||
|
filesMatching(filePattern) {
|
||||||
|
expand replacements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
plugins {
|
||||||
|
id 'com.matthewprenger.cursegradle'
|
||||||
|
id 'com.modrinth.minotaur'
|
||||||
|
}
|
||||||
|
|
||||||
|
loom {
|
||||||
|
mods {
|
||||||
|
main { // to match the default mod generated for Forge
|
||||||
|
sourceSet project.sourceSets.main
|
||||||
|
sourceSet project(':common').sourceSets.main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runs {
|
||||||
|
client {
|
||||||
|
vmArgs "-Xmx1G"
|
||||||
|
vmArgs "-Xms1G"
|
||||||
|
property("mixin.debug.export", "true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def copyJarNameConsistent = tasks.register('copyJarNameConsistent', Copy) {
|
||||||
|
from remapJar // shortcut for createJar.outputs.files
|
||||||
|
into project.file("build/libs")
|
||||||
|
rename { name -> "modernfix-" + project.name + "-latest.jar" }
|
||||||
|
}
|
||||||
|
|
||||||
|
def copyJarToBin = tasks.register('copyJarToBin', Copy) {
|
||||||
|
from remapJar // shortcut for createJar.outputs.files
|
||||||
|
into rootProject.file("bin")
|
||||||
|
mustRunAfter "copyJarNameConsistent"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.build.dependsOn(copyJarToBin, copyJarNameConsistent)
|
||||||
|
|
||||||
|
def isBeta = project.version.toString().contains("beta")
|
||||||
|
|
||||||
|
curseforge {
|
||||||
|
if (System.getenv("CURSEFORGE_TOKEN") != null) {
|
||||||
|
apiKey = System.getenv("CURSEFORGE_TOKEN")
|
||||||
|
project {
|
||||||
|
id = "790626"
|
||||||
|
changelog = file("${rootDir}/CHANGELOG.md")
|
||||||
|
changelogType = "markdown"
|
||||||
|
releaseType = isBeta ? "beta" : "release"
|
||||||
|
addGameVersion project.name.capitalize()
|
||||||
|
gameVersionStrings.addAll(supported_minecraft_versions.tokenize(","))
|
||||||
|
mainArtifact remapJar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modrinth {
|
||||||
|
token = System.getenv("MODRINTH_TOKEN")
|
||||||
|
projectId = "modernfix" // This can be the project ID or the slug. Either will work!
|
||||||
|
versionType = isBeta ? "beta" : "release" // This is the default -- can also be `beta` or `alpha`
|
||||||
|
uploadFile = remapJar
|
||||||
|
gameVersions = supported_minecraft_versions.tokenize(",")
|
||||||
|
loaders = [project.name]
|
||||||
|
changelog.set(provider { file("${rootDir}/CHANGELOG.md").getText('UTF-8') })
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.curseforge.dependsOn(rootProject.generateChangelog)
|
||||||
|
tasks.modrinth.dependsOn(rootProject.generateChangelog)
|
||||||
|
|
||||||
|
tasks.register('publishToModSites') {
|
||||||
|
publishToModSites.dependsOn(tasks.modrinth)
|
||||||
|
publishToModSites.dependsOn(tasks.curseforge)
|
||||||
|
}
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import org.gradle.api.file.DirectoryProperty
|
|
||||||
import org.gradle.api.provider.Property
|
|
||||||
import org.gradle.api.provider.ValueSource
|
|
||||||
import org.gradle.api.provider.ValueSourceParameters
|
|
||||||
import org.gradle.process.ExecOperations
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.File
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
abstract class GitVersionSource : ValueSource<String, GitVersionSource.Parameters> {
|
|
||||||
|
|
||||||
interface Parameters : ValueSourceParameters {
|
|
||||||
val minecraftVersion: Property<String>
|
|
||||||
val projectDir: DirectoryProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Inject
|
|
||||||
abstract val execOperations: ExecOperations
|
|
||||||
|
|
||||||
override fun obtain(): String {
|
|
||||||
val minecraftVersion = parameters.minecraftVersion.get()
|
|
||||||
val workDir = parameters.projectDir.get().asFile
|
|
||||||
|
|
||||||
val releaseLine = workDir.resolve("release_line.txt").readText().trim()
|
|
||||||
|
|
||||||
val patch = try {
|
|
||||||
// Find the most recent first-parent commit that touched release_line.txt
|
|
||||||
val lineStartCommit = git(workDir,
|
|
||||||
"log", "--first-parent",
|
|
||||||
"-n", "1",
|
|
||||||
"--format=%H",
|
|
||||||
"--",
|
|
||||||
"release_line.txt"
|
|
||||||
).trim()
|
|
||||||
|
|
||||||
if (lineStartCommit.isEmpty()) {
|
|
||||||
// count all first-parent commits as a safe fallback
|
|
||||||
git(workDir, "rev-list", "--count", "--first-parent", "HEAD")
|
|
||||||
.trim().toIntOrNull() ?: 0
|
|
||||||
} else {
|
|
||||||
git(workDir, "rev-list", "--count", "--first-parent", "$lineStartCommit..HEAD")
|
|
||||||
.trim().toIntOrNull() ?: 0
|
|
||||||
}
|
|
||||||
} catch (_: Exception) {
|
|
||||||
// Git is unavailable or this is not a git repository
|
|
||||||
999
|
|
||||||
}
|
|
||||||
|
|
||||||
return "$releaseLine.$patch+mc$minecraftVersion"
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun git(workDir: File, vararg args: String): String {
|
|
||||||
val output = ByteArrayOutputStream()
|
|
||||||
execOperations.exec {
|
|
||||||
commandLine("git", *args)
|
|
||||||
standardOutput = output
|
|
||||||
workingDir(workDir)
|
|
||||||
}
|
|
||||||
return output.toString(Charsets.UTF_8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
54
common/build.gradle
Normal file
54
common/build.gradle
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
plugins {
|
||||||
|
id "modernfix.mod-common-conventions"
|
||||||
|
}
|
||||||
|
|
||||||
|
architectury {
|
||||||
|
common(rootProject.enabled_platforms.split(","))
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.jei_minecraft_version = "1.20.2" /* temporary, till 1.20 releases */
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
|
||||||
|
// Do NOT use other classes from fabric loader
|
||||||
|
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||||
|
implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}"))
|
||||||
|
|
||||||
|
modCompileOnly("dev.latvian.mods:kubejs:${kubejs_version}") {
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
|
modApi("dev.latvian.mods:rhino:${rhino_version}") {
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
|
modApi("me.shedaniel:RoughlyEnoughItems-api:${rei_version}") {
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
|
modCompileOnly("me.shedaniel:RoughlyEnoughItems-fabric:${rei_version}") {
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||||
|
// compile against the JEI API but do not include it at runtime
|
||||||
|
modCompileOnly("mezz.jei:jei-${jei_minecraft_version}-common:${jei_version}")
|
||||||
|
modCompileOnly("mezz.jei:jei-${jei_minecraft_version}-gui:${jei_version}")
|
||||||
|
modCompileOnly("mezz.jei:jei-${jei_minecraft_version}-lib:${jei_version}")
|
||||||
|
// Remove the next line if you don't want to depend on the API
|
||||||
|
// modApi "me.shedaniel:architectury:${rootProject.architectury_version}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't need remapped common jar
|
||||||
|
tasks.named('remapJar') { enabled = false }
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenCommon(MavenPublication) {
|
||||||
|
artifactId = rootProject.archives_base_name
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
||||||
|
repositories {
|
||||||
|
// Add repositories to publish to here.
|
||||||
|
}
|
||||||
|
}
|
||||||
21
common/src/main/java/org/embeddedt/modernfix/FileWalker.java
Normal file
21
common/src/main/java/org/embeddedt/modernfix/FileWalker.java
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.embeddedt.modernfix;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class FileWalker extends CacheLoader<Pair<Path, Integer>, List<Path>> {
|
||||||
|
public static final FileWalker INSTANCE = new FileWalker();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Path> load(Pair<Path, Integer> key) throws Exception {
|
||||||
|
try(Stream<Path> stream = Files.walk(key.getLeft(), key.getRight())) {
|
||||||
|
return stream.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,6 @@ package org.embeddedt.modernfix;
|
||||||
|
|
||||||
import net.minecraft.SharedConstants;
|
import net.minecraft.SharedConstants;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
|
@ -13,7 +12,6 @@ import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||||
import org.embeddedt.modernfix.resources.ReloadExecutor;
|
import org.embeddedt.modernfix.resources.ReloadExecutor;
|
||||||
import org.embeddedt.modernfix.util.ClassInfoManager;
|
import org.embeddedt.modernfix.util.ClassInfoManager;
|
||||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
@ -47,17 +45,6 @@ public class ModernFix {
|
||||||
return resourceReloadService;
|
return resourceReloadService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void runAuditIfRequested() {
|
|
||||||
boolean auditAndExit = Boolean.getBoolean("modernfix.auditAndExit");
|
|
||||||
if (auditAndExit || Boolean.getBoolean("modernfix.auditMixinsAtStart")) {
|
|
||||||
MixinEnvironment.getCurrentEnvironment().audit();
|
|
||||||
if (auditAndExit) {
|
|
||||||
// Prevents Crash Assistant from treating mixin audit as a crash
|
|
||||||
Minecraft.getInstance().stop();
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModernFix() {
|
public ModernFix() {
|
||||||
INSTANCE = this;
|
INSTANCE = this;
|
||||||
|
|
@ -76,19 +63,14 @@ public class ModernFix {
|
||||||
ClassInfoManager.clear();
|
ClassInfoManager.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantValue")
|
|
||||||
public void onServerDead(MinecraftServer server) {
|
public void onServerDead(MinecraftServer server) {
|
||||||
/* Clear as much data from the integrated server as possible, in case a mod holds on to it */
|
/* Clear as much data from the integrated server as possible, in case a mod holds on to it */
|
||||||
try {
|
try {
|
||||||
for(ServerLevel level : server.getAllLevels()) {
|
for(ServerLevel level : server.getAllLevels()) {
|
||||||
ChunkMap chunkMap = level.getChunkSource().chunkMap;
|
ChunkMap chunkMap = level.getChunkSource().chunkMap;
|
||||||
// Null check for mods that replace chunk system
|
chunkMap.updatingChunkMap.clear();
|
||||||
if(chunkMap.updatingChunkMap != null)
|
chunkMap.visibleChunkMap.clear();
|
||||||
chunkMap.updatingChunkMap.clear();
|
chunkMap.pendingUnloads.clear();
|
||||||
if(chunkMap.visibleChunkMap != null)
|
|
||||||
chunkMap.visibleChunkMap.clear();
|
|
||||||
if(chunkMap.pendingUnloads != null)
|
|
||||||
chunkMap.pendingUnloads.clear();
|
|
||||||
}
|
}
|
||||||
} catch(RuntimeException e) {
|
} catch(RuntimeException e) {
|
||||||
ModernFix.LOGGER.error("Couldn't clear chunk data", e);
|
ModernFix.LOGGER.error("Couldn't clear chunk data", e);
|
||||||
|
|
@ -0,0 +1,205 @@
|
||||||
|
package org.embeddedt.modernfix;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||||
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.util.MemoryReserve;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
||||||
|
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||||
|
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||||
|
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
||||||
|
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||||
|
import org.embeddedt.modernfix.searchtree.JEIBackedSearchTree;
|
||||||
|
import org.embeddedt.modernfix.searchtree.REIBackedSearchTree;
|
||||||
|
import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry;
|
||||||
|
import org.embeddedt.modernfix.util.ClassInfoManager;
|
||||||
|
import org.embeddedt.modernfix.world.IntegratedWatchdog;
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
public class ModernFixClient {
|
||||||
|
public static ModernFixClient INSTANCE;
|
||||||
|
public static long worldLoadStartTime = -1;
|
||||||
|
private static int numRenderTicks;
|
||||||
|
|
||||||
|
public static float gameStartTimeSeconds = -1;
|
||||||
|
|
||||||
|
public static boolean recipesUpdated, tagsUpdated = false;
|
||||||
|
|
||||||
|
public String brandingString = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of loaded client integrations.
|
||||||
|
*/
|
||||||
|
public static List<ModernFixClientIntegration> CLIENT_INTEGRATIONS = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
public ModernFixClient() {
|
||||||
|
INSTANCE = this;
|
||||||
|
// clear reserve as it's not needed
|
||||||
|
MemoryReserve.release();
|
||||||
|
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
|
||||||
|
brandingString = ModernFix.NAME + " " + ModernFixPlatformHooks.INSTANCE.getVersionString();
|
||||||
|
}
|
||||||
|
SearchTreeProviderRegistry.register(JEIBackedSearchTree.PROVIDER);
|
||||||
|
SearchTreeProviderRegistry.register(REIBackedSearchTree.PROVIDER);
|
||||||
|
for(String className : ModernFixPlatformHooks.INSTANCE.getCustomModOptions().get(IntegrationConstants.CLIENT_INTEGRATION_CLASS)) {
|
||||||
|
try {
|
||||||
|
CLIENT_INTEGRATIONS.add((ModernFixClientIntegration)Class.forName(className).getDeclaredConstructor().newInstance());
|
||||||
|
} catch(ReflectiveOperationException | ClassCastException e) {
|
||||||
|
ModernFix.LOGGER.error("Could not instantiate integration {}", className, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetWorldLoadStateMachine() {
|
||||||
|
numRenderTicks = 0;
|
||||||
|
worldLoadStartTime = -1;
|
||||||
|
recipesUpdated = false;
|
||||||
|
tagsUpdated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onGameLaunchFinish() {
|
||||||
|
if(gameStartTimeSeconds >= 0)
|
||||||
|
return;
|
||||||
|
gameStartTimeSeconds = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||||
|
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.measure_time.GameLoad"))
|
||||||
|
ModernFix.LOGGER.warn("Game took " + gameStartTimeSeconds + " seconds to start");
|
||||||
|
ModernFixPlatformHooks.INSTANCE.onLaunchComplete();
|
||||||
|
ClassInfoManager.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRecipesUpdated() {
|
||||||
|
recipesUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTagsUpdated() {
|
||||||
|
tagsUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRenderTickEnd() {
|
||||||
|
if(recipesUpdated
|
||||||
|
&& tagsUpdated
|
||||||
|
&& worldLoadStartTime != -1
|
||||||
|
&& Minecraft.getInstance().player != null
|
||||||
|
&& numRenderTicks++ >= 10) {
|
||||||
|
float timeSpentLoading = ((float)(System.nanoTime() - worldLoadStartTime) / 1000000000f);
|
||||||
|
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.measure_time.WorldLoad")) {
|
||||||
|
ModernFix.LOGGER.warn("Time from main menu to in-game was " + timeSpentLoading + " seconds");
|
||||||
|
ModernFix.LOGGER.warn("Total time to load game and open world was " + (timeSpentLoading + gameStartTimeSeconds) + " seconds");
|
||||||
|
}
|
||||||
|
resetWorldLoadStateMachine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the IDs match and remap them if not.
|
||||||
|
* @return true if ID remap was needed
|
||||||
|
*/
|
||||||
|
private static boolean compareAndSwitchIds(Class<? extends Entity> eClass, String fieldName, EntityDataAccessor<?> accessor, int newId) {
|
||||||
|
if(accessor.id != newId) {
|
||||||
|
ModernFix.LOGGER.warn("Corrected ID mismatch on {} field {}. Client had {} but server wants {}.",
|
||||||
|
eClass,
|
||||||
|
fieldName,
|
||||||
|
accessor.id,
|
||||||
|
newId);
|
||||||
|
accessor.id = newId;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
ModernFix.LOGGER.debug("{} {} ID fine: {}", eClass, fieldName, newId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horrendous hack to allow tracking every synced entity data manager.
|
||||||
|
*
|
||||||
|
* This is to ensure we can perform ID fixup on already constructed managers.
|
||||||
|
*/
|
||||||
|
public static final Set<SynchedEntityData> allEntityDatas = Collections.newSetFromMap(new WeakHashMap<>());
|
||||||
|
|
||||||
|
private static final Field entriesArrayField;
|
||||||
|
static {
|
||||||
|
Field field;
|
||||||
|
try {
|
||||||
|
field = SynchedEntityData.class.getDeclaredField("entriesArray");
|
||||||
|
field.setAccessible(true);
|
||||||
|
} catch(ReflectiveOperationException e) {
|
||||||
|
field = null;
|
||||||
|
}
|
||||||
|
entriesArrayField = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extremely hacky method to detect and correct mismatched entity data parameter IDs on the client and server.
|
||||||
|
*
|
||||||
|
* The technique is far from ideal, but it should detect reliably and also not break already constructed entities.
|
||||||
|
*/
|
||||||
|
public static void handleEntityIDSync(EntityIDSyncPacket packet) {
|
||||||
|
Map<Class<? extends Entity>, List<Pair<String, Integer>>> info = packet.getFieldInfo();
|
||||||
|
boolean fixNeeded = false;
|
||||||
|
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : info.entrySet()) {
|
||||||
|
Class<? extends Entity> eClass = entry.getKey();
|
||||||
|
for(Pair<String, Integer> field : entry.getValue()) {
|
||||||
|
String fieldName = field.getFirst();
|
||||||
|
int newId = field.getSecond();
|
||||||
|
try {
|
||||||
|
Field f = eClass.getDeclaredField(fieldName);
|
||||||
|
f.setAccessible(true);
|
||||||
|
EntityDataAccessor<?> accessor = (EntityDataAccessor<?>)f.get(null);
|
||||||
|
if(compareAndSwitchIds(eClass, fieldName, accessor, newId))
|
||||||
|
fixNeeded = true;
|
||||||
|
} catch(NoSuchFieldException e) {
|
||||||
|
ModernFix.LOGGER.warn("Couldn't find field on {}: {}", eClass, fieldName);
|
||||||
|
} catch(ReflectiveOperationException e) {
|
||||||
|
throw new RuntimeException("Unexpected exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Now the ID mappings on synced entity data instances are probably all wrong. Fix that. */
|
||||||
|
List<SynchedEntityData> dataEntries;
|
||||||
|
synchronized (allEntityDatas) {
|
||||||
|
if(fixNeeded) {
|
||||||
|
dataEntries = new ArrayList<>(allEntityDatas);
|
||||||
|
for(SynchedEntityData manager : dataEntries) {
|
||||||
|
Int2ObjectOpenHashMap<SynchedEntityData.DataItem<?>> fixedMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
List<SynchedEntityData.DataItem<?>> items = new ArrayList<>(manager.itemsById.values());
|
||||||
|
for(SynchedEntityData.DataItem<?> item : items) {
|
||||||
|
fixedMap.put(item.getAccessor().id, item);
|
||||||
|
}
|
||||||
|
manager.lock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
manager.itemsById.replaceAll((id, parameter) -> fixedMap.get((int)id));
|
||||||
|
if(entriesArrayField != null) {
|
||||||
|
try {
|
||||||
|
SynchedEntityData.DataItem<?>[] dataArray = new SynchedEntityData.DataItem[items.size()];
|
||||||
|
for(int i = 0; i < dataArray.length; i++) {
|
||||||
|
dataArray[i] = fixedMap.get(i);
|
||||||
|
}
|
||||||
|
entriesArrayField.set(manager, dataArray);
|
||||||
|
} catch(ReflectiveOperationException e) {
|
||||||
|
ModernFix.LOGGER.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
manager.lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allEntityDatas.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServerStarted(MinecraftServer server) {
|
||||||
|
if(!ModernFixMixinPlugin.instance.isOptionEnabled("feature.integrated_server_watchdog.IntegratedWatchdog"))
|
||||||
|
return;
|
||||||
|
IntegratedWatchdog watchdog = new IntegratedWatchdog(server);
|
||||||
|
watchdog.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.CLASS)
|
@Retention(RetentionPolicy.CLASS)
|
||||||
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
@Target(ElementType.TYPE)
|
||||||
public @interface RequiresMod {
|
public @interface RequiresMod {
|
||||||
String value() default "";
|
String value() default "";
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package org.embeddedt.modernfix.api.entrypoint;
|
package org.embeddedt.modernfix.api.entrypoint;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.client.resources.model.*;
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
import net.minecraft.client.resources.model.ModelState;
|
||||||
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this interface in a mod class and add it to "modernfix:integration_v1" in your mod metadata file
|
* Implement this interface in a mod class and add it to "modernfix:integration_v1" in your mod metadata file
|
||||||
|
|
@ -56,22 +56,7 @@ public interface ModernFixClientIntegration {
|
||||||
* with dynamic resources on
|
* with dynamic resources on
|
||||||
* @return the model which should actually be loaded for this resource location
|
* @return the model which should actually be loaded for this resource location
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
default BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery) {
|
default BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery) {
|
||||||
return originalModel;
|
return originalModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to allow mods to observe the loading of a baked model and either make changes to it or wrap it with their
|
|
||||||
* own instance.
|
|
||||||
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
|
|
||||||
* @param originalModel the original model
|
|
||||||
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
|
|
||||||
* with dynamic resources on
|
|
||||||
* @param textureGetter function to retrieve textures for this model
|
|
||||||
* @return the model which should actually be loaded for this resource location
|
|
||||||
*/
|
|
||||||
default BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery, Function<Material, TextureAtlasSprite> textureGetter) {
|
|
||||||
return onBakedModelLoad(location, baseModel, originalModel, state, bakery);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package org.embeddedt.modernfix.api.helpers;
|
package org.embeddedt.modernfix.api.helpers;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.client.resources.model.*;
|
import net.minecraft.client.resources.model.*;
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
@ -17,7 +15,6 @@ import org.jetbrains.annotations.Nullable;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class ModelHelpers {
|
public final class ModelHelpers {
|
||||||
|
|
@ -72,16 +69,6 @@ public final class ModelHelpers {
|
||||||
public BakedModel bake(ResourceLocation resourceLocation, ModelState modelState) {
|
public BakedModel bake(ResourceLocation resourceLocation, ModelState modelState) {
|
||||||
return ((IExtendedModelBakery)bakery).bakeDefault(resourceLocation, modelState);
|
return ((IExtendedModelBakery)bakery).bakeDefault(resourceLocation, modelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable BakedModel bake(ResourceLocation location, ModelState state, Function<Material, TextureAtlasSprite> sprites) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Function<Material, TextureAtlasSprite> getModelTextureGetter() {
|
|
||||||
return Material::sprite;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||||
import org.embeddedt.modernfix.duck.IBlockState;
|
import org.embeddedt.modernfix.duck.IBlockState;
|
||||||
|
|
||||||
public class BlockStateCacheHandler {
|
public class BlockStateCacheHandler {
|
||||||
public static void invalidateCache() {
|
public static void rebuildParallel(boolean force) {
|
||||||
synchronized (BlockBehaviour.BlockStateBase.class) {
|
synchronized (BlockBehaviour.BlockStateBase.class) {
|
||||||
for (BlockState blockState : Block.BLOCK_STATE_REGISTRY) {
|
for (BlockState blockState : Block.BLOCK_STATE_REGISTRY) {
|
||||||
((IBlockState)blockState).clearCache();
|
((IBlockState)blockState).clearCache();
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package org.embeddedt.modernfix.blockstate;
|
package org.embeddedt.modernfix.blockstate;
|
||||||
|
|
||||||
import com.google.common.collect.Iterators;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.world.level.block.state.properties.Property;
|
import net.minecraft.world.level.block.state.properties.Property;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -16,7 +14,6 @@ import java.util.*;
|
||||||
*/
|
*/
|
||||||
public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S> {
|
public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S> {
|
||||||
private final Map<Property<?>, Comparable<?>>[] keys;
|
private final Map<Property<?>, Comparable<?>>[] keys;
|
||||||
private Map<Map<Property<?>, Comparable<?>>, S> fastLookup;
|
|
||||||
private final Object[] values;
|
private final Object[] values;
|
||||||
private int usedSlots;
|
private int usedSlots;
|
||||||
public FakeStateMap(int numStates) {
|
public FakeStateMap(int numStates) {
|
||||||
|
|
@ -37,39 +34,22 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(Object o) {
|
public boolean containsKey(Object o) {
|
||||||
return getFastLookup().containsKey(o);
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsValue(Object o) {
|
public boolean containsValue(Object o) {
|
||||||
return getFastLookup().containsValue(o);
|
throw new UnsupportedOperationException();
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private Map<Map<Property<?>, Comparable<?>>, S> getFastLookup() {
|
|
||||||
if(fastLookup == null) {
|
|
||||||
var map = new Object2ObjectOpenHashMap<Map<Property<?>, Comparable<?>>, S>(usedSlots);
|
|
||||||
Map<Property<?>, Comparable<?>>[] keys = this.keys;
|
|
||||||
Object[] values = this.values;
|
|
||||||
for(int i = 0; i < usedSlots; i++) {
|
|
||||||
map.put(keys[i], (S)values[i]);
|
|
||||||
}
|
|
||||||
fastLookup = map;
|
|
||||||
}
|
|
||||||
return fastLookup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public S get(Object o) {
|
public S get(Object o) {
|
||||||
return getFastLookup().get(o);
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public S put(Map<Property<?>, Comparable<?>> propertyComparableMap, S s) {
|
public S put(Map<Property<?>, Comparable<?>> propertyComparableMap, S s) {
|
||||||
if(fastLookup != null) {
|
|
||||||
throw new IllegalStateException("Cannot populate map after fast lookup is built");
|
|
||||||
}
|
|
||||||
keys[usedSlots] = propertyComparableMap;
|
keys[usedSlots] = propertyComparableMap;
|
||||||
values[usedSlots] = s;
|
values[usedSlots] = s;
|
||||||
usedSlots++;
|
usedSlots++;
|
||||||
|
|
@ -90,58 +70,49 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
for(int i = 0; i < usedSlots; i++) {
|
for(int i = 0; i < this.keys.length; i++) {
|
||||||
this.keys[i] = null;
|
this.keys[i] = null;
|
||||||
this.values[i] = null;
|
this.values[i] = null;
|
||||||
}
|
}
|
||||||
this.usedSlots = 0;
|
this.usedSlots = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> List<T> asList(T... array) {
|
|
||||||
var list = Arrays.asList(array);
|
|
||||||
if(usedSlots < array.length) {
|
|
||||||
list = list.subList(0, usedSlots);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Set<Map<Property<?>, Comparable<?>>> keySet() {
|
public Set<Map<Property<?>, Comparable<?>>> keySet() {
|
||||||
return new AbstractSet<>() {
|
throw new UnsupportedOperationException();
|
||||||
@Override
|
|
||||||
public Iterator<Map<Property<?>, Comparable<?>>> iterator() {
|
|
||||||
return keys.length == usedSlots ? Iterators.forArray(keys) : asList(keys).iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return usedSlots;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Collection<S> values() {
|
public Collection<S> values() {
|
||||||
return (Collection<S>)asList(values);
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Set<Entry<Map<Property<?>, Comparable<?>>, S>> entrySet() {
|
public Set<Entry<Map<Property<?>, Comparable<?>>, S>> entrySet() {
|
||||||
return new AbstractSet<>() {
|
return new Set<Entry<Map<Property<?>, Comparable<?>>, S>>() {
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return usedSlots;
|
return usedSlots;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return FakeStateMap.this.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Entry<Map<Property<?>, Comparable<?>>, S>> iterator() {
|
public Iterator<Entry<Map<Property<?>, Comparable<?>>, S>> iterator() {
|
||||||
return new Iterator<>() {
|
return new Iterator<Entry<Map<Property<?>, Comparable<?>>, S>>() {
|
||||||
int currentIdx = 0;
|
int currentIdx = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return currentIdx < usedSlots;
|
return currentIdx < usedSlots;
|
||||||
|
|
@ -149,14 +120,61 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entry<Map<Property<?>, Comparable<?>>, S> next() {
|
public Entry<Map<Property<?>, Comparable<?>>, S> next() {
|
||||||
if (currentIdx >= usedSlots)
|
if(currentIdx >= usedSlots)
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
Entry<Map<Property<?>, Comparable<?>>, S> entry = new AbstractMap.SimpleImmutableEntry<>(keys[currentIdx], (S) values[currentIdx]);
|
Entry<Map<Property<?>, Comparable<?>>, S> entry = new AbstractMap.SimpleImmutableEntry<>(keys[currentIdx], (S)values[currentIdx]);
|
||||||
currentIdx++;
|
currentIdx++;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(@NotNull T[] ts) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(Entry<Map<Property<?>, Comparable<?>>, S> mapSEntry) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(@NotNull Collection<?> collection) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(@NotNull Collection<? extends Entry<Map<Property<?>, Comparable<?>>, S>> collection) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(@NotNull Collection<?> collection) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(@NotNull Collection<?> collection) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
package org.embeddedt.modernfix.blockstate;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.StateHolder;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class FerriteCorePostProcess {
|
||||||
|
private static final boolean willPostProcess;
|
||||||
|
|
||||||
|
private static final MethodHandle theTable, toKeyIndex;
|
||||||
|
|
||||||
|
static {
|
||||||
|
boolean success = true;
|
||||||
|
MethodHandle table = null, keyIndex = null;
|
||||||
|
try {
|
||||||
|
Class<?> fastMap = Class.forName("malte0811.ferritecore.fastmap.FastMap");
|
||||||
|
Field field = fastMap.getDeclaredField("toKeyIndex");
|
||||||
|
field.setAccessible(true);
|
||||||
|
keyIndex = MethodHandles.publicLookup().unreflectSetter(field);
|
||||||
|
field = StateHolder.class.getDeclaredField("ferritecore_globalTable");
|
||||||
|
field.setAccessible(true);
|
||||||
|
table = MethodHandles.publicLookup().unreflectGetter(field);
|
||||||
|
} catch(ReflectiveOperationException | RuntimeException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
willPostProcess = success;
|
||||||
|
theTable = table;
|
||||||
|
toKeyIndex = keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Object2IntMap<?> EMPTY_MAP;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Object2IntArrayMap<?> map = new Object2IntArrayMap<>();
|
||||||
|
map.defaultReturnValue(-1);
|
||||||
|
EMPTY_MAP = Object2IntMaps.unmodifiable(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <O, S extends StateHolder<O, S>> void postProcess(StateDefinition<O, S> state) {
|
||||||
|
if(!willPostProcess)
|
||||||
|
return;
|
||||||
|
try {
|
||||||
|
if(state.getProperties().size() == 0) {
|
||||||
|
for(S holder : state.getPossibleStates()) {
|
||||||
|
// deduplicate Object2IntMap objects from FerriteCore
|
||||||
|
// will probably be fixed upstream at some point, but likely not for older versions
|
||||||
|
Object table = theTable.invoke(holder);
|
||||||
|
toKeyIndex.invoke(table, EMPTY_MAP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
package org.embeddedt.modernfix.chunk;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class SafeBlockGetter implements BlockGetter {
|
||||||
|
private final ServerLevel wrapped;
|
||||||
|
private final Thread mainThread;
|
||||||
|
|
||||||
|
public SafeBlockGetter(ServerLevel wrapped) {
|
||||||
|
this.wrapped = wrapped;
|
||||||
|
this.mainThread = Thread.currentThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldUse() {
|
||||||
|
return Thread.currentThread() != this.mainThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private BlockGetter getChunkSafe(BlockPos pos) {
|
||||||
|
// can safely call getChunkForLighting off-thread
|
||||||
|
BlockGetter access = this.wrapped.getChunkSource().getChunkForLighting(pos.getX() >> 4, pos.getZ() >> 4);
|
||||||
|
if(!(access instanceof ChunkAccess))
|
||||||
|
return null;
|
||||||
|
ChunkAccess chunk = (ChunkAccess)access;
|
||||||
|
if(!chunk.getStatus().isOrAfter(ChunkStatus.FULL))
|
||||||
|
return null;
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxBuildHeight() {
|
||||||
|
return this.wrapped.getMaxBuildHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxLightLevel() {
|
||||||
|
return this.wrapped.getMaxLightLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinBuildHeight() {
|
||||||
|
return this.wrapped.getMinBuildHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return this.wrapped.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BlockEntity getBlockEntity(BlockPos pos) {
|
||||||
|
BlockGetter g = getChunkSafe(pos);
|
||||||
|
return g == null ? null : g.getBlockEntity(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getBlockState(BlockPos pos) {
|
||||||
|
BlockGetter g = getChunkSafe(pos);
|
||||||
|
return g == null ? Blocks.AIR.defaultBlockState() : g.getBlockState(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidState getFluidState(BlockPos pos) {
|
||||||
|
BlockGetter g = getChunkSafe(pos);
|
||||||
|
return g == null ? Fluids.EMPTY.defaultFluidState() : g.getFluidState(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,6 @@ import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.packs.resources.Resource;
|
import net.minecraft.server.packs.resources.Resource;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
import org.embeddedt.modernfix.duck.IProfilingServerFunctionManager;
|
|
||||||
import org.embeddedt.modernfix.structure.CachingStructureManager;
|
import org.embeddedt.modernfix.structure.CachingStructureManager;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
@ -55,25 +54,6 @@ public class ModernFixCommands {
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}))
|
}))
|
||||||
.then(literal("mcfunctions").requires(source -> source.hasPermission(3))
|
|
||||||
.executes(context -> {
|
|
||||||
ServerLevel level = context.getSource().getLevel();
|
|
||||||
if(level == null) {
|
|
||||||
context.getSource().sendFailure(Component.literal("Couldn't find server level"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (level.getServer().getFunctions() instanceof IProfilingServerFunctionManager profiler) {
|
|
||||||
context.getSource().sendSuccess(() -> Component.literal("mcfunction runtime breakdown:"), false);
|
|
||||||
for(String line : profiler.mfix$getProfilingResults().split("\n")) {
|
|
||||||
context.getSource().sendSuccess(() -> Component.literal(line), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
context.getSource().sendFailure(Component.literal("ModernFix mcfunction profiling is not enabled on this server."));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,7 +4,6 @@ import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
import org.embeddedt.modernfix.render.UnsafeBufferHelper;
|
import org.embeddedt.modernfix.render.UnsafeBufferHelper;
|
||||||
import org.spongepowered.asm.mixin.Dynamic;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
@ -13,21 +12,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
@Mixin(value = BufferBuilder.class, priority = 1500)
|
@Mixin(BufferBuilder.class)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class BufferBuilderMixin {
|
public class BufferBuilderMixin {
|
||||||
@Shadow private ByteBuffer buffer;
|
@Shadow private ByteBuffer buffer;
|
||||||
|
|
||||||
private static boolean leakReported = false;
|
private static boolean leakReported = false;
|
||||||
|
|
||||||
private boolean mfix$shouldFree = true;
|
|
||||||
|
|
||||||
@Dynamic
|
|
||||||
@Inject(method = "flywheel$injectForRender", at = @At("RETURN"), remap = false, require = 0)
|
|
||||||
private void preventFree(CallbackInfo ci) {
|
|
||||||
mfix$shouldFree = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure UnsafeBufferHelper is classloaded early, to avoid Forge's event transformer showing an error in the log.
|
* Ensure UnsafeBufferHelper is classloaded early, to avoid Forge's event transformer showing an error in the log.
|
||||||
*/
|
*/
|
||||||
|
|
@ -41,7 +32,7 @@ public class BufferBuilderMixin {
|
||||||
try {
|
try {
|
||||||
ByteBuffer buf = buffer;
|
ByteBuffer buf = buffer;
|
||||||
// can be null if a mod already tried to free the buffer
|
// can be null if a mod already tried to free the buffer
|
||||||
if(buf != null && mfix$shouldFree) {
|
if(buf != null) {
|
||||||
if(!leakReported) {
|
if(!leakReported) {
|
||||||
leakReported = true;
|
leakReported = true;
|
||||||
ModernFix.LOGGER.warn("One or more BufferBuilders have been leaked, ModernFix will attempt to correct this.");
|
ModernFix.LOGGER.warn("One or more BufferBuilders have been leaked, ModernFix will attempt to correct this.");
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||||
|
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
|
||||||
|
@Mixin(value = BlockBehaviour.BlockStateBase.class, priority = 100)
|
||||||
|
public class BlockStateBaseMixin {
|
||||||
|
@ModifyVariable(method = "getOffset", at = @At("HEAD"), argsOnly = true, index = 1)
|
||||||
|
private BlockGetter useSafeGetter(BlockGetter g) {
|
||||||
|
if(g instanceof ISafeBlockGetter) {
|
||||||
|
SafeBlockGetter replacement = ((ISafeBlockGetter) g).mfix$getSafeBlockGetter();
|
||||||
|
if(replacement.shouldUse())
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
|
import net.minecraft.server.level.ServerChunkCache;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.biome.Biome;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.chunk.EmptyLevelChunk;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
@Mixin(value = ServerChunkCache.class, priority = 1100)
|
||||||
|
public abstract class ServerChunkCacheMixin {
|
||||||
|
@Shadow @Final private Thread mainThread;
|
||||||
|
@Shadow @Final public ServerLevel level;
|
||||||
|
|
||||||
|
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFutureMainThread(int k, int l, ChunkStatus arg, boolean bl);
|
||||||
|
|
||||||
|
@Shadow @Final private ServerChunkCache.MainThreadExecutor mainThreadProcessor;
|
||||||
|
private final boolean debugDeadServerAccess = Boolean.getBoolean("modernfix.debugBadChunkloading");
|
||||||
|
|
||||||
|
@Inject(method = "getChunk", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void bailIfServerDead(int chunkX, int chunkZ, ChunkStatus requiredStatus, boolean load, CallbackInfoReturnable<ChunkAccess> cir) {
|
||||||
|
if(!this.level.getServer().isRunning() && !this.mainThread.isAlive()) {
|
||||||
|
ModernFix.LOGGER.fatal("A mod is accessing chunks from a stopped server (this will also cause memory leaks)");
|
||||||
|
if(debugDeadServerAccess) {
|
||||||
|
new Exception().printStackTrace();
|
||||||
|
}
|
||||||
|
Holder<Biome> plains = this.level.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.PLAINS);
|
||||||
|
cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ), plains));
|
||||||
|
} else if(Thread.currentThread() != this.mainThread) {
|
||||||
|
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = CompletableFuture.supplyAsync(() -> this.getChunkFutureMainThread(chunkX, chunkZ, requiredStatus, false), this.mainThreadProcessor).join();
|
||||||
|
if(!future.isDone()) {
|
||||||
|
// Wait at least 500 milliseconds before printing anything
|
||||||
|
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> resultingChunk = null;
|
||||||
|
try {
|
||||||
|
resultingChunk = future.get(500, TimeUnit.MILLISECONDS);
|
||||||
|
} catch(InterruptedException | ExecutionException | TimeoutException ignored) {
|
||||||
|
}
|
||||||
|
if(resultingChunk != null && resultingChunk.left().isPresent()) {
|
||||||
|
cir.setReturnValue(resultingChunk.left().get());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(debugDeadServerAccess)
|
||||||
|
ModernFix.LOGGER.warn("Async loading of a chunk was requested, this might not be desirable", new Exception());
|
||||||
|
try {
|
||||||
|
resultingChunk = future.get(10, TimeUnit.SECONDS);
|
||||||
|
if(resultingChunk.left().isPresent()) {
|
||||||
|
cir.setReturnValue(resultingChunk.left().get());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch(InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
ModernFix.LOGGER.error("Async chunk load took way too long, this needs to be reported to the appropriate mod.", e);
|
||||||
|
}
|
||||||
|
//cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
|
||||||
|
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||||
|
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
|
@Mixin(ServerLevel.class)
|
||||||
|
public class ServerLevelMixin implements ISafeBlockGetter {
|
||||||
|
@Unique
|
||||||
|
private final SafeBlockGetter mfix$safeBlockGetter = new SafeBlockGetter((ServerLevel)(Object)this);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SafeBlockGetter mfix$getSafeBlockGetter() {
|
||||||
|
return mfix$safeBlockGetter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.bugfix.concurrency;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.util.thread.BlockableEventLoop;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
|
@Mixin(Minecraft.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public abstract class MinecraftMixin<R extends Runnable> extends BlockableEventLoop<R> {
|
||||||
|
|
||||||
|
protected MinecraftMixin(String p_i50403_1_) {
|
||||||
|
super(p_i50403_1_);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void managedBlock(BooleanSupplier pIsDone) {
|
||||||
|
if(!this.isSameThread()) {
|
||||||
|
ModernFix.LOGGER.warn("A mod is calling Minecraft.managedBlock from the wrong thread. This is most likely related to one of our parallelizations.");
|
||||||
|
ModernFix.LOGGER.warn("ModernFix will work around this, however ideally the issue should be patched in the other mod.");
|
||||||
|
ModernFix.LOGGER.warn("Stacktrace", new IllegalThreadStateException());
|
||||||
|
while(!pIsDone.getAsBoolean()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.managedBlock(pIsDone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import net.minecraft.server.level.*;
|
||||||
|
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||||
|
import net.minecraft.util.thread.BlockableEventLoop;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
|
||||||
|
@Mixin(ChunkMap.class)
|
||||||
|
public abstract class ChunkMapMixin {
|
||||||
|
@Shadow @Final private BlockableEventLoop<Runnable> mainThreadExecutor;
|
||||||
|
|
||||||
|
@Shadow @Final private ChunkMap.DistanceManager distanceManager;
|
||||||
|
|
||||||
|
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> protoChunkToFullChunk(ChunkHolder arg);
|
||||||
|
|
||||||
|
@Shadow @Final private ServerLevel level;
|
||||||
|
@Shadow @Final private ThreadedLevelLightEngine lightEngine;
|
||||||
|
@Shadow @Final private ChunkProgressListener progressListener;
|
||||||
|
|
||||||
|
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> scheduleChunkGeneration(ChunkHolder chunkHolder, ChunkStatus chunkStatus);
|
||||||
|
|
||||||
|
@Shadow @Final private StructureTemplateManager structureTemplateManager;
|
||||||
|
|
||||||
|
/* https://github.com/PaperMC/Paper/blob/ver/1.17.1/patches/server/0752-Fix-chunks-refusing-to-unload-at-low-TPS.patch */
|
||||||
|
@ModifyArg(method = "prepareAccessibleChunk", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1)
|
||||||
|
private Executor useMainThreadExecutor(Executor executor) {
|
||||||
|
return this.mainThreadExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author embeddedt
|
||||||
|
* @reason revert 1.17 chunk system changes, significantly reduces time and RAM needed to load chunks
|
||||||
|
*/
|
||||||
|
@Inject(method = "schedule", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void useLegacySchedulingLogic(ChunkHolder holder, ChunkStatus requiredStatus, CallbackInfoReturnable<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> cir) {
|
||||||
|
if(requiredStatus != ChunkStatus.EMPTY && !requiredStatus.hasLoadDependencies()) {
|
||||||
|
ChunkPos chunkpos = holder.getPos();
|
||||||
|
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = holder.getOrScheduleFuture(requiredStatus.getParent(), (ChunkMap)(Object)this);
|
||||||
|
cir.setReturnValue(future.thenComposeAsync((either) -> {
|
||||||
|
Optional<ChunkAccess> optional = either.left();
|
||||||
|
|
||||||
|
if (requiredStatus == ChunkStatus.LIGHT) {
|
||||||
|
this.distanceManager.addTicket(TicketType.LIGHT, chunkpos, 33 + ChunkStatus.getDistance(ChunkStatus.LIGHT), chunkpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from original method
|
||||||
|
if (optional.isPresent() && optional.get().getStatus().isOrAfter(requiredStatus)) {
|
||||||
|
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = requiredStatus.load(this.level, this.structureTemplateManager, this.lightEngine, (arg2) -> {
|
||||||
|
return this.protoChunkToFullChunk(holder);
|
||||||
|
}, (ChunkAccess)optional.get());
|
||||||
|
this.progressListener.onStatusChange(chunkpos, requiredStatus);
|
||||||
|
return completablefuture;
|
||||||
|
} else {
|
||||||
|
return this.scheduleChunkGeneration(holder, requiredStatus);
|
||||||
|
}
|
||||||
|
}, this.mainThreadExecutor).thenComposeAsync(CompletableFuture::completedFuture, this.mainThreadExecutor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
||||||
|
|
||||||
import net.minecraft.util.SortedArraySet;
|
import net.minecraft.util.SortedArraySet;
|
||||||
import org.embeddedt.modernfix.annotation.RequiresMod;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
|
@ -10,7 +9,6 @@ import java.util.Arrays;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@Mixin(SortedArraySet.class)
|
@Mixin(SortedArraySet.class)
|
||||||
@RequiresMod("!moonrise")
|
|
||||||
public abstract class SortedArraySetMixin<T> extends AbstractSet<T> {
|
public abstract class SortedArraySetMixin<T> extends AbstractSet<T> {
|
||||||
@Shadow private int size;
|
@Shadow private int size;
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ public class MinecraftMixin {
|
||||||
/**
|
/**
|
||||||
* To mitigate the effect of leaked client worlds, clear most of the data structures that waste memory.
|
* To mitigate the effect of leaked client worlds, clear most of the data structures that waste memory.
|
||||||
*/
|
*/
|
||||||
@Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/Minecraft;level:Lnet/minecraft/client/multiplayer/ClientLevel;"))
|
@Inject(method = "disconnect(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/Minecraft;level:Lnet/minecraft/client/multiplayer/ClientLevel;"))
|
||||||
private void clearLevelDataForLeaks(CallbackInfo ci) {
|
private void clearLevelDataForLeaks(CallbackInfo ci) {
|
||||||
if(this.level != null) {
|
if(this.level != null) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.core;
|
||||||
|
|
||||||
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import org.embeddedt.modernfix.ModernFixClient;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(SynchedEntityData.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public class SynchedEntityDataMixin {
|
||||||
|
/**
|
||||||
|
* Store this in our set of all entity data objects.
|
||||||
|
*
|
||||||
|
* Not an ideal solution, but it should guarantee compatibility with mods.
|
||||||
|
*/
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void storeInSet(Entity arg, CallbackInfo ci) {
|
||||||
|
synchronized (ModernFixClient.allEntityDatas) {
|
||||||
|
ModernFixClient.allEntityDatas.add((SynchedEntityData)(Object)this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.devenv;
|
||||||
|
|
||||||
|
import com.mojang.text2speech.Narrator;
|
||||||
|
import net.minecraft.client.GameNarrator;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin(GameNarrator.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public class NarratorMixin {
|
||||||
|
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/text2speech/Narrator;getNarrator()Lcom/mojang/text2speech/Narrator;", remap = false))
|
||||||
|
private Narrator useDummyNarrator() {
|
||||||
|
return Narrator.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.feature.direct_stack_trace;
|
||||||
|
|
||||||
|
import net.minecraft.CrashReport;
|
||||||
|
import net.minecraft.CrashReportCategory;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin(CrashReport.class)
|
||||||
|
public class CrashReportMixin {
|
||||||
|
@Shadow @Final private Throwable exception;
|
||||||
|
|
||||||
|
@Inject(method = "addCategory(Ljava/lang/String;I)Lnet/minecraft/CrashReportCategory;", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;[Ljava/lang/Object;)V", remap = false))
|
||||||
|
private void dumpStacktrace(String s, int i, CallbackInfoReturnable<CrashReportCategory> cir) {
|
||||||
|
new Exception("ModernFix crash stacktrace").printStackTrace();
|
||||||
|
if(this.exception != null)
|
||||||
|
this.exception.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.feature.disable_unihex_font;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.font.GlyphProvider;
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import net.minecraft.client.gui.font.CodepointMap;
|
||||||
|
import net.minecraft.client.gui.font.providers.GlyphProviderDefinition;
|
||||||
|
import net.minecraft.client.gui.font.providers.UnihexProvider;
|
||||||
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
|
@Mixin(UnihexProvider.Definition.class)
|
||||||
|
public class UnihexProviderDefinitionMixin {
|
||||||
|
@Inject(method = "unpack", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void disableProvider(CallbackInfoReturnable<Either<GlyphProviderDefinition.Loader, GlyphProviderDefinition.Reference>> cir) {
|
||||||
|
cir.setReturnValue(Either.left(this::mfix$loadEmpty));
|
||||||
|
}
|
||||||
|
|
||||||
|
private GlyphProvider mfix$loadEmpty(ResourceManager resourceManager) throws IOException {
|
||||||
|
try {
|
||||||
|
ModernFix.LOGGER.warn("Unihex provider is disabled, a number of Unicode characters will likely not render");
|
||||||
|
Constructor<UnihexProvider> constructor = UnihexProvider.class.getDeclaredConstructor(CodepointMap.class);
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
return constructor.newInstance(new CodepointMap<>(Object[]::new, Object[][]::new));
|
||||||
|
} catch(ReflectiveOperationException e) {
|
||||||
|
throw new IOException("Failed to create empty loader", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.feature.measure_time;
|
||||||
|
|
||||||
|
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||||
|
import net.minecraft.server.packs.resources.ProfiledReloadInstance;
|
||||||
|
import org.embeddedt.modernfix.util.NamedPreparableResourceListener;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mixin(ProfiledReloadInstance.class)
|
||||||
|
public class ProfiledReloadInstanceMixin {
|
||||||
|
@ModifyVariable(method = "<init>", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
||||||
|
private static List<PreparableReloadListener> getWrappedListeners(List<PreparableReloadListener> listeners) {
|
||||||
|
List<PreparableReloadListener> newList = new ArrayList<>(listeners.size());
|
||||||
|
for(PreparableReloadListener listener : listeners) {
|
||||||
|
newList.add(new NamedPreparableResourceListener(listener));
|
||||||
|
}
|
||||||
|
return newList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.feature.measure_time;
|
||||||
|
|
||||||
|
import net.minecraft.server.packs.resources.ReloadableResourceManager;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
|
||||||
|
@Mixin(ReloadableResourceManager.class)
|
||||||
|
public class SimpleReloadableResourceManagerMixin {
|
||||||
|
// TODO maybe expose as a mixin config
|
||||||
|
private static final boolean ENABLE_DEBUG_RELOADER = Boolean.getBoolean("modernfix.debugReloader");
|
||||||
|
/**
|
||||||
|
* @author embeddedt
|
||||||
|
* @reason add ability to use this feature in modpacks
|
||||||
|
*/
|
||||||
|
@ModifyArg(method = "createReload", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/SimpleReloadInstance;create(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/List;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Z)Lnet/minecraft/server/packs/resources/ReloadInstance;"), index = 5)
|
||||||
|
private boolean enableDebugReloader(boolean bl) {
|
||||||
|
return bl || ENABLE_DEBUG_RELOADER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ import net.minecraft.world.item.ItemStack;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||||
import org.embeddedt.modernfix.searchtree.RecipeBookSearchTree;
|
import org.embeddedt.modernfix.searchtree.DummySearchTree;
|
||||||
import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry;
|
import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||||
|
|
@ -37,7 +37,7 @@ public abstract class MinecraftMixin {
|
||||||
SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier = list -> provider.getSearchTree(true);
|
SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier = list -> provider.getSearchTree(true);
|
||||||
this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, nameSupplier);
|
this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, nameSupplier);
|
||||||
this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, tagSupplier);
|
this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, tagSupplier);
|
||||||
this.searchRegistry.register(SearchRegistry.RECIPE_COLLECTIONS, list -> new RecipeBookSearchTree(provider.getSearchTree(false), list));
|
this.searchRegistry.register(SearchRegistry.RECIPE_COLLECTIONS, list -> new DummySearchTree<>());
|
||||||
ModernFixPlatformHooks.INSTANCE.registerCreativeSearchTrees(this.searchRegistry, nameSupplier, tagSupplier, this::populateSearchTree);
|
ModernFixPlatformHooks.INSTANCE.registerCreativeSearchTrees(this.searchRegistry, nameSupplier, tagSupplier, this::populateSearchTree);
|
||||||
// grab components for all key mappings in order to prevent them from being loaded off-thread later
|
// grab components for all key mappings in order to prevent them from being loaded off-thread later
|
||||||
// this populates the LazyLoadedValues
|
// this populates the LazyLoadedValues
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.cache_model_materials;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.block.model.multipart.MultiPart;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@Mixin(MultiPart.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public class MultipartMixin {
|
||||||
|
private Collection<ResourceLocation> dependencyCache = null;
|
||||||
|
@Inject(method = "getDependencies", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void useDependencyCache(CallbackInfoReturnable<Collection<ResourceLocation>> cir) {
|
||||||
|
if(dependencyCache != null)
|
||||||
|
cir.setReturnValue(dependencyCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "getDependencies", at = @At("RETURN"))
|
||||||
|
private void storeDependencyCache(CallbackInfoReturnable<Collection<ResourceLocation>> cir) {
|
||||||
|
if(dependencyCache == null)
|
||||||
|
dependencyCache = cir.getReturnValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@ package org.embeddedt.modernfix.common.mixin.perf.cache_profile_texture_url;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||||
import net.minecraft.client.resources.SkinManager;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
@ -13,7 +12,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Mixin(SkinManager.class)
|
@Mixin(targets = {"net/minecraft/client/resources/SkinManager$TextureCache" })
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class SkinManagerMixin {
|
public class SkinManagerMixin {
|
||||||
@Unique
|
@Unique
|
||||||
|
|
@ -22,7 +21,7 @@ public class SkinManagerMixin {
|
||||||
.concurrencyLevel(1)
|
.concurrencyLevel(1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@Redirect(method = "registerTexture(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinTextureCallback;)Lnet/minecraft/resources/ResourceLocation;",
|
@Redirect(method = { "getOrLoad", "registerTexture" },
|
||||||
at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;getHash()Ljava/lang/String;", remap = false))
|
at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;getHash()Ljava/lang/String;", remap = false))
|
||||||
private String useCachedHash(MinecraftProfileTexture texture) {
|
private String useCachedHash(MinecraftProfileTexture texture) {
|
||||||
// avoid lambda allocation for common case
|
// avoid lambda allocation for common case
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.cache_strongholds;
|
||||||
|
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.embeddedt.modernfix.duck.IChunkGenerator;
|
||||||
|
import org.embeddedt.modernfix.duck.IServerLevel;
|
||||||
|
import org.embeddedt.modernfix.world.StrongholdLocationCache;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
@Mixin(ChunkGeneratorStructureState.class)
|
||||||
|
public class ChunkGeneratorMixin implements IChunkGenerator {
|
||||||
|
private WeakReference<ServerLevel> mfix$serverLevel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mfix$setAssociatedServerLevel(ServerLevel level) {
|
||||||
|
mfix$serverLevel = new WeakReference<>(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "generateRingPositions", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void useCachedDataIfAvailable(Holder<StructureSet> structureSet, ConcentricRingsStructurePlacement placement, CallbackInfoReturnable<CompletableFuture<List<ChunkPos>>> cir) {
|
||||||
|
if(placement.count() == 0)
|
||||||
|
return;
|
||||||
|
ServerLevel level = searchLevel();
|
||||||
|
if(level == null)
|
||||||
|
return;
|
||||||
|
StrongholdLocationCache cache = ((IServerLevel)level).mfix$getStrongholdCache();
|
||||||
|
List<ChunkPos> positions = cache.getChunkPosList();
|
||||||
|
if(positions.isEmpty())
|
||||||
|
return;
|
||||||
|
ModernFix.LOGGER.debug("Loaded stronghold cache for dimension {} with {} positions", level.dimension().location(), positions.size());
|
||||||
|
cir.setReturnValue(CompletableFuture.completedFuture(positions));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerLevel searchLevel() {
|
||||||
|
if(mfix$serverLevel != null)
|
||||||
|
return mfix$serverLevel.get();
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "generateRingPositions", at = @At("RETURN"), cancellable = true)
|
||||||
|
private void saveCachedData(Holder<StructureSet> structureSet, ConcentricRingsStructurePlacement placement, CallbackInfoReturnable<CompletableFuture<List<ChunkPos>>> cir) {
|
||||||
|
cir.setReturnValue(cir.getReturnValue().thenApplyAsync(list -> {
|
||||||
|
if(list.size() == 0)
|
||||||
|
return list;
|
||||||
|
ServerLevel level = searchLevel();
|
||||||
|
if(level != null) {
|
||||||
|
StrongholdLocationCache cache = ((IServerLevel)level).mfix$getStrongholdCache();
|
||||||
|
cache.setChunkPosList(list);
|
||||||
|
ModernFix.LOGGER.debug("Saved stronghold cache for dimension {}", level.dimension().location());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}, Util.backgroundExecutor()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.cache_strongholds;
|
||||||
|
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.server.level.ServerChunkCache;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
|
import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||||
|
import net.minecraft.world.level.storage.WritableLevelData;
|
||||||
|
import org.embeddedt.modernfix.duck.IChunkGenerator;
|
||||||
|
import org.embeddedt.modernfix.duck.IServerLevel;
|
||||||
|
import org.embeddedt.modernfix.world.StrongholdLocationCache;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@Mixin(ServerLevel.class)
|
||||||
|
public abstract class ServerLevelMixin extends Level implements IServerLevel {
|
||||||
|
protected ServerLevelMixin(WritableLevelData arg, ResourceKey<Level> arg2, RegistryAccess arg3, Holder<DimensionType> arg4, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
|
||||||
|
super(arg, arg2, arg3, arg4, supplier, bl, bl2, l, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Shadow public abstract DimensionDataStorage getDataStorage();
|
||||||
|
|
||||||
|
@Shadow @Final private ServerChunkCache chunkSource;
|
||||||
|
private StrongholdLocationCache mfix$strongholdCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the stronghold cache but don't force any structure generation yet.
|
||||||
|
*/
|
||||||
|
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/ChunkGeneratorStructureState;ensureStructuresGenerated()V"))
|
||||||
|
private void hookStrongholdCache(ChunkGeneratorStructureState generator) {
|
||||||
|
((IChunkGenerator)generator).mfix$setAssociatedServerLevel((ServerLevel)(Object)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Now start the stronghold generation process.
|
||||||
|
*/
|
||||||
|
@Inject(method = "<init>", at = @At("TAIL"))
|
||||||
|
private void ensureGeneration(CallbackInfo ci) {
|
||||||
|
mfix$strongholdCache = this.getDataStorage().computeIfAbsent(
|
||||||
|
StrongholdLocationCache.factory((ServerLevel)(Object)this),
|
||||||
|
StrongholdLocationCache.getFileId(this.dimensionTypeRegistration()));
|
||||||
|
this.chunkSource.getGeneratorState().ensureStructuresGenerated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StrongholdLocationCache mfix$getStrongholdCache() {
|
||||||
|
return mfix$strongholdCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,15 +30,8 @@ public abstract class PalettedContainerMixin<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(empty && storArray.length > 0) {
|
if(empty && storArray.length > 0) {
|
||||||
T value;
|
|
||||||
/* it means the chunk is oversized and wasting memory, take the ID out of the palette and recreate a smaller chunk */
|
/* it means the chunk is oversized and wasting memory, take the ID out of the palette and recreate a smaller chunk */
|
||||||
try {
|
T value = this.data.palette().valueFor(0);
|
||||||
value = this.data.palette().valueFor(0);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
// Some mods/servers seem to generate buggy palettes. This is not our fault (the game will likely crash later),
|
|
||||||
// but we catch it here to avoid receiving bug reports for an issue we didn't cause.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.data = this.createOrReuseData(null, 0);
|
this.data = this.createOrReuseData(null, 0);
|
||||||
this.data.palette().idFor(value);
|
this.data.palette().idFor(value);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
|
||||||
|
import org.embeddedt.modernfix.registry.DirectStorageRegistryObject;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
||||||
|
@Mixin({ Block.class, Item.class })
|
||||||
|
@IgnoreOutsideDev
|
||||||
|
public class DirectObjectMixin implements DirectStorageRegistryObject {
|
||||||
|
private ResourceLocation mfix$resourceKey;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation mfix$getResourceKey() {
|
||||||
|
return mfix$resourceKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mfix$setResourceKey(ResourceLocation key) {
|
||||||
|
mfix$resourceKey = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
|
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
import net.minecraft.core.MappedRegistry;
|
import net.minecraft.core.MappedRegistry;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
|
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
|
||||||
import org.embeddedt.modernfix.registry.LifecycleMap;
|
import org.embeddedt.modernfix.registry.LifecycleMap;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
|
@ -22,8 +24,17 @@ public abstract class MappedRegistryMixin<T> {
|
||||||
@Mutable
|
@Mutable
|
||||||
private Map<T, Lifecycle> lifecycles;
|
private Map<T, Lifecycle> lifecycles;
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
private static final ImmutableSet<ResourceLocation> MFIX$NEW_STORAGE_KEYS = ImmutableSet.of(new ResourceLocation("block"), new ResourceLocation("item"));
|
||||||
|
|
||||||
|
@Inject(method = "<init>(Lnet/minecraft/resources/ResourceKey;Lcom/mojang/serialization/Lifecycle;Z)V", at = @At("RETURN"))
|
||||||
private void replaceStorage(CallbackInfo ci) {
|
private void replaceStorage(CallbackInfo ci) {
|
||||||
this.lifecycles = new LifecycleMap<>();
|
this.lifecycles = new LifecycleMap<>();
|
||||||
|
/*
|
||||||
|
if(MFIX$NEW_STORAGE_KEYS.contains(this.key().location())) {
|
||||||
|
ModernFixMixinPlugin.instance.logger.info("Using experimental registry storage for {}", this.key());
|
||||||
|
this.storage = (BiMap<ResourceLocation, T>) RegistryStorage.createStorage();
|
||||||
|
this.keyStorage = (BiMap<ResourceKey<T>, T>)RegistryStorage.createKeyStorage(this.key(), (BiMap<ResourceLocation, DirectStorageRegistryObject>)this.storage);
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,7 +12,7 @@ import java.util.concurrent.ExecutorService;
|
||||||
@Mixin(Minecraft.class)
|
@Mixin(Minecraft.class)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class MinecraftMixin {
|
public class MinecraftMixin {
|
||||||
@Redirect(method = { "<init>", "reloadResourcePacks(Z)Ljava/util/concurrent/CompletableFuture;" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;backgroundExecutor()Ljava/util/concurrent/ExecutorService;", ordinal = 0))
|
@Redirect(method = { "<init>", "reloadResourcePacks(ZLnet/minecraft/client/Minecraft$GameLoadCookie;)Ljava/util/concurrent/CompletableFuture;" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;backgroundExecutor()Ljava/util/concurrent/ExecutorService;", ordinal = 0))
|
||||||
private ExecutorService getResourceReloadExecutor() {
|
private ExecutorService getResourceReloadExecutor() {
|
||||||
return ModernFix.resourceReloadExecutor();
|
return ModernFix.resourceReloadExecutor();
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.deduplicate_climate_parameters;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.biome.Climate;
|
||||||
|
import org.embeddedt.modernfix.dedup.ClimateCache;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin({ Climate.Parameter.class, Climate.ParameterPoint.class })
|
||||||
|
public class ParameterMixin {
|
||||||
|
@Redirect(method = "*", at = @At(value = "NEW", target = "net/minecraft/world/level/biome/Climate$Parameter"), require = 0)
|
||||||
|
private static Climate.Parameter internParameterStatic(long min, long max) {
|
||||||
|
return ClimateCache.MFIX_INTERNER.intern(new Climate.Parameter(min, max));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.deduplicate_location;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.embeddedt.modernfix.dedup.IdentifierCaches;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mutable;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(ResourceLocation.class)
|
||||||
|
public class MixinResourceLocation {
|
||||||
|
@Mutable
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
protected String namespace;
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
protected String path;
|
||||||
|
|
||||||
|
@Inject(method = "<init>([Ljava/lang/String;)V", at = @At("RETURN"))
|
||||||
|
private void reinit(String[] id, CallbackInfo ci) {
|
||||||
|
this.namespace = IdentifierCaches.NAMESPACES.deduplicate(this.namespace);
|
||||||
|
this.path = IdentifierCaches.PATH.deduplicate(this.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_block_codecs;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.StateHolder;
|
||||||
|
import net.minecraft.world.level.block.state.properties.Property;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@Mixin(StateDefinition.class)
|
||||||
|
public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
|
||||||
|
@Shadow @Final private O owner;
|
||||||
|
|
||||||
|
@ModifyVariable(method = "<init>", at = @At("HEAD"), ordinal = 0, argsOnly = true)
|
||||||
|
private static <O, S extends StateHolder<O, S>> StateDefinition.Factory<O, S> replaceMapCodec(StateDefinition.Factory<O, S> factory, Function<O, S> function, O object, StateDefinition.Factory<O, S> factory2, Map<String, Property<?>> map) {
|
||||||
|
if(object instanceof Block)
|
||||||
|
return (o, m, c) -> factory.create(o, m, null);
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_block_codecs;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.Decoder;
|
||||||
|
import com.mojang.serialization.Encoder;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.StateHolder;
|
||||||
|
import net.minecraft.world.level.block.state.properties.Property;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@Mixin(StateHolder.class)
|
||||||
|
public class StateHolderMixin {
|
||||||
|
private static final LoadingCache<Block, MapCodec<BlockState>> MODERNFIX_CODEC_CACHE = CacheBuilder.newBuilder()
|
||||||
|
.maximumSize(100000)
|
||||||
|
.build(new CacheLoader<Block, MapCodec<BlockState>>() {
|
||||||
|
@Override
|
||||||
|
public MapCodec<BlockState> load(Block block) throws Exception {
|
||||||
|
Supplier<BlockState> stateSupplier = block::defaultBlockState;
|
||||||
|
MapCodec<BlockState> mapCodec = MapCodec.of(Encoder.empty(), Decoder.unit(stateSupplier));
|
||||||
|
for(Property<?> property : block.getStateDefinition().getProperties()) {
|
||||||
|
mapCodec = StateDefinition.appendPropertyCodec(mapCodec, stateSupplier, property.getName(), property);
|
||||||
|
}
|
||||||
|
return mapCodec;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
@Redirect(method = "codec", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Codec;dispatch(Ljava/lang/String;Ljava/util/function/Function;Ljava/util/function/Function;)Lcom/mojang/serialization/Codec;", remap = false))
|
||||||
|
private static <O, S extends StateHolder<O, S>> Codec<S> obtainCodec(Codec<O> codec, String typeKey, Function<S, O> type, Function<O, ? extends Codec<S>> codecFn, Codec<O> codecMethodArg, Function<O, S> stateSupplier) {
|
||||||
|
return codec.dispatch(typeKey, type, block -> {
|
||||||
|
if(block instanceof Block) {
|
||||||
|
S state = stateSupplier.apply(block);
|
||||||
|
if(state.getValues().isEmpty())
|
||||||
|
return Codec.unit(state);
|
||||||
|
MapCodec<S> mapCodec;
|
||||||
|
try {
|
||||||
|
mapCodec = (MapCodec<S>)MODERNFIX_CODEC_CACHE.get((Block)block);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return mapCodec.fieldOf("Properties").codec();
|
||||||
|
} else {
|
||||||
|
return codecFn.apply(block);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_entity_renderers;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.embeddedt.modernfix.entity.EntityRendererMap;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mixin(EntityRenderDispatcher.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public class EntityRenderDispatcherMixin {
|
||||||
|
@Shadow private Map<EntityType<?>, EntityRenderer<?>> renderers;
|
||||||
|
|
||||||
|
private EntityRendererMap mfix$dynamicRenderers;
|
||||||
|
|
||||||
|
@Inject(method = "getRenderer", at = @At("RETURN"), cancellable = true)
|
||||||
|
private <T extends Entity> void checkNullness(T entity, CallbackInfoReturnable<EntityRenderer<? super T>> cir) {
|
||||||
|
// apparently some mods yeet the renderers map and cause issues
|
||||||
|
if(cir.getReturnValue() == null)
|
||||||
|
cir.setReturnValue((EntityRenderer<? super T>)mfix$dynamicRenderers.get(entity.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "onResourceManagerReload", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/renderer/entity/EntityRenderDispatcher;renderers:Ljava/util/Map;"))
|
||||||
|
private void setRendererField(EntityRenderDispatcher instance, Map<EntityType<?>, EntityRenderer<?>> incomingMap) {
|
||||||
|
this.renderers = incomingMap;
|
||||||
|
this.mfix$dynamicRenderers = (EntityRendererMap)incomingMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_entity_renderers;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRenderers;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.embeddedt.modernfix.entity.EntityRendererMap;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mixin(EntityRenderers.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public class EntityRenderersMixin {
|
||||||
|
@Shadow @Final private static Map<EntityType<?>, EntityRendererProvider<?>> PROVIDERS;
|
||||||
|
|
||||||
|
@Inject(method = "createEntityRenderers", at = @At("HEAD"), cancellable = true)
|
||||||
|
private static void createDynamicRendererLoader(EntityRendererProvider.Context context, CallbackInfoReturnable<Map<EntityType<?>, EntityRenderer<?>>> cir) {
|
||||||
|
cir.setReturnValue(new EntityRendererMap(PROVIDERS, context));
|
||||||
|
ModernFix.LOGGER.info("Dynamic entity renderer hook setup");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import net.minecraft.client.renderer.block.model.BlockElementFace;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.embeddedt.modernfix.dynamicresources.UVController;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
@Mixin(BlockElementFace.Deserializer.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public class BlockElementFaceDeserializerMixin {
|
||||||
|
|
||||||
|
@Redirect(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockElementFace;",
|
||||||
|
at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonDeserializationContext;deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;)Ljava/lang/Object;", ordinal = 0, remap = false))
|
||||||
|
private Object skipUvsForInitialLoad(JsonDeserializationContext context, JsonElement element, Type type) {
|
||||||
|
return UVController.useDummyUv.get() ? UVController.dummyUv : context.deserialize(element, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,11 +4,9 @@ import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.client.resources.model.ModelManager;
|
import net.minecraft.client.resources.model.ModelManager;
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
import org.embeddedt.modernfix.duck.IModelHoldingBlockState;
|
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
|
||||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||||
import org.embeddedt.modernfix.util.DynamicOverridableMap;
|
import org.embeddedt.modernfix.util.DynamicOverridableMap;
|
||||||
import org.spongepowered.asm.mixin.*;
|
import org.spongepowered.asm.mixin.*;
|
||||||
|
|
@ -26,18 +24,14 @@ public class BlockModelShaperMixin {
|
||||||
@Shadow
|
@Shadow
|
||||||
private Map<BlockState, BakedModel> modelByStateCache;
|
private Map<BlockState, BakedModel> modelByStateCache;
|
||||||
|
|
||||||
|
private final DynamicModelCache<BlockState> mfix$modelCache = new DynamicModelCache<>(k -> this.cacheBlockModel((BlockState)k), false);
|
||||||
|
|
||||||
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
|
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
|
||||||
private void replaceModelMap(CallbackInfo ci) {
|
private void replaceModelMap(CallbackInfo ci) {
|
||||||
// replace the backing map for mods which will access it
|
// replace the backing map for mods which will access it
|
||||||
this.modelByStateCache = new DynamicOverridableMap<>(state -> modelManager.getModel(ModelLocationCache.get(state)));
|
this.modelByStateCache = new DynamicOverridableMap<>(state -> modelManager.getModel(ModelLocationCache.get(state)));
|
||||||
// Clear the cached models on blockstate objects
|
if(this.mfix$modelCache != null)
|
||||||
for(Block block : BuiltInRegistries.BLOCK) {
|
this.mfix$modelCache.clear();
|
||||||
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
|
|
||||||
if(state instanceof IModelHoldingBlockState modelHolder) {
|
|
||||||
modelHolder.mfix$setModel(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BakedModel cacheBlockModel(BlockState state) {
|
private BakedModel cacheBlockModel(BlockState state) {
|
||||||
|
|
@ -57,18 +51,6 @@ public class BlockModelShaperMixin {
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public BakedModel getBlockModel(BlockState state) {
|
public BakedModel getBlockModel(BlockState state) {
|
||||||
if(state instanceof IModelHoldingBlockState modelHolder) {
|
return this.mfix$modelCache.get(state);
|
||||||
BakedModel model = modelHolder.mfix$getModel();
|
|
||||||
|
|
||||||
if(model != null) {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
model = this.cacheBlockModel(state);
|
|
||||||
modelHolder.mfix$setModel(model);
|
|
||||||
return model;
|
|
||||||
} else {
|
|
||||||
return this.cacheBlockModel(state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,7 +7,6 @@ import net.minecraft.client.resources.model.ModelManager;
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
|
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
|
||||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||||
import org.embeddedt.modernfix.util.DynamicInt2ObjectMap;
|
import org.embeddedt.modernfix.util.DynamicInt2ObjectMap;
|
||||||
|
|
@ -20,7 +19,6 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Mixin(ItemModelShaper.class)
|
@Mixin(ItemModelShaper.class)
|
||||||
@ClientOnlyMixin
|
|
||||||
public abstract class ItemModelShaperMixin {
|
public abstract class ItemModelShaperMixin {
|
||||||
|
|
||||||
@Shadow public abstract ModelManager getModelManager();
|
@Shadow public abstract ModelManager getModelManager();
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import org.embeddedt.modernfix.dynamicresources.ItemOverrideBakedModel;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin(ItemOverrides.class)
|
||||||
|
public class ItemOverridesMixin {
|
||||||
|
@Inject(method = "resolve", at = @At("RETURN"), cancellable = true)
|
||||||
|
private void getRealModel(CallbackInfoReturnable<BakedModel> cir) {
|
||||||
|
BakedModel original = cir.getReturnValue();
|
||||||
|
if(original instanceof ItemOverrideBakedModel) {
|
||||||
|
ItemOverrideBakedModel override = (ItemOverrideBakedModel)original;
|
||||||
|
cir.setReturnValue(override.getRealModel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,69 +1,40 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.llamalad7.mixinextras.sugar.Local;
|
|
||||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
import net.minecraft.client.resources.model.ModelManager;
|
import net.minecraft.client.resources.model.ModelManager;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.Resource;
|
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
import net.minecraft.util.GsonHelper;
|
import net.minecraft.util.GsonHelper;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.block.state.StateDefinition;
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.util.CacheUtil;
|
|
||||||
import org.embeddedt.modernfix.util.LambdaMap;
|
import org.embeddedt.modernfix.util.LambdaMap;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionStage;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Mixin(ModelManager.class)
|
@Mixin(ModelManager.class)
|
||||||
@ClientOnlyMixin
|
|
||||||
public class ModelManagerMixin {
|
public class ModelManagerMixin {
|
||||||
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
|
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockModels(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||||
|
private CompletableFuture<Map<ResourceLocation, BlockModel>> deferBlockModelLoad(ResourceManager manager, Executor executor) {
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
return CompletableFuture.completedFuture(new LambdaMap<>(location -> loadSingleBlockModel(manager, location)));
|
||||||
private void injectDummyBakedRegistry(CallbackInfo ci) {
|
|
||||||
if(this.bakedRegistry == null) {
|
|
||||||
this.bakedRegistry = new HashMap<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ModifyArg(method = "loadBlockModels", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenCompose(Ljava/util/function/Function;)Ljava/util/concurrent/CompletableFuture;", ordinal = 0), index = 0)
|
|
||||||
private static Function<Map<ResourceLocation, Resource>, ? extends CompletionStage<Map<ResourceLocation, BlockModel>>> deferBlockModelLoad(Function<Map<ResourceLocation, Resource>, ? extends CompletionStage<Map<ResourceLocation, BlockModel>>> fn, @Local(ordinal = 0, argsOnly = true) ResourceManager manager) {
|
|
||||||
return resourceMap -> {
|
|
||||||
var fallbackModel = BlockModel.fromString(ModelBakery.MISSING_MODEL_MESH);
|
|
||||||
var cache = CacheUtil.<ResourceLocation, BlockModel>simpleCacheForLambda(location -> loadSingleBlockModel(manager, location, fallbackModel), 100L);
|
|
||||||
return CompletableFuture.completedFuture(Maps.asMap(Set.copyOf(resourceMap.keySet()), location -> cache.getUnchecked(location)));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockStates(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockStates(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||||
private CompletableFuture<Map<ResourceLocation, List<ModelBakery.LoadedJson>>> deferBlockStateLoad(ResourceManager manager, Executor executor) {
|
private CompletableFuture<Map<ResourceLocation, List<ModelBakery.LoadedJson>>> deferBlockStateLoad(ResourceManager manager, Executor executor) {
|
||||||
var cache = CacheUtil.<ResourceLocation, List<ModelBakery.LoadedJson>>simpleCacheForLambda(location -> loadSingleBlockState(manager, location), 100L);
|
return CompletableFuture.completedFuture(new LambdaMap<>(location -> loadSingleBlockState(manager, location)));
|
||||||
return CompletableFuture.completedFuture(new LambdaMap<>(location -> cache.getUnchecked(location)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "loadModels", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
|
@Redirect(method = "loadModels", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
|
||||||
|
|
@ -71,15 +42,13 @@ public class ModelManagerMixin {
|
||||||
return ImmutableList.of();
|
return ImmutableList.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BlockModel loadSingleBlockModel(ResourceManager manager, ResourceLocation location, BlockModel fallbackModel) {
|
private BlockModel loadSingleBlockModel(ResourceManager manager, ResourceLocation location) {
|
||||||
return manager.getResource(location).map(resource -> {
|
return manager.getResource(location).map(resource -> {
|
||||||
try (BufferedReader reader = resource.openAsReader()) {
|
try (BufferedReader reader = resource.openAsReader()) {
|
||||||
return BlockModel.fromStream(reader);
|
return BlockModel.fromStream(reader);
|
||||||
} catch (Exception e) {
|
} catch(IOException e) {
|
||||||
// We must return some nonnull value to avoid breaking the map convention. The easiest solution
|
ModernFix.LOGGER.error("Couldn't load model", e);
|
||||||
// is to just return a missing model template.
|
return null;
|
||||||
ModernFix.LOGGER.error("Couldn't load model {}, substituting missing", location, e);
|
|
||||||
return fallbackModel;
|
|
||||||
}
|
}
|
||||||
}).orElse(null);
|
}).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_sounds;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.RemovalCause;
|
||||||
|
import com.google.common.cache.RemovalNotification;
|
||||||
|
import com.mojang.blaze3d.audio.SoundBuffer;
|
||||||
|
import net.minecraft.client.sounds.SoundBufferLibrary;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.embeddedt.modernfix.dynamicresources.DynamicSoundHelpers;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mutable;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mixin(SoundBufferLibrary.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public abstract class SoundBufferLibraryMixin {
|
||||||
|
|
||||||
|
private static final boolean debugDynamicSoundLoading = Boolean.getBoolean("modernfix.debugDynamicSoundLoading");
|
||||||
|
|
||||||
|
@Shadow @Final @Mutable
|
||||||
|
private Map<ResourceLocation, CompletableFuture<SoundBuffer>> cache = CacheBuilder.newBuilder()
|
||||||
|
.expireAfterAccess(DynamicSoundHelpers.MAX_SOUND_LIFETIME_SECS, TimeUnit.SECONDS)
|
||||||
|
.concurrencyLevel(1)
|
||||||
|
// Excessive use of type hinting due to it assuming Object as the broadest correct type
|
||||||
|
.<ResourceLocation, CompletableFuture<SoundBuffer>>removalListener(this::onSoundRemoval)
|
||||||
|
.build()
|
||||||
|
.asMap();
|
||||||
|
|
||||||
|
private <K extends ResourceLocation, V extends CompletableFuture<SoundBuffer>> void onSoundRemoval(RemovalNotification<K, V> notification) {
|
||||||
|
if(notification.getCause() == RemovalCause.REPLACED && notification.getValue() == cache.get(notification.getKey()))
|
||||||
|
return;
|
||||||
|
notification.getValue().thenAccept(SoundBuffer::discardAlBuffer);
|
||||||
|
if(!debugDynamicSoundLoading)
|
||||||
|
return;
|
||||||
|
K k = notification.getKey();
|
||||||
|
if(k == null)
|
||||||
|
return;
|
||||||
|
ModernFix.LOGGER.warn("Evicted sound {}", k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering;
|
package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.GameRenderer;
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.render.RenderState;
|
import org.embeddedt.modernfix.render.RenderState;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
@ -9,7 +8,6 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(GameRenderer.class)
|
@Mixin(GameRenderer.class)
|
||||||
@ClientOnlyMixin
|
|
||||||
public class GameRendererMixin {
|
public class GameRendererMixin {
|
||||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(FJLcom/mojang/blaze3d/vertex/PoseStack;)V", shift = At.Shift.BEFORE))
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(FJLcom/mojang/blaze3d/vertex/PoseStack;)V", shift = At.Shift.BEFORE))
|
||||||
private void markRenderingLevel(CallbackInfo ci) {
|
private void markRenderingLevel(CallbackInfo ci) {
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering;
|
package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering;
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.sugar.Local;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
|
@ -11,7 +10,6 @@ import net.minecraft.client.resources.model.SimpleBakedModel;
|
||||||
import net.minecraft.world.item.ItemDisplayContext;
|
import net.minecraft.world.item.ItemDisplayContext;
|
||||||
import net.minecraft.world.item.BlockItem;
|
import net.minecraft.world.item.BlockItem;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.render.FastItemRenderType;
|
import org.embeddedt.modernfix.render.FastItemRenderType;
|
||||||
import org.embeddedt.modernfix.render.RenderState;
|
import org.embeddedt.modernfix.render.RenderState;
|
||||||
import org.embeddedt.modernfix.render.SimpleItemModelView;
|
import org.embeddedt.modernfix.render.SimpleItemModelView;
|
||||||
|
|
@ -22,7 +20,6 @@ import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(value = ItemRenderer.class, priority = 600)
|
@Mixin(value = ItemRenderer.class, priority = 600)
|
||||||
@ClientOnlyMixin
|
|
||||||
public abstract class ItemRendererMixin {
|
public abstract class ItemRendererMixin {
|
||||||
private ItemDisplayContext transformType;
|
private ItemDisplayContext transformType;
|
||||||
private final SimpleItemModelView modelView = new SimpleItemModelView();
|
private final SimpleItemModelView modelView = new SimpleItemModelView();
|
||||||
|
|
@ -41,13 +38,7 @@ public abstract class ItemRendererMixin {
|
||||||
* camera).
|
* camera).
|
||||||
*/
|
*/
|
||||||
@ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;renderModelLists(Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/item/ItemStack;IILcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;)V"), index = 0)
|
@ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;renderModelLists(Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/item/ItemStack;IILcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;)V"), index = 0)
|
||||||
private BakedModel useSimpleWrappedItemModel(BakedModel model, ItemStack stack, int combinedLight, int combinedOverlay, PoseStack matrixStack, VertexConsumer buffer, @Local(ordinal = 0) BakedModel originalModel) {
|
private BakedModel useSimpleWrappedItemModel(BakedModel model, ItemStack stack, int combinedLight, int combinedOverlay, PoseStack matrixStack, VertexConsumer buffer) {
|
||||||
// Forge composite models split themselves into a smaller simple model, we need to detect that the parent
|
|
||||||
// was not simple
|
|
||||||
if(originalModel != null && originalModel.getClass() != SimpleBakedModel.class) {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!RenderState.IS_RENDERING_LEVEL && !stack.isEmpty() && model.getClass() == SimpleBakedModel.class && transformType == ItemDisplayContext.GUI) {
|
if(!RenderState.IS_RENDERING_LEVEL && !stack.isEmpty() && model.getClass() == SimpleBakedModel.class && transformType == ItemDisplayContext.GUI) {
|
||||||
FastItemRenderType type;
|
FastItemRenderType type;
|
||||||
ItemTransform transform = model.getTransforms().gui;
|
ItemTransform transform = model.getTransforms().gui;
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.faster_texture_stitching;
|
package org.embeddedt.modernfix.common.mixin.perf.faster_texture_stitching;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
import net.minecraft.client.renderer.texture.Stitcher;
|
import net.minecraft.client.renderer.texture.Stitcher;
|
||||||
import net.minecraft.client.renderer.texture.StitcherException;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||||
|
|
@ -18,7 +16,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
@Mixin(Stitcher.class)
|
@Mixin(Stitcher.class)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
|
|
@ -29,9 +26,6 @@ public class StitcherMixin<T extends Stitcher.Entry> {
|
||||||
|
|
||||||
@Shadow private int storageY;
|
@Shadow private int storageY;
|
||||||
|
|
||||||
@Shadow @Final private int maxWidth;
|
|
||||||
@Shadow @Final private int maxHeight;
|
|
||||||
|
|
||||||
@Shadow @Final private static Comparator<Stitcher.Holder<?>> HOLDER_COMPARATOR;
|
@Shadow @Final private static Comparator<Stitcher.Holder<?>> HOLDER_COMPARATOR;
|
||||||
private List<StbStitcher.LoadableSpriteInfo<T>> loadableSpriteInfos;
|
private List<StbStitcher.LoadableSpriteInfo<T>> loadableSpriteInfos;
|
||||||
|
|
||||||
|
|
@ -41,16 +35,10 @@ public class StitcherMixin<T extends Stitcher.Entry> {
|
||||||
*/
|
*/
|
||||||
@Inject(method = "stitch", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "stitch", at = @At("HEAD"), cancellable = true)
|
||||||
private void stitchFast(CallbackInfo ci) {
|
private void stitchFast(CallbackInfo ci) {
|
||||||
this.loadableSpriteInfos = null;
|
|
||||||
if(!ModernFixPlatformHooks.INSTANCE.isLoadingNormally()) {
|
if(!ModernFixPlatformHooks.INSTANCE.isLoadingNormally()) {
|
||||||
ModernFix.LOGGER.error("Using vanilla stitcher implementation due to invalid loading state");
|
ModernFix.LOGGER.error("Using vanilla stitcher implementation due to invalid loading state");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(this.texturesToBeStitched.size() < 100) {
|
|
||||||
// The vanilla implementation is fine for small atlases, and using it allows mods like JEI that depend on
|
|
||||||
// precise texture alignments to avoid bugs.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ci.cancel();
|
ci.cancel();
|
||||||
ObjectArrayList<Stitcher.Holder<T>> holderList = new ObjectArrayList<>(this.texturesToBeStitched);
|
ObjectArrayList<Stitcher.Holder<T>> holderList = new ObjectArrayList<>(this.texturesToBeStitched);
|
||||||
holderList.sort(HOLDER_COMPARATOR);
|
holderList.sort(HOLDER_COMPARATOR);
|
||||||
|
|
@ -59,13 +47,6 @@ public class StitcherMixin<T extends Stitcher.Entry> {
|
||||||
Pair<Pair<Integer, Integer>, List<StbStitcher.LoadableSpriteInfo<T>>> packingInfo = StbStitcher.packRects(aholder);
|
Pair<Pair<Integer, Integer>, List<StbStitcher.LoadableSpriteInfo<T>>> packingInfo = StbStitcher.packRects(aholder);
|
||||||
this.storageX = packingInfo.getFirst().getFirst();
|
this.storageX = packingInfo.getFirst().getFirst();
|
||||||
this.storageY = packingInfo.getFirst().getSecond();
|
this.storageY = packingInfo.getFirst().getSecond();
|
||||||
|
|
||||||
// Detect an oversized atlas generated in the previous step.
|
|
||||||
if(this.storageX > this.maxWidth || this.storageY > this.maxHeight) {
|
|
||||||
ModernFix.LOGGER.error("Requested atlas size {}x{} exceeds maximum of {}x{}", this.storageX, this.storageY, this.maxWidth, this.maxHeight);
|
|
||||||
throw new StitcherException(aholder[0].entry(), Stream.of(aholder).map(arg -> arg.entry()).collect(ImmutableList.toImmutableList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadableSpriteInfos = packingInfo.getSecond();
|
this.loadableSpriteInfos = packingInfo.getSecond();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +56,7 @@ public class StitcherMixin<T extends Stitcher.Entry> {
|
||||||
*/
|
*/
|
||||||
@Inject(method = "gatherSprites", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "gatherSprites", at = @At("HEAD"), cancellable = true)
|
||||||
private void gatherSpritesFast(Stitcher.SpriteLoader<T> spriteLoader, CallbackInfo ci) {
|
private void gatherSpritesFast(Stitcher.SpriteLoader<T> spriteLoader, CallbackInfo ci) {
|
||||||
if(this.loadableSpriteInfos == null)
|
if(!ModernFixPlatformHooks.INSTANCE.isLoadingNormally())
|
||||||
return;
|
return;
|
||||||
ci.cancel();
|
ci.cancel();
|
||||||
for(StbStitcher.LoadableSpriteInfo<T> info : loadableSpriteInfos) {
|
for(StbStitcher.LoadableSpriteInfo<T> info : loadableSpriteInfos) {
|
||||||
|
|
@ -1,22 +1,25 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.model_optimizations;
|
package org.embeddedt.modernfix.common.mixin.perf.model_optimizations;
|
||||||
|
|
||||||
import net.minecraft.world.level.block.state.properties.Property;
|
import net.minecraft.world.level.block.state.properties.Property;
|
||||||
|
import org.embeddedt.modernfix.dedup.IdentifierCaches;
|
||||||
import org.spongepowered.asm.mixin.*;
|
import org.spongepowered.asm.mixin.*;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
@Mixin(Property.class)
|
@Mixin(Property.class)
|
||||||
public class PropertyMixin {
|
public class PropertyMixin {
|
||||||
|
|
||||||
@Shadow @Final private String name;
|
@Shadow @Mutable
|
||||||
|
@Final private String name;
|
||||||
|
|
||||||
|
@Shadow private Integer hashCode;
|
||||||
|
|
||||||
@Shadow @Final private Class clazz;
|
@Shadow @Final private Class clazz;
|
||||||
|
|
||||||
@ModifyVariable(method = "<init>", at = @At("HEAD"), ordinal = 0, argsOnly = true)
|
@Redirect(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/state/properties/Property;name:Ljava/lang/String;"))
|
||||||
private static String internName(String name) {
|
private void internName(Property instance, String name) {
|
||||||
return name.intern();
|
this.name = IdentifierCaches.PROPERTY.deduplicate(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author embeddedt
|
* @author embeddedt
|
||||||
* @reason compare hashcodes if generated, use reference equality for speed
|
* @reason compare hashcodes if generated, use reference equality for speed
|
||||||
|
|
@ -29,8 +32,7 @@ public class PropertyMixin {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
Property<?> property = (Property)p_equals_1_;
|
Property<?> property = (Property)p_equals_1_;
|
||||||
/* reference equality is safe here because of interning above */
|
/* reference equality is safe here because of deduplication */
|
||||||
//noinspection StringEquality
|
|
||||||
return this.clazz == property.getValueClass() && this.name == property.getName();
|
return this.clazz == property.getValueClass() && this.name == property.getName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user