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