Compare commits
No commits in common. "1.20" and "eol/1.16" have entirely different histories.
82
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
82
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -4,75 +4,51 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: >-
|
||||
**Need help?** Ask on [Discord](https://discord.gg/rN9Y7caguP) instead of opening an issue.
|
||||
**Note: This issue tracker is not intended for support requests!** If you need help with crashes or other issues, then
|
||||
you should [ask on our Discord server](https://discord.gg/rN9Y7caguP) instead. Unless you are certain that you
|
||||
have found a defect, and you are able to point to where the problem is, you should not open an issue.
|
||||
<br><br>
|
||||
Additionally, please make sure you have done the following:
|
||||
|
||||
**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
|
||||
- **Have you ensured that all of your mods (including ModernFix) are up-to-date?** The latest version of ModernFix
|
||||
can always be found [on Modrinth](https://modrinth.com/mod/modernfix).
|
||||
|
||||
- **Have you used the [search tool](https://github.com/embeddedt/ModernFix/issues) to check whether your issue
|
||||
has already been reported?** If it has been, then consider adding more information to the existing issue instead.
|
||||
|
||||
- **Have you determined the minimum set of instructions to reproduce the issue?** If your problem only occurs
|
||||
with other mods installed, then you should narrow down exactly which mods are causing the issue. Please do not
|
||||
provide your entire list of mods to us and expect that we will be able to figure out the problem.
|
||||
- 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.
|
||||
Use this section to describe the issue you are experiencing in as much depth as possible. The description should
|
||||
explain what behavior you were expecting, and why you believe the issue to be a bug. If the issue you are reporting
|
||||
only occurs with specific mods installed, then provide the name and version of each mod.
|
||||
|
||||
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
|
||||
**Hint:** If you have any screenshots, videos, or other information that you feel is necessary to
|
||||
explain the issue, you can attach them here.
|
||||
- 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. "
|
||||
Provide as much information as possible on how to reproduce this bug. Make sure your instructions are as clear and
|
||||
concise as possible, because other people will need to be able to follow your guide in order to re-create the issue.
|
||||
|
||||
**Hint:** A common way to fill this section out is to write a step-by-step guide.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: diagnostic-info
|
||||
id: log-file
|
||||
attributes:
|
||||
label: Diagnostic Info
|
||||
label: Log File
|
||||
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/`.
|
||||
**Hint:** You can usually find the log files within the folder `.minecraft/logs`. Most often, you will want the `latest.log`
|
||||
file, since that file belongs to the last played session of the game.
|
||||
placeholder: >-
|
||||
Drag-and-drop the log file here.
|
||||
validations:
|
||||
required: true
|
||||
|
|
|
|||
115
.github/workflows/gradle.yml
vendored
115
.github/workflows/gradle.yml
vendored
|
|
@ -11,124 +11,31 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
issues: write
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up JDK 21
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
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
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
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
|
||||
- name: Remove tags for release on other versions
|
||||
if: steps.check_branch.outputs.is_release == 'true'
|
||||
run: ./scripts/tagcleaner.sh
|
||||
- name: Setup project Loom cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.gradle/loom-cache
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle.properties', '**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
- 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:
|
||||
script: |
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const branch = context.ref.replace('refs/heads/', '');
|
||||
const { data: runs } = await github.rest.actions.listWorkflowRuns({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'gradle.yml',
|
||||
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
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
|
|
|||
41
.github/workflows/release.yml
vendored
Normal file
41
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
name: Release ModernFix Artifacts
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: github.repository_owner == 'embeddedt'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
check-latest: true
|
||||
- name: Remove tags for release on other versions
|
||||
run: ./scripts/tagcleaner.sh
|
||||
- name: Build and publish mod to CurseForge & Modrinth
|
||||
run: ./gradlew publishToModSites copyJarToBin
|
||||
env:
|
||||
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
- name: Upload assets to GitHub
|
||||
uses: AButler/upload-release-assets@v3.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"
|
||||
|
|
@ -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`
|
||||
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
|
||||
compatible with Java 17.
|
||||
You must use Java 17 to develop ModernFix as the toolchain requires it. Nonetheless, the 1.16 mod JARs will work on
|
||||
a Minecraft instance with Java 8.
|
||||
|
||||
## Submitting pull requests
|
||||
|
||||
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)
|
||||
and then they will be ported forward.
|
||||
* This project supports many Minecraft versions. Ideally, contributions should be made to the oldest relevant MC version.
|
||||
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,
|
||||
rather than the onus being on other modders and players to backport changes that are needed. Changes to older versions are
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
plugins {
|
||||
id 'com.gradleup.shadow' version '8.3.9'
|
||||
id 'com.github.johnrengelman.shadow'
|
||||
id 'java-library'
|
||||
id 'com.diffplug.spotless'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
|
@ -30,6 +31,7 @@ dependencies {
|
|||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs += '--enable-preview'
|
||||
options.release = 17
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +44,6 @@ shadowJar {
|
|||
// 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'}
|
||||
|
|
@ -51,4 +52,9 @@ shadowJar {
|
|||
include {it.getName() == 'EnvType.class'}
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
removeUnusedImports()
|
||||
}
|
||||
}
|
||||
version = '1.1.4'
|
||||
|
|
|
|||
|
|
@ -90,19 +90,24 @@ public class ClientMixinValidator {
|
|||
}
|
||||
|
||||
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: "
|
||||
return switch (classTarget) {
|
||||
case TypeElement te ->
|
||||
isClientMarked(te);
|
||||
case TypeMirror tm -> {
|
||||
var el = types.asElement(tm);
|
||||
yield el != null ? targetsClient(el) : warn("TypeMirror of " + tm);
|
||||
}
|
||||
// If you're using a dollar sign in class names you are insane
|
||||
case String s -> {
|
||||
var te =
|
||||
elemUtils.getTypeElement(toSourceString(s.split("\\$")[0]));
|
||||
yield te != null ? targetsClient(te) : warn(s);
|
||||
}
|
||||
default ->
|
||||
throw new IllegalArgumentException("Unhandled type: "
|
||||
+ classTarget.getClass() + "\n" + "Stringified contents: "
|
||||
+ classTarget.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isClientMarked(TypeElement te) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import javax.annotation.processing.Processor;
|
|||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedOptions;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
|
@ -25,6 +26,7 @@ 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"})
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||
@AutoService(Processor.class)
|
||||
public class MixinProcessor extends AbstractProcessor {
|
||||
|
||||
|
|
@ -36,11 +38,6 @@ public class MixinProcessor extends AbstractProcessor {
|
|||
|
||||
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 {
|
||||
|
|
@ -80,7 +77,7 @@ public class MixinProcessor extends AbstractProcessor {
|
|||
List<String> mixins =
|
||||
annotatedMixins.stream()
|
||||
.map(TypeElement.class::cast)
|
||||
.map(e -> processingEnv.getElementUtils().getBinaryName(e).toString())
|
||||
.map(TypeElement::toString)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
mixinConfigList.putIfAbsent(aliases.get(annotation.getSimpleName().toString()), mixins);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public record MixinConfig(
|
|||
@SerializedName("package")
|
||||
String packageName,
|
||||
String plugin,
|
||||
String compatibilityLevel,
|
||||
String compatabilityLevel,
|
||||
@SerializedName("mixins")
|
||||
List<String> commonMixins,
|
||||
@SerializedName("client")
|
||||
|
|
@ -25,7 +25,7 @@ public record MixinConfig(
|
|||
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",
|
||||
this(true, "0.8", packageName, "org.embeddedt.modernfix.core.ModernFixMixinPlugin", "JAVA_8",
|
||||
commonMixins, clientMixins, InjectorOptions.DEFAULT, OverwriteOptions.DEFAULT);
|
||||
}
|
||||
public record InjectorOptions(int defaultRequire) {
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
org.fury_phoenix.mixinAp.annotation.MixinProcessor,aggregating
|
||||
6
annotations/build.gradle
Normal file
6
annotations/build.gradle
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
plugins {
|
||||
id 'modernfix.common-conventions'
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
version = '1.1.0'
|
||||
|
|
@ -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,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;
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface RequiresMod {
|
||||
String value() default "";
|
||||
}
|
||||
|
|
|
|||
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.6-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
137
buildSrc/src/main/groovy/modernfix.common-conventions.gradle
Normal file
137
buildSrc/src/main/groovy/modernfix.common-conventions.gradle
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
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_1_8
|
||||
|
||||
repositories {
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url "https://modmaven.dev"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "appeng"
|
||||
includeGroup "vazkii.patchouli"
|
||||
includeGroup "mezz.jei"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url "https://cursemaven.com"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "curse.maven"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = 'ParchmentMC'
|
||||
url = 'https://maven.parchmentmc.org'
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "org.parchmentmc.data"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url = 'https://maven.architectury.dev'
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "me.shedaniel"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url = 'https://maven.saps.dev/minecraft'
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "dev.latvian.mods"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = "Fuzs Mod Resources"
|
||||
url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "fuzs"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = "Fabric maven"
|
||||
url = "https://maven.fabricmc.net/"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "net.fabricmc"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = "Mod Menu"
|
||||
url = "https://maven.terraformersmc.com/releases/"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "com.terraformersmc"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url "https://maven.tterrag.com"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "team.chisel.ctm"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
implementation project(":annotations")
|
||||
annotationProcessor project(path: ":annotation-processor", configuration: 'shadow')
|
||||
}
|
||||
|
||||
project.sourceSets {
|
||||
main.resources.srcDirs += [layout.buildDirectory.dir("generated/sources/annotationProcessor/java/main/resources")]
|
||||
}
|
||||
// hack to shut up gradle about the hack to include generated resources
|
||||
tasks {
|
||||
processResources {
|
||||
dependsOn compileJava
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.fork = true
|
||||
options.forkOptions.jvmArgs << '--enable-preview'
|
||||
configure(options) {
|
||||
if (!name.toLowerCase().contains('test')) {
|
||||
options.compilerArgs << "-ArootProject.name=${rootProject.name}" << "-Aproject.name=${project.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
37
common/build.gradle
Normal file
37
common/build.gradle
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
plugins {
|
||||
id "modernfix.mod-common-conventions"
|
||||
}
|
||||
|
||||
architectury {
|
||||
common(rootProject.enabled_platforms.split(","))
|
||||
}
|
||||
|
||||
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("io.github.llamalad7:mixinextras-common:${rootProject.mixinextras_version}"))
|
||||
|
||||
modCompileOnly("dev.latvian.mods:kubejs:${kubejs_version}") {
|
||||
transitive = false
|
||||
}
|
||||
// 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.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
|
@ -13,10 +12,9 @@ import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
|||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
import org.embeddedt.modernfix.resources.ReloadExecutor;
|
||||
import org.embeddedt.modernfix.util.ClassInfoManager;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
// The value here should match an entry in the META-INF/mods.toml file
|
||||
public class ModernFix {
|
||||
|
|
@ -33,7 +31,7 @@ public class ModernFix {
|
|||
// Used to skip computing the blockstate caches twice
|
||||
public static boolean runningFirstInjection = false;
|
||||
|
||||
private static ExecutorService resourceReloadService = null;
|
||||
private static Executor resourceReloadService = null;
|
||||
|
||||
static {
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.dedicated_reload_executor.ReloadExecutor")) {
|
||||
|
|
@ -43,21 +41,10 @@ public class ModernFix {
|
|||
}
|
||||
}
|
||||
|
||||
public static ExecutorService resourceReloadExecutor() {
|
||||
public static Executor resourceReloadExecutor() {
|
||||
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() {
|
||||
INSTANCE = this;
|
||||
|
|
@ -76,19 +63,14 @@ public class ModernFix {
|
|||
ClassInfoManager.clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantValue")
|
||||
public void onServerDead(MinecraftServer server) {
|
||||
/* Clear as much data from the integrated server as possible, in case a mod holds on to it */
|
||||
try {
|
||||
for(ServerLevel level : server.getAllLevels()) {
|
||||
ChunkMap chunkMap = level.getChunkSource().chunkMap;
|
||||
// Null check for mods that replace chunk system
|
||||
if(chunkMap.updatingChunkMap != null)
|
||||
chunkMap.updatingChunkMap.clear();
|
||||
if(chunkMap.visibleChunkMap != null)
|
||||
chunkMap.visibleChunkMap.clear();
|
||||
if(chunkMap.pendingUnloads != null)
|
||||
chunkMap.pendingUnloads.clear();
|
||||
chunkMap.updatingChunkMap.clear();
|
||||
chunkMap.visibleChunkMap.clear();
|
||||
chunkMap.pendingUnloads.clear();
|
||||
}
|
||||
} catch(RuntimeException 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.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.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
|
||||
Minecraft.reserve = new byte[0];
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
|
||||
brandingString = ModernFix.NAME + " " + ModernFixPlatformHooks.INSTANCE.getVersionString();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.dynamic_resources.FireIntegrationHook")) {
|
||||
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
||||
integration.onDynamicResourcesStatusChange(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package org.embeddedt.modernfix.api.entrypoint;
|
||||
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
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 java.util.function.Function;
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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) {
|
||||
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,23 +1,21 @@
|
|||
package org.embeddedt.modernfix.api.helpers;
|
||||
|
||||
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.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.BlockModelRotation;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.ModelState;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
|
||||
import org.embeddedt.modernfix.util.DynamicMap;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class ModelHelpers {
|
||||
|
|
@ -28,7 +26,7 @@ public final class ModelHelpers {
|
|||
* @return a list of all blockstates related to the model
|
||||
*/
|
||||
public static ImmutableList<BlockState> getBlockStateForLocation(ModelResourceLocation location) {
|
||||
Optional<Block> blockOpt = BuiltInRegistries.BLOCK.getOptional(new ResourceLocation(location.getNamespace(), location.getPath()));
|
||||
Optional<Block> blockOpt = Registry.BLOCK.getOptional(new ResourceLocation(location.getNamespace(), location.getPath()));
|
||||
if(blockOpt.isPresent())
|
||||
return ModelBakeryHelpers.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), location);
|
||||
else
|
||||
|
|
@ -54,34 +52,4 @@ public final class ModelHelpers {
|
|||
public static Map<ResourceLocation, BakedModel> createFakeTopLevelMap(BiFunction<ResourceLocation, ModelState, BakedModel> modelGetter) {
|
||||
return new DynamicMap<>(location -> modelGetter.apply(location, BlockModelRotation.X0_Y0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a ModelBaker for mods to use.
|
||||
* @param bakery the ModelBakery supplied to your integration
|
||||
* @return an appropriate ModelBaker
|
||||
*/
|
||||
public static ModelBaker adaptBakery(ModelBakery bakery) {
|
||||
return new ModelBaker() {
|
||||
@Override
|
||||
public UnbakedModel getModel(ResourceLocation resourceLocation) {
|
||||
return bakery.getModel(resourceLocation);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel bake(ResourceLocation resourceLocation, ModelState 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;
|
||||
|
||||
public class BlockStateCacheHandler {
|
||||
public static void invalidateCache() {
|
||||
public static void rebuildParallel(boolean force) {
|
||||
synchronized (BlockBehaviour.BlockStateBase.class) {
|
||||
for (BlockState blockState : Block.BLOCK_STATE_REGISTRY) {
|
||||
((IBlockState)blockState).clearCache();
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
|
@ -16,7 +14,6 @@ import java.util.*;
|
|||
*/
|
||||
public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S> {
|
||||
private final Map<Property<?>, Comparable<?>>[] keys;
|
||||
private Map<Map<Property<?>, Comparable<?>>, S> fastLookup;
|
||||
private final Object[] values;
|
||||
private int usedSlots;
|
||||
public FakeStateMap(int numStates) {
|
||||
|
|
@ -37,39 +34,22 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
|
|||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return getFastLookup().containsKey(o);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return getFastLookup().containsValue(o);
|
||||
}
|
||||
|
||||
@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;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public S get(Object o) {
|
||||
return getFastLookup().get(o);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
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;
|
||||
values[usedSlots] = s;
|
||||
usedSlots++;
|
||||
|
|
@ -90,58 +70,49 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
|
|||
|
||||
@Override
|
||||
public void clear() {
|
||||
for(int i = 0; i < usedSlots; i++) {
|
||||
for(int i = 0; i < this.keys.length; i++) {
|
||||
this.keys[i] = null;
|
||||
this.values[i] = null;
|
||||
}
|
||||
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
|
||||
@Override
|
||||
public Set<Map<Property<?>, Comparable<?>>> keySet() {
|
||||
return new AbstractSet<>() {
|
||||
@Override
|
||||
public Iterator<Map<Property<?>, Comparable<?>>> iterator() {
|
||||
return keys.length == usedSlots ? Iterators.forArray(keys) : asList(keys).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return usedSlots;
|
||||
}
|
||||
};
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<S> values() {
|
||||
return (Collection<S>)asList(values);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<Map<Property<?>, Comparable<?>>, S>> entrySet() {
|
||||
return new AbstractSet<>() {
|
||||
return new Set<Entry<Map<Property<?>, Comparable<?>>, S>>() {
|
||||
@Override
|
||||
public int size() {
|
||||
return usedSlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return FakeStateMap.this.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<Entry<Map<Property<?>, Comparable<?>>, S>> iterator() {
|
||||
return new Iterator<>() {
|
||||
return new Iterator<Entry<Map<Property<?>, Comparable<?>>, S>>() {
|
||||
int currentIdx = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return currentIdx < usedSlots;
|
||||
|
|
@ -149,14 +120,61 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
|
|||
|
||||
@Override
|
||||
public Entry<Map<Property<?>, Comparable<?>>, S> next() {
|
||||
if (currentIdx >= usedSlots)
|
||||
if(currentIdx >= usedSlots)
|
||||
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++;
|
||||
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,68 @@
|
|||
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();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package org.embeddedt.modernfix.command;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.structure.CachingStructureManager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static net.minecraft.commands.Commands.*;
|
||||
|
||||
public class ModernFixCommands {
|
||||
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||
dispatcher.register(literal("modernfix")
|
||||
.then(literal("upgradeStructures")
|
||||
.requires(source -> source.hasPermission(3))
|
||||
.executes(context -> {
|
||||
ServerLevel level = context.getSource().getLevel();
|
||||
if(level == null) {
|
||||
context.getSource().sendFailure(new TextComponent("Couldn't find server level"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResourceManager manager = level.getServer().resources.getResourceManager();
|
||||
Collection<ResourceLocation> structures = manager.listResources("structures", p -> p.endsWith(".nbt"));
|
||||
int upgradedNum = 0;
|
||||
Pattern pathPattern = Pattern.compile("^structures/(.*)\\.nbt$");
|
||||
for(ResourceLocation found : structures) {
|
||||
upgradedNum++;
|
||||
Matcher matcher = pathPattern.matcher(found.getPath());
|
||||
if(!matcher.matches())
|
||||
continue;
|
||||
ResourceLocation structureLocation = new ResourceLocation(found.getNamespace(), matcher.group(1));
|
||||
try(Resource resource = manager.getResource(found)) {
|
||||
CachingStructureManager.readStructureTag(structureLocation, level.getServer().getFixerUpper(), resource.getInputStream());
|
||||
context.getSource().sendSuccess(new TextComponent("checked " + structureLocation + " (" + upgradedNum + "/" + structures.size() + ")"), false);
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Couldn't upgrade structure " + found, e);
|
||||
context.getSource().sendFailure(new TextComponent("error reading " + structureLocation + " (" + upgradedNum + "/" + structures.size() + ")"));
|
||||
}
|
||||
}
|
||||
|
||||
context.getSource().sendSuccess(new TextComponent("All structures upgraded"), false);
|
||||
|
||||
return 1;
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,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,19 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.concurrency;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
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;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(targets = { "net/minecraft/client/renderer/RenderType$CompositeRenderType$EqualsStrategy"})
|
||||
@ClientOnlyMixin
|
||||
public class RenderTypeEqualsStrategyMixin {
|
||||
@Redirect(method = "equals(Lnet/minecraft/client/renderer/RenderType$CompositeRenderType;Lnet/minecraft/client/renderer/RenderType$CompositeRenderType;)Z", at = @At(value = "INVOKE", target = "Ljava/util/Objects;equals(Ljava/lang/Object;Ljava/lang/Object;)Z", ordinal = 0))
|
||||
private boolean alsoCheckName(Object a, Object b, RenderType.CompositeRenderType type1, RenderType.CompositeRenderType type2) {
|
||||
boolean supposedlyEqual = Objects.equals(a, b);
|
||||
return supposedlyEqual && Objects.equals(type1.name, type2.name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.concurrency;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(RenderType.CompositeRenderType.class)
|
||||
@ClientOnlyMixin
|
||||
public class RenderTypeMixin {
|
||||
@Shadow @Final private static ObjectOpenCustomHashSet<RenderType.CompositeRenderType> INSTANCES;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason synchronize, can be accessed by multiple mods during modloading
|
||||
*/
|
||||
@Overwrite
|
||||
private static RenderType.CompositeRenderType memoize(String name, VertexFormat format, int drawMode, int bufferSize, boolean useDelegate, boolean needsSorting, RenderType.CompositeState renderState) {
|
||||
synchronized (INSTANCES){
|
||||
return INSTANCES.addOrGet(new RenderType.CompositeRenderType(name, format, drawMode, bufferSize, useDelegate, needsSorting, renderState));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.concurrency;
|
||||
|
||||
import net.minecraft.tags.StaticTagHelper;
|
||||
import net.minecraft.tags.TagCollection;
|
||||
import net.minecraft.tags.TagContainer;
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(StaticTagHelper.class)
|
||||
public class StaticTagHelperMixin<T> {
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Shadow @Mutable
|
||||
@Final private List wrappers;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void useCOWArrayList(Function<TagContainer, TagCollection<T>> function, CallbackInfo ci) {
|
||||
this.wrappers = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.item_cache_flag;
|
||||
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
/**
|
||||
* Remove emptyCacheFlag from ItemStack, as Mojang did in 1.20 due to <a href="https://bugs.mojang.com/browse/MC-258939">MC-258939</a>.
|
||||
*/
|
||||
@Mixin(ItemStack.class)
|
||||
public class ItemStackMixin {
|
||||
@Shadow @Final @Deprecated private Item item;
|
||||
|
||||
/**
|
||||
* @author embeddedt, Mojang
|
||||
* @reason avoid getItem()
|
||||
*/
|
||||
@Redirect(method = "isEmpty", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;getItem()Lnet/minecraft/world/item/Item;"))
|
||||
private Item getItemDirect(ItemStack stack) {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/world/item/ItemStack;emptyCacheFlag:Z"))
|
||||
private boolean checkEmptyDirect(ItemStack stack) {
|
||||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt, Mojang
|
||||
* @reason flag is no longer used
|
||||
*/
|
||||
@Overwrite
|
||||
private void updateEmptyCacheFlag() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.mc218112;
|
||||
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
@Mixin(SynchedEntityData.class)
|
||||
@ClientOnlyMixin
|
||||
public abstract class SynchedEntityDataMixin_Client {
|
||||
@Shadow @Final private ReadWriteLock lock;
|
||||
|
||||
@Shadow private boolean isDirty;
|
||||
|
||||
@Shadow protected abstract <T> void assignValue(SynchedEntityData.DataItem<T> target, SynchedEntityData.DataItem<?> source);
|
||||
|
||||
@Shadow @Final private Entity entity;
|
||||
|
||||
@Shadow @Final private Map<Integer, SynchedEntityData.DataItem<?>> itemsById;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason always unlock
|
||||
*/
|
||||
@Overwrite
|
||||
public void assignValues(List<SynchedEntityData.DataItem<?>> entries) {
|
||||
this.lock.writeLock().lock();
|
||||
try {
|
||||
for(SynchedEntityData.DataItem<?> dataentry : entries) {
|
||||
SynchedEntityData.DataItem<?> dataentry1 = this.itemsById.get(dataentry.getAccessor().getId());
|
||||
if (dataentry1 != null) {
|
||||
this.assignValue(dataentry1, dataentry);
|
||||
this.entity.onSyncedDataUpdated(dataentry.getAccessor());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lock.writeLock().unlock();
|
||||
}
|
||||
this.isDirty = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.packet_leak;
|
||||
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
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;
|
||||
|
||||
@Mixin(ClientboundCustomPayloadPacket.class)
|
||||
@ClientOnlyMixin
|
||||
public class SCustomPayloadPlayPacketMixin {
|
||||
@Shadow private FriendlyByteBuf data;
|
||||
|
||||
private boolean needsRelease;
|
||||
|
||||
@Inject(method = "<init>(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/network/FriendlyByteBuf;)V", at = @At("RETURN"))
|
||||
private void markNotOwned(ResourceLocation pIdentifier, FriendlyByteBuf pData, CallbackInfo ci) {
|
||||
this.needsRelease = false;
|
||||
}
|
||||
|
||||
@Inject(method = "read", at = @At("RETURN"))
|
||||
private void markOwned(FriendlyByteBuf p_148837_1_, CallbackInfo ci) {
|
||||
this.needsRelease = true;
|
||||
}
|
||||
|
||||
@Redirect(method = "handle(Lnet/minecraft/network/protocol/game/ClientGamePacketListener;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ClientGamePacketListener;handleCustomPayload(Lnet/minecraft/network/protocol/game/ClientboundCustomPayloadPacket;)V"))
|
||||
private void handleAndFree(ClientGamePacketListener instance, ClientboundCustomPayloadPacket sCustomPayloadPlayPacket) {
|
||||
/* in 1.16, this method creates a copy inside it, but does not handle freeing correctly. fixing that breaks mods though */
|
||||
instance.handleCustomPayload(sCustomPayloadPlayPacket);
|
||||
if(this.needsRelease)
|
||||
this.data.release(); /* free our own copy of the data if needed */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import org.embeddedt.modernfix.duck.IPaperChunkHolder;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(ChunkHolder.class)
|
||||
public abstract class ChunkHolderMixin implements IPaperChunkHolder {
|
||||
|
||||
@Shadow public abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getFutureIfPresentUnchecked(ChunkStatus arg);
|
||||
|
||||
@Shadow @Final private static List<ChunkStatus> CHUNK_STATUSES;
|
||||
|
||||
public ChunkStatus mfix$getChunkHolderStatus() {
|
||||
for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) {
|
||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getFutureIfPresentUnchecked(curr);
|
||||
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = future.getNow(null);
|
||||
if (either == null || !either.left().isPresent()) {
|
||||
continue;
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ChunkAccess mfix$getAvailableChunkNow() {
|
||||
// TODO can we just getStatusFuture(EMPTY)?
|
||||
for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) {
|
||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getFutureIfPresentUnchecked(curr);
|
||||
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = future.getNow(null);
|
||||
if (either == null || !either.left().isPresent()) {
|
||||
continue;
|
||||
}
|
||||
return either.left().get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ChunkStatus mfix$getNextStatus(ChunkStatus status) {
|
||||
if (status == ChunkStatus.FULL) {
|
||||
return status;
|
||||
}
|
||||
return CHUNK_STATUSES.get(status.getIndex() + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mfix$canAdvanceStatus() {
|
||||
ChunkStatus status = mfix$getChunkHolderStatus();
|
||||
ChunkAccess chunk = mfix$getAvailableChunkNow();
|
||||
return chunk != null && (status == null || chunk.getStatus().isOrAfter(mfix$getNextStatus(status)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
import org.embeddedt.modernfix.duck.IPaperChunkHolder;
|
||||
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.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
||||
@Mixin(ChunkMap.class)
|
||||
public class ChunkMapMixin {
|
||||
@Shadow @Final private BlockableEventLoop<Runnable> mainThreadExecutor;
|
||||
|
||||
@Shadow @Final private ServerLevel level;
|
||||
private Executor mainInvokingExecutor;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void setup(CallbackInfo ci) {
|
||||
MinecraftServer server = this.level.getServer();
|
||||
this.mainInvokingExecutor = (runnable) -> {
|
||||
if(server.isSameThread())
|
||||
runnable.run();
|
||||
else
|
||||
this.mainThreadExecutor.execute(runnable);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/* https://github.com/PaperMC/Paper/blob/ver/1.17.1/patches/server/0752-Fix-chunks-refusing-to-unload-at-low-TPS.patch */
|
||||
@ModifyArg(method = "unpackTicks", 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;
|
||||
}
|
||||
|
||||
/* https://github.com/PaperMC/Paper/blob/master/patches/removed/1.19.2-legacy-chunksystem/0482-Improve-Chunk-Status-Transition-Speed.patch */
|
||||
@ModifyArg(method = "getEntityTickingRangeFuture", 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 useMainInvokingExecutor(Executor executor) {
|
||||
return this.mainInvokingExecutor;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Redirect(method = "scheduleChunkGeneration", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenComposeAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private CompletableFuture skipWorkerIfPossible(CompletableFuture inputFuture, Function function, Executor executor, ChunkHolder holder) {
|
||||
Executor targetExecutor = (runnable) -> {
|
||||
if(((IPaperChunkHolder)holder).mfix$canAdvanceStatus()) {
|
||||
this.mainInvokingExecutor.execute(runnable);
|
||||
return;
|
||||
}
|
||||
executor.execute(runnable);
|
||||
};
|
||||
return inputFuture.thenComposeAsync(function, targetExecutor);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
||||
|
||||
import net.minecraft.util.SortedArraySet;
|
||||
import org.embeddedt.modernfix.annotation.RequiresMod;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
|
|
@ -10,7 +9,6 @@ import java.util.Arrays;
|
|||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(SortedArraySet.class)
|
||||
@RequiresMod("!moonrise")
|
||||
public abstract class SortedArraySetMixin<T> extends AbstractSet<T> {
|
||||
@Shadow private int size;
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ public class MinecraftMixin {
|
|||
}
|
||||
this.level.getChunkSource().lightEngine = new LevelLightEngine(this.level.getChunkSource(), false, false);
|
||||
// clear BE list otherwise they will hold chunks
|
||||
this.level.blockEntityTickers.clear();
|
||||
this.level.blockEntityList.clear();
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Exception clearing level data", e);
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package org.embeddedt.modernfix.common.mixin.core;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.embeddedt.modernfix.duck.ITimeTrackingServer;
|
||||
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(MinecraftServer.class)
|
||||
public class MinecraftServerMixin implements ITimeTrackingServer {
|
||||
private long mfix$lastTickStartTime = -1L;
|
||||
|
||||
@Override
|
||||
public long mfix$getLastTickStartTime() {
|
||||
return mfix$lastTickStartTime;
|
||||
}
|
||||
|
||||
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;tickServer(Ljava/util/function/BooleanSupplier;)V"))
|
||||
private void trackTickTime(CallbackInfo ci) {
|
||||
mfix$lastTickStartTime = Util.getMillis();
|
||||
}
|
||||
}
|
||||
|
|
@ -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,19 @@
|
|||
package org.embeddedt.modernfix.common.mixin.devenv;
|
||||
|
||||
import com.mojang.authlib.minecraft.OfflineSocialInteractions;
|
||||
import com.mojang.authlib.minecraft.SocialInteractionsService;
|
||||
import net.minecraft.client.Minecraft;
|
||||
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;
|
||||
|
||||
@Mixin(Minecraft.class)
|
||||
@ClientOnlyMixin
|
||||
public class MinecraftMixin {
|
||||
@Inject(method = "createSocialInteractions", at = @At("HEAD"), cancellable = true)
|
||||
private void noSocialInteraction(CallbackInfoReturnable<SocialInteractionsService> cir) {
|
||||
cir.setReturnValue(new OfflineSocialInteractions());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package org.embeddedt.modernfix.common.mixin.devenv;
|
||||
|
||||
import com.mojang.text2speech.Narrator;
|
||||
import com.mojang.text2speech.NarratorDummy;
|
||||
import net.minecraft.client.gui.chat.NarratorChatListener;
|
||||
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(NarratorChatListener.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 new NarratorDummy();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = "Ljava/io/PrintStream;println(Ljava/lang/String;)V"))
|
||||
private void dumpStacktrace(String s, int i, CallbackInfoReturnable<CrashReportCategory> cir) {
|
||||
new Exception("ModernFix crash stacktrace").printStackTrace();
|
||||
if(this.exception != null)
|
||||
this.exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@ package org.embeddedt.modernfix.common.mixin.feature.measure_time;
|
|||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.measure_time;
|
||||
|
||||
import com.mojang.datafixers.util.Function4;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Overlay;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.world.level.DataPackConfig;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.world.level.storage.WorldData;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.ModernFixClient;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
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.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(Minecraft.class)
|
||||
@ClientOnlyMixin
|
||||
public class MinecraftMixin {
|
||||
@Shadow @Nullable public Overlay overlay;
|
||||
private long datapackReloadStartTime;
|
||||
|
||||
@Inject(method = "makeServerStem", at = @At(value = "HEAD"))
|
||||
private void recordReloadStart(RegistryAccess.RegistryHolder p_238189_1_, Function<LevelStorageSource.LevelStorageAccess, DataPackConfig> p_238189_2_, Function4<LevelStorageSource.LevelStorageAccess, RegistryAccess.RegistryHolder, ResourceManager, DataPackConfig, WorldData> p_238189_3_, boolean p_238189_4_, LevelStorageSource.LevelStorageAccess p_238189_5_, CallbackInfoReturnable<Minecraft.ServerStem> cir) {
|
||||
datapackReloadStartTime = System.nanoTime();
|
||||
}
|
||||
|
||||
@Inject(method = "makeServerStem", at = @At(value = "RETURN"))
|
||||
private void recordReloadEnd(RegistryAccess.RegistryHolder p_238189_1_, Function<LevelStorageSource.LevelStorageAccess, DataPackConfig> p_238189_2_, Function4<LevelStorageSource.LevelStorageAccess, RegistryAccess.RegistryHolder, ResourceManager, DataPackConfig, WorldData> p_238189_3_, boolean p_238189_4_, LevelStorageSource.LevelStorageAccess p_238189_5_, CallbackInfoReturnable<Minecraft.ServerStem> cir) {
|
||||
float timeSpentReloading = ((float)(System.nanoTime() - datapackReloadStartTime) / 1000000000f);
|
||||
ModernFix.LOGGER.warn("Datapack reload took " + timeSpentReloading + " seconds.");
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
private void onClientTick(CallbackInfo ci) {
|
||||
if(this.overlay == null && ModernFixClient.INSTANCE != null) {
|
||||
ModernFixClient.INSTANCE.onGameLaunchFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,21 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.measure_time;
|
||||
|
||||
import net.minecraft.server.packs.resources.SimpleReloadableResourceManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(SimpleReloadableResourceManager.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
|
||||
*/
|
||||
@Redirect(method = "createReload", at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;isDebugEnabled()Z", remap = false))
|
||||
private boolean enableDebugReloader(Logger logger) {
|
||||
return logger.isDebugEnabled() || ENABLE_DEBUG_RELOADER;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.stalled_chunk_load_detection;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
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.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();
|
||||
}
|
||||
cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ)));
|
||||
} 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,57 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.biome_zoomer;
|
||||
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.biome.FuzzyOffsetBiomeZoomer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(FuzzyOffsetBiomeZoomer.class)
|
||||
public abstract class FuzzyOffsetBiomeZoomerMixin {
|
||||
@Shadow protected static double getFiddledDistance(long seed, int x, int y, int z, double scaleX, double scaleY, double scaleZ) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason use the modern logic that doesn't allocate an array of 8 doubles every time
|
||||
*/
|
||||
@Overwrite
|
||||
public Biome getBiome(long seed, int xIn, int yIn, int zIn, BiomeManager.NoiseBiomeSource biomeReader) {
|
||||
int i = xIn - 2;
|
||||
int j = yIn - 2;
|
||||
int k = zIn - 2;
|
||||
int l = i >> 2;
|
||||
int i1 = j >> 2;
|
||||
int j1 = k >> 2;
|
||||
double d0 = (double)(i & 3) / 4.0D;
|
||||
double d1 = (double)(j & 3) / 4.0D;
|
||||
double d2 = (double)(k & 3) / 4.0D;
|
||||
int k1 = 0;
|
||||
double d3 = Double.POSITIVE_INFINITY;
|
||||
|
||||
for(int l1 = 0; l1 < 8; ++l1) {
|
||||
boolean flag = (l1 & 4) == 0;
|
||||
boolean flag1 = (l1 & 2) == 0;
|
||||
boolean flag2 = (l1 & 1) == 0;
|
||||
int i2 = flag ? l : l + 1;
|
||||
int j2 = flag1 ? i1 : i1 + 1;
|
||||
int k2 = flag2 ? j1 : j1 + 1;
|
||||
double d4 = flag ? d0 : d0 - 1.0D;
|
||||
double d5 = flag1 ? d1 : d1 - 1.0D;
|
||||
double d6 = flag2 ? d2 : d2 - 1.0D;
|
||||
double d7 = getFiddledDistance(seed, i2, j2, k2, d4, d5, d6);
|
||||
if (d3 > d7) {
|
||||
k1 = l1;
|
||||
d3 = d7;
|
||||
}
|
||||
}
|
||||
|
||||
int l2 = (k1 & 4) == 0 ? l : l + 1;
|
||||
int i3 = (k1 & 2) == 0 ? i1 : i1 + 1;
|
||||
int j3 = (k1 & 1) == 0 ? j1 : j1 + 1;
|
||||
return biomeReader.getNoiseBiome(l2, i3, j3);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.boost_worker_count;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
|
||||
@Mixin(Util.class)
|
||||
public class UtilMixin {
|
||||
@ModifyConstant(method = "makeExecutor", constant = @Constant(intValue = 7))
|
||||
private static int useHigherThreadCount(int old) {
|
||||
String requestedMax = System.getProperty("max.bg.threads");
|
||||
if(requestedMax != null) {
|
||||
try {
|
||||
int newMax = Integer.parseInt(requestedMax);
|
||||
if(newMax >= 1 && newMax <= 255)
|
||||
return newMax;
|
||||
} catch(NumberFormatException e) {
|
||||
ModernFix.LOGGER.error("max.bg.threads is not a number");
|
||||
}
|
||||
}
|
||||
return 255;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_model_materials;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.duck.ICachedMaterialsModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(BlockModel.class)
|
||||
@ClientOnlyMixin
|
||||
public class BlockModelMixin {
|
||||
@Shadow @Final @Mutable public Map<String, Either<Material, String>> textureMap;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason detect changes to the texture map, and clear the material cache as needed
|
||||
*/
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void useTrackingTextureMap(CallbackInfo ci) {
|
||||
Map<String, Either<Material, String>> backingMap = this.textureMap;
|
||||
ICachedMaterialsModel cacheHolder = (ICachedMaterialsModel)this;
|
||||
this.textureMap = new Map<String, Either<Material, String>>() {
|
||||
@Override
|
||||
public int size() {
|
||||
return backingMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return backingMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return backingMap.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return backingMap.containsValue(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Either<Material, String> get(Object o) {
|
||||
return backingMap.get(o);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Either<Material, String> put(String s, Either<Material, String> materialStringEither) {
|
||||
Either<Material, String> old = backingMap.put(s, materialStringEither);
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Either<Material, String> remove(Object o) {
|
||||
Either<Material, String> e = backingMap.remove(o);
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends String, ? extends Either<Material, String>> map) {
|
||||
backingMap.putAll(map);
|
||||
cacheHolder.clearMaterialsCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
backingMap.clear();
|
||||
cacheHolder.clearMaterialsCache();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return backingMap.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<Either<Material, String>> values() {
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return backingMap.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<String, Either<Material, String>>> entrySet() {
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return backingMap.entrySet();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_model_materials;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.duck.ICachedMaterialsModel;
|
||||
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;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/* only cache BlockModel to prevent issues with CTM on Fabric */
|
||||
@Mixin(value = {BlockModel.class})
|
||||
@ClientOnlyMixin
|
||||
public class VanillaModelMixin implements ICachedMaterialsModel {
|
||||
private Collection<Material> materialsCache = null;
|
||||
|
||||
@Inject(method = "getMaterials", at = @At("HEAD"), cancellable = true)
|
||||
private void useCachedMaterials(Function<ResourceLocation, UnbakedModel> pModelGetter, Set<Pair<String, String>> pMissingTextureErrors, CallbackInfoReturnable<Collection<Material>> cir) {
|
||||
if(materialsCache != null) {
|
||||
cir.setReturnValue(materialsCache);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "getMaterials", at = @At("RETURN"))
|
||||
private void storeCachedMaterials(Function<ResourceLocation, UnbakedModel> pModelGetter, Set<Pair<String, String>> pMissingTextureErrors, CallbackInfoReturnable<Collection<Material>> cir) {
|
||||
if(materialsCache == null)
|
||||
materialsCache = Collections.unmodifiableCollection(cir.getReturnValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearMaterialsCache() {
|
||||
materialsCache = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_strongholds;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.duck.IServerLevel;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
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.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(ChunkGenerator.class)
|
||||
public class ChunkGeneratorMixin {
|
||||
@Shadow @Final private List<ChunkPos> strongholdPositions;
|
||||
|
||||
@Inject(method = "generateStrongholds", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Lists;newArrayList()Ljava/util/ArrayList;", ordinal = 0, remap = false), cancellable = true)
|
||||
private void useCachedDataIfAvailable(CallbackInfo ci) {
|
||||
ServerLevel level = searchLevel();
|
||||
if(level == null) {
|
||||
ModernFix.LOGGER.error("Can't find server level for " + this);
|
||||
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());
|
||||
this.strongholdPositions.addAll(positions);
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
private ServerLevel searchLevel() {
|
||||
MinecraftServer server = ModernFixPlatformHooks.INSTANCE.getCurrentServer();
|
||||
if(server != null) {
|
||||
ServerLevel ourLevel = null;
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
if (level.getChunkSource().getGenerator() == ((ChunkGenerator) (Object) this)) {
|
||||
ourLevel = level;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ourLevel;
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Inject(method = "generateStrongholds", at = @At("TAIL"))
|
||||
private void saveCachedData(CallbackInfo ci) {
|
||||
if(this.strongholdPositions.size() > 0) {
|
||||
ServerLevel level = searchLevel();
|
||||
if(level != null) {
|
||||
StrongholdLocationCache cache = ((IServerLevel)level).mfix$getStrongholdCache();
|
||||
cache.setChunkPosList(this.strongholdPositions);
|
||||
ModernFix.LOGGER.debug("Saved stronghold cache for dimension {}", level.dimension().location());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_strongholds;
|
||||
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.level.CustomSpawner;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.ServerLevelData;
|
||||
import net.minecraft.world.level.storage.WritableLevelData;
|
||||
import org.embeddedt.modernfix.duck.IServerLevel;
|
||||
import org.embeddedt.modernfix.world.StrongholdLocationCache;
|
||||
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.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
public abstract class ServerLevelMixin extends Level implements IServerLevel {
|
||||
protected ServerLevelMixin(WritableLevelData arg, ResourceKey<Level> arg2, DimensionType arg3, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l) {
|
||||
super(arg, arg2, arg3, supplier, bl, bl2, l);
|
||||
}
|
||||
|
||||
@Shadow public abstract DimensionDataStorage getDataStorage();
|
||||
|
||||
private StrongholdLocationCache mfix$strongholdCache;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void addStrongholdCache(MinecraftServer minecraftServer, Executor executor, LevelStorageSource.LevelStorageAccess arg,
|
||||
ServerLevelData arg2, ResourceKey<Level> arg3, DimensionType arg4, ChunkProgressListener arg5,
|
||||
ChunkGenerator arg6, boolean bl, long l, List<CustomSpawner> list, boolean bl2, CallbackInfo ci) {
|
||||
mfix$strongholdCache = this.getDataStorage().computeIfAbsent(() -> new StrongholdLocationCache((ServerLevel)(Object)this), StrongholdLocationCache.getFileId(this.dimensionType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StrongholdLocationCache mfix$getStrongholdCache() {
|
||||
return mfix$strongholdCache;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_upgraded_structures;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import org.embeddedt.modernfix.structure.CachingStructureManager;
|
||||
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.Redirect;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@Mixin(StructureManager.class)
|
||||
public class StructureManagerMixin {
|
||||
@Shadow @Final private DataFixer fixerUpper;
|
||||
|
||||
@Redirect(method = "loadFromResource", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureManager;readStructure(Ljava/io/InputStream;)Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate;"))
|
||||
private StructureTemplate readViaCache(StructureManager manager, InputStream stream, ResourceLocation arg) throws IOException {
|
||||
return CachingStructureManager.readStructure(arg, this.fixerUpper, stream);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
|
|||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
|
||||
import org.embeddedt.modernfix.registry.LifecycleMap;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
|
|
@ -16,12 +18,16 @@ import java.util.Map;
|
|||
|
||||
@Mixin(MappedRegistry.class)
|
||||
@IgnoreOutsideDev
|
||||
public abstract class MappedRegistryMixin<T> {
|
||||
public abstract class MappedRegistryMixin<T> extends Registry<T> {
|
||||
@Shadow
|
||||
@Final
|
||||
@Mutable
|
||||
private Map<T, Lifecycle> lifecycles;
|
||||
|
||||
protected MappedRegistryMixin(ResourceKey<? extends Registry<T>> resourceKey, Lifecycle lifecycle) {
|
||||
super(resourceKey, lifecycle);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void replaceStorage(CallbackInfo ci) {
|
||||
this.lifecycles = new LifecycleMap<>();
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.compress_biome_container;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ShortMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ShortOpenHashMap;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ChunkBiomeContainer.class)
|
||||
public class MixinBiomeContainer {
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
private Biome[] biomes;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private IdMap<Biome> biomeRegistry;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static int WIDTH_BITS;
|
||||
|
||||
private Biome[] palette;
|
||||
private BitStorage intArray;
|
||||
|
||||
@Inject(method = "<init>(Lnet/minecraft/core/IdMap;[I)V", at = @At("RETURN"), require = 0)
|
||||
private void reinit1(IdMap p_i241970_1_, int[] p_i241970_2_, CallbackInfo ci) {
|
||||
this.createCompact();
|
||||
}
|
||||
|
||||
@Inject(method = "<init>(Lnet/minecraft/core/IdMap;[Lnet/minecraft/world/level/biome/Biome;)V", at = @At("RETURN"))
|
||||
private void reinit2(IdMap p_i241971_1_, Biome[] p_i241971_2_, CallbackInfo ci) {
|
||||
this.createCompact();
|
||||
}
|
||||
|
||||
@Inject(method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/biome/BiomeSource;)V", at = @At("RETURN"))
|
||||
private void reinit3(IdMap p_i241968_1_, ChunkPos p_i241968_2_, BiomeSource p_i241968_3_, CallbackInfo ci) {
|
||||
this.createCompact();
|
||||
}
|
||||
|
||||
@Inject(method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/biome/BiomeSource;[I)V", at = @At("RETURN"))
|
||||
private void reinit4(IdMap p_i241969_1_, ChunkPos p_i241969_2_, BiomeSource p_i241969_3_, int[] p_i241969_4_, CallbackInfo ci) {
|
||||
this.createCompact();
|
||||
}
|
||||
|
||||
private void createCompact() {
|
||||
if (this.intArray != null || this.biomes[0] == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference2ShortOpenHashMap<Biome> paletteTable = this.createPalette();
|
||||
Biome[] paletteIndexed = new Biome[paletteTable.size()];
|
||||
|
||||
for (Reference2ShortMap.Entry<Biome> entry : paletteTable.reference2ShortEntrySet()) {
|
||||
paletteIndexed[entry.getShortValue()] = entry.getKey();
|
||||
}
|
||||
|
||||
int packedIntSize = Math.max(2, Mth.ceillog2(paletteTable.size()));
|
||||
BitStorage integerArray = new BitStorage(packedIntSize, ChunkBiomeContainer.BIOMES_SIZE);
|
||||
|
||||
Biome prevBiome = null;
|
||||
short prevId = -1;
|
||||
|
||||
for (int i = 0; i < this.biomes.length; i++) {
|
||||
Biome biome = this.biomes[i];
|
||||
short id;
|
||||
|
||||
if (prevBiome == biome) {
|
||||
id = prevId;
|
||||
} else {
|
||||
id = paletteTable.getShort(biome);
|
||||
|
||||
if (id < 0) {
|
||||
throw new IllegalStateException("Palette is missing entry: " + biome);
|
||||
}
|
||||
|
||||
prevId = id;
|
||||
prevBiome = biome;
|
||||
}
|
||||
|
||||
integerArray.set(i, id);
|
||||
}
|
||||
|
||||
this.palette = paletteIndexed;
|
||||
this.intArray = integerArray;
|
||||
this.biomes = null;
|
||||
}
|
||||
|
||||
private Reference2ShortOpenHashMap<Biome> createPalette() {
|
||||
Reference2ShortOpenHashMap<Biome> map = new Reference2ShortOpenHashMap<>();
|
||||
map.defaultReturnValue(Short.MIN_VALUE);
|
||||
|
||||
Biome prevObj = null;
|
||||
short id = 0;
|
||||
|
||||
for (Biome obj : this.biomes) {
|
||||
if (obj == prevObj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (map.getShort(obj) < 0) {
|
||||
map.put(obj, id++);
|
||||
}
|
||||
|
||||
prevObj = obj;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author JellySquid
|
||||
* @reason Use paletted lookup
|
||||
*/
|
||||
@Overwrite
|
||||
public int[] writeBiomes() {
|
||||
int size = this.intArray.getSize();
|
||||
int[] array = new int[size];
|
||||
|
||||
for(int i = 0; i < size; ++i) {
|
||||
array[i] = this.biomeRegistry.getId(this.palette[this.intArray.get(i)]);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author JellySquid
|
||||
* @reason Use paletted lookup
|
||||
*/
|
||||
@Overwrite
|
||||
public Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
int x = biomeX & ChunkBiomeContainer.HORIZONTAL_MASK;
|
||||
int y = Mth.clamp(biomeY, 0, ChunkBiomeContainer.VERTICAL_MASK);
|
||||
int z = biomeZ & ChunkBiomeContainer.HORIZONTAL_MASK;
|
||||
|
||||
return this.palette[this.intArray.get(y << WIDTH_BITS + WIDTH_BITS | z << WIDTH_BITS | x)];
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,13 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Mixin(Minecraft.class)
|
||||
@ClientOnlyMixin
|
||||
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))
|
||||
private ExecutorService getResourceReloadExecutor() {
|
||||
@Redirect(method = { "<init>", "makeServerStem", "reloadResourcePacks" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;backgroundExecutor()Ljava/util/concurrent/Executor;", ordinal = 0))
|
||||
private Executor getResourceReloadExecutor() {
|
||||
return ModernFix.resourceReloadExecutor();
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ import java.util.concurrent.Executor;
|
|||
|
||||
@Mixin(MinecraftServer.class)
|
||||
public class MinecraftServerMixin {
|
||||
@ModifyArg(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ReloadableServerResources;loadResources(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/core/RegistryAccess$Frozen;Lnet/minecraft/world/flag/FeatureFlagSet;Lnet/minecraft/commands/Commands$CommandSelection;ILjava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 5)
|
||||
@ModifyArg(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ServerResources;loadResources(Ljava/util/List;Lnet/minecraft/commands/Commands$CommandSelection;ILjava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 3)
|
||||
private Executor getReloadExecutor(Executor asyncExecutor) {
|
||||
return ModernFix.resourceReloadExecutor();
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
|
||||
|
||||
import com.mojang.datafixers.DSL;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.util.datafix.DataFixers;
|
||||
import org.embeddedt.modernfix.dfu.LazyDataFixer;
|
||||
|
|
@ -10,11 +9,9 @@ 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.Set;
|
||||
|
||||
@Mixin(DataFixers.class)
|
||||
public abstract class DataFixersMixin {
|
||||
@Shadow protected static DataFixer createFixerUpper(Set<DSL.TypeReference> set) {
|
||||
@Shadow protected static DataFixer createFixerUpper() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
|
|
@ -24,9 +21,9 @@ public abstract class DataFixersMixin {
|
|||
* Avoid classloading the DFU logic until we actually need it.
|
||||
*/
|
||||
@Inject(method = "createFixerUpper", at = @At("HEAD"), cancellable = true)
|
||||
private static void createLazyFixerUpper(Set<DSL.TypeReference> set, CallbackInfoReturnable<DataFixer> cir) {
|
||||
private static void createLazyFixerUpper(CallbackInfoReturnable<DataFixer> cir) {
|
||||
if(lazyDataFixer == null) {
|
||||
lazyDataFixer = new LazyDataFixer(() -> createFixerUpper(set));
|
||||
lazyDataFixer = new LazyDataFixer(DataFixersMixin::createFixerUpper);
|
||||
cir.setReturnValue(lazyDataFixer);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
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 org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.duck.IModelHoldingBlockState;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||
import org.embeddedt.modernfix.util.DynamicOverridableMap;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
|
|
@ -18,26 +16,30 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Mixin(BlockModelShaper.class)
|
||||
@ClientOnlyMixin
|
||||
public class BlockModelShaperMixin {
|
||||
@Shadow @Final private ModelManager modelManager;
|
||||
|
||||
@Shadow
|
||||
@Shadow @Final @Mutable
|
||||
private Map<BlockState, BakedModel> modelByStateCache;
|
||||
|
||||
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
|
||||
private ThreadLocal<Reference2ReferenceLinkedOpenHashMap<BlockState, BakedModel>> mfix$modelCache = ThreadLocal.withInitial(Reference2ReferenceLinkedOpenHashMap::new);
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void replaceModelMap(CallbackInfo ci) {
|
||||
// replace the backing map for mods which will access it
|
||||
this.modelByStateCache = new DynamicOverridableMap<>(state -> modelManager.getModel(ModelLocationCache.get(state)));
|
||||
// Clear the cached models on blockstate objects
|
||||
for(Block block : BuiltInRegistries.BLOCK) {
|
||||
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
|
||||
if(state instanceof IModelHoldingBlockState modelHolder) {
|
||||
modelHolder.mfix$setModel(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason no need to rebuild vanilla model cache
|
||||
*/
|
||||
@Overwrite
|
||||
public void rebuildCache() {
|
||||
this.mfix$modelCache = ThreadLocal.withInitial(Reference2ReferenceLinkedOpenHashMap::new);
|
||||
}
|
||||
|
||||
private BakedModel cacheBlockModel(BlockState state) {
|
||||
|
|
@ -57,18 +59,18 @@ public class BlockModelShaperMixin {
|
|||
*/
|
||||
@Overwrite
|
||||
public BakedModel getBlockModel(BlockState state) {
|
||||
if(state instanceof IModelHoldingBlockState modelHolder) {
|
||||
BakedModel model = modelHolder.mfix$getModel();
|
||||
Reference2ReferenceLinkedOpenHashMap<BlockState, BakedModel> map = this.mfix$modelCache.get();
|
||||
BakedModel model = map.get(state);
|
||||
|
||||
if(model != null) {
|
||||
return model;
|
||||
}
|
||||
|
||||
model = this.cacheBlockModel(state);
|
||||
modelHolder.mfix$setModel(model);
|
||||
if(model != null) {
|
||||
return model;
|
||||
} else {
|
||||
return this.cacheBlockModel(state);
|
||||
}
|
||||
|
||||
model = this.cacheBlockModel(state);
|
||||
map.putAndMoveToFirst(state, model);
|
||||
if(map.size() > 500) {
|
||||
map.removeLast();
|
||||
}
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import net.minecraft.client.renderer.ItemModelShaper;
|
|||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
|
||||
|
|
@ -33,7 +32,7 @@ public abstract class ItemModelShaperMixin {
|
|||
super();
|
||||
}
|
||||
|
||||
private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation(new ResourceLocation("modernfix", "sentinel"), "sentinel");
|
||||
private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation("modernfix", "sentinel");
|
||||
|
||||
private final DynamicModelCache<Item> mfix$itemModelCache = new DynamicModelCache<>(k -> this.mfix$getModelForItem((Item)k), true);
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
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.CallbackInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ModelManager.class)
|
||||
@ClientOnlyMixin
|
||||
public class ModelManagerMixin {
|
||||
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void injectDummyBakedRegistry(CallbackInfo ci) {
|
||||
if(this.bakedRegistry == null) {
|
||||
// Create a dummy baked registry. This prevents NPEs when mods query block models too early
|
||||
this.bakedRegistry = new HashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,10 @@ package org.embeddedt.modernfix.common.mixin.perf.dynamic_structure_manager;
|
|||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.core.HolderGetter;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
@ -19,17 +17,16 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(StructureTemplateManager.class)
|
||||
@Mixin(StructureManager.class)
|
||||
public class StructureManagerMixin {
|
||||
@Shadow @Final @Mutable
|
||||
private Map<ResourceLocation, Optional<StructureTemplate>> structureRepository;
|
||||
private Map<ResourceLocation, StructureTemplate> structureRepository;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void makeStructuresSafe(ResourceManager arg, LevelStorageSource.LevelStorageAccess arg2, DataFixer dataFixer, HolderGetter<Block> arg3, CallbackInfo ci) {
|
||||
private void makeStructuresSafe(ResourceManager arg, LevelStorageSource.LevelStorageAccess arg2, DataFixer dataFixer, CallbackInfo ci) {
|
||||
/* Structures needing to be reloaded is not a huge issue since we optimize loading them already */
|
||||
Cache<ResourceLocation, Optional<StructureTemplate>> structureCache = CacheBuilder.newBuilder()
|
||||
Cache<ResourceLocation, StructureTemplate> structureCache = CacheBuilder.newBuilder()
|
||||
.softValues()
|
||||
.build();
|
||||
this.structureRepository = structureCache.asMap();
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.faster_font_loading;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import net.minecraft.client.gui.font.providers.LegacyUnicodeBitmapsProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Objective: avoid recomputing locations many times, as well as loading all the font sheets in the constructor
|
||||
* only to do it again later.
|
||||
*/
|
||||
@Mixin(LegacyUnicodeBitmapsProvider.class)
|
||||
@ClientOnlyMixin
|
||||
public abstract class LegacyUnicodeBitmapsProviderMixin {
|
||||
@Shadow protected abstract ResourceLocation getSheetLocation(int i);
|
||||
|
||||
@Shadow @Final private Map<ResourceLocation, NativeImage> textures;
|
||||
private final ResourceLocation[] glyphLocations = new ResourceLocation[256];
|
||||
|
||||
private ResourceLocation currentCharIdx;
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/font/providers/LegacyUnicodeBitmapsProvider;getSheetLocation(I)Lnet/minecraft/resources/ResourceLocation;"))
|
||||
private ResourceLocation storeCurrentCharIdx(LegacyUnicodeBitmapsProvider provider, int i) {
|
||||
ResourceLocation location = getSheetLocation(i);
|
||||
currentCharIdx = location;
|
||||
return location;
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/NativeImage;read(Lcom/mojang/blaze3d/platform/NativeImage$Format;Ljava/io/InputStream;)Lcom/mojang/blaze3d/platform/NativeImage;"))
|
||||
private NativeImage storeLoadedFontSheet(NativeImage.Format format, InputStream stream) throws IOException {
|
||||
NativeImage image = NativeImage.read(format, stream);
|
||||
textures.put(currentCharIdx, image);
|
||||
return image;
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/NativeImage;close()V"))
|
||||
private void skipCloseNativeImage(NativeImage image) {
|
||||
/* we can't close here, as the image has been stored for use later */
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void clearLocation(CallbackInfo ci) {
|
||||
currentCharIdx = null;
|
||||
}
|
||||
|
||||
@Inject(method = "getSheetLocation", at = @At("HEAD"), cancellable = true)
|
||||
private void useCachedLocation(int idx, CallbackInfoReturnable<ResourceLocation> cir) {
|
||||
int cachedIdx = idx / 256;
|
||||
if(cachedIdx >= 0 && cachedIdx < glyphLocations.length && glyphLocations[cachedIdx] != null)
|
||||
cir.setReturnValue(glyphLocations[cachedIdx]);
|
||||
}
|
||||
|
||||
@Inject(method = "getSheetLocation", at = @At("RETURN"))
|
||||
private void saveCachedLocation(int idx, CallbackInfoReturnable<ResourceLocation> cir) {
|
||||
int cachedIdx = idx / 256;
|
||||
if(cachedIdx >= 0 && cachedIdx < glyphLocations.length)
|
||||
glyphLocations[cachedIdx] = cir.getReturnValue();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,10 +5,10 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
|||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransform;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.SimpleBakedModel;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
|
|
@ -24,11 +24,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
@Mixin(value = ItemRenderer.class, priority = 600)
|
||||
@ClientOnlyMixin
|
||||
public abstract class ItemRendererMixin {
|
||||
private ItemDisplayContext transformType;
|
||||
private ItemTransforms.TransformType transformType;
|
||||
private final SimpleItemModelView modelView = new SimpleItemModelView();
|
||||
|
||||
@Inject(method = "render", at = @At("HEAD"))
|
||||
private void markRenderingType(ItemStack itemStack, ItemDisplayContext transformType, boolean leftHand, PoseStack matrixStack, MultiBufferSource buffer, int combinedLight, int combinedOverlay, BakedModel model, CallbackInfo ci) {
|
||||
private void markRenderingType(ItemStack itemStack, ItemTransforms.TransformType transformType, boolean leftHand, PoseStack matrixStack, MultiBufferSource buffer, int combinedLight, int combinedOverlay, BakedModel model, CallbackInfo ci) {
|
||||
this.transformType = transformType;
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ public abstract class ItemRendererMixin {
|
|||
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 == ItemTransforms.TransformType.GUI) {
|
||||
FastItemRenderType type;
|
||||
ItemTransform transform = model.getTransforms().gui;
|
||||
if(transform == ItemTransform.NO_TRANSFORM)
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.faster_texture_loading;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.metadata.animation.AnimationMetadataSection;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
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.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
@Mixin(value = TextureAtlas.class, priority = 600)
|
||||
@ClientOnlyMixin
|
||||
public abstract class TextureAtlasMixin {
|
||||
@Shadow protected abstract ResourceLocation getResourceLocation(ResourceLocation location);
|
||||
|
||||
@Shadow protected abstract Collection<TextureAtlasSprite.Info> getBasicSpriteInfos(ResourceManager resourceManager, Set<ResourceLocation> spriteLocations);
|
||||
|
||||
private Map<ResourceLocation, Pair<Resource, NativeImage>> loadedImages = new ConcurrentHashMap<>();
|
||||
private boolean usingFasterLoad;
|
||||
private Collection<TextureAtlasSprite.Info> storedResults;
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason simplify texture loading by loading whole image once, avoid slow PngInfo code
|
||||
*/
|
||||
@Inject(method = "getBasicSpriteInfos", at = @At("HEAD"))
|
||||
private void loadImages(ResourceManager manager, Set<ResourceLocation> imageLocations, CallbackInfoReturnable<Collection<TextureAtlasSprite.Info>> cir) {
|
||||
usingFasterLoad = ModernFixPlatformHooks.INSTANCE.isLoadingNormally();
|
||||
}
|
||||
|
||||
@Redirect(method = "getBasicSpriteInfos", at = @At(value = "INVOKE", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;", ordinal = 0))
|
||||
private Iterator<?> skipIteration(Set<?> instance, ResourceManager manager, Set<ResourceLocation> imageLocations) {
|
||||
// bail if Forge is erroring to avoid AT crashes
|
||||
if(!usingFasterLoad)
|
||||
return instance.iterator();
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
ConcurrentLinkedQueue<TextureAtlasSprite.Info> results = new ConcurrentLinkedQueue<>();
|
||||
for(ResourceLocation location : imageLocations) {
|
||||
if(MissingTextureAtlasSprite.getLocation().equals(location))
|
||||
continue;
|
||||
futures.add(CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
ResourceLocation fileLocation = this.getResourceLocation(location);
|
||||
Resource resource = manager.getResource(fileLocation);
|
||||
NativeImage image = NativeImage.read(resource.getInputStream());
|
||||
AnimationMetadataSection animData = resource.getMetadata(AnimationMetadataSection.SERIALIZER);
|
||||
if (animData == null) {
|
||||
animData = AnimationMetadataSection.EMPTY;
|
||||
}
|
||||
Pair<Integer, Integer> dimensions = animData.getFrameSize(image.getWidth(), image.getHeight());
|
||||
loadedImages.put(location, Pair.of(resource, image));
|
||||
results.add(new TextureAtlasSprite.Info(location, dimensions.getFirst(), dimensions.getSecond(), animData));
|
||||
} catch(IOException e) {
|
||||
ModernFix.LOGGER.error("Using missing texture, unable to load {} : {}", location, e);
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Unable to parse metadata from {} : {}", location, e);
|
||||
}
|
||||
}, ModernFix.resourceReloadExecutor()));
|
||||
}
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
storedResults = results;
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
@Inject(method = "getBasicSpriteInfos", at = @At("RETURN"))
|
||||
private void injectFastSprites(ResourceManager resourceManager, Set<ResourceLocation> spriteLocations, CallbackInfoReturnable<Collection<TextureAtlasSprite.Info>> cir) {
|
||||
if(usingFasterLoad)
|
||||
cir.getReturnValue().addAll(storedResults);
|
||||
}
|
||||
|
||||
@Inject(method = "prepareToStitch", at = @At("HEAD"))
|
||||
private void initMap(CallbackInfoReturnable<TextureAtlas.Preparations> cir) {
|
||||
loadedImages = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Inject(method = "prepareToStitch", at = @At("RETURN"))
|
||||
private void clearLoadedImages(CallbackInfoReturnable<TextureAtlas.Preparations> cir) {
|
||||
loadedImages = Collections.emptyMap();
|
||||
storedResults = null;
|
||||
}
|
||||
|
||||
@Inject(method = "load(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIII)Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/texture/TextureAtlas;getResourceLocation(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/resources/ResourceLocation;"), cancellable = true)
|
||||
private void loadFromExisting(ResourceManager resourceManager, TextureAtlasSprite.Info spriteInfo, int width, int height, int mipmapLevel, int originX, int originY, CallbackInfoReturnable<TextureAtlasSprite> cir) {
|
||||
if(!usingFasterLoad)
|
||||
return;
|
||||
Pair<Resource, NativeImage> pair = loadedImages.get(spriteInfo.name());
|
||||
if(pair == null) {
|
||||
ModernFix.LOGGER.error("Texture {} was not loaded in early stage", spriteInfo.name());
|
||||
cir.setReturnValue(null);
|
||||
} else {
|
||||
TextureAtlasSprite sprite = null;
|
||||
try {
|
||||
sprite = ModernFixPlatformHooks.INSTANCE.loadTextureAtlasSprite((TextureAtlas)(Object)this, resourceManager, spriteInfo, pair.getFirst(), width, height, originX, originY, mipmapLevel, pair.getSecond());
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Error loading texture {}: {}", spriteInfo.name(), e);
|
||||
} finally {
|
||||
try {
|
||||
pair.getFirst().close();
|
||||
} catch(IOException ignored) {
|
||||
// not much we can do
|
||||
}
|
||||
}
|
||||
cir.setReturnValue(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,22 +18,22 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Mixin(Stitcher.class)
|
||||
@ClientOnlyMixin
|
||||
public class StitcherMixin<T extends Stitcher.Entry> {
|
||||
@Shadow @Final private List<Stitcher.Holder<T>> texturesToBeStitched;
|
||||
public class StitcherMixin {
|
||||
@Shadow @Final private Set<Stitcher.Holder> texturesToBeStitched;
|
||||
|
||||
@Shadow private int storageX;
|
||||
|
||||
@Shadow private int storageY;
|
||||
|
||||
@Shadow @Final private static Comparator<Stitcher.Holder> HOLDER_COMPARATOR;
|
||||
@Shadow @Final private int maxWidth;
|
||||
@Shadow @Final private int maxHeight;
|
||||
|
||||
@Shadow @Final private static Comparator<Stitcher.Holder<?>> HOLDER_COMPARATOR;
|
||||
private List<StbStitcher.LoadableSpriteInfo<T>> loadableSpriteInfos;
|
||||
private List<StbStitcher.LoadableSpriteInfo> loadableSpriteInfos;
|
||||
|
||||
/**
|
||||
* @author embeddedt, SuperCoder79
|
||||
|
|
@ -52,18 +52,18 @@ public class StitcherMixin<T extends Stitcher.Entry> {
|
|||
return;
|
||||
}
|
||||
ci.cancel();
|
||||
ObjectArrayList<Stitcher.Holder<T>> holderList = new ObjectArrayList<>(this.texturesToBeStitched);
|
||||
ObjectArrayList<Stitcher.Holder> holderList = new ObjectArrayList<>(this.texturesToBeStitched);
|
||||
holderList.sort(HOLDER_COMPARATOR);
|
||||
Stitcher.Holder<T>[] aholder = holderList.toArray(new Stitcher.Holder[0]);
|
||||
Stitcher.Holder[] aholder = holderList.toArray(new Stitcher.Holder[0]);
|
||||
|
||||
Pair<Pair<Integer, Integer>, List<StbStitcher.LoadableSpriteInfo<T>>> packingInfo = StbStitcher.packRects(aholder);
|
||||
Pair<Pair<Integer, Integer>, List<StbStitcher.LoadableSpriteInfo>> packingInfo = StbStitcher.packRects(aholder);
|
||||
this.storageX = packingInfo.getFirst().getFirst();
|
||||
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()));
|
||||
throw new StitcherException(aholder[0].spriteInfo, Stream.of(aholder).map(arg -> arg.spriteInfo).collect(ImmutableList.toImmutableList()));
|
||||
}
|
||||
|
||||
this.loadableSpriteInfos = packingInfo.getSecond();
|
||||
|
|
@ -74,12 +74,12 @@ public class StitcherMixin<T extends Stitcher.Entry> {
|
|||
* @reason We setup the image ourselves in the StbStitcher, so we just feed this information back into the vanilla code
|
||||
*/
|
||||
@Inject(method = "gatherSprites", at = @At("HEAD"), cancellable = true)
|
||||
private void gatherSpritesFast(Stitcher.SpriteLoader<T> spriteLoader, CallbackInfo ci) {
|
||||
private void gatherSpritesFast(Stitcher.SpriteLoader spriteLoader, CallbackInfo ci) {
|
||||
if(this.loadableSpriteInfos == null)
|
||||
return;
|
||||
ci.cancel();
|
||||
for(StbStitcher.LoadableSpriteInfo<T> info : loadableSpriteInfos) {
|
||||
spriteLoader.load(info.info, info.x, info.y);
|
||||
for(StbStitcher.LoadableSpriteInfo info : loadableSpriteInfos) {
|
||||
spriteLoader.load(info.info, info.width, info.height, info.x, info.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.fix_loop_spin_waiting;
|
||||
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
// This should fix https://bugs.mojang.com/browse/MC-183518
|
||||
@Mixin(value = BlockableEventLoop.class, priority = 500)
|
||||
public class BlockableEventLoopMixin {
|
||||
private static final long MFIX$TICK_WAIT_TIME = TimeUnit.MILLISECONDS.toNanos(2);
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason yielding the thread is pretty pointless if we're about to park anyway
|
||||
*/
|
||||
@Overwrite
|
||||
protected void waitForTasks() {
|
||||
LockSupport.parkNanos("waiting for tasks", MFIX$TICK_WAIT_TIME);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ public class BooleanPropertyMixin {
|
|||
* There is no point comparing the immutable sets in any two instances of this class, as they will always be
|
||||
* the same.
|
||||
*/
|
||||
@Redirect(method = "equals", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableSet;equals(Ljava/lang/Object;)Z", remap = false), remap = false)
|
||||
@Redirect(method = "equals", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableSet;equals(Ljava/lang/Object;)Z", remap = false))
|
||||
private boolean skipEqualityCheck(ImmutableSet instance, Object object) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,27 +1,30 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.model_optimizations;
|
||||
|
||||
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.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(Property.class)
|
||||
public class PropertyMixin {
|
||||
|
||||
@Shadow @Final private String name;
|
||||
@Shadow @Mutable
|
||||
@Final private String name;
|
||||
|
||||
@Shadow private Integer hashCode;
|
||||
|
||||
@Shadow @Final private Class clazz;
|
||||
|
||||
@ModifyVariable(method = "<init>", at = @At("HEAD"), ordinal = 0, argsOnly = true)
|
||||
private static String internName(String name) {
|
||||
return name.intern();
|
||||
@Redirect(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/state/properties/Property;name:Ljava/lang/String;"))
|
||||
private void internName(Property instance, String name) {
|
||||
this.name = IdentifierCaches.PROPERTY.deduplicate(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason compare hashcodes if generated, use reference equality for speed
|
||||
*/
|
||||
@Overwrite(remap = false)
|
||||
@Overwrite
|
||||
public boolean equals(Object p_equals_1_) {
|
||||
if (this == p_equals_1_) {
|
||||
return true;
|
||||
|
|
@ -29,8 +32,7 @@ public class PropertyMixin {
|
|||
return false;
|
||||
} else {
|
||||
Property<?> property = (Property)p_equals_1_;
|
||||
/* reference equality is safe here because of interning above */
|
||||
//noinspection StringEquality
|
||||
/* reference equality is safe here because of deduplication */
|
||||
return this.clazz == property.getValueClass() && this.name == property.getName();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.model_optimizations;
|
||||
|
||||
import com.mojang.math.Matrix4f;
|
||||
import com.mojang.math.Transformation;
|
||||
import org.joml.Matrix4f;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
@ -19,7 +19,7 @@ public class TransformationMatrixMixin {
|
|||
* @author embeddedt
|
||||
* @reason use cached hashcode if exists
|
||||
*/
|
||||
@Overwrite(remap = false)
|
||||
@Overwrite
|
||||
public int hashCode() {
|
||||
int hash;
|
||||
if(cachedHashCode != null) {
|
||||
|
|
@ -14,7 +14,7 @@ public class MappedRegistryMixin {
|
|||
* The original behavior causes O(n) time complexity for registration.
|
||||
*/
|
||||
@Redirect(
|
||||
method = "registerMapping(ILnet/minecraft/resources/ResourceKey;Ljava/lang/Object;Lcom/mojang/serialization/Lifecycle;)Lnet/minecraft/core/Holder$Reference;",
|
||||
method = "registerMapping(ILnet/minecraft/resources/ResourceKey;Ljava/lang/Object;Lcom/mojang/serialization/Lifecycle;Z)Ljava/lang/Object;",
|
||||
at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/ObjectList;size(I)V", remap = false)
|
||||
)
|
||||
private void setSizeSmart(ObjectList<?> list, int size) {
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.mojang_registry_size;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
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.Map;
|
||||
|
||||
@Mixin(ResourceKey.class)
|
||||
public class ResourceKeyMixin<T> {
|
||||
private static final Map<ResourceLocation, Map<ResourceLocation, ResourceKey<?>>> INTERNING_MAP = new Object2ObjectOpenHashMap<>();
|
||||
@Inject(method = "create(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/resources/ResourceKey;", at = @At("HEAD"), cancellable = true)
|
||||
private static <T> void createEfficient(ResourceLocation parent, ResourceLocation location, CallbackInfoReturnable<ResourceKey<T>> cir) {
|
||||
synchronized (ResourceKey.class) {
|
||||
Map<ResourceLocation, ResourceKey<?>> keys = INTERNING_MAP.computeIfAbsent(parent, k -> new Object2ObjectOpenHashMap<>());
|
||||
ResourceKey<?> key = keys.get(location);
|
||||
if(key == null) {
|
||||
key = new ResourceKey<>(parent, location);
|
||||
keys.put(location, key);
|
||||
}
|
||||
cir.setReturnValue((ResourceKey<T>)key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.nbt_memory_usage;
|
||||
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.embeddedt.modernfix.util.CanonizingStringMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(targets = "net/minecraft/nbt/CompoundTag$1")
|
||||
public class CompoundTag1Mixin {
|
||||
@ModifyVariable(method = "load(Ljava/io/DataInput;ILnet/minecraft/nbt/NbtAccounter;)Lnet/minecraft/nbt/CompoundTag;", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false))
|
||||
private Map<String, Tag> modifyMap(Map<String, Tag> map) {
|
||||
CanonizingStringMap<Tag> newMap = new CanonizingStringMap<>();
|
||||
if(map != null)
|
||||
newMap.putAll(map);
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.nbt_memory_usage;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.embeddedt.modernfix.util.CanonizingStringMap;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
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.Map;
|
||||
|
||||
@Mixin(CompoundTag.class)
|
||||
public class CompoundTagMixin {
|
||||
@Shadow @Final
|
||||
private Map<String, Tag> tags;
|
||||
|
||||
/**
|
||||
* Ensure that the default backing map is a CanonizingStringMap.
|
||||
*/
|
||||
@ModifyArg(method = "<init>()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;<init>(Ljava/util/Map;)V"), index = 0)
|
||||
private static Map<String, Tag> useCanonizingStringMap(Map<String, Tag> incoming) {
|
||||
CanonizingStringMap<Tag> newMap = new CanonizingStringMap<>();
|
||||
if(incoming != null)
|
||||
newMap.putAll(incoming);
|
||||
return newMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason use more efficient method when copying canonizing string map
|
||||
*/
|
||||
@Inject(method = "copy()Lnet/minecraft/nbt/Tag;", at = @At("HEAD"), cancellable = true)
|
||||
public void copyEfficient(CallbackInfoReturnable<Tag> cir) {
|
||||
if(this.tags instanceof CanonizingStringMap) {
|
||||
cir.setReturnValue(new CompoundTag(CanonizingStringMap.deepCopy((CanonizingStringMap<Tag>)this.tags, Tag::copy)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user