Compare commits
131 Commits
5.25.2+1.2
...
1.20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
667ac6c6ee | ||
|
|
2d760eecbb | ||
|
|
292a6aeab3 | ||
|
|
7fbfcf1a92 | ||
|
|
1bcb28a1ad | ||
|
|
d51b0f60a2 | ||
|
|
ab9880159e | ||
|
|
0f94634361 | ||
|
|
f1492cc829 | ||
|
|
0ecee529d7 | ||
|
|
e9bfd96dd9 | ||
|
|
fb9dcf77c6 | ||
|
|
33851c1cb6 | ||
|
|
494203ef5a | ||
|
|
74f76f7305 | ||
|
|
62dbbea083 | ||
|
|
538c52bc2a | ||
|
|
b62eb1845b | ||
|
|
7c45564979 | ||
|
|
f8d2425242 | ||
|
|
50cedfc699 | ||
|
|
f4f596ca0c | ||
|
|
85aab426c5 | ||
|
|
29ff5f152e | ||
|
|
8213a720a3 | ||
|
|
afe3e09a27 | ||
|
|
ae20fa17c9 | ||
|
|
a6c03e9928 | ||
|
|
864c751aea | ||
|
|
f931d5c442 | ||
|
|
55cec86e5f | ||
|
|
4ec8ef753a | ||
|
|
3f22e23565 | ||
|
|
a73dd5ef6a | ||
|
|
653a477060 | ||
|
|
44113d2536 | ||
|
|
1165d3bdd1 | ||
|
|
c73cdc49a4 | ||
|
|
4e3ecf9b6d | ||
|
|
a40363c1fb | ||
|
|
46dd5ecddd | ||
|
|
b765bcb51f | ||
|
|
26bd7116a1 | ||
|
|
4d2f0da1fc | ||
|
|
c2f585da95 | ||
|
|
327c3cd9ff | ||
|
|
c64ca2e54b | ||
|
|
85955ebf75 | ||
|
|
d749205427 | ||
|
|
438ceb1984 | ||
|
|
5acb5115b9 | ||
|
|
37dc9e60eb | ||
|
|
c2191df359 | ||
|
|
d08da1b3c8 | ||
|
|
36f425b8cd | ||
|
|
dc3c379049 | ||
|
|
4ff7d4c554 | ||
|
|
db13f39b30 | ||
|
|
5a9c49f8d4 | ||
|
|
8ee85f2c16 | ||
|
|
2081b63b56 | ||
|
|
94f1fbf4db | ||
|
|
ab8a8068e0 | ||
|
|
79d2b28d5b | ||
|
|
18dc488ab9 | ||
|
|
a9340b2642 | ||
|
|
670e06816b | ||
|
|
53349cbd1a | ||
|
|
1794c81b61 | ||
|
|
dbe9acb3d8 | ||
|
|
22915a91a1 | ||
|
|
1289897004 | ||
|
|
9692da12b4 | ||
|
|
e34a99b38c | ||
|
|
f79eae8b83 | ||
|
|
38288d5e6a | ||
|
|
2050516bf1 | ||
|
|
02f486ebf4 | ||
|
|
9edce9ad91 | ||
|
|
ac8d93d5b9 | ||
|
|
bee4536c1a | ||
|
|
da2206168b | ||
|
|
17f930ea6f | ||
|
|
f23348c6cb | ||
|
|
21cbcb0e04 | ||
|
|
925c7526ee | ||
|
|
30e3deb8e2 | ||
|
|
ee34dcf96e | ||
|
|
49d800ff27 | ||
|
|
15f30b532c | ||
|
|
df06010846 | ||
|
|
696b344ef5 | ||
|
|
e63d99763e | ||
|
|
60850610f9 | ||
|
|
e16179b797 | ||
|
|
784b914a43 | ||
|
|
b9933b1158 | ||
|
|
878b3798f3 | ||
|
|
bc0e9a09fc | ||
|
|
8c34c0de50 | ||
|
|
5a93bc6109 | ||
|
|
8125da7882 | ||
|
|
d699187006 | ||
|
|
cff29149db | ||
|
|
3926f27d33 | ||
|
|
9bc5f06a19 | ||
|
|
a70f76a34d | ||
|
|
4dcdf09a01 | ||
|
|
f26d35070e | ||
|
|
a04266df54 | ||
|
|
2ec6a6afbc | ||
|
|
49f5b527db | ||
|
|
00287612de | ||
|
|
4b18cc2cc6 | ||
|
|
b2ed5b9341 | ||
|
|
a30dd08cd1 | ||
|
|
7420a7c7ab | ||
|
|
3f9148fa62 | ||
|
|
8cc41fa222 | ||
|
|
f06fb8c32e | ||
|
|
6ee15122f9 | ||
|
|
c9843e08bd | ||
|
|
7b47c39e6b | ||
|
|
b26ab375b5 | ||
|
|
9c4da7fa68 | ||
|
|
d64a1c760b | ||
|
|
555213714f | ||
|
|
7f27141a16 | ||
|
|
a8227a964d | ||
|
|
a7a9aac23a | ||
|
|
1176cc98e3 |
82
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
82
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -4,51 +4,75 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: >-
|
||||
**Note: This issue tracker is not intended for support requests!** If you need help with crashes or other issues, then
|
||||
you should [ask on our Discord server](https://discord.gg/rN9Y7caguP) instead. Unless you are certain that you
|
||||
have found a defect, and you are able to point to where the problem is, you should not open an issue.
|
||||
<br><br>
|
||||
Additionally, please make sure you have done the following:
|
||||
**Need help?** Ask on [Discord](https://discord.gg/rN9Y7caguP) instead of opening an issue.
|
||||
|
||||
- **Have you ensured that all of your mods (including ModernFix) are up-to-date?** The latest version of ModernFix
|
||||
can always be found [on Modrinth](https://modrinth.com/mod/modernfix).
|
||||
|
||||
- **Have you used the [search tool](https://github.com/embeddedt/ModernFix/issues) to check whether your issue
|
||||
has already been reported?** If it has been, then consider adding more information to the existing issue instead.
|
||||
|
||||
- **Have you determined the minimum set of instructions to reproduce the issue?** If your problem only occurs
|
||||
with other mods installed, then you should narrow down exactly which mods are causing the issue. Please do not
|
||||
provide your entire list of mods to us and expect that we will be able to figure out the problem.
|
||||
**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: >-
|
||||
Use this section to describe the issue you are experiencing in as much depth as possible. The description should
|
||||
explain what behavior you were expecting, and why you believe the issue to be a bug. If the issue you are reporting
|
||||
only occurs with specific mods installed, then provide the name and version of each mod.
|
||||
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.
|
||||
|
||||
**Hint:** If you have any screenshots, videos, or other information that you feel is necessary to
|
||||
explain the issue, you can attach them here.
|
||||
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 as much information as possible on how to reproduce this bug. Make sure your instructions are as clear and
|
||||
concise as possible, because other people will need to be able to follow your guide in order to re-create the issue.
|
||||
|
||||
**Hint:** A common way to fill this section out is to write a step-by-step guide.
|
||||
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: log-file
|
||||
id: diagnostic-info
|
||||
attributes:
|
||||
label: Log File
|
||||
label: Diagnostic Info
|
||||
description: >-
|
||||
**Hint:** You can usually find the log files within the folder `.minecraft/logs`. Most often, you will want the `latest.log`
|
||||
file, since that file belongs to the last played session of the game.
|
||||
placeholder: >-
|
||||
Drag-and-drop the log file here.
|
||||
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
|
||||
|
|
|
|||
109
.github/workflows/gradle.yml
vendored
109
.github/workflows/gradle.yml
vendored
|
|
@ -11,6 +11,11 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
issues: write
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
|
@ -22,20 +27,108 @@ jobs:
|
|||
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
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/1.') }}
|
||||
cache-read-only: ${{ steps.check_branch.outputs.is_release != 'true' }}
|
||||
gradle-home-cache-cleanup: true
|
||||
- name: Setup project Loom cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.gradle/loom-cache
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle.properties', '**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
- name: 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
|
||||
with:
|
||||
script: |
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const branch = context.ref.replace('refs/heads/', '');
|
||||
const { data: runs } = await github.rest.actions.listWorkflowRuns({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'gradle.yml',
|
||||
branch,
|
||||
status: 'success',
|
||||
per_page: 1
|
||||
});
|
||||
|
||||
const logArgs = runs.workflow_runs.length > 0
|
||||
? `${runs.workflow_runs[0].head_sha}..${context.sha}`
|
||||
: `-1 ${context.sha}`;
|
||||
const log = execSync(`git log ${logArgs} --format=%s%n%b`, { encoding: 'utf8' });
|
||||
|
||||
const issueNumbers = new Set();
|
||||
const pattern = /(?:fix(?:es|ed)?|close[sd]?|resolve[sd]?)\s+#(\d+)/gi;
|
||||
let match;
|
||||
while ((match = pattern.exec(log)) !== null) {
|
||||
issueNumbers.add(parseInt(match[1]));
|
||||
}
|
||||
|
||||
if (issueNumbers.size === 0) {
|
||||
console.log('No fixed issues found in commits');
|
||||
return;
|
||||
}
|
||||
|
||||
const MARKER = '<!-- modernfix-fix-tracker -->';
|
||||
const modVersion = process.env.MOD_VERSION;
|
||||
const mcVersion = process.env.MC_VERSION;
|
||||
const newLine = `- ${modVersion} for Minecraft ${mcVersion}`;
|
||||
|
||||
for (const issueNumber of issueNumbers) {
|
||||
try {
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
const existing = comments.find(c => c.body.includes(MARKER));
|
||||
if (existing) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existing.id,
|
||||
body: existing.body + `\n${newLine}`
|
||||
});
|
||||
console.log(`Updated comment on issue #${issueNumber}`);
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
body: `${MARKER}\nThe fix for this issue has been released in the following versions of ModernFix:\n${newLine}`
|
||||
});
|
||||
console.log(`Created comment on issue #${issueNumber}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`Could not comment on #${issueNumber}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
- name: Upload Artifacts to GitHub
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
|
|
|||
41
.github/workflows/release.yml
vendored
41
.github/workflows/release.yml
vendored
|
|
@ -1,41 +0,0 @@
|
|||
name: Release ModernFix Artifacts
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: github.repository_owner == 'embeddedt'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 21
|
||||
check-latest: true
|
||||
- name: Remove tags for release on other versions
|
||||
run: ./scripts/tagcleaner.sh
|
||||
- name: Build and publish mod to CurseForge & Modrinth
|
||||
run: ./gradlew publishToModSites copyJarToBin
|
||||
env:
|
||||
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
- name: Upload assets to GitHub
|
||||
uses: AButler/upload-release-assets@v3.0
|
||||
with:
|
||||
files: 'bin/*'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Add changelog to release
|
||||
uses: irongut/EditRelease@v1.2.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
id: ${{ github.event.release.id }}
|
||||
replacebody: true
|
||||
files: "CHANGELOG.md"
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
plugins {
|
||||
id 'com.github.johnrengelman.shadow'
|
||||
id 'com.gradleup.shadow' version '8.3.9'
|
||||
id 'java-library'
|
||||
id 'com.diffplug.spotless'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
|
@ -31,7 +30,7 @@ dependencies {
|
|||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.release = 21
|
||||
options.release = 17
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
|
|
@ -52,9 +51,4 @@ shadowJar {
|
|||
include {it.getName() == 'EnvType.class'}
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
removeUnusedImports()
|
||||
}
|
||||
}
|
||||
version = '1.1.4'
|
||||
|
|
|
|||
|
|
@ -90,24 +90,19 @@ public class ClientMixinValidator {
|
|||
}
|
||||
|
||||
private boolean targetsClient(Object classTarget) {
|
||||
return switch (classTarget) {
|
||||
case TypeElement te ->
|
||||
isClientMarked(te);
|
||||
case TypeMirror tm -> {
|
||||
var el = types.asElement(tm);
|
||||
yield el != null ? targetsClient(el) : warn("TypeMirror of " + tm);
|
||||
}
|
||||
// If you're using a dollar sign in class names you are insane
|
||||
case String s -> {
|
||||
var te =
|
||||
elemUtils.getTypeElement(toSourceString(s.split("\\$")[0]));
|
||||
yield te != null ? targetsClient(te) : warn(s);
|
||||
}
|
||||
default ->
|
||||
throw new IllegalArgumentException("Unhandled type: "
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
plugins {
|
||||
id 'modernfix.common-conventions'
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
version = '1.1.0'
|
||||
10
annotations/build.gradle.kts
Normal file
10
annotations/build.gradle.kts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
plugins {
|
||||
id("java")
|
||||
}
|
||||
|
||||
version = "1.1.0"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.embeddedt.modernfix.annotation;
|
||||
|
||||
public enum FeatureLevel {
|
||||
GA, BETA;
|
||||
|
||||
public boolean isAtLeast(FeatureLevel required) {
|
||||
return this.ordinal() >= required.ordinal();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package org.embeddedt.modernfix.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
||||
public @interface RequiresFeatureLevel {
|
||||
FeatureLevel value() default FeatureLevel.GA;
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target(ElementType.TYPE)
|
||||
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
||||
public @interface RequiresMod {
|
||||
String value() default "";
|
||||
}
|
||||
|
|
|
|||
67
build.gradle
67
build.gradle
|
|
@ -1,67 +0,0 @@
|
|||
plugins {
|
||||
id "architectury-plugin" version "3.4-SNAPSHOT"
|
||||
id "dev.architectury.loom" version "1.9-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.25.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
Normal file
210
build.gradle.kts
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
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)
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
plugins {
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
7
buildSrc/build.gradle.kts
Normal file
7
buildSrc/build.gradle.kts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
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 {
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url "https://modmaven.dev"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "appeng"
|
||||
includeGroup "vazkii.patchouli"
|
||||
includeGroup "mezz.jei"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url "https://cursemaven.com"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "curse.maven"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = 'ParchmentMC'
|
||||
url = 'https://maven.parchmentmc.org'
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "org.parchmentmc.data"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url = 'https://maven.architectury.dev'
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "me.shedaniel"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url = 'https://maven.latvian.dev/releases'
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "dev.latvian.mods"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = "Fuzs Mod Resources"
|
||||
url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "fuzs"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = "Fabric maven"
|
||||
url = "https://maven.fabricmc.net/"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "net.fabricmc"
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = "Mod Menu"
|
||||
url = "https://maven.terraformersmc.com/releases/"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "com.terraformersmc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
plugins {
|
||||
id 'modernfix.common-conventions'
|
||||
id 'dev.architectury.loom'
|
||||
}
|
||||
|
||||
loom {
|
||||
silentMojangMappingsLicense()
|
||||
accessWidenerPath = file("${rootDir}/common/src/main/resources/modernfix.accesswidener")
|
||||
mixin {
|
||||
useLegacyMixinAp = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
|
||||
mappings loom.layered() {
|
||||
officialMojangMappings()
|
||||
if(rootProject.hasProperty("parchment_version")) {
|
||||
parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
|
||||
}
|
||||
}
|
||||
implementation project(":annotations")
|
||||
annotationProcessor project(path: ":annotation-processor", configuration: 'shadow')
|
||||
}
|
||||
|
||||
project.sourceSets {
|
||||
main.resources.srcDirs += [layout.buildDirectory.dir("generated/sources/annotationProcessor/java/main/resources")]
|
||||
}
|
||||
// hack to shut up gradle about the hack to include generated resources
|
||||
tasks {
|
||||
processResources {
|
||||
dependsOn compileJava
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
configure(options) {
|
||||
if (!name.toLowerCase().contains('test')) {
|
||||
options.compilerArgs << "-ArootProject.name=${rootProject.name}" << "-Aproject.name=${project.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
plugins {
|
||||
id 'com.matthewprenger.cursegradle'
|
||||
id 'com.modrinth.minotaur'
|
||||
}
|
||||
|
||||
loom {
|
||||
mods {
|
||||
main { // to match the default mod generated for Forge
|
||||
sourceSet project.sourceSets.main
|
||||
sourceSet project(':common').sourceSets.main
|
||||
}
|
||||
}
|
||||
runs {
|
||||
client {
|
||||
vmArgs "-Xmx1G"
|
||||
vmArgs "-Xms1G"
|
||||
property("mixin.debug.export", "true")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def copyJarNameConsistent = tasks.register('copyJarNameConsistent', Copy) {
|
||||
from remapJar // shortcut for createJar.outputs.files
|
||||
into project.file("build/libs")
|
||||
rename { name -> "modernfix-" + project.name + "-latest.jar" }
|
||||
}
|
||||
|
||||
def copyJarToBin = tasks.register('copyJarToBin', Copy) {
|
||||
from remapJar // shortcut for createJar.outputs.files
|
||||
into rootProject.file("bin")
|
||||
mustRunAfter "copyJarNameConsistent"
|
||||
}
|
||||
|
||||
tasks.build.dependsOn(copyJarToBin, copyJarNameConsistent)
|
||||
|
||||
def isBeta = project.version.toString().contains("beta")
|
||||
|
||||
curseforge {
|
||||
if (System.getenv("CURSEFORGE_TOKEN") != null) {
|
||||
apiKey = System.getenv("CURSEFORGE_TOKEN")
|
||||
project {
|
||||
id = "790626"
|
||||
changelog = file("${rootDir}/CHANGELOG.md")
|
||||
changelogType = "markdown"
|
||||
releaseType = isBeta ? "beta" : "release"
|
||||
addGameVersion project.name.capitalize()
|
||||
gameVersionStrings.addAll(supported_minecraft_versions.tokenize(","))
|
||||
mainArtifact remapJar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
token = System.getenv("MODRINTH_TOKEN")
|
||||
projectId = "modernfix" // This can be the project ID or the slug. Either will work!
|
||||
versionType = isBeta ? "beta" : "release" // This is the default -- can also be `beta` or `alpha`
|
||||
uploadFile = remapJar
|
||||
gameVersions = supported_minecraft_versions.tokenize(",")
|
||||
loaders = [project.name]
|
||||
changelog.set(provider { file("${rootDir}/CHANGELOG.md").getText('UTF-8') })
|
||||
}
|
||||
|
||||
tasks.curseforge.dependsOn(rootProject.generateChangelog)
|
||||
tasks.modrinth.dependsOn(rootProject.generateChangelog)
|
||||
|
||||
tasks.register('publishToModSites') {
|
||||
publishToModSites.dependsOn(tasks.modrinth)
|
||||
publishToModSites.dependsOn(tasks.curseforge)
|
||||
}
|
||||
61
buildSrc/src/main/kotlin/GitVersionSource.kt
Normal file
61
buildSrc/src/main/kotlin/GitVersionSource.kt
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
plugins {
|
||||
id "modernfix.mod-common-conventions"
|
||||
}
|
||||
|
||||
architectury {
|
||||
common(rootProject.enabled_platforms.split(","))
|
||||
}
|
||||
|
||||
ext.jei_minecraft_version = "1.20.1" /* 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.
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
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.spark.SparkLaunchProfiler;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.dynamic_resources.FireIntegrationHook")) {
|
||||
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
||||
integration.onDynamicResourcesStatusChange(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resetWorldLoadStateMachine() {
|
||||
numRenderTicks = 0;
|
||||
worldLoadStartTime = -1;
|
||||
recipesUpdated = false;
|
||||
tagsUpdated = false;
|
||||
}
|
||||
|
||||
public void onGameLaunchFinish() {
|
||||
if(gameStartTimeSeconds >= 0)
|
||||
return;
|
||||
gameStartTimeSeconds = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.measure_time.GameLoad"))
|
||||
ModernFix.LOGGER.warn("Game took " + gameStartTimeSeconds + " seconds to start");
|
||||
ModernFixPlatformHooks.INSTANCE.onLaunchComplete();
|
||||
ClassInfoManager.clear();
|
||||
}
|
||||
|
||||
public void onRecipesUpdated() {
|
||||
recipesUpdated = true;
|
||||
}
|
||||
|
||||
public void onTagsUpdated() {
|
||||
tagsUpdated = true;
|
||||
}
|
||||
|
||||
public void onRenderTickEnd() {
|
||||
if(recipesUpdated
|
||||
&& tagsUpdated
|
||||
&& worldLoadStartTime != -1
|
||||
&& Minecraft.getInstance().player != null
|
||||
&& numRenderTicks++ >= 10) {
|
||||
float timeSpentLoading = ((float)(System.nanoTime() - worldLoadStartTime) / 1000000000f);
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.measure_time.WorldLoad")) {
|
||||
ModernFix.LOGGER.warn("Time from main menu to in-game was " + timeSpentLoading + " seconds");
|
||||
ModernFix.LOGGER.warn("Total time to load game and open world was " + (timeSpentLoading + gameStartTimeSeconds) + " seconds");
|
||||
}
|
||||
if (ModernFixPlatformHooks.INSTANCE.modPresent("spark") && ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_world_join.WorldJoin")) {
|
||||
SparkLaunchProfiler.stop("world_join");
|
||||
}
|
||||
resetWorldLoadStateMachine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the IDs match and remap them if not.
|
||||
* @return true if ID remap was needed
|
||||
*/
|
||||
private static boolean compareAndSwitchIds(Class<? extends Entity> eClass, String fieldName, EntityDataAccessor<?> accessor, int newId) {
|
||||
if(accessor.id != newId) {
|
||||
ModernFix.LOGGER.warn("Corrected ID mismatch on {} field {}. Client had {} but server wants {}.",
|
||||
eClass,
|
||||
fieldName,
|
||||
accessor.id,
|
||||
newId);
|
||||
accessor.id = newId;
|
||||
return true;
|
||||
} else {
|
||||
ModernFix.LOGGER.debug("{} {} ID fine: {}", eClass, fieldName, newId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Horrendous hack to allow tracking every synced entity data manager.
|
||||
*
|
||||
* This is to ensure we can perform ID fixup on already constructed managers.
|
||||
*/
|
||||
public static final Set<SynchedEntityData> allEntityDatas = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
|
||||
private static final Field entriesArrayField;
|
||||
static {
|
||||
Field field;
|
||||
try {
|
||||
field = SynchedEntityData.class.getDeclaredField("entriesArray");
|
||||
field.setAccessible(true);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
field = null;
|
||||
}
|
||||
entriesArrayField = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extremely hacky method to detect and correct mismatched entity data parameter IDs on the client and server.
|
||||
*
|
||||
* The technique is far from ideal, but it should detect reliably and also not break already constructed entities.
|
||||
*/
|
||||
public static void handleEntityIDSync(EntityIDSyncPacket packet) {
|
||||
Map<Class<? extends Entity>, List<Pair<String, Integer>>> info = packet.getFieldInfo();
|
||||
boolean fixNeeded = false;
|
||||
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : info.entrySet()) {
|
||||
Class<? extends Entity> eClass = entry.getKey();
|
||||
for(Pair<String, Integer> field : entry.getValue()) {
|
||||
String fieldName = field.getFirst();
|
||||
int newId = field.getSecond();
|
||||
try {
|
||||
Field f = eClass.getDeclaredField(fieldName);
|
||||
f.setAccessible(true);
|
||||
EntityDataAccessor<?> accessor = (EntityDataAccessor<?>)f.get(null);
|
||||
if(compareAndSwitchIds(eClass, fieldName, accessor, newId))
|
||||
fixNeeded = true;
|
||||
} catch(NoSuchFieldException e) {
|
||||
ModernFix.LOGGER.warn("Couldn't find field on {}: {}", eClass, fieldName);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Unexpected exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Now the ID mappings on synced entity data instances are probably all wrong. Fix that. */
|
||||
List<SynchedEntityData> dataEntries;
|
||||
synchronized (allEntityDatas) {
|
||||
if(fixNeeded) {
|
||||
dataEntries = new ArrayList<>(allEntityDatas);
|
||||
for(SynchedEntityData manager : dataEntries) {
|
||||
Int2ObjectOpenHashMap<SynchedEntityData.DataItem<?>> fixedMap = new Int2ObjectOpenHashMap<>();
|
||||
List<SynchedEntityData.DataItem<?>> items = new ArrayList<>(manager.itemsById.values());
|
||||
for(SynchedEntityData.DataItem<?> item : items) {
|
||||
fixedMap.put(item.getAccessor().id, item);
|
||||
}
|
||||
manager.lock.writeLock().lock();
|
||||
try {
|
||||
manager.itemsById.replaceAll((id, parameter) -> fixedMap.get((int)id));
|
||||
if(entriesArrayField != null) {
|
||||
try {
|
||||
SynchedEntityData.DataItem<?>[] dataArray = new SynchedEntityData.DataItem[items.size()];
|
||||
for(int i = 0; i < dataArray.length; i++) {
|
||||
dataArray[i] = fixedMap.get(i);
|
||||
}
|
||||
entriesArrayField.set(manager, dataArray);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
ModernFix.LOGGER.error(e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
manager.lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
allEntityDatas.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void onServerStarted(MinecraftServer server) {
|
||||
if(!ModernFixMixinPlugin.instance.isOptionEnabled("feature.integrated_server_watchdog.IntegratedWatchdog"))
|
||||
return;
|
||||
IntegratedWatchdog watchdog = new IntegratedWatchdog(server);
|
||||
watchdog.start();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
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,24 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.core;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.embeddedt.modernfix.duck.ITimeTrackingServer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(MinecraftServer.class)
|
||||
public class MinecraftServerMixin implements ITimeTrackingServer {
|
||||
private long mfix$lastTickStartTime = -1L;
|
||||
|
||||
@Override
|
||||
public long mfix$getLastTickStartTime() {
|
||||
return mfix$lastTickStartTime;
|
||||
}
|
||||
|
||||
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;tickServer(Ljava/util/function/BooleanSupplier;)V"))
|
||||
private void trackTickTime(CallbackInfo ci) {
|
||||
mfix$lastTickStartTime = Util.getMillis();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.direct_stack_trace;
|
||||
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(CrashReport.class)
|
||||
public class CrashReportMixin {
|
||||
@Shadow @Final private Throwable exception;
|
||||
|
||||
@Inject(method = "addCategory(Ljava/lang/String;I)Lnet/minecraft/CrashReportCategory;", at = @At(value = "INVOKE", target = "Ljava/io/PrintStream;println(Ljava/lang/String;)V"))
|
||||
private void dumpStacktrace(String s, int i, CallbackInfoReturnable<CrashReportCategory> cir) {
|
||||
new Exception("ModernFix crash stacktrace").printStackTrace();
|
||||
if(this.exception != null)
|
||||
this.exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.stalled_chunk_load_detection;
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
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()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
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::load,
|
||||
StrongholdLocationCache::new,
|
||||
StrongholdLocationCache.getFileId(this.dimensionTypeRegistration()));
|
||||
this.chunkSource.getGeneratorState().ensureStructuresGenerated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StrongholdLocationCache mfix$getStrongholdCache() {
|
||||
return mfix$strongholdCache;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
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));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.deduplicate_location;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.dedup.IdentifierCaches;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ResourceLocation.class)
|
||||
public class MixinResourceLocation {
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
protected String namespace;
|
||||
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
protected String path;
|
||||
|
||||
@Inject(method = "<init>([Ljava/lang/String;)V", at = @At("RETURN"))
|
||||
private void reinit(String[] id, CallbackInfo ci) {
|
||||
this.namespace = IdentifierCaches.NAMESPACES.deduplicate(this.namespace);
|
||||
this.path = IdentifierCaches.PATH.deduplicate(this.path);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
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");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.client.renderer.block.model.BlockElementFace;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.dynamicresources.UVController;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
@Mixin(BlockElementFace.Deserializer.class)
|
||||
@ClientOnlyMixin
|
||||
public class BlockElementFaceDeserializerMixin {
|
||||
|
||||
@Redirect(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockElementFace;",
|
||||
at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonDeserializationContext;deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;)Ljava/lang/Object;", ordinal = 0, remap = false))
|
||||
private Object skipUvsForInitialLoad(JsonDeserializationContext context, JsonElement element, Type type) {
|
||||
return UVController.useDummyUv.get() ? UVController.dummyUv : context.deserialize(element, type);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.mojang_registry_size;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ResourceKey.class)
|
||||
public class ResourceKeyMixin<T> {
|
||||
private static final Map<ResourceLocation, Map<ResourceLocation, ResourceKey<?>>> INTERNING_MAP = new Object2ObjectOpenHashMap<>();
|
||||
@Inject(method = "create(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/resources/ResourceKey;", at = @At("HEAD"), cancellable = true)
|
||||
private static <T> void createEfficient(ResourceLocation parent, ResourceLocation location, CallbackInfoReturnable<ResourceKey<T>> cir) {
|
||||
synchronized (ResourceKey.class) {
|
||||
Map<ResourceLocation, ResourceKey<?>> keys = INTERNING_MAP.computeIfAbsent(parent, k -> new Object2ObjectOpenHashMap<>());
|
||||
ResourceKey<?> key = keys.get(location);
|
||||
if(key == null) {
|
||||
key = new ResourceKey<>(parent, location);
|
||||
keys.put(location, key);
|
||||
}
|
||||
cir.setReturnValue((ResourceKey<T>)key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = MinecraftServer.class, priority = 1100)
|
||||
public class MinecraftServerMixin {
|
||||
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"))
|
||||
private void addSpawnChunkTicket(ServerChunkCache cache, TicketType<?> type, ChunkPos pos, int distance, Object o) {
|
||||
// load first chunk
|
||||
cache.getChunk(pos.x, pos.z, ChunkStatus.FULL, true);
|
||||
}
|
||||
|
||||
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;getTickingGenerated()I"), require = 0)
|
||||
private int getGenerated(ServerChunkCache cache) {
|
||||
return 441;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
|
||||
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ServerChunkCache.class)
|
||||
public interface ServerChunkCacheAccessor {
|
||||
@Accessor("distanceManager")
|
||||
DistanceManager getDistanceManager();
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.safety;
|
||||
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.layers.RenderLayer;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(LivingEntityRenderer.class)
|
||||
@ClientOnlyMixin
|
||||
public class LivingEntityRendererMixin {
|
||||
@Shadow @Final @Mutable
|
||||
protected List<RenderLayer<?, ?>> layers;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void synchronizeLayerList(CallbackInfo ci) {
|
||||
/* allows buggy mods to call addLayer concurrently, order is not deterministic but can't fix that */
|
||||
this.layers = Collections.synchronizedList(layers);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package org.embeddedt.modernfix.dedup;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
|
||||
public class ClimateCache {
|
||||
public static final Interner<Climate.Parameter> MFIX_INTERNER = Interners.newStrongInterner();
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package org.embeddedt.modernfix.dedup;
|
||||
|
||||
import it.unimi.dsi.fastutil.Hash;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DeduplicationCache<T> {
|
||||
private final ObjectOpenCustomHashSet<T> pool;
|
||||
|
||||
private int attemptedInsertions = 0;
|
||||
private int deduplicated = 0;
|
||||
|
||||
public DeduplicationCache(Hash.Strategy<T> strategy) {
|
||||
this.pool = new ObjectOpenCustomHashSet<>(strategy);
|
||||
}
|
||||
|
||||
public DeduplicationCache() {
|
||||
this.pool = new ObjectOpenCustomHashSet<>(new Hash.Strategy<T>() {
|
||||
@Override
|
||||
public int hashCode(T o) {
|
||||
return Objects.hashCode(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(T a, T b) {
|
||||
return Objects.equals(a, b);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public synchronized T deduplicate(T item) {
|
||||
this.attemptedInsertions++;
|
||||
|
||||
T result = this.pool.addOrGet(item);
|
||||
|
||||
if (result != item) {
|
||||
this.deduplicated++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized void clearCache() {
|
||||
this.attemptedInsertions = 0;
|
||||
this.deduplicated = 0;
|
||||
|
||||
this.pool.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return String.format("DeduplicationCache ( %d/%d de-duplicated, %d pooled )",
|
||||
this.deduplicated, this.attemptedInsertions, this.pool.size());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package org.embeddedt.modernfix.dedup;
|
||||
|
||||
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
public class IdentifierCaches {
|
||||
public static final DeduplicationCache<String> NAMESPACES = new DeduplicationCache<>();
|
||||
public static final DeduplicationCache<String> PATH = new DeduplicationCache<>();
|
||||
public static final DeduplicationCache<String> PROPERTY = new DeduplicationCache<>();
|
||||
|
||||
public static void printDebug() {
|
||||
ModernFix.LOGGER.info("[[[ Identifier de-duplication statistics ]]]");
|
||||
ModernFix.LOGGER.info("Namespace cache: {}", NAMESPACES);
|
||||
ModernFix.LOGGER.info("Path cache: {}", PATH);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
public interface ICachedMaterialsModel {
|
||||
public void clearMaterialsCache();
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
||||
public interface IChunkGenerator {
|
||||
void mfix$setAssociatedServerLevel(ServerLevel level);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
|
||||
public interface ILevelSave {
|
||||
public void runWorldPersistenceHooks(LevelStorageSource format);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||
|
||||
public interface ISafeBlockGetter {
|
||||
SafeBlockGetter mfix$getSafeBlockGetter();
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import org.embeddedt.modernfix.world.StrongholdLocationCache;
|
||||
|
||||
public interface IServerLevel {
|
||||
StrongholdLocationCache mfix$getStrongholdCache();
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck.reuse_datapacks;
|
||||
|
||||
import net.minecraft.server.ReloadableServerResources;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface ICachingResourceClient {
|
||||
void setCachedResources(ReloadableServerResources r);
|
||||
void setCachedDataPackConfig(Collection<String> c);
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BlockFaceUV;
|
||||
|
||||
public class UVController {
|
||||
public static final ThreadLocal<Boolean> useDummyUv = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
||||
public static final BlockFaceUV dummyUv = new BlockFaceUV(new float[4], 0);
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
package org.embeddedt.modernfix.entity;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class EntityDataIDSyncHandler {
|
||||
private static Map<Class<? extends Entity>, List<Pair<String, Integer>>> fieldsToSyncMap;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void onDatapackSyncEvent(ServerPlayer targetPlayer) {
|
||||
if(targetPlayer != null) {
|
||||
/* Compute the current set of serializer IDs in use and send them */
|
||||
if(fieldsToSyncMap == null) {
|
||||
fieldsToSyncMap = new HashMap<>();
|
||||
Map<Class<? extends Entity>, Integer> entityPoolMap = SynchedEntityData.ENTITY_ID_POOL;
|
||||
List<Field> fieldsToSync = new ArrayList<>();
|
||||
for(Class<? extends Entity> eClass : entityPoolMap.keySet()) {
|
||||
fieldsToSync.clear();
|
||||
try {
|
||||
Field[] classFields = eClass.getDeclaredFields();
|
||||
for(Field field : classFields) {
|
||||
if(!Modifier.isStatic(field.getModifiers()))
|
||||
continue;
|
||||
field.setAccessible(true);
|
||||
Object o = field.get(null);
|
||||
if(o != null && EntityDataAccessor.class.isAssignableFrom(o.getClass())) {
|
||||
fieldsToSync.add(field);
|
||||
}
|
||||
}
|
||||
for(Field field : fieldsToSync) {
|
||||
int id = ((EntityDataAccessor<?>)field.get(null)).id;
|
||||
fieldsToSyncMap.computeIfAbsent(eClass, k -> new ArrayList<>()).add(Pair.of(field.getName(), id));
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Skipping entity ID sync for {}: {}", eClass.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
EntityIDSyncPacket packet = new EntityIDSyncPacket(fieldsToSyncMap);
|
||||
ModernFix.LOGGER.debug("Sending ID correction packet to client with " + fieldsToSyncMap.size() + " classes");
|
||||
ModernFixPlatformHooks.INSTANCE.sendPacket(targetPlayer, packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
package org.embeddedt.modernfix.entity;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.collect.Maps;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderers;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@SuppressWarnings("OptionalAssignedToNull")
|
||||
public class EntityRendererMap implements Map<EntityType<?>, EntityRenderer<?>> {
|
||||
private final Map<EntityType<?>, EntityRendererProvider<?>> rendererProviders;
|
||||
private final LoadingCache<EntityType<?>, Optional<EntityRenderer<?>>> rendererMap;
|
||||
private final EntityRendererProvider.Context context;
|
||||
|
||||
public EntityRendererMap(Map<EntityType<?>, EntityRendererProvider<?>> rendererProviders, EntityRendererProvider.Context context) {
|
||||
this.rendererProviders = rendererProviders;
|
||||
this.context = context;
|
||||
this.rendererMap = CacheBuilder.newBuilder().build(new RenderConstructor());
|
||||
}
|
||||
|
||||
class RenderConstructor extends CacheLoader<EntityType<?>, Optional<EntityRenderer<?>>> {
|
||||
@Override
|
||||
public Optional<EntityRenderer<?>> load(EntityType<?> key) throws Exception {
|
||||
EntityRendererProvider<?> provider = rendererProviders.get(key);
|
||||
if(provider == null)
|
||||
return Optional.empty();
|
||||
synchronized(EntityRenderers.class) {
|
||||
EntityRenderer<?> renderer;
|
||||
try {
|
||||
renderer = provider.create(context);
|
||||
ModernFix.LOGGER.info("Loaded entity {}", BuiltInRegistries.ENTITY_TYPE.getKey(key));
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Failed to create entity model for " + BuiltInRegistries.ENTITY_TYPE.getKey(key) + ":", e);
|
||||
renderer = new ErroredEntityRenderer<>(context);
|
||||
}
|
||||
return Optional.ofNullable(renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return rendererProviders.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rendererProviders.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return rendererProviders.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRenderer<?> get(Object o) {
|
||||
try {
|
||||
Optional<EntityRenderer<?>> renderer = rendererMap.get((EntityType<?>)o);
|
||||
return renderer.orElse(null);
|
||||
} catch (IllegalStateException e) {
|
||||
return null; /* emulate value not being present if recursive load occurs */
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public EntityRenderer<?> put(EntityType<?> entityType, EntityRenderer<?> entityRenderer) {
|
||||
Optional<EntityRenderer<?>> old = rendererMap.getIfPresent(entityType);
|
||||
rendererMap.put(entityType, Optional.ofNullable(entityRenderer));
|
||||
return old != null ? old.orElse(null) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRenderer<?> remove(Object o) {
|
||||
Optional<EntityRenderer<?>> old = rendererMap.getIfPresent(o);
|
||||
rendererMap.invalidate(o);
|
||||
return old != null ? old.orElse(null) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends EntityType<?>, ? extends EntityRenderer<?>> map) {
|
||||
rendererMap.putAll(Maps.transformValues(map, Optional::ofNullable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
rendererMap.invalidateAll();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<EntityType<?>> keySet() {
|
||||
return rendererProviders.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<EntityRenderer<?>> values() {
|
||||
return new AbstractCollection<>() {
|
||||
@Override
|
||||
public Iterator<EntityRenderer<?>> iterator() {
|
||||
return Iterators.transform(Iterators.unmodifiableIterator(rendererProviders.keySet().iterator()), EntityRendererMap.this::get);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return rendererProviders.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class Entry implements Map.Entry<EntityType<?>, EntityRenderer<?>> {
|
||||
private final EntityType<?> key;
|
||||
|
||||
private Entry(EntityType<?> key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityType<?> getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRenderer<?> getValue() {
|
||||
return get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRenderer<?> setValue(EntityRenderer<?> value) {
|
||||
return put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Map.Entry<EntityType<?>, EntityRenderer<?>>> entrySet() {
|
||||
return new AbstractSet<>() {
|
||||
@Override
|
||||
public Iterator<Map.Entry<EntityType<?>, EntityRenderer<?>>> iterator() {
|
||||
return Iterators.transform(Iterators.unmodifiableIterator(rendererProviders.keySet().iterator()), Entry::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
if (o instanceof Map.Entry<?,?> e) {
|
||||
return rendererProviders.containsKey(e.getKey()) && Objects.equals(get(e.getKey()), e.getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return rendererProviders.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package org.embeddedt.modernfix.entity;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
public class ErroredEntityRenderer<T extends Entity> extends EntityRenderer<T> {
|
||||
public ErroredEntityRenderer(EntityRendererProvider.Context arg) {
|
||||
super(arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureLocation(T entity) {
|
||||
return TextureAtlas.LOCATION_BLOCKS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender(T livingEntity, Frustum camera, double camX, double camY, double camZ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(T entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
package org.embeddedt.modernfix.packet;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
public class EntityIDSyncPacket {
|
||||
private Map<Class<? extends Entity>, List<Pair<String, Integer>>> map;
|
||||
|
||||
public EntityIDSyncPacket(Map<Class<? extends Entity>, List<Pair<String, Integer>>> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public Map<Class<? extends Entity>, List<Pair<String, Integer>>> getFieldInfo() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
public EntityIDSyncPacket() {
|
||||
this.map = new HashMap<>();
|
||||
}
|
||||
|
||||
public void serialize(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(map.keySet().size());
|
||||
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : map.entrySet()) {
|
||||
buf.writeUtf(entry.getKey().getName());
|
||||
buf.writeVarInt(entry.getValue().size());
|
||||
for(Pair<String, Integer> field : entry.getValue()) {
|
||||
buf.writeUtf(field.getFirst());
|
||||
buf.writeVarInt(field.getSecond());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static EntityIDSyncPacket deserialize(FriendlyByteBuf buf) {
|
||||
EntityIDSyncPacket self = new EntityIDSyncPacket();
|
||||
int numEntityClasses = buf.readVarInt();
|
||||
for(int i = 0; i < numEntityClasses; i++) {
|
||||
String clzName = buf.readUtf();
|
||||
try {
|
||||
Class<?> clz;
|
||||
try {
|
||||
clz = Class.forName(clzName);
|
||||
} catch(ClassNotFoundException e) {
|
||||
ModernFix.LOGGER.warn("Entity class not found: {}", clzName);
|
||||
break;
|
||||
}
|
||||
if(!Entity.class.isAssignableFrom(clz)) {
|
||||
ModernFix.LOGGER.error("Not an entity: " + clzName);
|
||||
break;
|
||||
}
|
||||
int numFields = buf.readVarInt();
|
||||
for(int j = 0; j < numFields; j++) {
|
||||
String fieldName = buf.readUtf();
|
||||
int id = buf.readVarInt();
|
||||
Field f = clz.getDeclaredField(fieldName);
|
||||
if(!Modifier.isStatic(f.getModifiers()))
|
||||
continue;
|
||||
f.setAccessible(true);
|
||||
if(!EntityDataAccessor.class.isAssignableFrom(f.get(null).getClass())) {
|
||||
ModernFix.LOGGER.error("Not a data accessor field: " + clz + "." + fieldName);
|
||||
continue;
|
||||
}
|
||||
self.map.computeIfAbsent((Class<? extends Entity>)clz, k -> new ArrayList<>()).add(Pair.of(fieldName, id));
|
||||
}
|
||||
} catch(ReflectiveOperationException e) {
|
||||
ModernFix.LOGGER.error("Error deserializing packet", e);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
package org.embeddedt.modernfix.searchtree;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
|
||||
import me.shedaniel.rei.api.common.entry.EntryStack;
|
||||
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
|
||||
import me.shedaniel.rei.impl.client.search.AsyncSearchManager;
|
||||
import me.shedaniel.rei.impl.common.entry.type.EntryRegistryImpl;
|
||||
import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper;
|
||||
import net.minecraft.client.searchtree.RefreshableSearchTree;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class REIBackedSearchTree extends DummySearchTree<ItemStack> {
|
||||
private final AsyncSearchManager searchManager = createSearchManager();
|
||||
|
||||
private final boolean filteringByTag;
|
||||
private String lastSearchText = "";
|
||||
private final List<ItemStack> listCache = new ArrayList<>();
|
||||
|
||||
public REIBackedSearchTree(boolean filteringByTag) {
|
||||
this.filteringByTag = filteringByTag;
|
||||
}
|
||||
@Override
|
||||
public List<ItemStack> search(String pSearchText) {
|
||||
if(true) {
|
||||
return this.searchREI(pSearchText);
|
||||
} else {
|
||||
/* Use the default, dummy implementation */
|
||||
return super.search(pSearchText);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ItemStack> searchREI(String pSearchText) {
|
||||
if(!pSearchText.equals(lastSearchText)) {
|
||||
listCache.clear();
|
||||
this.searchManager.updateFilter(pSearchText);
|
||||
List stacks;
|
||||
try {
|
||||
stacks = this.searchManager.getNow();
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Couldn't search for '" + pSearchText + "'", e);
|
||||
stacks = Collections.emptyList();
|
||||
}
|
||||
for(Object o : stacks) {
|
||||
EntryStack<?> stack;
|
||||
if(o instanceof EntryStack<?>)
|
||||
stack = (EntryStack<?>)o;
|
||||
else if(o instanceof HashedEntryStackWrapper) {
|
||||
stack = ((HashedEntryStackWrapper)o).unwrap();
|
||||
} else {
|
||||
ModernFix.LOGGER.error("Don't know how to handle {}", o.getClass().getName());
|
||||
continue;
|
||||
}
|
||||
if(stack.getType() == VanillaEntryTypes.ITEM) {
|
||||
listCache.add(stack.cheatsAs().getValue());
|
||||
}
|
||||
}
|
||||
lastSearchText = pSearchText;
|
||||
}
|
||||
return listCache;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static AsyncSearchManager createSearchManager() {
|
||||
Method m, normalizeMethod;
|
||||
try {
|
||||
try {
|
||||
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredComplexList");
|
||||
m.setAccessible(true);
|
||||
normalizeMethod = HashedEntryStackWrapper.class.getDeclaredMethod("normalize");
|
||||
normalizeMethod.setAccessible(true);
|
||||
} catch(NoSuchMethodException e) {
|
||||
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredList");
|
||||
m.setAccessible(true);
|
||||
normalizeMethod = EntryStack.class.getDeclaredMethod("normalize");
|
||||
normalizeMethod.setAccessible(true);
|
||||
}
|
||||
final MethodHandle getListMethod = MethodHandles.publicLookup().unreflect(m);
|
||||
final MethodHandle normalize = MethodHandles.publicLookup().unreflect(normalizeMethod);
|
||||
final EntryRegistryImpl registry = (EntryRegistryImpl)EntryRegistry.getInstance();
|
||||
Supplier stackListSupplier = () -> {
|
||||
try {
|
||||
return (List)getListMethod.invokeExact(registry);
|
||||
} catch(Throwable e) {
|
||||
if(e instanceof RuntimeException)
|
||||
throw (RuntimeException)e;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
UnaryOperator normalizeOperator = o -> {
|
||||
try {
|
||||
return normalize.invoke(o);
|
||||
} catch(Throwable e) {
|
||||
if(e instanceof RuntimeException)
|
||||
throw (RuntimeException)e;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
Supplier<Predicate<Boolean>> shouldShowStack = () -> {
|
||||
return Predicates.alwaysTrue();
|
||||
};
|
||||
try {
|
||||
try {
|
||||
// Old constructor taking Supplier as first arg
|
||||
MethodHandle cn = MethodHandles.publicLookup().findConstructor(AsyncSearchManager.class, MethodType.methodType(void.class, Supplier.class, Supplier.class, UnaryOperator.class));
|
||||
return (AsyncSearchManager)cn.invoke(stackListSupplier, shouldShowStack, normalizeOperator);
|
||||
} catch(NoSuchMethodException e) {
|
||||
// New constructor taking Function as first arg
|
||||
MethodHandle cn = MethodHandles.publicLookup().findConstructor(AsyncSearchManager.class, MethodType.methodType(void.class, Function.class, Supplier.class, UnaryOperator.class));
|
||||
return (AsyncSearchManager)cn.invoke((Function<?, ?>)o -> stackListSupplier.get(), shouldShowStack, normalizeOperator);
|
||||
}
|
||||
} catch(Throwable mhThrowable) {
|
||||
throw new ReflectiveOperationException(mhThrowable);
|
||||
}
|
||||
} catch(ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
|
||||
@Override
|
||||
public RefreshableSearchTree<ItemStack> getSearchTree(boolean tag) {
|
||||
return new REIBackedSearchTree(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return ModernFixPlatformHooks.INSTANCE.modPresent("roughlyenoughitems");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "REI";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package org.embeddedt.modernfix.sound;
|
||||
|
||||
import com.mojang.blaze3d.audio.SoundBuffer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SoundBufferCache extends LinkedHashMap<ResourceLocation, CompletableFuture<SoundBuffer>> {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<ResourceLocation, CompletableFuture<SoundBuffer>> eldest) {
|
||||
return super.removeEldestEntry(eldest);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package org.embeddedt.modernfix.tickables;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LoadableTickableObject<T> implements TickableObject {
|
||||
private volatile int ticksInactive = 0;
|
||||
private final int timeout;
|
||||
private final Supplier<T> loader;
|
||||
private final Consumer<T> finalizer;
|
||||
private volatile T theObject = null;
|
||||
|
||||
public LoadableTickableObject(int timeout, Supplier<T> loader, Consumer<T> finalizer) {
|
||||
this(timeout, loader, finalizer, null);
|
||||
}
|
||||
|
||||
public LoadableTickableObject(int timeout, Supplier<T> loader, Consumer<T> finalizer, @Nullable T initialValue) {
|
||||
this.timeout = timeout;
|
||||
this.loader = loader;
|
||||
this.finalizer = finalizer;
|
||||
this.theObject = initialValue;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
synchronized (this) {
|
||||
ticksInactive++;
|
||||
T obj = theObject;
|
||||
if(obj == null) {
|
||||
obj = loader.get();
|
||||
theObject = obj;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public final void tick() {
|
||||
synchronized (this) {
|
||||
ticksInactive++;
|
||||
if(ticksInactive >= this.timeout) {
|
||||
finalizer.accept(theObject);
|
||||
theObject = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package org.embeddedt.modernfix.tickables;
|
||||
|
||||
public interface TickableObject {
|
||||
void tick();
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.tickables;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class TickableObjectManager {
|
||||
private static final List<TickableObject> TICKABLE_OBJECT_LIST = new CopyOnWriteArrayList<>();
|
||||
|
||||
public static void register(TickableObject object) {
|
||||
TICKABLE_OBJECT_LIST.add(object);
|
||||
}
|
||||
|
||||
public static void runTick() {
|
||||
for(TickableObject o : TICKABLE_OBJECT_LIST) {
|
||||
o.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
public enum BakeReason {
|
||||
FREEZE,
|
||||
REMOTE_SNAPSHOT_INJECT,
|
||||
LOCAL_SNAPSHOT_INJECT,
|
||||
REVERT,
|
||||
UNKNOWN;
|
||||
private static BakeReason currentBakeReason = null;
|
||||
private static boolean bakeReasonWarned = false;
|
||||
|
||||
public static BakeReason getCurrentBakeReason() {
|
||||
if(currentBakeReason == null && !bakeReasonWarned) {
|
||||
ModernFix.LOGGER.warn("No bake reason found, mixin probably not applied correctly", new IllegalStateException());
|
||||
bakeReasonWarned = false;
|
||||
}
|
||||
return currentBakeReason;
|
||||
}
|
||||
|
||||
public static void setCurrentBakeReason(BakeReason reason) {
|
||||
currentBakeReason = reason;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Replacement backing map for CompoundTags that interns keys.
|
||||
*/
|
||||
public class CanonizingStringMap<T> extends HashMap<String, T> {
|
||||
private static final Interner<String> KEY_INTERNER = Interners.newWeakInterner();
|
||||
|
||||
private static String intern(String key) {
|
||||
return key != null ? KEY_INTERNER.intern(key) : null;
|
||||
}
|
||||
|
||||
public CanonizingStringMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T put(String key, T value) {
|
||||
return super.put(intern(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends T> m) {
|
||||
if(m.isEmpty())
|
||||
return;
|
||||
HashMap<String, T> tmp = new HashMap<>();
|
||||
m.forEach((k, v) -> tmp.put(intern(k), v));
|
||||
super.putAll(tmp);
|
||||
}
|
||||
|
||||
private void putAllWithoutInterning(Map<? extends String, ? extends T> m) {
|
||||
super.putAll(m);
|
||||
}
|
||||
|
||||
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> incomingMap, Function<T, T> deepCopier) {
|
||||
CanonizingStringMap<T> newMap = new CanonizingStringMap<>();
|
||||
newMap.putAllWithoutInterning(Maps.transformValues(incomingMap, deepCopier));
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.AbstractExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DirectExecutorService extends AbstractExecutorService {
|
||||
private boolean isShutdown;
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
isShutdown = true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
isShutdown = true;
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return isShutdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return isShutdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull Runnable command) {
|
||||
command.run();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.level.dimension.end.EndDragonFight;
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.world.level.storage.WorldData;
|
||||
import net.minecraft.world.level.storage.ServerLevelData;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class DummyServerConfiguration implements WorldData {
|
||||
@Override
|
||||
public WorldDataConfiguration getDataConfiguration() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataConfiguration(WorldDataConfiguration arg) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRemovedFeatureFlags() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasModded() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getKnownServerBrands() {
|
||||
return ImmutableSet.of("forge");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModdedInfo(String name, boolean isModded) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getCustomBossEvents() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomBossEvents(CompoundTag nbt) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLevelData overworldData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelSettings getLevelSettings() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag createTag(RegistryAccess registries, CompoundTag hostPlayerNBT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHardcore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLevelName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameType getGameType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGameType(GameType type) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllowCommands() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Difficulty getDifficulty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDifficulty(Difficulty difficulty) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDifficultyLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDifficultyLocked(boolean locked) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameRules getGameRules() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getLoadedPlayerTag() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EndDragonFight.Data endDragonFightData() {
|
||||
return EndDragonFight.Data.DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEndDragonFightData(EndDragonFight.Data data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldOptions worldGenOptions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlatWorld() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugWorld() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lifecycle worldGenSettingsLifecycle() {
|
||||
return Lifecycle.stable();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Simple forwarding map implementation that allows layering multiple maps together.
|
||||
*/
|
||||
public class LayeredForwardingMap<K, V> implements Map<K, V> {
|
||||
private final Map<K, V>[] layers;
|
||||
|
||||
public LayeredForwardingMap(Map<K, V>[] layers) {
|
||||
if(layers.length < 1)
|
||||
throw new IllegalArgumentException();
|
||||
for(Map<K, V> layer : layers) {
|
||||
if(layer == null)
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.layers = layers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
for(Map<K, V> map : layers) {
|
||||
if(!map.isEmpty())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
for(Map<K, V> map : layers) {
|
||||
if(map.containsKey(key))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
for(Map<K, V> map : layers) {
|
||||
if(map.containsValue(value))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
for(Map<K, V> map : layers) {
|
||||
V value = map.get(key);
|
||||
if(value != null)
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
if(value == null)
|
||||
throw new IllegalArgumentException();
|
||||
V originalValue = null;
|
||||
for(Map<K, V> map : layers) {
|
||||
V oldVal = map.remove(key);
|
||||
if(originalValue == null)
|
||||
originalValue = oldVal;
|
||||
map.put(key, value);
|
||||
}
|
||||
return originalValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
for(Map<K, V> map : layers) {
|
||||
map.remove(key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
|
||||
for(V value : m.values()) {
|
||||
if(value == null)
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
for(Map<K, V> map : layers) {
|
||||
map.putAll(m);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
Set<K> keys = new ObjectOpenHashSet<>();
|
||||
for(Map<K, V> map : layers) {
|
||||
keys.addAll(map.keySet());
|
||||
}
|
||||
return Collections.unmodifiableSet(keys);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
Set<K> keys = keySet();
|
||||
List<V> vals = new ArrayList<>();
|
||||
for(K key : keys) {
|
||||
vals.add(get(key));
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
package org.embeddedt.modernfix.world;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.saveddata.SavedData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StrongholdLocationCache extends SavedData {
|
||||
private List<ChunkPos> chunkPosList;
|
||||
public StrongholdLocationCache() {
|
||||
super();
|
||||
chunkPosList = new ArrayList<>();
|
||||
}
|
||||
|
||||
public List<ChunkPos> getChunkPosList() {
|
||||
return new ArrayList<>(chunkPosList);
|
||||
}
|
||||
|
||||
public void setChunkPosList(List<ChunkPos> positions) {
|
||||
this.chunkPosList = new ArrayList<>(positions);
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
public static StrongholdLocationCache load(CompoundTag arg) {
|
||||
StrongholdLocationCache cache = new StrongholdLocationCache();
|
||||
if(arg.contains("Positions", Tag.TAG_LONG_ARRAY)) {
|
||||
long[] positions = arg.getLongArray("Positions");
|
||||
for(long position : positions) {
|
||||
cache.chunkPosList.add(new ChunkPos(position));
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag save(CompoundTag compoundTag) {
|
||||
long[] serialized = new long[chunkPosList.size()];
|
||||
for(int i = 0; i < chunkPosList.size(); i++) {
|
||||
ChunkPos thePos = chunkPosList.get(i);
|
||||
serialized[i] = thePos.toLong();
|
||||
}
|
||||
compoundTag.putLongArray("Positions", serialized);
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
public static String getFileId(Holder<DimensionType> dimensionType) {
|
||||
return "mfix_strongholds";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
accessWidener v2 named
|
||||
|
||||
accessible field net/minecraft/client/multiplayer/ClientChunkCache storage Lnet/minecraft/client/multiplayer/ClientChunkCache$Storage;
|
||||
accessible field net/minecraft/client/multiplayer/ClientChunkCache lightEngine Lnet/minecraft/world/level/lighting/LevelLightEngine;
|
||||
mutable field net/minecraft/client/multiplayer/ClientChunkCache lightEngine Lnet/minecraft/world/level/lighting/LevelLightEngine;
|
||||
accessible class net/minecraft/client/multiplayer/ClientChunkCache$Storage
|
||||
accessible field net/minecraft/client/multiplayer/ClientChunkCache$Storage chunks Ljava/util/concurrent/atomic/AtomicReferenceArray;
|
||||
|
||||
accessible field net/minecraft/world/level/Level blockEntityTickers Ljava/util/List;
|
||||
|
||||
accessible class net/minecraft/client/renderer/RenderType$CompositeRenderType
|
||||
accessible method net/minecraft/nbt/CompoundTag <init> (Ljava/util/Map;)V
|
||||
|
||||
accessible class net/minecraft/world/level/levelgen/SurfaceRules$Condition
|
||||
accessible class net/minecraft/world/level/levelgen/SurfaceRules$LazyCondition
|
||||
accessible class net/minecraft/world/level/levelgen/SurfaceRules$SequenceRule
|
||||
accessible class net/minecraft/world/level/levelgen/SurfaceRules$SurfaceRule
|
||||
accessible class net/minecraft/world/level/levelgen/SurfaceRules$Context
|
||||
accessible class net/minecraft/world/level/levelgen/DensityFunctions$Marker
|
||||
accessible class net/minecraft/world/level/levelgen/DensityFunctions$Marker$Type
|
||||
accessible method net/minecraft/world/level/levelgen/DensityFunctions$Marker <init> (Lnet/minecraft/world/level/levelgen/DensityFunctions$Marker$Type;Lnet/minecraft/world/level/levelgen/DensityFunction;)V
|
||||
accessible class net/minecraft/world/level/levelgen/DensityFunctions$Mapped
|
||||
accessible class net/minecraft/world/level/levelgen/DensityFunctions$Mapped$Type
|
||||
accessible method net/minecraft/world/level/levelgen/DensityFunctions$Mapped <init> (Lnet/minecraft/world/level/levelgen/DensityFunctions$Mapped$Type;Lnet/minecraft/world/level/levelgen/DensityFunction;DD)V
|
||||
accessible class net/minecraft/world/level/levelgen/DensityFunctions$MulOrAdd
|
||||
accessible class net/minecraft/world/level/levelgen/DensityFunctions$MulOrAdd$Type
|
||||
accessible method net/minecraft/world/level/levelgen/DensityFunctions$MulOrAdd <init> (Lnet/minecraft/world/level/levelgen/DensityFunctions$MulOrAdd$Type;Lnet/minecraft/world/level/levelgen/DensityFunction;DDD)V
|
||||
|
||||
accessible class net/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache
|
||||
accessible class net/minecraft/server/level/ServerChunkCache$MainThreadExecutor
|
||||
accessible field net/minecraft/world/level/block/state/BlockBehaviour properties Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;
|
||||
accessible class net/minecraft/client/renderer/block/model/BlockElementFace$Deserializer
|
||||
accessible class net/minecraft/client/renderer/texture/Stitcher$Holder
|
||||
accessible field net/minecraft/client/renderer/texture/Stitcher$Holder width I
|
||||
accessible field net/minecraft/client/renderer/texture/Stitcher$Holder height I
|
||||
accessible field net/minecraft/network/syncher/EntityDataAccessor id I
|
||||
mutable field net/minecraft/network/syncher/EntityDataAccessor id I
|
||||
accessible class net/minecraft/client/resources/model/ModelBakery$BlockStateDefinitionException
|
||||
accessible field net/minecraft/network/syncher/SynchedEntityData itemsById Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;
|
||||
accessible field net/minecraft/network/syncher/SynchedEntityData ENTITY_ID_POOL Lit/unimi/dsi/fastutil/objects/Object2IntMap;
|
||||
accessible field net/minecraft/network/syncher/SynchedEntityData lock Ljava/util/concurrent/locks/ReadWriteLock;
|
||||
accessible method net/minecraft/Util makeExecutor (Ljava/lang/String;)Ljava/util/concurrent/ExecutorService;
|
||||
accessible field net/minecraft/server/level/ChunkMap updatingChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
||||
accessible field net/minecraft/server/level/ChunkMap visibleChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
||||
accessible field net/minecraft/server/level/ChunkMap pendingUnloads Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
||||
accessible method net/minecraft/resources/ResourceKey <init> (Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)V
|
||||
accessible field net/minecraft/client/renderer/block/model/BlockModel GSON Lcom/google/gson/Gson;
|
||||
accessible class net/minecraft/server/level/ChunkMap$DistanceManager
|
||||
accessible class net/minecraft/client/resources/model/ModelBakery$BakedCacheKey
|
||||
accessible field net/minecraft/client/resources/model/ModelBakery bakedCache Ljava/util/Map;
|
||||
accessible field net/minecraft/client/resources/model/ModelBakery ITEM_MODEL_GENERATOR Lnet/minecraft/client/renderer/block/model/ItemModelGenerator;
|
||||
accessible method net/minecraft/client/resources/model/ModelBakery loadTopLevel (Lnet/minecraft/client/resources/model/ModelResourceLocation;)V
|
||||
accessible field net/minecraft/client/resources/model/ModelBakery topLevelModels Ljava/util/Map;
|
||||
accessible class net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl
|
||||
accessible method net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl <init> (Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V
|
||||
accessible method net/minecraft/client/resources/model/ModelBakery$BakedCacheKey <init> (Lnet/minecraft/resources/ResourceLocation;Lcom/mojang/math/Transformation;Z)V
|
||||
accessible class net/minecraft/world/level/chunk/PalettedContainer$Data
|
||||
accessible field net/minecraft/server/MinecraftServer resources Lnet/minecraft/server/MinecraftServer$ReloadableResources;
|
||||
accessible class net/minecraft/server/MinecraftServer$ReloadableResources
|
||||
accessible method net/minecraft/client/gui/screens/Screen addRenderableWidget (Lnet/minecraft/client/gui/components/events/GuiEventListener;)Lnet/minecraft/client/gui/components/events/GuiEventListener;
|
||||
accessible field net/minecraft/client/KeyMapping ALL Ljava/util/Map;
|
||||
accessible field net/minecraft/client/renderer/block/model/multipart/MultiPart definition Lnet/minecraft/world/level/block/state/StateDefinition;
|
||||
accessible field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
||||
mutable field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
||||
accessible field net/minecraft/client/renderer/entity/EnderDragonRenderer$DragonModel entity Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;
|
||||
accessible method net/minecraft/world/level/block/state/StateDefinition appendPropertyCodec (Lcom/mojang/serialization/MapCodec;Ljava/util/function/Supplier;Ljava/lang/String;Lnet/minecraft/world/level/block/state/properties/Property;)Lcom/mojang/serialization/MapCodec;
|
||||
|
||||
accessible field net/minecraft/server/packs/resources/ProfiledReloadInstance$State preparationNanos Ljava/util/concurrent/atomic/AtomicLong;
|
||||
accessible field net/minecraft/server/packs/resources/ProfiledReloadInstance$State reloadNanos Ljava/util/concurrent/atomic/AtomicLong;
|
||||
|
||||
accessible class net/minecraft/world/item/crafting/Ingredient$Value
|
||||
accessible field net/minecraft/world/item/crafting/Ingredient$TagValue tag Lnet/minecraft/tags/TagKey;
|
||||
accessible field net/minecraft/world/item/crafting/Ingredient$ItemValue item Lnet/minecraft/world/item/ItemStack;
|
||||
accessible class net/minecraft/world/item/crafting/Ingredient$ItemValue
|
||||
accessible class net/minecraft/client/searchtree/SearchRegistry$TreeEntry
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
plugins {
|
||||
id "com.github.johnrengelman.shadow"
|
||||
id 'com.adarshr.test-logger' version '3.2.0'
|
||||
id "modernfix.mod-common-conventions"
|
||||
id "modernfix.platform-conventions"
|
||||
}
|
||||
|
||||
architectury {
|
||||
platformSetupLoomIde()
|
||||
fabric()
|
||||
}
|
||||
|
||||
configurations {
|
||||
common
|
||||
shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files.
|
||||
compileClasspath.extendsFrom common
|
||||
runtimeClasspath.extendsFrom common
|
||||
|
||||
modIncludeImplementation
|
||||
|
||||
include.extendsFrom modIncludeImplementation
|
||||
modImplementation.extendsFrom modIncludeImplementation
|
||||
|
||||
testAgent {
|
||||
canBeConsumed = false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||
testImplementation "net.fabricmc:fabric-loader-junit:${rootProject.fabric_loader_version}"
|
||||
annotationProcessor("io.github.llamalad7:mixinextras-fabric:${rootProject.mixinextras_version}")
|
||||
|
||||
modCompileOnly(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly(fabricApi.module("fabric-model-loading-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly(fabricApi.module("fabric-models-v0", "0.84.0+1.20.1")) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly(fabricApi.module("fabric-data-generation-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
if(project.use_fabric_api_at_runtime.toBoolean()) {
|
||||
modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
|
||||
modImplementation "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||
modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}") { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
} else {
|
||||
modCompileOnly("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
|
||||
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||
}
|
||||
|
||||
// Remove the next line if you don't want to depend on the API
|
||||
// modApi "me.shedaniel:architectury-fabric:${rootProject.architectury_version}"
|
||||
|
||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
||||
testImplementation(shadowCommon(project(path: ":common", configuration: "transformProductionFabric"))) { transitive false }
|
||||
shadowCommon(project(path: ":annotations"))
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:${project.junit_version}"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||
testImplementation("org.junit.platform:junit-platform-launcher")
|
||||
testImplementation("org.assertj:assertj-core:3.19.0")
|
||||
testImplementation("com.google.guava:guava-testlib:21.0")
|
||||
testImplementation("org.mockito:mockito-junit-jupiter:5.3.1")
|
||||
|
||||
testAgent(project("path": ":test_agent", "configuration": "agentJar"))
|
||||
}
|
||||
|
||||
tasks.named("test") {
|
||||
useJUnitPlatform()
|
||||
def runDir = file('test_run')
|
||||
doFirst() {
|
||||
runDir.mkdir()
|
||||
}
|
||||
workingDir = runDir
|
||||
systemProperty 'modernfix.ignoreConfigForTesting', 'true'
|
||||
|
||||
// inject our custom agent to fix #817
|
||||
FileCollection agentFile = configurations.getByName("testAgent")
|
||||
jvmArgs "-javaagent:${agentFile.singleFile.absolutePath}"
|
||||
dependsOn(agentFile)
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand "version": project.version
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
exclude "architectury.common.json"
|
||||
|
||||
configurations = [project.configurations.shadowCommon]
|
||||
archiveClassifier.set("dev-shadow")
|
||||
}
|
||||
|
||||
remapJar {
|
||||
injectAccessWidener = true
|
||||
input.set shadowJar.archiveFile
|
||||
dependsOn shadowJar
|
||||
archiveClassifier.set(null)
|
||||
}
|
||||
|
||||
jar {
|
||||
archiveClassifier.set("dev")
|
||||
}
|
||||
|
||||
components.java {
|
||||
withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
|
||||
skip()
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenFabric(MavenPublication) {
|
||||
artifactId = rootProject.archives_base_name + "-" + project.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.
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import org.embeddedt.modernfix.fabric.datagen.RuntimeDatagen;
|
||||
|
||||
public class ModernFixClientFabric implements ClientModInitializer {
|
||||
public static ModernFixClient commonMod;
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
commonMod = new ModernFixClient();
|
||||
|
||||
if(FabricLoader.getInstance().isModLoaded("fabric-data-generation-api-v1")) {
|
||||
RuntimeDatagen.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package org.embeddedt.modernfix;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class ModernFixFabric implements ModInitializer {
|
||||
public static ModernFix commonMod;
|
||||
public static WeakReference<MinecraftServer> theServer = new WeakReference<>(null);
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
commonMod = new ModernFix();
|
||||
|
||||
// TODO: implement entity ID desync
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package org.embeddedt.modernfix;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
|
||||
import net.fabricmc.loader.impl.gui.FabricGuiEntry;
|
||||
import net.fabricmc.loader.impl.gui.FabricStatusTree;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.spark.SparkLaunchProfiler;
|
||||
import org.embeddedt.modernfix.util.CommonModUtil;
|
||||
|
||||
public class ModernFixPreLaunchFabric implements PreLaunchEntrypoint {
|
||||
@Override
|
||||
public void onPreLaunch() {
|
||||
if(ModernFixMixinPlugin.instance == null) {
|
||||
System.err.println("Mixin plugin not loaded yet");
|
||||
return;
|
||||
}
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnFabric")) {
|
||||
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.start("launch"), "Failed to start profiler");
|
||||
}
|
||||
|
||||
// Prevent launching with Continuity when dynamic resources is on
|
||||
if(false && ModernFixMixinPlugin.instance.isOptionEnabled("perf.dynamic_resources.ContinuityCheck")
|
||||
&& FabricLoader.getInstance().isModLoaded("continuity")) {
|
||||
CommonModUtil.runWithoutCrash(() -> {
|
||||
FabricGuiEntry.displayError("Compatibility warning", null, tree -> {
|
||||
FabricStatusTree.FabricStatusTab crashTab = tree.addTab("Warning");
|
||||
crashTab.node.addMessage("Continuity and ModernFix's dynamic resources option are not compatible before Minecraft 1.19.4.", FabricStatusTree.FabricTreeWarningLevel.ERROR);
|
||||
crashTab.node.addMessage("Remove Continuity or disable dynamic resources in the ModernFix config.", FabricStatusTree.FabricTreeWarningLevel.ERROR);
|
||||
tree.tabs.removeIf(tab -> tab != crashTab);
|
||||
}, true);
|
||||
}, "display Continuity warning");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.api.dynresources;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ModelScanController {
|
||||
public static final List<Predicate<ResourceLocation>> SCAN_PREDICATES = new ArrayList<>();
|
||||
public static boolean shouldScanAndTestWrapping(ResourceLocation location) {
|
||||
if(SCAN_PREDICATES.size() > 0) {
|
||||
for(Predicate<ResourceLocation> predicate : SCAN_PREDICATES) {
|
||||
if(!predicate.test(location))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.datagen;
|
||||
|
||||
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
|
||||
import net.fabricmc.fabric.impl.datagen.FabricDataGenHelper;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class RuntimeDatagen {
|
||||
private static final boolean SHOULD_RUNTIME_DATAGEN = System.getProperty("fabric-api.datagen.output-dir") != null;
|
||||
|
||||
private static void runRuntimeDatagen() {
|
||||
// call runInternal directly to avoid exiting immediately
|
||||
try {
|
||||
System.setProperty("fabric-api.datagen", "true");
|
||||
Method method = FabricDataGenHelper.class.getDeclaredMethod("runInternal");
|
||||
method.setAccessible(true);
|
||||
method.invoke(null);
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Error running datagen", e);
|
||||
} finally {
|
||||
System.clearProperty("fabric-api.datagen");
|
||||
}
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
if(!SHOULD_RUNTIME_DATAGEN)
|
||||
return;
|
||||
ScreenEvents.AFTER_INIT.register(((client, s, scaledWidth, scaledHeight) -> {
|
||||
if(s instanceof TitleScreen screen) {
|
||||
screen.addRenderableWidget(Button.builder(Component.literal("DG"), (arg) -> {
|
||||
runRuntimeDatagen();
|
||||
}).pos(screen.width / 2 - 100 - 50, screen.height / 4 + 48).size(50, 20).build());
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.core;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
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(MinecraftServer.class)
|
||||
@ClientOnlyMixin
|
||||
public class ClientMinecraftServerMixin {
|
||||
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getMillis()J", ordinal = 0))
|
||||
private void markServerStarted(CallbackInfo ci) {
|
||||
ModernFixClient.INSTANCE.onServerStarted((MinecraftServer)(Object)this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.core;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import org.embeddedt.modernfix.ModernFixClientFabric;
|
||||
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(value = ClientPacketListener.class, priority = 1500)
|
||||
@ClientOnlyMixin
|
||||
public class ClientPlayNetHandlerMixin {
|
||||
@Inject(method = "handleUpdateRecipes", at = @At("RETURN"))
|
||||
private void signalRecipes(CallbackInfo ci) {
|
||||
ModernFixClientFabric.commonMod.onRecipesUpdated();
|
||||
}
|
||||
|
||||
@Inject(method = "handleUpdateTags", at = @At("RETURN"))
|
||||
private void signalTags(CallbackInfo ci) {
|
||||
ModernFixClientFabric.commonMod.onTagsUpdated();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.core;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
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(Minecraft.class)
|
||||
@ClientOnlyMixin
|
||||
public class MCMixin_Fabric {
|
||||
@Inject(method = "tick", at = @At("RETURN"))
|
||||
private void onRenderTickEnd(CallbackInfo ci) {
|
||||
ModernFixClient.INSTANCE.onRenderTickEnd();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.core;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.ModernFixFabric;
|
||||
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;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
@Mixin(MinecraftServer.class)
|
||||
public class MinecraftServerMixin {
|
||||
@Inject(method = "runServer", at = @At("HEAD"))
|
||||
private void changeServerReference(CallbackInfo ci) {
|
||||
ModernFixFabric.theServer = new WeakReference<>((MinecraftServer)(Object)this);
|
||||
}
|
||||
|
||||
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getMillis()J", ordinal = 0))
|
||||
private void hookServerStarted(CallbackInfo ci) {
|
||||
ModernFix.INSTANCE.onServerStarted();
|
||||
}
|
||||
|
||||
@Inject(method = "stopServer", at = @At("RETURN"))
|
||||
private void hookServerShutdown(CallbackInfo ci) {
|
||||
ModernFix.INSTANCE.onServerDead((MinecraftServer)(Object)this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.feature.branding;
|
||||
|
||||
import net.minecraft.client.gui.components.DebugScreenOverlay;
|
||||
import org.embeddedt.modernfix.ModernFixClientFabric;
|
||||
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.ModifyVariable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(DebugScreenOverlay.class)
|
||||
@ClientOnlyMixin
|
||||
public class GuiMixin {
|
||||
@ModifyVariable(method = "getSystemInformation", at = @At("STORE"), ordinal = 0, require = 0)
|
||||
private List<String> addModernFix(List<String> list) {
|
||||
list.add("");
|
||||
list.add(ModernFixClientFabric.commonMod.brandingString);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.feature.measure_time;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
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(Minecraft.class)
|
||||
@ClientOnlyMixin
|
||||
public class MinecraftMixin_Fabric {
|
||||
@Inject(method = "doWorldLoad", at = @At("HEAD"))
|
||||
private void recordWorldLoadStart(CallbackInfo ci) {
|
||||
ModernFixClient.worldLoadStartTime = System.nanoTime();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelBaker;
|
||||
import net.minecraft.client.resources.model.ModelState;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(ItemOverrides.class)
|
||||
@ClientOnlyMixin
|
||||
public class ItemOverridesFabricMixin {
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason servers insist on generating invalid item overrides that have missing models
|
||||
*/
|
||||
@WrapOperation(method = "bakeModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBaker;bake(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/resources/model/ModelState;)Lnet/minecraft/client/resources/model/BakedModel;"))
|
||||
private BakedModel bake(ModelBaker instance, ResourceLocation resourceLocation, ModelState modelState, Operation<BakedModel> original) {
|
||||
boolean prevState = ((IExtendedModelBaker)instance).throwOnMissingModel(false);
|
||||
try {
|
||||
return original.call(instance, resourceLocation, modelState);
|
||||
} finally {
|
||||
((IExtendedModelBaker)instance).throwOnMissingModel(prevState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.model.ModelLoadingRegistryImpl;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.annotation.RequiresMod;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(ModelLoadingRegistryImpl.LoaderInstance.class)
|
||||
@RequiresMod("fabric-models-v0")
|
||||
@ClientOnlyMixin
|
||||
public class LoaderInstanceMixin {
|
||||
@Redirect(method = "finish", at = @At(value = "FIELD", target = "Lnet/fabricmc/fabric/impl/client/model/ModelLoadingRegistryImpl$LoaderInstance;loader:Lnet/minecraft/client/resources/model/ModelBakery;"), require = 0)
|
||||
private void keepLoader(ModelLoadingRegistryImpl.LoaderInstance instance, ModelBakery value) {
|
||||
/* allow loading models to happen later */
|
||||
}
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.ModernFixClient;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelMissingException;
|
||||
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.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(value = ModelBakery.ModelBakerImpl.class, priority = 600)
|
||||
@ClientOnlyMixin
|
||||
public abstract class ModelBakerImplMixin implements IExtendedModelBaker {
|
||||
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
||||
@Shadow @Final private ModelBakery field_40571;
|
||||
|
||||
@Shadow public abstract UnbakedModel getModel(ResourceLocation arg);
|
||||
|
||||
@Shadow @Final private Function<Material, TextureAtlasSprite> modelTextureGetter;
|
||||
|
||||
private static final MethodHandle blockStateLoaderHandle;
|
||||
static {
|
||||
try {
|
||||
blockStateLoaderHandle = MethodHandles.lookup().unreflect(ModelBakery.class.getDeclaredMethod(
|
||||
FabricLoader.getInstance().getMappingResolver().mapMethodName(
|
||||
"intermediary",
|
||||
"net.minecraft.client.resources.model.ModelBakery",
|
||||
"method_4716",
|
||||
"(Lnet/minecraft/world/level/block/state/BlockState;)V"
|
||||
),
|
||||
BlockState.class
|
||||
));
|
||||
} catch(ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean throwIfMissing;
|
||||
|
||||
@Override
|
||||
public boolean throwOnMissingModel(boolean flag) {
|
||||
boolean old = throwIfMissing;
|
||||
throwIfMissing = flag;
|
||||
return old;
|
||||
}
|
||||
|
||||
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
|
||||
private void obtainModel(ResourceLocation arg, CallbackInfoReturnable<UnbakedModel> cir) {
|
||||
if(debugDynamicModelLoading)
|
||||
ModernFix.LOGGER.info("Baking {}", arg);
|
||||
IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571;
|
||||
if(arg instanceof ModelResourceLocation && arg != ModelBakery.MISSING_MODEL_LOCATION) {
|
||||
// synchronized because we use topLevelModels
|
||||
synchronized (this.field_40571) {
|
||||
/* to emulate vanilla model loading, treat as top-level */
|
||||
Optional<Block> blockOpt = Objects.equals(((ModelResourceLocation)arg).getVariant(), "inventory") ? Optional.empty() : BuiltInRegistries.BLOCK.getOptional(new ResourceLocation(arg.getNamespace(), arg.getPath()));
|
||||
boolean invalidMRL = false;
|
||||
if(blockOpt.isPresent()) {
|
||||
/* load via lambda for mods that expect blockstate to get loaded */
|
||||
ImmutableList<BlockState> states;
|
||||
try {
|
||||
states = extendedBakery.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), (ModelResourceLocation)arg);
|
||||
} catch(RuntimeException e) {
|
||||
states = ImmutableList.of();
|
||||
invalidMRL = true;
|
||||
// Fall back to getModel
|
||||
cir.setReturnValue(this.field_40571.getModel(arg));
|
||||
}
|
||||
for(BlockState state : states) {
|
||||
try {
|
||||
blockStateLoaderHandle.invokeExact(this.field_40571, state);
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Error loading model", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.field_40571.loadTopLevel((ModelResourceLocation)arg);
|
||||
}
|
||||
if(!invalidMRL) {
|
||||
cir.setReturnValue(this.field_40571.topLevelModels.getOrDefault(arg, extendedBakery.mfix$getUnbakedMissingModel()));
|
||||
// avoid leaks
|
||||
this.field_40571.topLevelModels.clear();
|
||||
}
|
||||
}
|
||||
} else
|
||||
cir.setReturnValue(this.field_40571.getModel(arg));
|
||||
UnbakedModel toReplace = cir.getReturnValue();
|
||||
if(true) {
|
||||
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
||||
try {
|
||||
toReplace = integration.onUnbakedModelPreBake(arg, toReplace, this.field_40571);
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Exception firing model pre-bake event for {}", arg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
cir.setReturnValue(toReplace);
|
||||
cir.getReturnValue().resolveParents(this.field_40571::getModel);
|
||||
if(cir.getReturnValue() == extendedBakery.mfix$getUnbakedMissingModel()) {
|
||||
if(arg != ModelBakery.MISSING_MODEL_LOCATION) {
|
||||
if(debugDynamicModelLoading)
|
||||
ModernFix.LOGGER.warn("Model {} not present", arg);
|
||||
if(throwIfMissing)
|
||||
throw new ModelMissingException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@WrapOperation(method = "bake", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/UnbakedModel;bake(Lnet/minecraft/client/resources/model/ModelBaker;Ljava/util/function/Function;Lnet/minecraft/client/resources/model/ModelState;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/BakedModel;"))
|
||||
private BakedModel callBakedModelIntegration(UnbakedModel unbakedModel, ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState state, ResourceLocation location, Operation<BakedModel> operation) {
|
||||
BakedModel model = operation.call(unbakedModel, baker, spriteGetter, state, location);
|
||||
|
||||
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
||||
model = integration.onBakedModelLoad(location, unbakedModel, model, state, this.field_40571, spriteGetter);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason emulate old function, to allow injectors to work
|
||||
*/
|
||||
/*
|
||||
@Overwrite
|
||||
public BakedModel bake(ResourceLocation arg, ModelState arg2) {
|
||||
ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(arg, arg2.getRotation(), arg2.isUvLocked());
|
||||
BakedModel existing = this.field_40571.bakedCache.get(key);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
} else {
|
||||
synchronized (this.field_40571) {
|
||||
if(debugDynamicModelLoading)
|
||||
ModernFix.LOGGER.info("Baking {}", arg);
|
||||
UnbakedModel iunbakedmodel = this.getModel(arg);
|
||||
// TODO: make sure parent resolution doesn't re-run many times
|
||||
iunbakedmodel.resolveParents(this::getModel);
|
||||
BakedModel ibakedmodel = null;
|
||||
if (iunbakedmodel instanceof BlockModel) {
|
||||
BlockModel blockmodel = (BlockModel)iunbakedmodel;
|
||||
if (blockmodel.getRootModel() == ModelBakery.GENERATION_MARKER) {
|
||||
ibakedmodel = ModelBakery.ITEM_MODEL_GENERATOR.generateBlockModel(this.modelTextureGetter, blockmodel).bake((ModelBaker)this, blockmodel, this.modelTextureGetter, arg2, arg, false);
|
||||
}
|
||||
}
|
||||
if(ibakedmodel == null) {
|
||||
// leave the original assignment in the same spot so wrapping injectors work
|
||||
// this means two bakes might happen for missing models, but not much we can do
|
||||
ibakedmodel = iunbakedmodel.bake((ModelBaker)this, this.modelTextureGetter, arg2, arg);
|
||||
IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571;
|
||||
if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel()) {
|
||||
// use a shared baked missing model
|
||||
createBakedMissingModelIfNeeded(extendedBakery, iunbakedmodel, arg2, arg);
|
||||
ibakedmodel = extendedBakery.getBakedMissingModel();
|
||||
}
|
||||
}
|
||||
this.field_40571.bakedCache.put(key, ibakedmodel);
|
||||
return ibakedmodel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
@ -1,334 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
import com.google.common.collect.ForwardingMap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.ModernFixClient;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
|
||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.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.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.*;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/* low priority so that our injectors are added after other mods' */
|
||||
@Mixin(value = ModelBakery.class, priority = 1100)
|
||||
@ClientOnlyMixin
|
||||
public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||
|
||||
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
||||
|
||||
@Shadow @Final @Mutable public Map<ResourceLocation, UnbakedModel> unbakedCache;
|
||||
|
||||
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION;
|
||||
|
||||
@Shadow @Final private Set<ResourceLocation> loadingStack;
|
||||
|
||||
@Shadow protected abstract void loadModel(ResourceLocation blockstateLocation) throws Exception;
|
||||
|
||||
@Shadow @Final @Mutable
|
||||
private Map<ResourceLocation, BakedModel> bakedTopLevelModels;
|
||||
|
||||
@Shadow @Final @Mutable private Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
|
||||
|
||||
@Shadow @Final @Mutable private BlockColors blockColors;
|
||||
@Shadow @Final private static Logger LOGGER;
|
||||
|
||||
@Shadow
|
||||
public abstract void loadTopLevel(ModelResourceLocation modelResourceLocation);
|
||||
|
||||
@Shadow public abstract UnbakedModel getModel(ResourceLocation resourceLocation);
|
||||
|
||||
private Cache<ModelBakery.BakedCacheKey, BakedModel> loadedBakedModels;
|
||||
|
||||
private Cache<ResourceLocation, UnbakedModel> loadedModels;
|
||||
|
||||
private HashMap<ResourceLocation, UnbakedModel> smallLoadingCache = new HashMap<>();
|
||||
|
||||
private boolean ignoreModelLoad;
|
||||
|
||||
// disable fabric recursion
|
||||
@SuppressWarnings("unused")
|
||||
private boolean fabric_enableGetOrLoadModelGuard;
|
||||
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/resources/model/ModelBakery;blockColors:Lnet/minecraft/client/color/block/BlockColors;"))
|
||||
private void replaceTopLevelBakedModels(ModelBakery bakery, BlockColors val) {
|
||||
// we can handle recursion in getModel without issues
|
||||
fabric_enableGetOrLoadModelGuard = false;
|
||||
this.blockColors = val;
|
||||
this.loadedBakedModels = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS)
|
||||
.maximumSize(ModelBakeryHelpers.MAX_BAKED_MODEL_COUNT)
|
||||
.concurrencyLevel(8)
|
||||
.removalListener(this::onModelRemoved)
|
||||
.softValues()
|
||||
.build();
|
||||
this.loadedModels = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS)
|
||||
.maximumSize(ModelBakeryHelpers.MAX_UNBAKED_MODEL_COUNT)
|
||||
.concurrencyLevel(8)
|
||||
.removalListener(this::onModelRemoved)
|
||||
//.softValues()
|
||||
.build();
|
||||
this.bakedCache = loadedBakedModels.asMap();
|
||||
ConcurrentMap<ResourceLocation, UnbakedModel> unbakedCacheBackingMap = loadedModels.asMap();
|
||||
this.unbakedCache = new ForwardingMap<ResourceLocation, UnbakedModel>() {
|
||||
@Override
|
||||
protected Map<ResourceLocation, UnbakedModel> delegate() {
|
||||
return unbakedCacheBackingMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnbakedModel put(ResourceLocation key, UnbakedModel value) {
|
||||
smallLoadingCache.put(key, value);
|
||||
return super.put(key, value);
|
||||
}
|
||||
};
|
||||
this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", ordinal = 0))
|
||||
private void ignoreFutureModelLoads(CallbackInfo ci) {
|
||||
this.ignoreModelLoad = true;
|
||||
}
|
||||
|
||||
private <K, V> void onModelRemoved(RemovalNotification<K, V> notification) {
|
||||
if(!debugDynamicModelLoading)
|
||||
return;
|
||||
Object k = notification.getKey();
|
||||
if(k == null)
|
||||
return;
|
||||
ResourceLocation rl;
|
||||
boolean baked = false;
|
||||
if(k instanceof ResourceLocation) {
|
||||
rl = (ResourceLocation)k;
|
||||
} else {
|
||||
rl = ((ModelBakery.BakedCacheKey)k).id();
|
||||
baked = true;
|
||||
}
|
||||
/* can fire when a model is replaced */
|
||||
if(!baked && this.loadedModels.getIfPresent(rl) != null)
|
||||
return;
|
||||
ModernFix.LOGGER.warn("Evicted {} model {}", baked ? "baked" : "unbaked", rl);
|
||||
}
|
||||
|
||||
private UnbakedModel missingModel;
|
||||
|
||||
private Set<ResourceLocation> blockStateFiles;
|
||||
private Set<ResourceLocation> modelFiles;
|
||||
|
||||
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 0), index = 1)
|
||||
private Object captureMissingModel(Object model) {
|
||||
this.missingModel = (UnbakedModel)model;
|
||||
this.blockStateFiles = new HashSet<>();
|
||||
this.modelFiles = new HashSet<>();
|
||||
return this.missingModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason don't actually load most models
|
||||
*/
|
||||
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBakery;loadTopLevel(Lnet/minecraft/client/resources/model/ModelResourceLocation;)V"))
|
||||
private void addTopLevelFile(ModelBakery bakery, ModelResourceLocation location) {
|
||||
if(location == MISSING_MODEL_LOCATION || !this.ignoreModelLoad) {
|
||||
loadTopLevel(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;forEach(Ljava/util/function/BiConsumer;)V", ordinal = 0))
|
||||
private void fetchStaticDefinitions(Map<ResourceLocation, StateDefinition<Block, BlockState>> map, BiConsumer<ResourceLocation, StateDefinition<Block, BlockState>> func) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;", ordinal = 0))
|
||||
private ImmutableList<BlockState> fetchBlocks(StateDefinition<Block, BlockState> def) {
|
||||
/* no-op */
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a copy of the top-level model list to avoid CME if more models get loaded here.
|
||||
*/
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;values()Ljava/util/Collection;", ordinal = 0))
|
||||
private Collection<?> copyTopLevelModelList(Map<?, ?> map) {
|
||||
return new ArrayList<>(map.values());
|
||||
}
|
||||
|
||||
private BiFunction<ResourceLocation, Material, TextureAtlasSprite> textureGetter;
|
||||
|
||||
@Inject(method = "bakeModels", at = @At("HEAD"))
|
||||
private void captureGetter(BiFunction<ResourceLocation, Material, TextureAtlasSprite> getter, CallbackInfo ci) {
|
||||
this.ignoreModelLoad = false;
|
||||
textureGetter = getter;
|
||||
DynamicBakedModelProvider.currentInstance = (DynamicBakedModelProvider)this.bakedTopLevelModels;
|
||||
}
|
||||
|
||||
@Redirect(method = "bakeModels", at = @At(value = "INVOKE", target = "Ljava/util/Map;keySet()Ljava/util/Set;"))
|
||||
private Set<ResourceLocation> skipBake(Map<ResourceLocation, UnbakedModel> instance) {
|
||||
Set<ResourceLocation> modelSet = new HashSet<>(instance.keySet());
|
||||
if(modelSet.size() > 0)
|
||||
ModernFix.LOGGER.info("Early baking {} models", modelSet.size());
|
||||
return modelSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the already loaded missing model instead of the cache entry (which will probably get evicted).
|
||||
*/
|
||||
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 1))
|
||||
private Object getMissingModel(Map map, Object rl) {
|
||||
if(rl == MISSING_MODEL_LOCATION && map == unbakedCache)
|
||||
return missingModel;
|
||||
return unbakedCache.get(rl);
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "cacheAndQueueDependencies", at = @At("HEAD"), argsOnly = true)
|
||||
private UnbakedModel fireUnbakedEvent(UnbakedModel model, ResourceLocation location) {
|
||||
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
||||
try {
|
||||
model = integration.onUnbakedModelLoad(location, model, (ModelBakery)(Object)this);
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Exception firing model load event for {}", location, e);
|
||||
}
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
private int mfix$nestedLoads = 0;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason synchronize
|
||||
*/
|
||||
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
|
||||
public void getOrLoadModelDynamic(ResourceLocation modelLocation, CallbackInfoReturnable<UnbakedModel> cir) {
|
||||
if(modelLocation.equals(MISSING_MODEL_LOCATION)) {
|
||||
cir.setReturnValue(missingModel);
|
||||
return;
|
||||
}
|
||||
UnbakedModel existing = this.unbakedCache.get(modelLocation);
|
||||
if (existing != null) {
|
||||
cir.setReturnValue(existing);
|
||||
} else {
|
||||
synchronized(this) {
|
||||
if (this.loadingStack.contains(modelLocation)) {
|
||||
throw new IllegalStateException("Circular reference while loading " + modelLocation);
|
||||
} else {
|
||||
this.loadingStack.add(modelLocation);
|
||||
UnbakedModel iunbakedmodel = missingModel;
|
||||
|
||||
while(!this.loadingStack.isEmpty()) {
|
||||
ResourceLocation resourcelocation = this.loadingStack.iterator().next();
|
||||
|
||||
mfix$nestedLoads++;
|
||||
try {
|
||||
existing = this.unbakedCache.get(resourcelocation);
|
||||
if (existing == null) {
|
||||
if(debugDynamicModelLoading)
|
||||
LOGGER.info("Loading {}", resourcelocation);
|
||||
this.loadModel(resourcelocation);
|
||||
} else
|
||||
smallLoadingCache.put(resourcelocation, existing);
|
||||
} catch (ModelBakery.BlockStateDefinitionException var9) {
|
||||
LOGGER.warn(var9.getMessage());
|
||||
this.unbakedCache.put(resourcelocation, iunbakedmodel);
|
||||
smallLoadingCache.put(resourcelocation, iunbakedmodel);
|
||||
} catch (Exception var10) {
|
||||
LOGGER.warn("Unable to load model: '{}' referenced from: {}: {}", resourcelocation, modelLocation, var10);
|
||||
this.unbakedCache.put(resourcelocation, iunbakedmodel);
|
||||
smallLoadingCache.put(resourcelocation, iunbakedmodel);
|
||||
} finally {
|
||||
mfix$nestedLoads--;
|
||||
this.loadingStack.remove(resourcelocation);
|
||||
}
|
||||
}
|
||||
|
||||
// We have to get the result from the temporary cache used for a model load
|
||||
// As in pathological cases (e.g. Pedestals on 1.19) unbakedCache can lose
|
||||
// the model immediately
|
||||
UnbakedModel result = smallLoadingCache.getOrDefault(modelLocation, iunbakedmodel);
|
||||
try {
|
||||
// required as some mods (e.g. EBE) call bake directly on the returned model, without resolving parents themselves
|
||||
result.resolveParents(this::getModel);
|
||||
} catch(RuntimeException ignored) {}
|
||||
// We are done with loading, so clear this cache to allow GC of any unneeded models
|
||||
if(mfix$nestedLoads == 0)
|
||||
smallLoadingCache.clear();
|
||||
cir.setReturnValue(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Comparable<T>, V extends T> BlockState setPropertyGeneric(BlockState state, Property<T> prop, Object o) {
|
||||
return state.setValue(prop, (V)o);
|
||||
}
|
||||
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
|
||||
private ImmutableList<BlockState> loadOnlyRelevantBlockState(StateDefinition<Block, BlockState> stateDefinition, ResourceLocation location) {
|
||||
if(!(location instanceof ModelResourceLocation) || Minecraft.getInstance().getOverlay() != null || Minecraft.getInstance().level == null)
|
||||
return stateDefinition.getPossibleStates();
|
||||
return ModelBakeryHelpers.getBlockStatesForMRL(stateDefinition, (ModelResourceLocation)location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel bakeDefault(ResourceLocation modelLocation, ModelState state) {
|
||||
ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(modelLocation, BlockModelRotation.X0_Y0.getRotation(), BlockModelRotation.X0_Y0.isUvLocked());
|
||||
BakedModel m = loadedBakedModels.getIfPresent(key);
|
||||
if(m != null)
|
||||
return m;
|
||||
ModelBakery self = (ModelBakery) (Object) this;
|
||||
ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation);
|
||||
((IExtendedModelBaker)theBaker).throwOnMissingModel(true);
|
||||
synchronized(this) { m = theBaker.bake(modelLocation, state); }
|
||||
if(m != null)
|
||||
loadedBakedModels.put(key, m);
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location) {
|
||||
return loadOnlyRelevantBlockState(stateDefinition, location);
|
||||
}
|
||||
|
||||
public UnbakedModel mfix$getUnbakedMissingModel() {
|
||||
return missingModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mfix$clearModels() {
|
||||
loadedModels.invalidateAll();
|
||||
loadedBakedModels.invalidateAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.perf.faster_command_suggestions;
|
||||
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Simple hack-fix to limit the number of suggestions being processed. Not a perfect fix but mitigates lag decently
|
||||
* on an i3-4150.
|
||||
*/
|
||||
@Mixin(SuggestionsBuilder.class)
|
||||
public class SuggestionsBuilderMixin {
|
||||
@Unique
|
||||
private static final int MAX_SUGGESTIONS = 10000;
|
||||
|
||||
@Shadow(remap = false) @Final @Mutable
|
||||
private List<Suggestion> result;
|
||||
|
||||
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"), require = 0)
|
||||
private <T> boolean addIfFits(List<T> list, T entry) {
|
||||
if(list != result || list.size() < MAX_SUGGESTIONS) {
|
||||
return list.add(entry);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.mixin.safety;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(DynamicTexture.class)
|
||||
@ClientOnlyMixin
|
||||
public class DynamicTextureMixin {
|
||||
@Shadow @Nullable private NativeImage pixels;
|
||||
|
||||
private Exception closeTrace;
|
||||
|
||||
@Inject(method = "method_22793", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/texture/DynamicTexture;pixels:Lcom/mojang/blaze3d/platform/NativeImage;", ordinal = 0))
|
||||
private void checkNullPixels(CallbackInfo ci) {
|
||||
if(pixels == null) {
|
||||
ModernFix.LOGGER.error("Attempted to upload null texture! This is not allowed, closed here", closeTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "close", at = @At("HEAD"))
|
||||
private void storeCloseTrace(CallbackInfo ci) {
|
||||
closeTrace = new Exception();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package org.embeddedt.modernfix.fabric.modmenu;
|
||||
|
||||
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||
import org.embeddedt.modernfix.screen.ModernFixConfigScreen;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class ModernFixModMenuApiImpl implements ModMenuApi {
|
||||
@Override
|
||||
public ConfigScreenFactory<ModernFixConfigScreen> getModConfigScreenFactory() {
|
||||
return ModernFixConfigScreen::new;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
package org.embeddedt.modernfix.platform.fabric;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import net.fabricmc.loader.api.metadata.CustomValue;
|
||||
import net.fabricmc.loader.api.metadata.ModMetadata;
|
||||
import net.minecraft.client.searchtree.SearchRegistry;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.embeddedt.modernfix.ModernFixFabric;
|
||||
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
import org.embeddedt.modernfix.spark.SparkLaunchProfiler;
|
||||
import org.embeddedt.modernfix.util.CommonModUtil;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
|
||||
public boolean isClient() {
|
||||
return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT;
|
||||
}
|
||||
|
||||
public boolean isDedicatedServer() {
|
||||
return FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER;
|
||||
}
|
||||
|
||||
private static final String verString = FabricLoader.getInstance().getModContainer("modernfix")
|
||||
.map(mfModContainer -> mfModContainer.getMetadata().getVersion().getFriendlyString())
|
||||
.orElse("[unknown]");
|
||||
|
||||
public String getVersionString() {
|
||||
return verString;
|
||||
}
|
||||
|
||||
public boolean modPresent(String modId) {
|
||||
return FabricLoader.getInstance().getModContainer(modId).isPresent();
|
||||
}
|
||||
|
||||
public boolean isDevEnv() {
|
||||
return FabricLoader.getInstance().isDevelopmentEnvironment();
|
||||
}
|
||||
|
||||
public MinecraftServer getCurrentServer() {
|
||||
return ModernFixFabric.theServer.get();
|
||||
}
|
||||
|
||||
public boolean isEarlyLoadingNormally() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isLoadingNormally() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Path getGameDirectory() {
|
||||
return FabricLoader.getInstance().getGameDir();
|
||||
}
|
||||
|
||||
public void sendPacket(ServerPlayer player, Object packet) {
|
||||
//PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), packet);
|
||||
}
|
||||
|
||||
public void injectPlatformSpecificHacks() {
|
||||
}
|
||||
|
||||
public void applyASMTransformers(String mixinClassName, ClassNode targetClass) {
|
||||
|
||||
}
|
||||
|
||||
public void onServerCommandRegister(Consumer<CommandDispatcher<CommandSourceStack>> handler) {
|
||||
if(FabricLoader.getInstance().isModLoaded("fabric-command-api-v2"))
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, arg, env) -> handler.accept(dispatcher));
|
||||
}
|
||||
|
||||
private static Multimap<String, String> modOptions;
|
||||
public Multimap<String, String> getCustomModOptions() {
|
||||
if(modOptions == null) {
|
||||
modOptions = ArrayListMultimap.create();
|
||||
for (ModContainer container : FabricLoader.getInstance().getAllMods()) {
|
||||
ModMetadata meta = container.getMetadata();
|
||||
if (meta.containsCustomValue(IntegrationConstants.INTEGRATIONS_KEY)) {
|
||||
CustomValue integrations = meta.getCustomValue(IntegrationConstants.INTEGRATIONS_KEY);
|
||||
if (integrations.getType() != CustomValue.CvType.OBJECT) {
|
||||
continue;
|
||||
}
|
||||
for (Map.Entry<String, CustomValue> entry : integrations.getAsObject()) {
|
||||
if(entry.getValue().getType() != CustomValue.CvType.STRING)
|
||||
continue;
|
||||
modOptions.put(entry.getKey(), entry.getValue().getAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return modOptions;
|
||||
}
|
||||
|
||||
public void registerCreativeSearchTrees(SearchRegistry registry, SearchRegistry.TreeBuilderSupplier<ItemStack> nameSupplier, SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier, BiConsumer<SearchRegistry.Key<ItemStack>, List<ItemStack>> populator) {
|
||||
CreativeModeTabs.searchTab().setSearchTreeBuilder((list) -> {
|
||||
populator.accept(SearchRegistry.CREATIVE_NAMES, list);
|
||||
populator.accept(SearchRegistry.CREATIVE_TAGS, list);
|
||||
});
|
||||
}
|
||||
|
||||
public void onLaunchComplete() {
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnFabric")) {
|
||||
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler");
|
||||
}
|
||||
}
|
||||
|
||||
public String getPlatformName() {
|
||||
return "Fabric";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "modernfix",
|
||||
"version": "${version}",
|
||||
"name": "ModernFix",
|
||||
"description": "Egregious, yet effective performance improvements for modern Minecraft",
|
||||
"authors": [
|
||||
"embeddedt"
|
||||
],
|
||||
"contact": {
|
||||
"sources": "https://github.com/embeddedt/ModernFix",
|
||||
"homepage": "https://modrinth.com/mod/modernfix",
|
||||
"issues": "https://github.com/embeddedt/ModernFix/issues"
|
||||
},
|
||||
"license": "LGPL-3.0",
|
||||
"icon": "icon.png",
|
||||
"custom": {
|
||||
"modmenu": {
|
||||
"links": {
|
||||
"modmenu.kofi": "https://ko-fi.com/embeddedt",
|
||||
"modmenu.github_releases": "https://github.com/embeddedt/ModernFix/releases",
|
||||
"modmenu.curseforge": "https://www.curseforge.com/minecraft/mc-mods/modernfix"
|
||||
}
|
||||
}
|
||||
},
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"org.embeddedt.modernfix.ModernFixFabric"
|
||||
],
|
||||
"client": [
|
||||
"org.embeddedt.modernfix.ModernFixClientFabric"
|
||||
],
|
||||
"modmenu": [ "org.embeddedt.modernfix.fabric.modmenu.ModernFixModMenuApiImpl" ],
|
||||
"preLaunch": [
|
||||
"org.embeddedt.modernfix.ModernFixPreLaunchFabric"
|
||||
],
|
||||
"jei_mod_plugin": [ "org.embeddedt.modernfix.searchtree.JEIRuntimeCapturer"]
|
||||
},
|
||||
"mixins": [
|
||||
"modernfix-fabric.mixins.json",
|
||||
"modernfix-common.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"minecraft": ">=1.16.2",
|
||||
"fabricloader": ">=0.16.10"
|
||||
},
|
||||
"breaks": {
|
||||
"dashloader": "<5.0.0-beta.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
package net.minecraft.world.level.block.state;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.embeddedt.modernfix.duck.IBlockState;
|
||||
import org.embeddedt.modernfix.testing.util.BootstrapMinecraft;
|
||||
import org.junit.jupiter.api.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@BootstrapMinecraft
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class BlockStateCacheTest {
|
||||
@BeforeEach
|
||||
public void rebuildCache() {
|
||||
Blocks.rebuildCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initially, the cache should be invalid, and null.
|
||||
*/
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testCacheNullInitially() {
|
||||
BlockState stoneBlock = Blocks.STONE.defaultBlockState();
|
||||
assertTrue(((IBlockState)stoneBlock).isCacheInvalid());
|
||||
assertNull(stoneBlock.cache);
|
||||
|
||||
// make sure lazy cache correctly handles solid
|
||||
assertTrue(stoneBlock.isSolid());
|
||||
}
|
||||
|
||||
/**
|
||||
* When an API that needs the cache is called, it should be built and the invalid flag
|
||||
* becomes false.
|
||||
*/
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testCacheBuiltByRequest() {
|
||||
BlockState stoneBlock = Blocks.STONE.defaultBlockState();
|
||||
stoneBlock.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||
assertFalse(((IBlockState)stoneBlock).isCacheInvalid());
|
||||
assertNotNull(stoneBlock.cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a second rebuild occurs, the invalid flag should be set to true, but the old cache
|
||||
* is not set to null, in order to prevent NPEs if a second thread is accessing the cache
|
||||
* when this takes place.
|
||||
*/
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testCacheInvalidatedByLateRebuild() {
|
||||
BlockState stoneBlock = Blocks.STONE.defaultBlockState();
|
||||
stoneBlock.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||
Blocks.rebuildCache();
|
||||
assertTrue(((IBlockState)stoneBlock).isCacheInvalid());
|
||||
assertNotNull(stoneBlock.cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the fluidState and isRandomlyTicking caching fields added by Mojang to blockstates are correctly
|
||||
* handled by the dynamic cache system.
|
||||
*/
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testExtraFieldCachingCorrect() {
|
||||
Block[] blocksToCheck = new Block[] { Blocks.WATER, Blocks.FARMLAND };
|
||||
for(Block block : blocksToCheck) {
|
||||
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
|
||||
// check that the fluid states match
|
||||
assertEquals(block.getFluidState(state), state.getFluidState(), "mismatched fluid state on " + state);
|
||||
// check that random ticking flag matches
|
||||
assertEquals(block.isRandomlyTicking(state), state.isRandomlyTicking(), "mismatched random tick state on " + state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void checkRecursiveFluidState() {
|
||||
Block b = new Block(BlockBehaviour.Properties.copy(Blocks.STONE)) {
|
||||
@Override
|
||||
public FluidState getFluidState(BlockState state) {
|
||||
return state.getFluidState();
|
||||
}
|
||||
};
|
||||
BlockState state = b.getStateDefinition().any();
|
||||
((IBlockState)state).clearCache();
|
||||
// this should not throw
|
||||
state.getFluidState();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.BuiltInModel;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Items;
|
||||
import org.embeddedt.modernfix.testing.util.BootstrapMinecraft;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@BootstrapMinecraft
|
||||
public class DynamicModelCacheTest {
|
||||
@Test
|
||||
public void testCacheReturnsNullForNullGetter() {
|
||||
DynamicModelCache<Item> cache = new DynamicModelCache(k -> null, true);
|
||||
assertNull(cache.get(Items.STONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheFunctions() {
|
||||
BakedModel model = new BuiltInModel(null, null, null, false);
|
||||
DynamicModelCache<Item> cache = new DynamicModelCache(k -> model, true);
|
||||
assertEquals(model, cache.get(Items.STONE));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package org.embeddedt.modernfix.testing.util;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@ExtendWith({ BootstrapMinecraftExtension.class })
|
||||
public @interface BootstrapMinecraft {
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package org.embeddedt.modernfix.testing.util;
|
||||
|
||||
import net.minecraft.DetectedVersion;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import org.junit.jupiter.api.extension.AfterAllCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
import org.junit.jupiter.api.extension.Extension;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.IdentityHashMap;
|
||||
|
||||
/**
|
||||
* Simple extension to run vanilla bootstrap, inspired by AE2.
|
||||
*/
|
||||
public class BootstrapMinecraftExtension implements Extension, BeforeAllCallback, AfterAllCallback {
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws Exception {
|
||||
DetectedVersion.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
// Allow blocks to be created in tests
|
||||
Field field = MappedRegistry.class.getDeclaredField("unregisteredIntrusiveHolders");
|
||||
field.setAccessible(true);
|
||||
if(field.get(BuiltInRegistries.BLOCK) == null) {
|
||||
field.set(BuiltInRegistries.BLOCK, new IdentityHashMap<>());
|
||||
field = MappedRegistry.class.getDeclaredField("frozen");
|
||||
field.setAccessible(true);
|
||||
field.setBoolean(BuiltInRegistries.BLOCK, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterAll(ExtensionContext context) throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
plugins {
|
||||
id 'dev.architectury.loom'
|
||||
}
|
||||
|
||||
loom {
|
||||
accessWidenerPath = project(":common").loom.accessWidenerPath
|
||||
runs {
|
||||
client {
|
||||
vmArgs "-Xmx8G"
|
||||
property("modernfix.config.mixin.perf.blast_search_trees", "true")
|
||||
property("modernfix.config.mixin.perf.dynamic_resources", "true")
|
||||
property("modernfix.config.mixin.perf.dynamic_block_codecs", "true")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
|
||||
mappings loom.layered() {
|
||||
officialMojangMappings()
|
||||
}
|
||||
|
||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||
modImplementation(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modImplementation(fabricApi.module("fabric-models-v0", "0.84.0+1.20.1")) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modImplementation(fabricApi.module("fabric-registry-sync-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modImplementation(fabricApi.module("fabric-renderer-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modImplementation(fabricApi.module("fabric-rendering-data-attachment-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modImplementation(fabricApi.module("fabric-rendering-fluids-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modRuntimeOnly(fabricApi.module("fabric-renderer-indigo", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
|
||||
implementation project(path: ":common", configuration: "namedElements")
|
||||
implementation project(path: ":fabric", configuration: "namedElements")
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand "version": project.version
|
||||
}
|
||||
}
|
||||
|
||||
// Make genSources do nothing in this project
|
||||
project.gradle.startParameter.excludedTaskNames.add("genSources")
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package org.embeddedt.modernfix.testmod;
|
||||
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
|
||||
public class TestBlock extends Block {
|
||||
private static final BlockBehaviour.Properties PROPERTIES = BlockBehaviour.Properties.copy(Blocks.STONE);
|
||||
|
||||
public TestBlock() {
|
||||
super(PROPERTIES);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package org.embeddedt.modernfix.testmod;
|
||||
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
public class TestBlockItem extends BlockItem {
|
||||
private static final Item.Properties PROPERTIES = new Item.Properties();
|
||||
|
||||
public TestBlockItem(TestBlock block) {
|
||||
super(block, PROPERTIES);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package org.embeddedt.modernfix.testmod;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TestMod implements ModInitializer {
|
||||
public static final String ID = "mfix_testmod";
|
||||
public static final Logger LOGGER = LogManager.getLogger("ModernFix TestMod");
|
||||
|
||||
public static final int NUM_COLORS = 32;
|
||||
public static final int MAX_COLOR = NUM_COLORS - 1;
|
||||
|
||||
public static final List<BlockState> WOOL_STATES = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
// Register 1 million blocks & items
|
||||
Stopwatch watch = Stopwatch.createStarted();
|
||||
int totalToRegister = NUM_COLORS * NUM_COLORS * NUM_COLORS;
|
||||
int progressReport = totalToRegister / 20;
|
||||
int numRegistered = 0;
|
||||
for(int r = 0; r < NUM_COLORS; r++) {
|
||||
for(int g = 0; g < NUM_COLORS; g++) {
|
||||
for(int b = 0; b < NUM_COLORS; b++) {
|
||||
ResourceLocation name = new ResourceLocation(ID, "wool_" + r + "_" + g + "_" + b);
|
||||
TestBlock block = Registry.register(BuiltInRegistries.BLOCK, name, new TestBlock());
|
||||
WOOL_STATES.add(block.defaultBlockState());
|
||||
//Registry.register(BuiltInRegistries.ITEM, name, new TestBlockItem(block));
|
||||
numRegistered++;
|
||||
if((numRegistered % progressReport) == 0) {
|
||||
LOGGER.info(String.format("Registering... %.02f%%", ((float)numRegistered)/totalToRegister * 100));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
watch.stop();
|
||||
LOGGER.info("Registered {} registry entries in {}", totalToRegister, watch);
|
||||
}
|
||||
|
||||
private static final BlockState AIR = Blocks.AIR.defaultBlockState();
|
||||
|
||||
public static BlockState getColorCubeStateFor(int chunkX, int chunkY, int chunkZ) {
|
||||
BlockState blockState = null;
|
||||
if (chunkX >= 0 && chunkY >= 0 && chunkZ >= 0) { // && chunkX % 2 == 0 && chunkY % 2 == 0 && chunkZ % 2 == 0) {
|
||||
/*
|
||||
chunkX /= 2;
|
||||
chunkY /= 2;
|
||||
chunkZ /= 2;
|
||||
|
||||
*/
|
||||
if(chunkX <= TestMod.MAX_COLOR && chunkY <= TestMod.MAX_COLOR && chunkZ <= TestMod.MAX_COLOR) {
|
||||
blockState = TestMod.WOOL_STATES.get((chunkX * TestMod.NUM_COLORS * TestMod.NUM_COLORS) + (chunkY * TestMod.NUM_COLORS) + chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
return blockState;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
package org.embeddedt.modernfix.testmod.client;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.embeddedt.modernfix.testmod.TestMod;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class TestModBlockModel implements UnbakedModel, BakedModel, FabricBakedModel {
|
||||
private static final Material BASE_WOOL = new Material(TextureAtlas.LOCATION_BLOCKS, new ResourceLocation(TestMod.ID, "block/base_wool"));
|
||||
|
||||
private Mesh mesh;
|
||||
private TextureAtlasSprite texture;
|
||||
|
||||
private final int r, g, b;
|
||||
|
||||
public TestModBlockModel(int r, int g, int b) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanillaAdapter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier<RandomSource> randomSupplier, RenderContext context) {
|
||||
context.meshConsumer().accept(mesh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitItemQuads(ItemStack stack, Supplier<RandomSource> randomSupplier, RenderContext context) {
|
||||
context.meshConsumer().accept(mesh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAmbientOcclusion() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGui3d() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesBlockLight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustomRenderer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleIcon() {
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemTransforms getTransforms() {
|
||||
return ModelHelper.MODEL_TRANSFORM_BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrides getOverrides() {
|
||||
return ItemOverrides.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getDependencies() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveParents(Function<ResourceLocation, UnbakedModel> function) {
|
||||
|
||||
}
|
||||
|
||||
private static int scaleColor(int c) {
|
||||
return c * 255 / TestMod.MAX_COLOR;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel bake(ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState state, ResourceLocation location) {
|
||||
// Build the mesh using the Renderer API
|
||||
Renderer renderer = RendererAccess.INSTANCE.getRenderer();
|
||||
MeshBuilder builder = renderer.meshBuilder();
|
||||
QuadEmitter emitter = builder.getEmitter();
|
||||
|
||||
texture = spriteGetter.apply(BASE_WOOL);
|
||||
|
||||
for(Direction direction : Direction.values()) {
|
||||
// Add a new face to the mesh
|
||||
emitter.square(direction, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f);
|
||||
// Set the sprite of the face, must be called after .square()
|
||||
// We haven't specified any UV coordinates, so we want to use the whole texture. BAKE_LOCK_UV does exactly that.
|
||||
emitter.spriteBake(0, texture, MutableQuadView.BAKE_LOCK_UV);
|
||||
int color = (255 << 24) | (scaleColor(r) << 16) | (scaleColor(g) << 8) | scaleColor(b);
|
||||
// Enable texture usage
|
||||
emitter.spriteColor(0, color, color, color, color);
|
||||
// Add the quad to the mesh
|
||||
emitter.emit();
|
||||
}
|
||||
mesh = builder.build();
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user