Merge branch 'refactor' into 'master'
Refactor See merge request jfmods/LibJF!1
This commit is contained in:
commit
d90671c005
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -117,3 +117,8 @@ run/
|
||||||
|
|
||||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||||
!gradle-wrapper.jar
|
!gradle-wrapper.jar
|
||||||
|
|
||||||
|
.classpath
|
||||||
|
bin
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
|
|
@ -9,21 +9,23 @@ before_script:
|
||||||
build_test:
|
build_test:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
script:
|
script:
|
||||||
- gradle --build-cache assemble
|
- gradle --build-cache build publish -PpublicMaven -Ppipeline=$CI_PIPELINE_ID
|
||||||
- cp build/libs/* ./
|
- cp build/libs/* ./
|
||||||
- mv *-dev.jar tmp.zip
|
- rm *-maven.jar *-sources.jar *-sources-dev.jar
|
||||||
|
- mv *-dev.jar dev.zip
|
||||||
- mv *.jar latest.jar
|
- mv *.jar latest.jar
|
||||||
- mv tmp.zip latest-dev.jar
|
- mv dev.zip latest-dev.jar
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/libs
|
- build/libs
|
||||||
- latest.jar
|
- latest.jar
|
||||||
- latest-dev.jar
|
- latest-dev.jar
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
when: manual
|
when: manual
|
||||||
script:
|
script:
|
||||||
- gradle --build-cache publishModrinth curseforge
|
- gradle --build-cache publish modrinth curseforge -PpublicMaven
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
|
||||||
|
|
317
build.gradle
317
build.gradle
|
@ -1,43 +1,300 @@
|
||||||
apply from: "https://gitlab.com/-/snippets/2121059/raw/master/jfbase.gradle"
|
import com.modrinth.minotaur.TaskModrinthUpload
|
||||||
|
|
||||||
repositories {
|
plugins {
|
||||||
maven {
|
id "java"
|
||||||
name = 'TerraformersMC'
|
id "idea"
|
||||||
url = 'https://maven.terraformersmc.com/'
|
id "eclipse"
|
||||||
|
id "java-library"
|
||||||
|
id "maven-publish"
|
||||||
|
id "fabric-loom" version "0.10-SNAPSHOT" apply false
|
||||||
|
id "com.matthewprenger.cursegradle" version "1.4.0"
|
||||||
|
id "com.modrinth.minotaur" version "1.1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
def ENV = System.getenv()
|
||||||
|
ext.isPublicMaven = project.hasProperty('publicMaven')
|
||||||
|
|
||||||
|
static Node getOrCreateNode(Node node, String name) {
|
||||||
|
Node dependencies = null
|
||||||
|
for(Node n : node) {
|
||||||
|
if(name == n.name()) {
|
||||||
|
dependencies = n
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(dependencies == null) {
|
||||||
|
dependencies = node.appendNode(name)
|
||||||
|
}
|
||||||
|
return dependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
def moduleDependencies(project, List<String> depNames) {
|
||||||
|
def deps = depNames.iterator().collect { project.dependencies.project(path: ":$it", configuration: 'dev') }
|
||||||
|
project.dependencies {
|
||||||
|
deps.each {
|
||||||
|
api it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
project.publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
pom.withXml {
|
||||||
|
def depsNode = getOrCreateNode(asNode(), "dependencies")
|
||||||
|
deps.each {
|
||||||
|
def depNode = depsNode.appendNode("dependency")
|
||||||
|
depNode.appendNode("groupId", it.group)
|
||||||
|
depNode.appendNode("artifactId", it.name)
|
||||||
|
depNode.appendNode("version", it.version)
|
||||||
|
depNode.appendNode("scope", "compile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
apply plugin: "java-library"
|
||||||
|
apply plugin: "maven-publish"
|
||||||
|
apply plugin: "fabric-loom"
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
it.options.release = 16
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "io.gitlab.jfronny.libjf"
|
||||||
|
version = "$project.mod_version" + (project.hasProperty('pipeline') ? "+" + project.getProperty('pipeline') : "")
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
testmod {
|
||||||
|
compileClasspath += main.compileClasspath
|
||||||
|
runtimeClasspath += main.runtimeClasspath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loom {
|
||||||
|
runs {
|
||||||
|
testmodClient {
|
||||||
|
client()
|
||||||
|
ideConfigGenerated project.rootProject == project
|
||||||
|
name = "Testmod Client"
|
||||||
|
source sourceSets.testmod
|
||||||
|
}
|
||||||
|
testmodServer {
|
||||||
|
server()
|
||||||
|
ideConfigGenerated project.rootProject == project
|
||||||
|
name = "Testmod Server"
|
||||||
|
source sourceSets.testmod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||||
|
mappings "net.fabricmc:yarn:${project.minecraft_version}+${project.yarn_mappings}:v2"
|
||||||
|
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||||
|
|
||||||
|
modRuntime modCompileOnly("com.terraformersmc:modmenu:2.0.14")
|
||||||
|
include modImplementation(fabricApi.module("fabric-tag-extensions-v0", "${project.fabric_version}"))
|
||||||
|
include modImplementation(fabricApi.module("fabric-resource-loader-v0", "${project.fabric_version}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
dev
|
||||||
|
}
|
||||||
|
|
||||||
|
loom {
|
||||||
|
shareRemapCaches = true
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = 'TerraformersMC'
|
||||||
|
url = 'https://maven.terraformersmc.com/'
|
||||||
|
}
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
archiveClassifier = "dev"
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
remapJar {
|
||||||
|
input = file("${project.buildDir}/libs/$archivesBaseName-${project.version}-dev.jar")
|
||||||
|
archiveFileName = "${archivesBaseName}-${project.version}.jar"
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
dev file: file("${project.buildDir}/libs/$archivesBaseName-${project.version}-dev.jar"), type: "jar", builtBy: jar
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
inputs.property "version", project.version
|
||||||
|
|
||||||
|
filesMatching("fabric.mod.json") {
|
||||||
|
expand "version": project.version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||||
|
archiveClassifier = "sources"
|
||||||
|
from sourceSets.main.allSource
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(AbstractArchiveTask) {
|
||||||
|
preserveFileTimestamps = false
|
||||||
|
reproducibleFileOrder = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
dependencies {
|
||||||
|
testmodImplementation sourceSets.main.output
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
afterEvaluate {
|
||||||
|
artifact(remapJar) {
|
||||||
|
builtBy remapJar
|
||||||
|
}
|
||||||
|
|
||||||
|
artifact(sourcesJar) {
|
||||||
|
builtBy remapSourcesJar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupRepositories(repositories)
|
||||||
|
}
|
||||||
|
|
||||||
|
loom.disableDeprecatedPomGeneration(publishing.publications.mavenJava)
|
||||||
|
|
||||||
|
javadoc.enabled = false
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
genSources.enabled = false
|
||||||
|
unpickJar.enabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task remapMavenJar(type: net.fabricmc.loom.task.RemapJarTask, dependsOn: jar) {
|
||||||
|
input = jar.archiveFile
|
||||||
|
archiveFileName = "${archivesBaseName}-${project.version}-maven.jar"
|
||||||
|
addNestedDependencies = false
|
||||||
|
}
|
||||||
|
build.dependsOn remapMavenJar
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
artifact(remapMavenJar) {
|
||||||
|
builtBy remapMavenJar
|
||||||
|
}
|
||||||
|
|
||||||
|
artifact(sourcesJar) {
|
||||||
|
builtBy remapSourcesJar
|
||||||
|
}
|
||||||
|
|
||||||
|
pom.withXml {
|
||||||
|
def depsNode = getOrCreateNode(asNode(), "dependencies")
|
||||||
|
subprojects.each {
|
||||||
|
def depNode = depsNode.appendNode("dependency")
|
||||||
|
depNode.appendNode("groupId", it.group)
|
||||||
|
depNode.appendNode("artifactId", it.name)
|
||||||
|
depNode.appendNode("version", it.version)
|
||||||
|
depNode.appendNode("scope", "compile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupRepositories(repositories)
|
||||||
|
}
|
||||||
|
|
||||||
|
loom.disableDeprecatedPomGeneration(publishing.publications.mavenJava)
|
||||||
|
|
||||||
|
void setupRepositories(RepositoryHandler repositories) {
|
||||||
|
//repositories.mavenLocal() // uncomment for testing
|
||||||
|
def ENV = System.getenv()
|
||||||
|
if (ext.isPublicMaven) {
|
||||||
|
repositories.maven {
|
||||||
|
url = "https://gitlab.com/api/v4/projects/25805200/packages/maven"
|
||||||
|
name = "gitlab"
|
||||||
|
|
||||||
|
credentials(HttpHeaderCredentials) {
|
||||||
|
name = "Job-Token"
|
||||||
|
value = ENV.CI_JOB_TOKEN
|
||||||
|
}
|
||||||
|
authentication {
|
||||||
|
header(HttpHeaderAuthentication)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories.mavenLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
subprojects.each { remapJar.dependsOn("${it.path}:remapJar") }
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
testmod {
|
testmod
|
||||||
compileClasspath += main.compileClasspath + main.output
|
|
||||||
runtimeClasspath += main.runtimeClasspath + main.output
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def devOnlyModules = [
|
||||||
|
"libjf-devutil-v0"
|
||||||
|
]
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
//to change the versions see the gradle.properties file
|
afterEvaluate {
|
||||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
subprojects.each {
|
||||||
mappings "net.fabricmc:yarn:${project.minecraft_version}+${project.yarn_mappings}:v2"
|
api project(path: ":${it.name}", configuration: "dev")
|
||||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
|
||||||
|
|
||||||
include modImplementation(fabricApi.module("fabric-tag-extensions-v0", "0.40.1+1.17"))
|
if (!(it.name in devOnlyModules)) {
|
||||||
include modImplementation(fabricApi.module("fabric-resource-loader-v0", "0.40.1+1.17"))
|
include it
|
||||||
modImplementation "com.terraformersmc:modmenu:2.0.10"
|
}
|
||||||
}
|
|
||||||
|
|
||||||
loom {
|
testmodImplementation it.sourceSets.testmod.output
|
||||||
runs {
|
|
||||||
testmodClient {
|
|
||||||
client()
|
|
||||||
ideConfigGenerated project.rootProject == project
|
|
||||||
name = "Testmod Client"
|
|
||||||
source sourceSets.testmod
|
|
||||||
}
|
|
||||||
testmodServer {
|
|
||||||
server()
|
|
||||||
ideConfigGenerated project.rootProject == project
|
|
||||||
name = "Testmod Server"
|
|
||||||
source sourceSets.testmod
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task modrinth(type: TaskModrinthUpload, dependsOn: remapJar) {
|
||||||
|
onlyIf {
|
||||||
|
ENV.MODRINTH_API_TOKEN
|
||||||
|
}
|
||||||
|
|
||||||
|
token = ENV.MODRINTH_API_TOKEN
|
||||||
|
projectId = "WKwQAwke"
|
||||||
|
versionNumber = version
|
||||||
|
versionName = "[${project.minecraft_version}] ${project.mod_version}"
|
||||||
|
uploadFile = remapJar
|
||||||
|
addGameVersion("${project.minecraft_version}")
|
||||||
|
addLoader('fabric')
|
||||||
|
}
|
||||||
|
|
||||||
|
curseforge {
|
||||||
|
if (ENV.CURSEFORGE_API_TOKEN) {
|
||||||
|
apiKey = ENV.CURSEFORGE_API_TOKEN
|
||||||
|
} else {
|
||||||
|
println "No CURSEFORGE_API_TOKEN specified"
|
||||||
|
}
|
||||||
|
project {
|
||||||
|
id = "482600"
|
||||||
|
releaseType = 'release'
|
||||||
|
addGameVersion "Fabric"
|
||||||
|
addGameVersion "${project.minecraft_version}"
|
||||||
|
changelog = ""
|
||||||
|
mainArtifact(file("${project.buildDir}/libs/${archivesBaseName}-${version}.jar"))
|
||||||
|
mainArtifact.displayName = "[${project.minecraft_version}] ${project.mod_version}"
|
||||||
|
afterEvaluate {
|
||||||
|
uploadTask.dependsOn(remapJar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options {
|
||||||
|
forgeGradleIntegration = false
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,15 +4,10 @@ org.gradle.jvmargs=-Xmx1G
|
||||||
# check these on https://fabricmc.net/versions.html
|
# check these on https://fabricmc.net/versions.html
|
||||||
minecraft_version=1.17.1
|
minecraft_version=1.17.1
|
||||||
yarn_mappings=build.61
|
yarn_mappings=build.61
|
||||||
loader_version=0.11.6
|
loader_version=0.12.1
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=1.2.3
|
mod_version=2.0
|
||||||
maven_group=io.gitlab.jfronny
|
maven_group=io.gitlab.jfronny
|
||||||
archives_base_name=libjf
|
archives_base_name=libjf
|
||||||
|
|
||||||
modrinth_id=WKwQAwke
|
fabric_version=0.40.6+1.17
|
||||||
modrinth_required_dependencies=
|
|
||||||
modrinth_optional_dependencies=
|
|
||||||
curseforge_id=482600
|
|
||||||
curseforge_required_dependencies=
|
|
||||||
curseforge_optional_dependencies=
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
269
gradlew
vendored
269
gradlew
vendored
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -17,67 +17,101 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
@ -106,80 +140,95 @@ location of your Java installation."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
warn "Could not query maximum file descriptor limit"
|
||||||
fi
|
esac
|
||||||
ulimit -n $MAX_FD
|
case $MAX_FD in #(
|
||||||
if [ $? -ne 0 ] ; then
|
'' | soft) :;; #(
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
*)
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
else
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=`save "$@"`
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|
4
libjf-base/build.gradle
Normal file
4
libjf-base/build.gradle
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
archivesBaseName = "libjf-base"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
}
|
25
libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java
Normal file
25
libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package io.gitlab.jfronny.libjf;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import io.gitlab.jfronny.libjf.gson.HiddenAnnotationExclusionStrategy;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
public class LibJf {
|
||||||
|
private LibJf() {
|
||||||
|
}
|
||||||
|
public static final String MOD_ID = "libjf";
|
||||||
|
public static final Logger LOGGER = LogManager.getLogger(MOD_ID);
|
||||||
|
public static final Gson GSON = new GsonBuilder()
|
||||||
|
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
|
||||||
|
.excludeFieldsWithModifiers(Modifier.PRIVATE)
|
||||||
|
.addSerializationExclusionStrategy(new HiddenAnnotationExclusionStrategy())
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.create();
|
||||||
|
// Only ever set through the libjf-unsafe-v0 testmod or user code since it causes logspam
|
||||||
|
public static boolean DEV = false;
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package io.gitlab.jfronny.libjf;
|
||||||
|
|
||||||
|
import net.minecraft.resource.ResourceType;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
public class ResourcePath {
|
||||||
|
private final ResourceType type;
|
||||||
|
private final Identifier id;
|
||||||
|
|
||||||
|
public ResourcePath(ResourceType type, Identifier id) {
|
||||||
|
this.type = type;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourcePath(String name) throws IllegalStateException {
|
||||||
|
String[] s1 = name.split("/", 3);
|
||||||
|
if (s1.length != 3) {
|
||||||
|
throw new IllegalStateException("Could not split path string into resource type and ID due to insufficient length: " + name);
|
||||||
|
}
|
||||||
|
type = switch (s1[0]) {
|
||||||
|
case "assets" -> ResourceType.CLIENT_RESOURCES;
|
||||||
|
case "data" -> ResourceType.SERVER_DATA;
|
||||||
|
default -> throw new IllegalStateException("Unexpected value for resource type: " + s1[0] + " in: " + name);
|
||||||
|
};
|
||||||
|
id = new Identifier(s1[1], s1[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Identifier getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return String.format("%s/%s/%s", type.getDirectory(), id.getNamespace(), id.getPath());
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,9 @@ import com.google.gson.ExclusionStrategy;
|
||||||
import com.google.gson.FieldAttributes;
|
import com.google.gson.FieldAttributes;
|
||||||
|
|
||||||
public class HiddenAnnotationExclusionStrategy implements ExclusionStrategy {
|
public class HiddenAnnotationExclusionStrategy implements ExclusionStrategy {
|
||||||
public boolean shouldSkipClass(Class<?> clazz) { return false; }
|
public boolean shouldSkipClass(Class<?> clazz) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
|
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
|
||||||
return fieldAttributes.getAnnotation(GsonHidden.class) != null;
|
return fieldAttributes.getAnnotation(GsonHidden.class) != null;
|
||||||
}
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package io.gitlab.jfronny.libjf.interfaces;
|
||||||
|
|
||||||
|
public interface ThrowingRunnable<TEx extends Throwable> {
|
||||||
|
void run() throws TEx;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package io.gitlab.jfronny.libjf.interfaces;
|
||||||
|
|
||||||
|
public interface ThrowingSupplier<T, TEx extends Throwable> {
|
||||||
|
T get() throws TEx;
|
||||||
|
}
|
25
libjf-base/src/main/resources/fabric.mod.json
Normal file
25
libjf-base/src/main/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-base",
|
||||||
|
"name": "LibJF Base",
|
||||||
|
"version": "${version}",
|
||||||
|
"environment": "*",
|
||||||
|
"license": "MIT",
|
||||||
|
"contact": {
|
||||||
|
"website": "https://jfronny.gitlab.io",
|
||||||
|
"repo": "https://gitlab.com/jfmods/libjf"
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
"JFronny"
|
||||||
|
],
|
||||||
|
"depends": {
|
||||||
|
"fabricloader": ">=0.12.0",
|
||||||
|
"minecraft": "*"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"modmenu": {
|
||||||
|
"parent": "libjf",
|
||||||
|
"badges": ["library"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
libjf-base/src/testmod/resources/fabric.mod.json
Normal file
6
libjf-base/src/testmod/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-base-testmod",
|
||||||
|
"version": "1.0",
|
||||||
|
"environment": "*"
|
||||||
|
}
|
5
libjf-config-v0/build.gradle
Normal file
5
libjf-config-v0/build.gradle
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
archivesBaseName = "libjf-config-v0"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
moduleDependencies(project, ["libjf-base", "libjf-unsafe-v0"])
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package io.gitlab.jfronny.libjf.config;
|
package io.gitlab.jfronny.libjf.config.api;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
|
@ -0,0 +1,4 @@
|
||||||
|
package io.gitlab.jfronny.libjf.config.api;
|
||||||
|
|
||||||
|
public interface JfConfig {
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package io.gitlab.jfronny.libjf.config.impl;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.config.api.Entry;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Based on https://github.com/TeamMidnightDust/MidnightLib which is based on https://github.com/Minenash/TinyConfig
|
||||||
|
* Credits to TeamMidnightDust and Minenash */
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
public final List<EntryInfo> entries = new ArrayList<>();
|
||||||
|
public Path path;
|
||||||
|
public final String modid;
|
||||||
|
public final Class<?> configClass;
|
||||||
|
|
||||||
|
public Config(String modid, Class<?> config) {
|
||||||
|
this.modid = modid;
|
||||||
|
configClass = config;
|
||||||
|
path = FabricLoader.getInstance().getConfigDir().resolve(modid + ".json");
|
||||||
|
|
||||||
|
for (Field field : config.getFields()) {
|
||||||
|
EntryInfo info = new EntryInfo();
|
||||||
|
info.field = field;
|
||||||
|
if (field.isAnnotationPresent(Entry.class))
|
||||||
|
try {
|
||||||
|
info.defaultValue = field.get(null);
|
||||||
|
} catch (IllegalAccessException ignored) {}
|
||||||
|
entries.add(info);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
LibJf.GSON.fromJson(Files.newBufferedReader(path), config); }
|
||||||
|
catch (Exception e) { write(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write() {
|
||||||
|
path = FabricLoader.getInstance().getConfigDir().resolve(modid + ".json");
|
||||||
|
try {
|
||||||
|
if (!Files.exists(path)) Files.createFile(path);
|
||||||
|
Files.write(path, LibJf.GSON.toJson(configClass.getDeclaredConstructor().newInstance()).getBytes());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package io.gitlab.jfronny.libjf.config.impl;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ConfigHolder {
|
||||||
|
private ConfigHolder() {
|
||||||
|
}
|
||||||
|
private static final Map<String, Config> configs = new HashMap<>();
|
||||||
|
|
||||||
|
public static void registerConfig(String modId, Class<?> config) {
|
||||||
|
if (!isRegistered(config))
|
||||||
|
configs.put(modId, new Config(modId, config));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Config> getConfigs() {
|
||||||
|
return ImmutableMap.copyOf(configs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRegistered(Class<?> config) {
|
||||||
|
for (Config value : configs.values()) {
|
||||||
|
if (value.configClass.equals(config))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package io.gitlab.jfronny.libjf.config;
|
package io.gitlab.jfronny.libjf.config.impl;
|
||||||
|
|
||||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
|
@ -1,9 +1,9 @@
|
||||||
package io.gitlab.jfronny.libjf.config;
|
package io.gitlab.jfronny.libjf.config.impl;
|
||||||
|
|
||||||
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||||
import com.terraformersmc.modmenu.api.ModMenuApi;
|
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||||
import io.gitlab.jfronny.libjf.Libjf;
|
import io.gitlab.jfronny.libjf.config.impl.gui.TinyConfigScreen;
|
||||||
import io.gitlab.jfronny.libjf.config.gui.TinyConfigScreen;
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -12,8 +12,8 @@ public class ModMenu implements ModMenuApi {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
|
public Map<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
|
||||||
Map<String, ConfigScreenFactory<?>> factories = new HashMap<>();
|
Map<String, ConfigScreenFactory<?>> factories = new HashMap<>();
|
||||||
for (Map.Entry<String, Config> entry : Libjf.getConfigs().entrySet()) {
|
for (Map.Entry<String, Config> entry : ConfigHolder.getConfigs().entrySet()) {
|
||||||
if (!Libjf.MOD_ID.equals(entry.getKey()))
|
if (!LibJf.MOD_ID.equals(entry.getKey()))
|
||||||
factories.put(entry.getKey(), buildFactory(entry.getValue()));
|
factories.put(entry.getKey(), buildFactory(entry.getValue()));
|
||||||
}
|
}
|
||||||
return factories;
|
return factories;
|
|
@ -0,0 +1,20 @@
|
||||||
|
package io.gitlab.jfronny.libjf.config.impl.entry;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.config.impl.Config;
|
||||||
|
import io.gitlab.jfronny.libjf.config.impl.ConfigHolder;
|
||||||
|
import io.gitlab.jfronny.libjf.config.impl.EntryInfo;
|
||||||
|
import io.gitlab.jfronny.libjf.config.api.Entry;
|
||||||
|
import io.gitlab.jfronny.libjf.config.impl.gui.EntryInfoWidgetBuilder;
|
||||||
|
import io.gitlab.jfronny.libjf.gson.GsonHidden;
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
|
|
||||||
|
public class JfConfigClient implements ClientModInitializer {
|
||||||
|
@Override
|
||||||
|
public void onInitializeClient() {
|
||||||
|
for (Config config : ConfigHolder.getConfigs().values()) {
|
||||||
|
LibJf.LOGGER.info("Registring config UI for " + config.modid);
|
||||||
|
EntryInfoWidgetBuilder.initConfig(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package io.gitlab.jfronny.libjf.config.impl.entry;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.config.impl.ConfigHolder;
|
||||||
|
import io.gitlab.jfronny.libjf.config.api.JfConfig;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.fabricmc.loader.api.entrypoint.EntrypointContainer;
|
||||||
|
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
|
||||||
|
|
||||||
|
public class JfConfigSafe implements PreLaunchEntrypoint {
|
||||||
|
@Override
|
||||||
|
public void onPreLaunch() {
|
||||||
|
for (EntrypointContainer<JfConfig> config : FabricLoader.getInstance().getEntrypointContainers(LibJf.MOD_ID + ":config", JfConfig.class)) {
|
||||||
|
ConfigHolder.registerConfig(config.getProvider().getMetadata().getId(), config.getEntrypoint().getClass());
|
||||||
|
LibJf.LOGGER.info("Registering config for " + config.getProvider().getMetadata().getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package io.gitlab.jfronny.libjf.config.impl.entry;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.config.impl.ConfigHolder;
|
||||||
|
import io.gitlab.jfronny.libjf.config.api.JfConfig;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.DynamicEntry;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.UltraEarlyInit;
|
||||||
|
|
||||||
|
public class JfConfigUnsafe implements UltraEarlyInit {
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
DynamicEntry.execute(LibJf.MOD_ID + ":config", JfConfig.class, s -> {
|
||||||
|
ConfigHolder.registerConfig(s.modId(), s.instance().getClass());
|
||||||
|
LibJf.LOGGER.info("Registering config for " + s.modId());
|
||||||
|
});
|
||||||
|
LibJf.LOGGER.info("Finished LibJF config entrypoint");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package io.gitlab.jfronny.libjf.config.gui;
|
package io.gitlab.jfronny.libjf.config.impl.gui;
|
||||||
|
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
|
@ -1,77 +1,62 @@
|
||||||
package io.gitlab.jfronny.libjf.config;
|
package io.gitlab.jfronny.libjf.config.impl.gui;
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.Libjf;
|
import io.gitlab.jfronny.libjf.config.api.Entry;
|
||||||
|
import io.gitlab.jfronny.libjf.config.impl.Config;
|
||||||
|
import io.gitlab.jfronny.libjf.config.impl.EntryInfo;
|
||||||
import io.gitlab.jfronny.libjf.gson.GsonHidden;
|
import io.gitlab.jfronny.libjf.gson.GsonHidden;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||||
import net.minecraft.client.gui.widget.*;
|
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||||
import net.minecraft.text.*;
|
import net.minecraft.text.LiteralText;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.text.TranslatableText;
|
||||||
import net.minecraft.util.Formatting;
|
import net.minecraft.util.Formatting;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.util.AbstractMap;
|
||||||
import java.nio.file.Files;
|
import java.util.Arrays;
|
||||||
import java.nio.file.Path;
|
import java.util.List;
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/** Based on https://github.com/TeamMidnightDust/MidnightLib which is based on https://github.com/Minenash/TinyConfig
|
@Environment(EnvType.CLIENT)
|
||||||
* Credits to TeamMidnightDust and Minenash */
|
public class EntryInfoWidgetBuilder {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public class Config {
|
|
||||||
private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)");
|
private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)");
|
||||||
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)");
|
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)");
|
||||||
|
|
||||||
public final List<EntryInfo> entries = new ArrayList<>();
|
public static void initConfig(Config config) {
|
||||||
public Path path;
|
for (EntryInfo info : config.entries) {
|
||||||
public final String modid;
|
if (info.field.isAnnotationPresent(Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))
|
||||||
public final Class<?> configClass;
|
|
||||||
|
|
||||||
public Config(String modid, Class<?> config) {
|
|
||||||
this.modid = modid;
|
|
||||||
configClass = config;
|
|
||||||
path = FabricLoader.getInstance().getConfigDir().resolve(modid + ".json");
|
|
||||||
|
|
||||||
for (Field field : config.getFields()) {
|
|
||||||
EntryInfo info = new EntryInfo();
|
|
||||||
info.field = field;
|
|
||||||
if (field.isAnnotationPresent(Entry.class))
|
|
||||||
try {
|
try {
|
||||||
info.defaultValue = field.get(null);
|
initEntry(config, info);
|
||||||
} catch (IllegalAccessException ignored) {}
|
} catch (Exception ignored) {
|
||||||
entries.add(info);
|
}
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
Libjf.GSON.fromJson(Files.newBufferedReader(path), config); }
|
|
||||||
catch (Exception e) { write(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
private static void initEntry(Config config, EntryInfo info) {
|
||||||
public void initClient(EntryInfo info) {
|
if (!(info.field.isAnnotationPresent(io.gitlab.jfronny.libjf.config.api.Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))) return;
|
||||||
if (!(info.field.isAnnotationPresent(Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))) return;
|
|
||||||
Class<?> type = info.field.getType();
|
Class<?> type = info.field.getType();
|
||||||
Entry e = info.field.getAnnotation(Entry.class);
|
io.gitlab.jfronny.libjf.config.api.Entry e = info.field.getAnnotation(Entry.class);
|
||||||
info.width = e != null ? e.width() : 0;
|
info.width = e != null ? e.width() : 0;
|
||||||
|
|
||||||
if (e == null) return;
|
if (e == null) return;
|
||||||
|
|
||||||
if (type == int.class) textField(info, Integer::parseInt, INTEGER_ONLY, e.min(), e.max(), true);
|
if (type == int.class) textField(config, info, Integer::parseInt, INTEGER_ONLY, e.min(), e.max(), true);
|
||||||
else if (type == double.class) textField(info, Double::parseDouble, DECIMAL_ONLY, e.min(), e.max(),false);
|
else if (type == double.class) textField(config, info, Double::parseDouble, DECIMAL_ONLY, e.min(), e.max(),false);
|
||||||
else if (type == String.class) textField(info, String::length, null, Math.min(e.min(),0), Math.max(e.max(),1),true);
|
else if (type == String.class) textField(config, info, String::length, null, Math.min(e.min(),0), Math.max(e.max(),1),true);
|
||||||
else if (type == boolean.class) {
|
else if (type == boolean.class) {
|
||||||
Function<Object,Text> func = value -> new LiteralText((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED);
|
Function<Object, Text> func = value -> new LiteralText((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED);
|
||||||
info.widget = new AbstractMap.SimpleEntry<ButtonWidget.PressAction, Function<Object, Text>>(button -> {
|
info.widget = new AbstractMap.SimpleEntry<ButtonWidget.PressAction, Function<Object, Text>>(button -> {
|
||||||
info.value = !(Boolean) info.value;
|
info.value = !(Boolean) info.value;
|
||||||
button.setMessage(func.apply(info.value));
|
button.setMessage(func.apply(info.value));
|
||||||
}, func);
|
}, func);
|
||||||
} else if (type.isEnum()) {
|
} else if (type.isEnum()) {
|
||||||
List<?> values = Arrays.asList(info.field.getType().getEnumConstants());
|
List<?> values = Arrays.asList(info.field.getType().getEnumConstants());
|
||||||
Function<Object,Text> func = value -> new TranslatableText(modid + ".jfconfig." + "enum." + type.getSimpleName() + "." + info.value.toString());
|
Function<Object,Text> func = value -> new TranslatableText(config.modid + ".jfconfig." + "enum." + type.getSimpleName() + "." + info.value.toString());
|
||||||
info.widget = new AbstractMap.SimpleEntry<ButtonWidget.PressAction, Function<Object,Text>>( button -> {
|
info.widget = new AbstractMap.SimpleEntry<ButtonWidget.PressAction, Function<Object, Text>>(button -> {
|
||||||
int index = values.indexOf(info.value) + 1;
|
int index = values.indexOf(info.value) + 1;
|
||||||
info.value = values.get(index >= values.size()? 0 : index);
|
info.value = values.get(index >= values.size()? 0 : index);
|
||||||
button.setMessage(func.apply(info.value));
|
button.setMessage(func.apply(info.value));
|
||||||
|
@ -85,7 +70,7 @@ public class Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void textField(EntryInfo info, Function<String,Number> f, Pattern pattern, double min, double max, boolean cast) {
|
private static void textField(Config config, EntryInfo info, Function<String,Number> f, Pattern pattern, double min, double max, boolean cast) {
|
||||||
boolean isNumber = pattern != null;
|
boolean isNumber = pattern != null;
|
||||||
info.widget = (BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) (t, b) -> s -> {
|
info.widget = (BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) (t, b) -> s -> {
|
||||||
s = s.trim();
|
s = s.trim();
|
||||||
|
@ -107,7 +92,7 @@ public class Config {
|
||||||
info.tempValue = s;
|
info.tempValue = s;
|
||||||
t.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777);
|
t.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777);
|
||||||
info.inLimits = inLimits;
|
info.inLimits = inLimits;
|
||||||
b.active = entries.stream().allMatch(e -> e.inLimits);
|
b.active = config.entries.stream().allMatch(e -> e.inLimits);
|
||||||
|
|
||||||
if (inLimits)
|
if (inLimits)
|
||||||
info.value = isNumber? value : s;
|
info.value = isNumber? value : s;
|
||||||
|
@ -115,14 +100,4 @@ public class Config {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write() {
|
|
||||||
path = FabricLoader.getInstance().getConfigDir().resolve(modid + ".json");
|
|
||||||
try {
|
|
||||||
if (!Files.exists(path)) Files.createFile(path);
|
|
||||||
Files.write(path, Libjf.GSON.toJson(configClass.getDeclaredConstructor().newInstance()).getBytes());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package io.gitlab.jfronny.libjf.config.gui;
|
package io.gitlab.jfronny.libjf.config.impl.gui;
|
||||||
|
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
|
@ -1,14 +1,11 @@
|
||||||
package io.gitlab.jfronny.libjf.config.gui;
|
package io.gitlab.jfronny.libjf.config.impl.gui;
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.Libjf;
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
import io.gitlab.jfronny.libjf.config.Config;
|
import io.gitlab.jfronny.libjf.config.impl.Config;
|
||||||
import io.gitlab.jfronny.libjf.config.Entry;
|
import io.gitlab.jfronny.libjf.config.api.Entry;
|
||||||
import io.gitlab.jfronny.libjf.config.EntryInfo;
|
import io.gitlab.jfronny.libjf.config.impl.EntryInfo;
|
||||||
import io.gitlab.jfronny.libjf.config.gui.ButtonEntry;
|
|
||||||
import io.gitlab.jfronny.libjf.config.gui.MidnightConfigListWidget;
|
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
import net.minecraft.client.gui.screen.Screen;
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
import net.minecraft.client.gui.screen.ScreenTexts;
|
import net.minecraft.client.gui.screen.ScreenTexts;
|
||||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||||
|
@ -57,7 +54,7 @@ public class TinyConfigScreen extends Screen {
|
||||||
|
|
||||||
this.addDrawableChild(new ButtonWidget(this.width / 2 - 154, this.height - 28, 150, 20, ScreenTexts.CANCEL, button -> {
|
this.addDrawableChild(new ButtonWidget(this.width / 2 - 154, this.height - 28, 150, 20, ScreenTexts.CANCEL, button -> {
|
||||||
try {
|
try {
|
||||||
Libjf.GSON.fromJson(Files.newBufferedReader(config.path), config.configClass); }
|
LibJf.GSON.fromJson(Files.newBufferedReader(config.path), config.configClass); }
|
||||||
catch (Exception e) { config.write(); }
|
catch (Exception e) { config.write(); }
|
||||||
|
|
||||||
for (EntryInfo info : config.entries) {
|
for (EntryInfo info : config.entries) {
|
41
libjf-config-v0/src/main/resources/fabric.mod.json
Normal file
41
libjf-config-v0/src/main/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-config-v0",
|
||||||
|
"name": "LibJF Config",
|
||||||
|
"version": "${version}",
|
||||||
|
"authors": [
|
||||||
|
"JFronny"
|
||||||
|
],
|
||||||
|
"contact": {
|
||||||
|
"website": "https://jfronny.gitlab.io",
|
||||||
|
"repo": "https://gitlab.com/jfmods/libjf"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"environment": "*",
|
||||||
|
"entrypoints": {
|
||||||
|
"modmenu": [
|
||||||
|
"io.gitlab.jfronny.libjf.config.impl.ModMenu"
|
||||||
|
],
|
||||||
|
"client": [
|
||||||
|
"io.gitlab.jfronny.libjf.config.impl.entry.JfConfigClient"
|
||||||
|
],
|
||||||
|
"libjf:preEarly": [
|
||||||
|
"io.gitlab.jfronny.libjf.config.impl.entry.JfConfigUnsafe"
|
||||||
|
],
|
||||||
|
"preLaunch": [
|
||||||
|
"io.gitlab.jfronny.libjf.config.impl.entry.JfConfigSafe"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"depends": {
|
||||||
|
"fabricloader": ">=0.12.0",
|
||||||
|
"minecraft": "*",
|
||||||
|
"libjf-base": "${version}",
|
||||||
|
"libjf-unsafe-v0": "${version}"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"modmenu": {
|
||||||
|
"badges": ["library"],
|
||||||
|
"parent": "libjf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,19 @@
|
||||||
package io.gitlab.jfronny.libjf.test;
|
package io.gitlab.jfronny.libjf.config.test;
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.config.Entry;
|
import io.gitlab.jfronny.libjf.config.api.JfConfig;
|
||||||
import io.gitlab.jfronny.libjf.config.JfConfig;
|
import io.gitlab.jfronny.libjf.config.api.Entry;
|
||||||
import io.gitlab.jfronny.libjf.gson.GsonHidden;
|
import io.gitlab.jfronny.libjf.gson.GsonHidden;
|
||||||
|
|
||||||
public class TestMod implements JfConfig {
|
public class TestConfig implements JfConfig {
|
||||||
@Entry public static boolean disablePacks = false;
|
@Entry public static boolean disablePacks = false;
|
||||||
@Entry public static int intTest = 20;
|
@Entry public static int intTest = 20;
|
||||||
@Entry public static double decimalTest = 20;
|
@Entry public static double decimalTest = 20;
|
||||||
@Entry public static String dieStr = "lolz";
|
@Entry public static String dieStr = "lolz";
|
||||||
@Entry @GsonHidden public static String guiOnlyStr = "lolz";
|
@Entry @GsonHidden
|
||||||
|
public static String guiOnlyStr = "lolz";
|
||||||
public static String gsonOnlyStr = "lolz";
|
public static String gsonOnlyStr = "lolz";
|
||||||
@Entry public static Test enumTest = Test.Test;
|
@Entry
|
||||||
|
public static Test enumTest = Test.Test;
|
||||||
|
|
||||||
public enum Test {
|
public enum Test {
|
||||||
Test, ER
|
Test, ER
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"libjf-config-v0-testmod.jfconfig.title": "JfConfig example",
|
||||||
|
"libjf-config-v0-testmod.jfconfig.disablePacks": "Disable resource packs",
|
||||||
|
"libjf-config-v0-testmod.jfconfig.intTest": "Int Test",
|
||||||
|
"libjf-config-v0-testmod.jfconfig.decimalTest": "Decimal Test",
|
||||||
|
"libjf-config-v0-testmod.jfconfig.dieStr": "String Test",
|
||||||
|
"libjf-config-v0-testmod.jfconfig.enumTest": "Enum Test",
|
||||||
|
"libjf-config-v0-testmod.jfconfig.enumTest.tooltip": "Enum Test Tooltip",
|
||||||
|
"libjf-config-v0-testmod.jfconfig.enum.Test.Test": "Test",
|
||||||
|
"libjf-config-v0-testmod.jfconfig.enum.Test.ER": "ER"
|
||||||
|
}
|
11
libjf-config-v0/src/testmod/resources/fabric.mod.json
Normal file
11
libjf-config-v0/src/testmod/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-config-v0-testmod",
|
||||||
|
"version": "1.0",
|
||||||
|
"environment": "*",
|
||||||
|
"entrypoints": {
|
||||||
|
"libjf:config": [
|
||||||
|
"io.gitlab.jfronny.libjf.config.test.TestConfig"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
5
libjf-data-manipulation-v0/build.gradle
Normal file
5
libjf-data-manipulation-v0/build.gradle
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
archivesBaseName = "libjf-data-manipulation-v0"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
moduleDependencies(project, ["libjf-base", "libjf-unsafe-v0"])
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package io.gitlab.jfronny.libjf.data.manipulation.api;
|
||||||
|
|
||||||
|
import net.minecraft.item.Item;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class RecipeUtil {
|
||||||
|
private static final Set<Identifier> REMOVAL_BY_ID = new HashSet<>();
|
||||||
|
private static final Set<ItemStack> RECIPES_FOR_REMOVAL = new HashSet<>();
|
||||||
|
|
||||||
|
public static void removeRecipe(Identifier identifier) {
|
||||||
|
REMOVAL_BY_ID.add(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeRecipeFor(Item product) {
|
||||||
|
RECIPES_FOR_REMOVAL.add(product.getDefaultStack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isIdBlocked(Identifier identifier) {
|
||||||
|
return REMOVAL_BY_ID.contains(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isOutputBlocked(ItemStack product) {
|
||||||
|
for (ItemStack stack : RECIPES_FOR_REMOVAL) {
|
||||||
|
if (product.isItemEqual(stack)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package io.gitlab.jfronny.libjf.data.manipulation.api;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.data.manipulation.impl.ResourcePackHook;
|
||||||
|
import io.gitlab.jfronny.libjf.interfaces.ThrowingRunnable;
|
||||||
|
import io.gitlab.jfronny.libjf.interfaces.ThrowingSupplier;
|
||||||
|
import net.fabricmc.fabric.api.event.Event;
|
||||||
|
import net.fabricmc.fabric.api.event.EventFactory;
|
||||||
|
import net.minecraft.resource.ResourcePack;
|
||||||
|
import net.minecraft.resource.ResourceType;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public class UserResourceEvents {
|
||||||
|
public static <TVal, TEx extends Throwable> TVal disable(ThrowingSupplier<TVal, TEx> then) throws TEx {
|
||||||
|
try {
|
||||||
|
ResourcePackHook.setDisabled(true);
|
||||||
|
return then.get();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ResourcePackHook.setDisabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <TEx extends Throwable> void disable(ThrowingRunnable<TEx> then) throws TEx {
|
||||||
|
try {
|
||||||
|
ResourcePackHook.setDisabled(true);
|
||||||
|
then.run();
|
||||||
|
} finally {
|
||||||
|
ResourcePackHook.setDisabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Event<Contains> CONTAINS = EventFactory.createArrayBacked(Contains.class,
|
||||||
|
(listeners) -> (type, id, previous, pack) -> {
|
||||||
|
for (Contains listener : listeners) {
|
||||||
|
previous = listener.contains(type, id, previous, pack);
|
||||||
|
}
|
||||||
|
return previous;
|
||||||
|
});
|
||||||
|
|
||||||
|
public static final Event<FindResource> FIND_RESOURCE = EventFactory.createArrayBacked(FindResource.class,
|
||||||
|
(listeners) -> ((type, namespace, prefix, maxDepth, pathFilter, previous, pack) -> {
|
||||||
|
for (FindResource listener : listeners) {
|
||||||
|
previous = listener.findResources(type, namespace, prefix, maxDepth, pathFilter, previous, pack);
|
||||||
|
}
|
||||||
|
return previous;
|
||||||
|
}));
|
||||||
|
|
||||||
|
public static final Event<Open> OPEN = EventFactory.createArrayBacked(Open.class,
|
||||||
|
(listeners) -> ((type, id, previous, pack) -> {
|
||||||
|
for (Open listener : listeners) {
|
||||||
|
previous = listener.open(type, id, previous, pack);
|
||||||
|
}
|
||||||
|
return previous;
|
||||||
|
}));
|
||||||
|
|
||||||
|
public static final Event<OpenRoot> OPEN_ROOT = EventFactory.createArrayBacked(OpenRoot.class,
|
||||||
|
(listeners) -> ((fileName, previous, pack) -> {
|
||||||
|
for (OpenRoot listener : listeners) {
|
||||||
|
previous = listener.openRoot(fileName, previous, pack);
|
||||||
|
}
|
||||||
|
return previous;
|
||||||
|
}));
|
||||||
|
|
||||||
|
public interface Contains {
|
||||||
|
boolean contains(ResourceType type, Identifier id, boolean previous, ResourcePack pack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface FindResource {
|
||||||
|
Collection<Identifier> findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter, Collection<Identifier> previous, ResourcePack pack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Open {
|
||||||
|
InputStream open(ResourceType type, Identifier id, InputStream previous, ResourcePack pack) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OpenRoot {
|
||||||
|
InputStream openRoot(String fileName, InputStream previous, ResourcePack pack) throws IOException;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package io.gitlab.jfronny.libjf.data.manipulation.impl;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.ResourcePath;
|
||||||
|
import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents;
|
||||||
|
import net.minecraft.resource.ResourcePack;
|
||||||
|
import net.minecraft.resource.ResourceType;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class ResourcePackHook {
|
||||||
|
private static boolean disabled = false;
|
||||||
|
|
||||||
|
public static void setDisabled(boolean disabled) {
|
||||||
|
ResourcePackHook.disabled = disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDisabled() {
|
||||||
|
return disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hookContains(boolean value, ResourcePack pack, ResourceType type, Identifier id) {
|
||||||
|
return disabled ? value : UserResourceEvents.CONTAINS.invoker().contains(type, id, value, pack);
|
||||||
|
}
|
||||||
|
public static InputStream hookOpen(InputStream value, ResourcePack pack, ResourceType type, Identifier id) throws IOException {
|
||||||
|
if (disabled) return value;
|
||||||
|
InputStream is = UserResourceEvents.OPEN.invoker().open(type, id, value, pack);
|
||||||
|
if (is == null)
|
||||||
|
throw new FileNotFoundException(new ResourcePath(type, id).getName() + "CN");
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
public static InputStream hookOpenEx(IOException ex, ResourcePack pack, ResourceType type, Identifier id) throws IOException {
|
||||||
|
if (disabled) throw ex;
|
||||||
|
try {
|
||||||
|
return hookOpen(null, pack, type, id);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static Collection<Identifier> hookFindResources(Collection<Identifier> value, ResourcePack pack, ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter) {
|
||||||
|
return disabled ? value : UserResourceEvents.FIND_RESOURCE.invoker().findResources(type, namespace, prefix, maxDepth, pathFilter, value, pack);
|
||||||
|
}
|
||||||
|
public static InputStream hookOpenRoot(InputStream value, ResourcePack pack, String fileName) throws IOException {
|
||||||
|
if (disabled) return value;
|
||||||
|
InputStream is = value;
|
||||||
|
is = UserResourceEvents.OPEN_ROOT.invoker().openRoot(fileName, is, pack);
|
||||||
|
if (is == null)
|
||||||
|
throw new FileNotFoundException(fileName);
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
public static InputStream hookOpenRootEx(IOException ex, ResourcePack pack, String fileName) throws IOException {
|
||||||
|
if (disabled) throw ex;
|
||||||
|
try {
|
||||||
|
return hookOpenRoot(null, pack, fileName);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package io.gitlab.jfronny.libjf.data.manipulation.impl;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.AsmConfig;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.PatchUtil;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.modification.MethodModificationPatch;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.targeting.InterfaceImplTargetPatch;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ResourcePackHookPatch implements AsmConfig {
|
||||||
|
private static final String TARGET_CLASS_INTERMEDIARY = "net.minecraft.class_3262";
|
||||||
|
private static final String HOOK_IMPLEMENTATION = Type.getInternalName(ResourcePackHook.class);
|
||||||
|
@Override
|
||||||
|
public Set<String> skipClasses() {
|
||||||
|
return Set.of(PatchUtil.getRemapped(TARGET_CLASS_INTERMEDIARY),
|
||||||
|
"io.gitlab.jfronny.libjf.data.WrappedPack", //TODO remove for 1.18
|
||||||
|
"io.gitlab.jfronny.libjf.data.wrappedPackImpl.WrappedResourcePack",
|
||||||
|
"io.gitlab.jfronny.libjf.data.wrappedPackImpl.SafeWrappedResourcePack");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Patch> getPatches() {
|
||||||
|
// https://maven.fabricmc.net/docs/yarn-21w38a+build.9/net/minecraft/resource/ResourcePack.html
|
||||||
|
return Set.of(new InterfaceImplTargetPatch(TARGET_CLASS_INTERMEDIARY, new MethodModificationPatch(TARGET_CLASS_INTERMEDIARY, Set.of(
|
||||||
|
// open root
|
||||||
|
new MethodModificationPatch.MethodDescriptorPatch("method_14410", "(Ljava/lang/String;)Ljava/io/InputStream;", method -> {
|
||||||
|
catchEx(method, "hookOpenRootEx", new String[]{"java/lang/String"});
|
||||||
|
hookReturn(method, "hookOpenRoot", "Ljava/io/InputStream;", new String[]{"java/lang/String"});
|
||||||
|
}),
|
||||||
|
// open
|
||||||
|
new MethodModificationPatch.MethodDescriptorPatch("method_14405", "(Lnet/minecraft/class_3264;Lnet/minecraft/class_2960;)Ljava/io/InputStream;", method -> {
|
||||||
|
catchEx(method, "hookOpenEx", new String[]{"net/minecraft/class_3264", "net/minecraft/class_2960"});
|
||||||
|
hookReturn(method, "hookOpen", "Ljava/io/InputStream;", new String[]{"net/minecraft/class_3264", "net/minecraft/class_2960"});
|
||||||
|
}),
|
||||||
|
// find resource
|
||||||
|
new MethodModificationPatch.MethodDescriptorPatch("method_14408", "(Lnet/minecraft/class_3264;Ljava/lang/String;Ljava/lang/String;ILjava/util/function/Predicate;)Ljava/util/Collection;", method -> {
|
||||||
|
hookReturn(method, "hookFindResources", "Ljava/util/Collection;", new String[]{"net/minecraft/class_3264", "java/lang/String", "java/lang/String", "I", "java/util/function/Predicate"});
|
||||||
|
}),
|
||||||
|
// contains
|
||||||
|
new MethodModificationPatch.MethodDescriptorPatch("method_14411", "(Lnet/minecraft/class_3264;Lnet/minecraft/class_2960;)Z", method -> {
|
||||||
|
hookReturn(method, "hookContains", "Z", new String[]{"net/minecraft/class_3264", "net/minecraft/class_2960"});
|
||||||
|
})
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hookReturn(MethodNode method, String targetMethod, String targetType, String[] extraParamTypes) {
|
||||||
|
PatchUtil.redirectReturn(method, TARGET_CLASS_INTERMEDIARY, HOOK_IMPLEMENTATION, targetMethod, targetType, extraParamTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void catchEx(MethodNode method, String targetMethod, String[] extraParamTypes) {
|
||||||
|
PatchUtil.redirectExceptions(method, TARGET_CLASS_INTERMEDIARY, HOOK_IMPLEMENTATION, Type.getInternalName(IOException.class), Type.getInternalName(InputStream.class), targetMethod, extraParamTypes);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
package io.gitlab.jfronny.libjf.mixin;
|
package io.gitlab.jfronny.libjf.data.manipulation.mixin;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.gitlab.jfronny.libjf.Libjf;
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
import io.gitlab.jfronny.libjf.data.RecipeUtil;
|
import io.gitlab.jfronny.libjf.data.manipulation.api.RecipeUtil;
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
import net.minecraft.recipe.Recipe;
|
import net.minecraft.recipe.Recipe;
|
||||||
import net.minecraft.recipe.RecipeManager;
|
import net.minecraft.recipe.RecipeManager;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
@ -19,15 +18,15 @@ import java.util.Map;
|
||||||
|
|
||||||
@Mixin(RecipeManager.class)
|
@Mixin(RecipeManager.class)
|
||||||
public class RecipeManagerMixin {
|
public class RecipeManagerMixin {
|
||||||
@ModifyVariable(method = "apply", at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;", ordinal = 0, remap = false))
|
@ModifyVariable(method = "apply(Ljava/util/Map;Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/util/profiler/Profiler;)V", at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;", ordinal = 0, remap = false))
|
||||||
private Iterator<Map.Entry<Identifier, JsonObject>> filterIterator(Iterator<Map.Entry<Identifier, JsonObject>> iterator) {
|
private Iterator<Map.Entry<Identifier, JsonObject>> filterIterator(Iterator<Map.Entry<Identifier, JsonObject>> iterator) {
|
||||||
ArrayList<Map.Entry<Identifier, JsonObject>> replacement = new ArrayList<>();
|
ArrayList<Map.Entry<Identifier, JsonObject>> replacement = new ArrayList<>();
|
||||||
while(iterator.hasNext()) {
|
while(iterator.hasNext()) {
|
||||||
Map.Entry<Identifier, JsonObject> cur = iterator.next();
|
Map.Entry<Identifier, JsonObject> cur = iterator.next();
|
||||||
Identifier recipeId = cur.getKey();
|
Identifier recipeId = cur.getKey();
|
||||||
|
|
||||||
if (RecipeUtil.getIdentifiersForRemoval().contains(recipeId.toString())) {
|
if (RecipeUtil.isIdBlocked(recipeId)) {
|
||||||
Libjf.LOGGER.info("Blocking recipe by identifier: " + recipeId);
|
LibJf.LOGGER.info("Blocking recipe by identifier: " + recipeId);
|
||||||
} else {
|
} else {
|
||||||
replacement.add(cur);
|
replacement.add(cur);
|
||||||
}
|
}
|
||||||
|
@ -36,16 +35,14 @@ public class RecipeManagerMixin {
|
||||||
return replacement.iterator();
|
return replacement.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "apply", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableMap$Builder;put(Ljava/lang/Object;Ljava/lang/Object;)Lcom/google/common/collect/ImmutableMap$Builder;", remap = false))
|
@Redirect(method = "apply(Ljava/util/Map;Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/util/profiler/Profiler;)V", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableMap$Builder;put(Ljava/lang/Object;Ljava/lang/Object;)Lcom/google/common/collect/ImmutableMap$Builder;", remap = false))
|
||||||
private ImmutableMap.Builder<Identifier, Recipe<?>> onPutRecipe(ImmutableMap.Builder<Identifier, Recipe<?>> builder, Object key, Object value) {
|
private ImmutableMap.Builder<Identifier, Recipe<?>> onPutRecipe(ImmutableMap.Builder<Identifier, Recipe<?>> builder, Object key, Object value) {
|
||||||
Identifier id = (Identifier) key;
|
Identifier id = (Identifier) key;
|
||||||
Recipe<?> recipe = (Recipe<?>) value;
|
Recipe<?> recipe = (Recipe<?>) value;
|
||||||
|
|
||||||
for (ItemStack stack : RecipeUtil.getRecipesForRemoval()) {
|
if (RecipeUtil.isOutputBlocked(recipe.getOutput())) {
|
||||||
if (recipe.getOutput().isItemEqual(stack)) {
|
LibJf.LOGGER.info("Blocked recipe by predicate: " + recipe.getId());
|
||||||
Libjf.LOGGER.info("Blocked recipe by predicate: " + recipe.getId());
|
return builder;
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.put(id, recipe);
|
return builder.put(id, recipe);
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-data-manipulation-v0",
|
||||||
|
"name": "LibJF Data Manipulation",
|
||||||
|
"version": "${version}",
|
||||||
|
"authors": [
|
||||||
|
"JFronny"
|
||||||
|
],
|
||||||
|
"contact": {
|
||||||
|
"website": "https://jfronny.gitlab.io",
|
||||||
|
"repo": "https://gitlab.com/jfmods/libjf"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"environment": "*",
|
||||||
|
"mixins": ["libjf-data-manipulation-v0.mixins.json"],
|
||||||
|
"entrypoints": {
|
||||||
|
"libjf:asm": [
|
||||||
|
"io.gitlab.jfronny.libjf.data.manipulation.impl.ResourcePackHookPatch"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"depends": {
|
||||||
|
"fabricloader": ">=0.12.0",
|
||||||
|
"minecraft": "*",
|
||||||
|
"libjf-base": "${version}",
|
||||||
|
"libjf-unsafe-v0": "${version}"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"modmenu": {
|
||||||
|
"parent": "libjf",
|
||||||
|
"badges": ["library"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "io.gitlab.jfronny.libjf.data.manipulation.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_16",
|
||||||
|
"mixins": [
|
||||||
|
"RecipeManagerMixin"
|
||||||
|
],
|
||||||
|
"client": [
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package io.gitlab.jfronny.libjf.data.manipulation.test;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.data.manipulation.api.RecipeUtil;
|
||||||
|
import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents;
|
||||||
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
import net.minecraft.item.Items;
|
||||||
|
import net.minecraft.resource.DirectoryResourcePack;
|
||||||
|
|
||||||
|
public class TestEntrypoint implements ModInitializer {
|
||||||
|
@Override
|
||||||
|
public void onInitialize() {
|
||||||
|
// This should prevent resource packs from doing anything if my hooks are working and
|
||||||
|
UserResourceEvents.OPEN.register((type, id, previous, pack) -> {
|
||||||
|
if (pack instanceof DirectoryResourcePack) {
|
||||||
|
LibJf.LOGGER.info(pack.getName() + " opened " + type.name() + "/" + id.toString());
|
||||||
|
}
|
||||||
|
return previous;
|
||||||
|
});
|
||||||
|
UserResourceEvents.CONTAINS.register((type, id, previous, pack) -> {
|
||||||
|
if (pack instanceof DirectoryResourcePack) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return previous;
|
||||||
|
});
|
||||||
|
RecipeUtil.removeRecipeFor(Items.DIAMOND_SWORD);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-data-manipulation-v0-testmod",
|
||||||
|
"version": "1.0",
|
||||||
|
"environment": "*",
|
||||||
|
"entrypoints": {
|
||||||
|
"main": [
|
||||||
|
"io.gitlab.jfronny.libjf.data.manipulation.test.TestEntrypoint"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
5
libjf-data-v0/build.gradle
Normal file
5
libjf-data-v0/build.gradle
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
archivesBaseName = "libjf-data-v0"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
moduleDependencies(project, ["libjf-base"])
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package io.gitlab.jfronny.libjf.data;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import net.fabricmc.fabric.api.tag.TagFactory;
|
||||||
|
import net.minecraft.item.Item;
|
||||||
|
import net.minecraft.tag.Tag;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
public class Tags {
|
||||||
|
public static final Tag<Item> SHULKER_ILLEGAL = TagFactory.ITEM.create(new Identifier(LibJf.MOD_ID, "shulker_boxes_illegal"));
|
||||||
|
public static final Tag<Item> OVERPOWERED = TagFactory.ITEM.create(new Identifier(LibJf.MOD_ID, "overpowered"));
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package io.gitlab.jfronny.libjf.mixin;
|
package io.gitlab.jfronny.libjf.data.mixin;
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.data.Tags;
|
import io.gitlab.jfronny.libjf.data.Tags;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
|
@ -1,4 +1,4 @@
|
||||||
package io.gitlab.jfronny.libjf.mixin;
|
package io.gitlab.jfronny.libjf.data.mixin;
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.data.Tags;
|
import io.gitlab.jfronny.libjf.data.Tags;
|
||||||
import net.minecraft.block.entity.ShulkerBoxBlockEntity;
|
import net.minecraft.block.entity.ShulkerBoxBlockEntity;
|
|
@ -1,4 +1,4 @@
|
||||||
package io.gitlab.jfronny.libjf.mixin;
|
package io.gitlab.jfronny.libjf.data.mixin;
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.data.Tags;
|
import io.gitlab.jfronny.libjf.data.Tags;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
27
libjf-data-v0/src/main/resources/fabric.mod.json
Normal file
27
libjf-data-v0/src/main/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-data-v0",
|
||||||
|
"name": "LibJF Data",
|
||||||
|
"version": "${version}",
|
||||||
|
"authors": [
|
||||||
|
"JFronny"
|
||||||
|
],
|
||||||
|
"contact": {
|
||||||
|
"website": "https://jfronny.gitlab.io",
|
||||||
|
"repo": "https://gitlab.com/jfmods/libjf"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"environment": "*",
|
||||||
|
"mixins": ["libjf-data-v0.mixins.json"],
|
||||||
|
"depends": {
|
||||||
|
"fabricloader": ">=0.12.0",
|
||||||
|
"minecraft": "*",
|
||||||
|
"libjf-base": "${version}"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"modmenu": {
|
||||||
|
"parent": "libjf",
|
||||||
|
"badges": ["library"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,10 @@
|
||||||
{
|
{
|
||||||
"required": true,
|
"required": true,
|
||||||
"minVersion": "0.8",
|
"minVersion": "0.8",
|
||||||
"package": "io.gitlab.jfronny.libjf.mixin",
|
"package": "io.gitlab.jfronny.libjf.data.mixin",
|
||||||
"compatibilityLevel": "JAVA_16",
|
"compatibilityLevel": "JAVA_16",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"EntityMixin",
|
"EntityMixin",
|
||||||
"RecipeManagerMixin",
|
|
||||||
"ReloadableResourceManagerImplMixin",
|
|
||||||
"ShulkerBoxBlockEntityMixin",
|
"ShulkerBoxBlockEntityMixin",
|
||||||
"ShulkerBoxSlotMixin"
|
"ShulkerBoxSlotMixin"
|
||||||
],
|
],
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"replace": false,
|
||||||
|
"values": [
|
||||||
|
"minecraft:netherite_helmet",
|
||||||
|
"minecraft:netherite_chestplate",
|
||||||
|
"minecraft:netherite_leggings",
|
||||||
|
"minecraft:netherite_boots"
|
||||||
|
]
|
||||||
|
}
|
6
libjf-data-v0/src/testmod/resources/fabric.mod.json
Normal file
6
libjf-data-v0/src/testmod/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-data-v0-testmod",
|
||||||
|
"version": "1.0",
|
||||||
|
"environment": "*"
|
||||||
|
}
|
5
libjf-devutil-v0/build.gradle
Normal file
5
libjf-devutil-v0/build.gradle
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
archivesBaseName = "libjf-devutil-v0"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
moduleDependencies(project, ["libjf-base"])
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package io.gitlab.jfronny.libjf.devutil;
|
||||||
|
|
||||||
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
import net.minecraft.SharedConstants;
|
||||||
|
|
||||||
|
public class DevUtilMain implements ModInitializer {
|
||||||
|
@Override
|
||||||
|
public void onInitialize() {
|
||||||
|
SharedConstants.isDevelopment = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package io.gitlab.jfronny.libjf.devutil;
|
||||||
|
|
||||||
|
import com.mojang.authlib.minecraft.TelemetryEvent;
|
||||||
|
import com.mojang.authlib.minecraft.TelemetryPropertyContainer;
|
||||||
|
import com.mojang.authlib.minecraft.TelemetrySession;
|
||||||
|
import com.mojang.authlib.minecraft.UserApiService;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class NoOpUserApi implements UserApiService {
|
||||||
|
@Override
|
||||||
|
public boolean serversAllowed() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean realmsAllowed() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean chatAllowed() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean telemetryAllowed() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBlockedPlayer(UUID playerID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshBlockList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TelemetrySession newTelemetrySession(Executor executor) {
|
||||||
|
return new TelemetrySession() {
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TelemetryPropertyContainer globalProperties() {
|
||||||
|
return new TelemetryPropertyContainer() {
|
||||||
|
@Override
|
||||||
|
public void addProperty(String id, String value) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addProperty(String id, int value) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addProperty(String id, boolean value) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addNullProperty(String id) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventSetupFunction(Consumer<TelemetryPropertyContainer> eventSetupFunction) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TelemetryEvent createNewEvent(String type) {
|
||||||
|
return new TelemetryEvent() {
|
||||||
|
@Override
|
||||||
|
public void send() {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addProperty(String id, String value) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addProperty(String id, int value) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addProperty(String id, boolean value) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addNullProperty(String id) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package io.gitlab.jfronny.libjf.devutil.mixin;
|
||||||
|
|
||||||
|
import com.mojang.authlib.minecraft.UserApiService;
|
||||||
|
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||||
|
import io.gitlab.jfronny.libjf.devutil.NoOpUserApi;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.RunArgs;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
|
||||||
|
@Mixin(MinecraftClient.class)
|
||||||
|
public class MinecraftClientMixin {
|
||||||
|
/**
|
||||||
|
* @author JFronny
|
||||||
|
* @reason The social interactions service will not work in dev envs and only produces log spam
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
private UserApiService createUserApiService(YggdrasilAuthenticationService yggdrasilAuthenticationService, RunArgs runArgs) {
|
||||||
|
return new NoOpUserApi();
|
||||||
|
}
|
||||||
|
}
|
32
libjf-devutil-v0/src/main/resources/fabric.mod.json
Normal file
32
libjf-devutil-v0/src/main/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-devutil-v0",
|
||||||
|
"name": "LibJF Development Util",
|
||||||
|
"version": "${version}",
|
||||||
|
"authors": [
|
||||||
|
"JFronny"
|
||||||
|
],
|
||||||
|
"contact": {
|
||||||
|
"website": "https://jfronny.gitlab.io",
|
||||||
|
"repo": "https://gitlab.com/jfmods/libjf"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"environment": "*",
|
||||||
|
"entrypoints": {
|
||||||
|
"main": [
|
||||||
|
"io.gitlab.jfronny.libjf.devutil.DevUtilMain"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mixins": ["libjf-devutil-v0.mixins.json"],
|
||||||
|
"depends": {
|
||||||
|
"fabricloader": ">=0.12.0",
|
||||||
|
"minecraft": "*",
|
||||||
|
"libjf-base": "${version}"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"modmenu": {
|
||||||
|
"parent": "libjf",
|
||||||
|
"badges": ["library"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "io.gitlab.jfronny.libjf.devutil.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_16",
|
||||||
|
"mixins": [
|
||||||
|
"MinecraftClientMixin"
|
||||||
|
],
|
||||||
|
"client": [
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
5
libjf-unsafe-v0/build.gradle
Normal file
5
libjf-unsafe-v0/build.gradle
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
archivesBaseName = "libjf-unsafe-v0"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
moduleDependencies(project, ["libjf-base"])
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
package io.gitlab.jfronny.libjf.entry;
|
package io.gitlab.jfronny.libjf.unsafe;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
||||||
import net.fabricmc.loader.ModContainer;
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.fabricmc.loader.metadata.EntrypointMetadata;
|
import net.fabricmc.loader.api.ModContainer;
|
||||||
|
import net.fabricmc.loader.impl.ModContainerImpl;
|
||||||
|
import net.fabricmc.loader.impl.metadata.EntrypointMetadata;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@ -46,9 +46,8 @@ public class DynamicEntry {
|
||||||
|
|
||||||
private static ReferenceArrayList<EntrypointContainer> getEntrypointTargets(final String entrypoint) {
|
private static ReferenceArrayList<EntrypointContainer> getEntrypointTargets(final String entrypoint) {
|
||||||
final ReferenceArrayList<EntrypointContainer> entrypoints = ReferenceArrayList.wrap(new EntrypointContainer[1], 0);
|
final ReferenceArrayList<EntrypointContainer> entrypoints = ReferenceArrayList.wrap(new EntrypointContainer[1], 0);
|
||||||
|
for (final ModContainer mod : FabricLoader.getInstance().getAllMods()) {
|
||||||
for (final ModContainer mod : (Collection<ModContainer>) (Object) FabricLoader.getInstance().getAllMods()) {
|
final List<EntrypointMetadata> modEntrypoints = ((ModContainerImpl)mod).getInfo().getEntrypoints(entrypoint);
|
||||||
final List<EntrypointMetadata> modEntrypoints = mod.getInfo().getEntrypoints(entrypoint);
|
|
||||||
|
|
||||||
if (modEntrypoints != null) {
|
if (modEntrypoints != null) {
|
||||||
for (final EntrypointMetadata metadata : modEntrypoints) {
|
for (final EntrypointMetadata metadata : modEntrypoints) {
|
|
@ -0,0 +1,15 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import net.fabricmc.loader.api.LanguageAdapter;
|
||||||
|
|
||||||
|
public class JfLanguageAdapter implements LanguageAdapter {
|
||||||
|
@Override
|
||||||
|
public native <T> T create(net.fabricmc.loader.api.ModContainer mod, String value, Class<T> type);
|
||||||
|
|
||||||
|
static {
|
||||||
|
DynamicEntry.execute(LibJf.MOD_ID + ":preEarly", UltraEarlyInit.class, s -> s.instance().init());
|
||||||
|
DynamicEntry.execute(LibJf.MOD_ID + ":early", UltraEarlyInit.class, s -> s.instance().init());
|
||||||
|
LibJf.LOGGER.info("LibJF unsafe init completed");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.AsmConfig;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.AsmTransformer;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.BakedAsmConfig;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||||
|
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||||
|
import org.spongepowered.asm.mixin.transformer.IMixinTransformer;
|
||||||
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class MixinPlugin implements IMixinConfigPlugin {
|
||||||
|
@Override
|
||||||
|
public void onLoad(String mixinPackage) {
|
||||||
|
try {
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
Class<?> classLoaderClass = classLoader.getClass();
|
||||||
|
|
||||||
|
Field delegateField = classLoaderClass.getDeclaredField("delegate");
|
||||||
|
delegateField.setAccessible(true);
|
||||||
|
Object delegate = delegateField.get(classLoader);
|
||||||
|
Class<?> delegateClass = delegate.getClass();
|
||||||
|
|
||||||
|
Field mixinTransformerField = delegateClass.getDeclaredField("mixinTransformer");
|
||||||
|
mixinTransformerField.setAccessible(true);
|
||||||
|
|
||||||
|
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
|
||||||
|
unsafeField.setAccessible(true);
|
||||||
|
Unsafe unsafe = (Unsafe) unsafeField.get(null);
|
||||||
|
|
||||||
|
AsmTransformer.INSTANCE = (AsmTransformer) unsafe.allocateInstance(AsmTransformer.class);
|
||||||
|
AsmTransformer.INSTANCE.delegate = (IMixinTransformer) mixinTransformerField.get(delegate);
|
||||||
|
AsmTransformer.INSTANCE.asmConfigs = new HashSet<>();
|
||||||
|
DynamicEntry.execute(LibJf.MOD_ID + ":asm", AsmConfig.class, s -> {
|
||||||
|
LibJf.LOGGER.info("Discovered LibJF asm plugin in " + s.modId());
|
||||||
|
AsmTransformer.INSTANCE.asmConfigs.add(new BakedAsmConfig(s.instance()));
|
||||||
|
});
|
||||||
|
|
||||||
|
mixinTransformerField.set(delegate, AsmTransformer.INSTANCE);
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException | InstantiationException e) {
|
||||||
|
LibJf.LOGGER.error("Could not initialize LibJF ASM", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRefMapperConfig() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getMixins() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe;
|
||||||
|
|
||||||
|
public interface UltraEarlyInit {
|
||||||
|
void init();
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.asm;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface AsmConfig {
|
||||||
|
Set<String> skipClasses();
|
||||||
|
Set<Patch> getPatches();
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.asm;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.fabricmc.loader.api.MappingResolver;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||||
|
import org.spongepowered.asm.mixin.transformer.IMixinTransformer;
|
||||||
|
import org.spongepowered.asm.mixin.transformer.ext.IExtensionRegistry;
|
||||||
|
import org.spongepowered.asm.transformers.MixinClassWriter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class AsmTransformer implements IMixinTransformer {
|
||||||
|
public static AsmTransformer INSTANCE;
|
||||||
|
public static final MappingResolver MAPPING_RESOLVER = FabricLoader.getInstance().getMappingResolver();
|
||||||
|
public static final String INTERMEDIARY = "intermediary";
|
||||||
|
public IMixinTransformer delegate;
|
||||||
|
public Set<AsmConfig> asmConfigs;
|
||||||
|
private AsmConfig currentConfig = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void audit(MixinEnvironment environment) {
|
||||||
|
delegate.audit(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> reload(String mixinClass, ClassNode classNode) {
|
||||||
|
return delegate.reload(mixinClass, classNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean computeFramesForClass(MixinEnvironment environment, String name, ClassNode classNode) {
|
||||||
|
return delegate.computeFramesForClass(environment, name, classNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transformClassBytes(String name, String transformedName, byte[] classBytes) {
|
||||||
|
classBytes = delegate.transformClassBytes(name, transformedName, classBytes);
|
||||||
|
if (classBytes == null || name == null)
|
||||||
|
return classBytes;
|
||||||
|
if (isClassUnmoddable(name, null)) {
|
||||||
|
if (LibJf.DEV) LibJf.LOGGER.info("Skipping " + name);
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassNode klazz = new ClassNode();
|
||||||
|
ClassReader reader = new ClassReader(classBytes);
|
||||||
|
reader.accept(klazz, ClassReader.EXPAND_FRAMES); //ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG
|
||||||
|
|
||||||
|
//if ((klazz.access & Opcodes.ACC_INTERFACE) != 0) {
|
||||||
|
// return classBytes;
|
||||||
|
//}
|
||||||
|
|
||||||
|
for (AsmConfig config : asmConfigs) {
|
||||||
|
currentConfig = config;
|
||||||
|
if (!isClassUnmoddable(name, config)) {
|
||||||
|
for (Patch patch : config.getPatches()) {
|
||||||
|
patch.apply(klazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentConfig = null;
|
||||||
|
|
||||||
|
ClassWriter writer = new MixinClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
try {
|
||||||
|
klazz.accept(writer);
|
||||||
|
}
|
||||||
|
catch (NullPointerException t) {
|
||||||
|
LibJf.LOGGER.error("Could not transform " + transformedName, t);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
classBytes = writer.toByteArray();
|
||||||
|
//MixinEnvironment.getCurrentEnvironment();
|
||||||
|
return classBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transformClass(MixinEnvironment environment, String name, byte[] classBytes) {
|
||||||
|
LibJf.LOGGER.error("transformClass called");
|
||||||
|
return delegate.transformClass(environment, name, classBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean transformClass(MixinEnvironment environment, String name, ClassNode classNode) {
|
||||||
|
LibJf.LOGGER.error("transformClass called");
|
||||||
|
return delegate.transformClass(environment, name, classNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] generateClass(MixinEnvironment environment, String name) {
|
||||||
|
LibJf.LOGGER.error("generateClass called");
|
||||||
|
return delegate.generateClass(environment, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean generateClass(MixinEnvironment environment, String name, ClassNode classNode) {
|
||||||
|
LibJf.LOGGER.error("generateClass called");
|
||||||
|
return delegate.generateClass(environment, name, classNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IExtensionRegistry getExtensions() {
|
||||||
|
return delegate.getExtensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isClassUnmoddable(String className, AsmConfig config) {
|
||||||
|
if (className.replace('/', '.').startsWith("org.objectweb.asm")
|
||||||
|
//|| className.startsWith("net.fabricmc.loader")
|
||||||
|
//|| className.startsWith("io.gitlab.jfronny.libjf.unsafe.asm")
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
if (config == null) return false;
|
||||||
|
Set<String> classes = config.skipClasses();
|
||||||
|
if (classes == null) return false;
|
||||||
|
return classes.contains(MAPPING_RESOLVER.unmapClassName(INTERMEDIARY, className));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsmConfig getCurrentConfig() {
|
||||||
|
return currentConfig;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.asm;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class BakedAsmConfig implements AsmConfig {
|
||||||
|
private final Set<String> skipClasses;
|
||||||
|
private final Set<Patch> patches;
|
||||||
|
public BakedAsmConfig(AsmConfig config) {
|
||||||
|
skipClasses = config.skipClasses();
|
||||||
|
patches = config.getPatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> skipClasses() {
|
||||||
|
return skipClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Patch> getPatches() {
|
||||||
|
return patches;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.asm.patch;
|
||||||
|
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
public interface MethodPatch {
|
||||||
|
void apply(MethodNode method);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.asm.patch;
|
||||||
|
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
|
||||||
|
public interface Patch {
|
||||||
|
void apply(ClassNode klazz);
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.asm.patch;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.AsmTransformer;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
|
public class PatchUtil {
|
||||||
|
public static String getRemappedInternal(String className) {
|
||||||
|
return getRemapped(className.replace('/', '.')).replace('.', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRemapped(String className) {
|
||||||
|
return AsmTransformer.MAPPING_RESOLVER.mapClassName(AsmTransformer.INTERMEDIARY, className);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void redirectReturn(MethodNode method, String targetClass, String hookClass, String targetMethod, String targetType, String[] extraParamTypes) {
|
||||||
|
for (AbstractInsnNode node : method.instructions) {
|
||||||
|
if (node.getOpcode() == Opcodes.ARETURN
|
||||||
|
|| node.getOpcode() == Opcodes.IRETURN
|
||||||
|
|| node.getOpcode() == Opcodes.DRETURN
|
||||||
|
|| node.getOpcode() == Opcodes.FRETURN
|
||||||
|
|| node.getOpcode() == Opcodes.LRETURN) {
|
||||||
|
method.instructions.insertBefore(node, PatchUtil.buildParamPassingInvoker(targetClass, hookClass, targetMethod, targetType, targetType, extraParamTypes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void redirectExceptions(MethodNode method, String targetClass, String hookClass, String exceptionType, String returnTypeNormal, String targetMethod, String[] extraParamTypes) {
|
||||||
|
LabelNode start = new LabelNode();
|
||||||
|
LabelNode end = new LabelNode();
|
||||||
|
LabelNode handler = new LabelNode();
|
||||||
|
method.instructions.insertBefore(method.instructions.getFirst(), start);
|
||||||
|
method.instructions.insertBefore(method.instructions.getLast(), end);
|
||||||
|
method.instructions.add(handler);
|
||||||
|
method.instructions.add(PatchUtil.buildParamPassingInvoker(targetClass,
|
||||||
|
hookClass,
|
||||||
|
targetMethod,
|
||||||
|
"L" + getRemappedInternal(exceptionType) + ";",
|
||||||
|
"L" + getRemappedInternal(returnTypeNormal) + ";",
|
||||||
|
extraParamTypes));
|
||||||
|
method.instructions.add(new InsnNode(Opcodes.ARETURN));
|
||||||
|
method.tryCatchBlocks.add(new TryCatchBlockNode(start, end, handler, exceptionType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InsnList buildParamPassingInvoker(String targetClass, String hookClass, String targetMethod, String inType, String outType, String[] extraParamTypes) {
|
||||||
|
InsnList instructions = new InsnList();
|
||||||
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
StringBuilder descriptor = new StringBuilder("(").append(inType);
|
||||||
|
descriptor.append('L').append(getRemappedInternal(targetClass)).append(';');
|
||||||
|
for (int i = 0; i < extraParamTypes.length; i++) {
|
||||||
|
instructions.add(new VarInsnNode(switch (extraParamTypes[i]) {
|
||||||
|
case "I" -> Opcodes.ILOAD;
|
||||||
|
case "D" -> Opcodes.DLOAD;
|
||||||
|
case "F" -> Opcodes.FLOAD;
|
||||||
|
case "L" -> Opcodes.LLOAD;
|
||||||
|
default -> Opcodes.ALOAD;
|
||||||
|
}, i + 1));
|
||||||
|
if (extraParamTypes[i].length() == 1)
|
||||||
|
descriptor.append(extraParamTypes[i]);
|
||||||
|
else
|
||||||
|
descriptor.append('L').append(PatchUtil.getRemappedInternal(extraParamTypes[i])).append(';');
|
||||||
|
}
|
||||||
|
descriptor.append(")").append(outType);
|
||||||
|
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, hookClass, targetMethod, descriptor.toString()));
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.asm.patch.method;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.MethodPatch;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.InsnList;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
public class MethodReplacementPatch implements MethodPatch {
|
||||||
|
private final InsnList instructions;
|
||||||
|
public MethodReplacementPatch(InsnList instructions) {
|
||||||
|
this.instructions = instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(MethodNode method) {
|
||||||
|
method.instructions.clear();
|
||||||
|
InsnList clone = new InsnList();
|
||||||
|
for (AbstractInsnNode instruction : instructions) {
|
||||||
|
clone.add(instruction);
|
||||||
|
}
|
||||||
|
method.instructions.insert(clone);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.asm.patch.modification;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.AsmTransformer;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.MethodPatch;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class MethodModificationPatch implements Patch {
|
||||||
|
private final Map<String, MethodPatch> patches = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public MethodModificationPatch(String targetMethodOwner, Set<MethodDescriptorPatch> patches) {
|
||||||
|
for (MethodDescriptorPatch patch : patches) {
|
||||||
|
this.patches.put(
|
||||||
|
AsmTransformer.MAPPING_RESOLVER.mapMethodName(AsmTransformer.INTERMEDIARY, targetMethodOwner, patch.methodName, patch.methodDescriptor),
|
||||||
|
patch.patch
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (String s : this.patches.keySet()) {
|
||||||
|
if (LibJf.DEV) LibJf.LOGGER.info("Registered patch for " + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(ClassNode klazz) {
|
||||||
|
for (MethodNode method : klazz.methods) {
|
||||||
|
if (patches.containsKey(method.name)) {
|
||||||
|
if (LibJf.DEV) LibJf.LOGGER.info("Patching " + method.name);
|
||||||
|
patches.get(method.name).apply(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static record MethodDescriptorPatch(String methodName, String methodDescriptor, MethodPatch patch) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.asm.patch.targeting;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.AsmTransformer;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class InterfaceImplTargetPatch implements Patch {
|
||||||
|
public static final Map<String, Set<String>> INTERFACES = new HashMap<>();
|
||||||
|
private final String targetInterface;
|
||||||
|
private final Patch methodPatch;
|
||||||
|
|
||||||
|
public InterfaceImplTargetPatch(String targetInterfaceIntermediary, Patch methodPatch) {
|
||||||
|
this.targetInterface = AsmTransformer.MAPPING_RESOLVER.mapClassName(AsmTransformer.INTERMEDIARY, targetInterfaceIntermediary).replace('.', '/');
|
||||||
|
this.methodPatch = methodPatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(ClassNode klazz) {
|
||||||
|
scanInterfaces(klazz);
|
||||||
|
if (getUpper(klazz.name).contains(targetInterface)) {
|
||||||
|
if (LibJf.DEV) LibJf.LOGGER.info("Found " + klazz.name + " implementing " + targetInterface);
|
||||||
|
methodPatch.apply(klazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void scanInterfaces(ClassNode klazz) {
|
||||||
|
if (INTERFACES.containsKey(klazz.name)) return;
|
||||||
|
INTERFACES.put(klazz.name, new HashSet<>());
|
||||||
|
for (String anInterface : klazz.interfaces) {
|
||||||
|
INTERFACES.get(klazz.name).add(anInterface);
|
||||||
|
}
|
||||||
|
if (klazz.superName != null) {
|
||||||
|
INTERFACES.get(klazz.name).add(klazz.superName);
|
||||||
|
}
|
||||||
|
INTERFACES.put(klazz.name, Set.copyOf(INTERFACES.get(klazz.name)));
|
||||||
|
for (String s : INTERFACES.get(klazz.name)) {
|
||||||
|
String n = s.replace('/', '.');
|
||||||
|
if (AsmTransformer.isClassUnmoddable(n, AsmTransformer.INSTANCE.getCurrentConfig()))
|
||||||
|
continue;
|
||||||
|
try {
|
||||||
|
InterfaceImplTargetPatch.class.getClassLoader().loadClass(n);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Could not load super class " + s + " of " + klazz.name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void scanInterfaces(Class<?> klazz) {
|
||||||
|
String n = Type.getInternalName(klazz);
|
||||||
|
if (INTERFACES.containsKey(n)) return;
|
||||||
|
INTERFACES.put(n, new HashSet<>());
|
||||||
|
for (Class<?> anInterface : klazz.getInterfaces()) {
|
||||||
|
INTERFACES.get(n).add(Type.getInternalName(anInterface));
|
||||||
|
}
|
||||||
|
Class<?> superC = klazz.getSuperclass();
|
||||||
|
if (superC != null) {
|
||||||
|
INTERFACES.get(n).add(Type.getInternalName(superC));
|
||||||
|
}
|
||||||
|
INTERFACES.put(n, Set.copyOf(INTERFACES.get(n)));
|
||||||
|
for (String s : INTERFACES.get(n)) {
|
||||||
|
String nn = s.replace('/', '.');
|
||||||
|
if (AsmTransformer.isClassUnmoddable(nn, AsmTransformer.INSTANCE.getCurrentConfig()))
|
||||||
|
continue;
|
||||||
|
try {
|
||||||
|
scanInterfaces(InterfaceImplTargetPatch.class.getClassLoader().loadClass(nn));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException("Could not load super class " + s + " of " + n, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getUpper(String className) {
|
||||||
|
Set<String> s = INTERFACES.get(className);
|
||||||
|
if (s == null) {
|
||||||
|
if (!className.startsWith("java/")
|
||||||
|
&& !className.startsWith("com/mojang/")
|
||||||
|
&& !className.startsWith("net/minecraft/")
|
||||||
|
&& !className.startsWith("jdk/")
|
||||||
|
&& !className.startsWith("it/unimi/dsi/fastutil/")
|
||||||
|
&& !className.startsWith("com/google/")
|
||||||
|
) {
|
||||||
|
if (LibJf.DEV) LibJf.LOGGER.info("Non-default class not considered for interface scanning: " + className);
|
||||||
|
INTERFACES.put(className, Set.of());
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
scanInterfaces(Class.forName(className.replace('/', '.')));
|
||||||
|
s = INTERFACES.get(className);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
LibJf.LOGGER.error("Could not get base for " + className, e);
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = new HashSet<>(s);
|
||||||
|
for (String s1 : s.toArray(new String[0])) {
|
||||||
|
s.addAll(getUpper(s1));
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
30
libjf-unsafe-v0/src/main/resources/fabric.mod.json
Normal file
30
libjf-unsafe-v0/src/main/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-unsafe-v0",
|
||||||
|
"name": "LibJF Unsafe",
|
||||||
|
"version": "${version}",
|
||||||
|
"authors": [
|
||||||
|
"JFronny"
|
||||||
|
],
|
||||||
|
"contact": {
|
||||||
|
"website": "https://jfronny.gitlab.io",
|
||||||
|
"repo": "https://gitlab.com/jfmods/libjf"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"environment": "*",
|
||||||
|
"languageAdapters": {
|
||||||
|
"libjf": "io.gitlab.jfronny.libjf.unsafe.JfLanguageAdapter"
|
||||||
|
},
|
||||||
|
"mixins": ["libjf-unsafe-v0.mixins.json"],
|
||||||
|
"depends": {
|
||||||
|
"fabricloader": ">=0.12.0",
|
||||||
|
"minecraft": "*",
|
||||||
|
"libjf-base": "${version}"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"modmenu": {
|
||||||
|
"parent": "libjf",
|
||||||
|
"badges": ["library"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "io.gitlab.jfronny.libjf.data.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_16",
|
||||||
|
"plugin": "io.gitlab.jfronny.libjf.unsafe.MixinPlugin",
|
||||||
|
"mixins": [
|
||||||
|
],
|
||||||
|
"client": [
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.test;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.AsmConfig;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
|
||||||
|
import net.minecraft.client.main.Main;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class AsmTest implements AsmConfig {
|
||||||
|
@Override
|
||||||
|
public Set<String> skipClasses() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Patch> getPatches() {
|
||||||
|
return Set.of(klazz -> {
|
||||||
|
if (klazz.name.equals("com/mojang/blaze3d/systems/RenderSystem")) {
|
||||||
|
for (MethodNode method : klazz.methods) {
|
||||||
|
if (method.name.equals("initRenderThread")) {
|
||||||
|
method.instructions.insert(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(AsmTest.class), "logSuccess", "()V"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logSuccess() {
|
||||||
|
LibJf.LOGGER.info("Successfully ASMd into RenderSystem\n" +
|
||||||
|
":::'###:::::'######::'##::::'##:'########:'########::'######::'########:\n" +
|
||||||
|
"::'## ##:::'##... ##: ###::'###:... ##..:: ##.....::'##... ##:... ##..::\n" +
|
||||||
|
":'##:. ##:: ##:::..:: ####'####:::: ##:::: ##::::::: ##:::..::::: ##::::\n" +
|
||||||
|
"'##:::. ##:. ######:: ## ### ##:::: ##:::: ######:::. ######::::: ##::::\n" +
|
||||||
|
" #########::..... ##: ##. #: ##:::: ##:::: ##...:::::..... ##:::: ##::::\n" +
|
||||||
|
" ##.... ##:'##::: ##: ##:.:: ##:::: ##:::: ##:::::::'##::: ##:::: ##::::\n" +
|
||||||
|
" ##:::: ##:. ######:: ##:::: ##:::: ##:::: ########:. ######::::: ##::::\n" +
|
||||||
|
"..:::::..:::......:::..:::::..:::::..:::::........:::......::::::..:::::");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package io.gitlab.jfronny.libjf.unsafe.test;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.LibJf;
|
||||||
|
import io.gitlab.jfronny.libjf.unsafe.UltraEarlyInit;
|
||||||
|
|
||||||
|
public class UnsafeEntryTest implements UltraEarlyInit {
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
LibJf.LOGGER.info("Successfully executed code before that should be possible\n" +
|
||||||
|
"'||' '|' '|| . '||''''| '|| \n" +
|
||||||
|
" || | || .||. ... .. .... || . .... ... .. || .... ... \n" +
|
||||||
|
" || | || || ||' '' '' .|| ||''| '' .|| ||' '' || '|. | \n" +
|
||||||
|
" || | || || || .|' || || .|' || || || '|.| \n" +
|
||||||
|
" '|..' .||. '|.' .||. '|..'|' .||.....| '|..'|' .||. .||. '| \n" +
|
||||||
|
" .. | \n" +
|
||||||
|
" '' ");
|
||||||
|
LibJf.DEV = true;
|
||||||
|
}
|
||||||
|
}
|
14
libjf-unsafe-v0/src/testmod/resources/fabric.mod.json
Normal file
14
libjf-unsafe-v0/src/testmod/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "libjf-unsafe-v0-testmod",
|
||||||
|
"version": "1.0",
|
||||||
|
"environment": "*",
|
||||||
|
"entrypoints": {
|
||||||
|
"libjf:asm": [
|
||||||
|
"io.gitlab.jfronny.libjf.unsafe.test.AsmTest"
|
||||||
|
],
|
||||||
|
"libjf:early": [
|
||||||
|
"io.gitlab.jfronny.libjf.unsafe.test.UnsafeEntryTest"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,3 +7,13 @@ pluginManagement {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rootProject.name = "libjf"
|
||||||
|
|
||||||
|
include 'libjf-base'
|
||||||
|
|
||||||
|
include 'libjf-config-v0'
|
||||||
|
include 'libjf-data-v0'
|
||||||
|
include 'libjf-data-manipulation-v0'
|
||||||
|
//include 'libjf-devutil-v0' //TODO re-enable for 1.18
|
||||||
|
include 'libjf-unsafe-v0'
|
|
@ -1,41 +1,27 @@
|
||||||
package io.gitlab.jfronny.libjf;
|
package io.gitlab.jfronny.libjf;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import io.gitlab.jfronny.libjf.config.impl.ConfigHolder;
|
||||||
import com.google.gson.Gson;
|
import io.gitlab.jfronny.libjf.data.WrappedPack;
|
||||||
import com.google.gson.GsonBuilder;
|
import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents;
|
||||||
import io.gitlab.jfronny.libjf.config.Config;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import io.gitlab.jfronny.libjf.gson.HiddenAnnotationExclusionStrategy;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
//TODO remove for 1.18
|
||||||
import java.util.HashMap;
|
@Deprecated(forRemoval = true)
|
||||||
import java.util.Map;
|
public class Libjf implements ModInitializer {
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
public class Libjf {
|
|
||||||
public static final String MOD_ID = "libjf";
|
|
||||||
public static final Logger LOGGER = LogManager.getLogger(MOD_ID);
|
|
||||||
public static final Gson GSON = new GsonBuilder()
|
|
||||||
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
|
|
||||||
.excludeFieldsWithModifiers(Modifier.PRIVATE)
|
|
||||||
.addSerializationExclusionStrategy(new HiddenAnnotationExclusionStrategy())
|
|
||||||
.setPrettyPrinting()
|
|
||||||
.create();
|
|
||||||
private static final Map<String, Config> configs = new HashMap<>();
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public static void registerConfig(String modId, Class<?> config) {
|
public static void registerConfig(String modId, Class<?> config) {
|
||||||
if (!isRegistered(config))
|
ConfigHolder.registerConfig(modId, config);
|
||||||
configs.put(modId, new Config(modId, config));
|
|
||||||
}
|
}
|
||||||
public static Map<String, Config> getConfigs() {
|
|
||||||
return ImmutableMap.copyOf(configs);
|
@Override
|
||||||
}
|
public void onInitialize() {
|
||||||
public static boolean isRegistered(Class<?> config) {
|
UserResourceEvents.CONTAINS.register((type, id, previous, pack) ->
|
||||||
for (Config value : configs.values()) {
|
io.gitlab.jfronny.libjf.data.UserResourceEvents.CONTAINS.invoker().contains(type, id, previous, WrappedPack.create(pack)));
|
||||||
if (value.configClass.equals(config))
|
UserResourceEvents.FIND_RESOURCE.register((type, namespace, prefix, maxDepth, pathFilter, previous, pack) ->
|
||||||
return true;
|
io.gitlab.jfronny.libjf.data.UserResourceEvents.FIND_RESOURCE.invoker().findResources(type, namespace, prefix, maxDepth, pathFilter, previous, WrappedPack.create(pack)));
|
||||||
}
|
UserResourceEvents.OPEN.register((type, id, previous, pack) ->
|
||||||
return false;
|
io.gitlab.jfronny.libjf.data.UserResourceEvents.OPEN.invoker().open(type, id, previous, WrappedPack.create(pack)));
|
||||||
|
UserResourceEvents.OPEN_ROOT.register((fileName, previous, pack) ->
|
||||||
|
io.gitlab.jfronny.libjf.data.UserResourceEvents.OPEN_ROOT.invoker().openRoot(fileName, previous, WrappedPack.create(pack)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
package io.gitlab.jfronny.libjf.config;
|
package io.gitlab.jfronny.libjf.config;
|
||||||
|
|
||||||
public interface JfConfig {
|
@Deprecated(forRemoval = true)
|
||||||
|
public interface JfConfig extends io.gitlab.jfronny.libjf.config.api.JfConfig {
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,23 @@ package io.gitlab.jfronny.libjf.data;
|
||||||
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
public class RecipeUtil {
|
public class RecipeUtil {
|
||||||
private final static Set<String> removalsByIdentifier = new HashSet<>();
|
|
||||||
private final static List<ItemStack> recipesForRemoval = new ArrayList<>();
|
|
||||||
|
|
||||||
public static void removeRecipe(String id) {
|
public static void removeRecipe(String id) {
|
||||||
removalsByIdentifier.add(id);
|
RecipeUtil.removeRecipe(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeRecipeFor(ItemStack product) {
|
public static void removeRecipeFor(ItemStack product) {
|
||||||
recipesForRemoval.add(product);
|
RecipeUtil.removeRecipeFor(product);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Iterable<ItemStack> getRecipesForRemoval() {
|
public static Iterable<ItemStack> getRecipesForRemoval() {
|
||||||
return recipesForRemoval;
|
return RecipeUtil.getRecipesForRemoval();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<String> getIdentifiersForRemoval() {
|
public static Set<String> getIdentifiersForRemoval() {
|
||||||
return removalsByIdentifier;
|
return RecipeUtil.getIdentifiersForRemoval();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,42 +3,13 @@ package io.gitlab.jfronny.libjf.data;
|
||||||
import net.minecraft.resource.ResourceType;
|
import net.minecraft.resource.ResourceType;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
public class ResourcePath implements AutoCloseable {
|
@Deprecated(forRemoval = true)
|
||||||
private final ResourceType type;
|
public class ResourcePath extends io.gitlab.jfronny.libjf.ResourcePath {
|
||||||
private final Identifier id;
|
|
||||||
|
|
||||||
public ResourcePath(ResourceType type, Identifier id) {
|
public ResourcePath(ResourceType type, Identifier id) {
|
||||||
this.type = type;
|
super(type, id);
|
||||||
this.id = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourcePath(String name) throws IllegalStateException {
|
public ResourcePath(String name) throws IllegalStateException {
|
||||||
String[] s1 = name.split("/", 3);
|
super(name);
|
||||||
if (s1.length != 3) {
|
|
||||||
throw new IllegalStateException("Could not split path string into resource type and ID due to insufficient length: " + name);
|
|
||||||
}
|
|
||||||
type = switch (s1[0]) {
|
|
||||||
case "assets" -> ResourceType.CLIENT_RESOURCES;
|
|
||||||
case "data" -> ResourceType.SERVER_DATA;
|
|
||||||
default -> throw new IllegalStateException("Unexpected value for resource type: " + s1[0] + " in: " + name);
|
|
||||||
};
|
|
||||||
id = new Identifier(s1[1], s1[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Identifier getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourceType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return String.format("%s/%s/%s", type.getDirectory(), id.getNamespace(), id.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws Exception {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package io.gitlab.jfronny.libjf.data;
|
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.tag.TagFactory;
|
|
||||||
import net.minecraft.item.Item;
|
|
||||||
import net.minecraft.tag.Tag;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import io.gitlab.jfronny.libjf.Libjf;
|
|
||||||
|
|
||||||
public class Tags {
|
|
||||||
public static Tag<Item> SHULKER_ILLEGAL = TagFactory.ITEM.create(new Identifier(Libjf.MOD_ID, "shulker_boxes_illegal"));
|
|
||||||
public static Tag<Item> OVERPOWERED = TagFactory.ITEM.create(new Identifier(Libjf.MOD_ID, "overpowered"));
|
|
||||||
}
|
|
|
@ -10,6 +10,7 @@ import java.io.InputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
public class UserResourceEvents {
|
public class UserResourceEvents {
|
||||||
public static final Event<Contains> CONTAINS = EventFactory.createArrayBacked(Contains.class,
|
public static final Event<Contains> CONTAINS = EventFactory.createArrayBacked(Contains.class,
|
||||||
(listeners) -> (type, id, previous, pack) -> {
|
(listeners) -> (type, id, previous, pack) -> {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package io.gitlab.jfronny.libjf.data;
|
||||||
import io.gitlab.jfronny.libjf.data.wrappedPackImpl.WrappedResourcePack;
|
import io.gitlab.jfronny.libjf.data.wrappedPackImpl.WrappedResourcePack;
|
||||||
import net.minecraft.resource.ResourcePack;
|
import net.minecraft.resource.ResourcePack;
|
||||||
|
|
||||||
//This is a class for binary compatibility with mods using libjf
|
@Deprecated(forRemoval = true)
|
||||||
public abstract class WrappedPack implements ResourcePack {
|
public abstract class WrappedPack implements ResourcePack {
|
||||||
public abstract ResourcePack getUnderlying();
|
public abstract ResourcePack getUnderlying();
|
||||||
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
package io.gitlab.jfronny.libjf.data.wrappedPackImpl;
|
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.data.ResourcePath;
|
|
||||||
import io.gitlab.jfronny.libjf.data.UserResourceEvents;
|
|
||||||
import io.gitlab.jfronny.libjf.data.WrappedPack;
|
|
||||||
import net.minecraft.resource.ResourceType;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public class EventCallImpl {
|
|
||||||
public static InputStream hookOpenRoot(WrappedPack pack, String fileName) throws IOException {
|
|
||||||
InputStream is = null;
|
|
||||||
IOException ex = null;
|
|
||||||
try {
|
|
||||||
is = pack.getUnderlying().openRoot(fileName);
|
|
||||||
}
|
|
||||||
catch (IOException ex1) {
|
|
||||||
ex = ex1;
|
|
||||||
}
|
|
||||||
is = UserResourceEvents.OPEN_ROOT.invoker().openRoot(fileName, is, pack);
|
|
||||||
if (is == null)
|
|
||||||
throw ex == null ? new FileNotFoundException(fileName) : ex;
|
|
||||||
return is;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InputStream hookOpen(WrappedPack pack, ResourceType type, Identifier id) throws IOException {
|
|
||||||
InputStream is = UserResourceEvents.OPEN.invoker().open(type, id, pack.getUnderlying().contains(type, id) ? pack.getUnderlying().open(type, id) : null, pack);
|
|
||||||
if (is == null)
|
|
||||||
throw new FileNotFoundException(new ResourcePath(type, id).getName());
|
|
||||||
return is;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Collection<Identifier> hookFindResources(WrappedPack pack, ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter) {
|
|
||||||
return UserResourceEvents.FIND_RESOURCE.invoker().findResources(type, namespace, prefix, maxDepth, pathFilter, pack.getUnderlying().findResources(type, namespace, prefix, maxDepth, pathFilter), pack);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean hookContains(WrappedPack pack, ResourceType type, Identifier id) {
|
|
||||||
return UserResourceEvents.CONTAINS.invoker().contains(type, id, pack.getUnderlying().contains(type, id), pack);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package io.gitlab.jfronny.libjf.data.wrappedPackImpl;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents;
|
||||||
|
import net.minecraft.resource.ResourcePack;
|
||||||
|
import net.minecraft.resource.ResourceType;
|
||||||
|
import net.minecraft.resource.metadata.ResourceMetadataReader;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
public class SafeWrappedResourcePack implements ResourcePack {
|
||||||
|
ResourcePack pack;
|
||||||
|
public SafeWrappedResourcePack(ResourcePack pack) {
|
||||||
|
this.pack = pack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public InputStream openRoot(String fileName) throws IOException {
|
||||||
|
return UserResourceEvents.disable(() -> pack.openRoot(fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream open(ResourceType type, Identifier id) throws IOException {
|
||||||
|
return UserResourceEvents.disable(() -> pack.open(type, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Identifier> findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter) {
|
||||||
|
return UserResourceEvents.disable(() -> pack.findResources(type, namespace, prefix, maxDepth, pathFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(ResourceType type, Identifier id) {
|
||||||
|
return UserResourceEvents.disable(() -> pack.contains(type, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getNamespaces(ResourceType type) {
|
||||||
|
return UserResourceEvents.disable(() -> pack.getNamespaces(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public <T> T parseMetadata(ResourceMetadataReader<T> metaReader) throws IOException {
|
||||||
|
return UserResourceEvents.disable(() -> pack.parseMetadata(metaReader));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return UserResourceEvents.disable(() -> pack.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
UserResourceEvents.disable(() -> pack.close());
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,31 +13,39 @@ import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
public class WrappedResourcePack extends WrappedPack {
|
public class WrappedResourcePack extends WrappedPack {
|
||||||
ResourcePack pack;
|
ResourcePack pack;
|
||||||
|
SafeWrappedResourcePack safeWrappedResourcePack;
|
||||||
public WrappedResourcePack(ResourcePack pack) {
|
public WrappedResourcePack(ResourcePack pack) {
|
||||||
this.pack = pack;
|
this.pack = pack;
|
||||||
|
safeWrappedResourcePack = new SafeWrappedResourcePack(pack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourcePack getUnderlying() {
|
||||||
|
return safeWrappedResourcePack;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public InputStream openRoot(String fileName) throws IOException {
|
public InputStream openRoot(String fileName) throws IOException {
|
||||||
return EventCallImpl.hookOpenRoot(this, fileName);
|
return pack.openRoot(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream open(ResourceType type, Identifier id) throws IOException {
|
public InputStream open(ResourceType type, Identifier id) throws IOException {
|
||||||
return EventCallImpl.hookOpen(this, type, id);
|
return pack.open(type, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Identifier> findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter) {
|
public Collection<Identifier> findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter) {
|
||||||
return EventCallImpl.hookFindResources(this, type, namespace, prefix, maxDepth, pathFilter);
|
return pack.findResources(type, namespace, prefix, maxDepth, pathFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(ResourceType type, Identifier id) {
|
public boolean contains(ResourceType type, Identifier id) {
|
||||||
return EventCallImpl.hookContains(this, type, id);
|
return pack.contains(type, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,8 +68,4 @@ public class WrappedResourcePack extends WrappedPack {
|
||||||
public void close() {
|
public void close() {
|
||||||
pack.close();
|
pack.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourcePack getUnderlying() {
|
|
||||||
return pack;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
package io.gitlab.jfronny.libjf.entry;
|
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.Libjf;
|
|
||||||
import io.gitlab.jfronny.libjf.config.JfConfig;
|
|
||||||
import net.fabricmc.loader.api.LanguageAdapter;
|
|
||||||
|
|
||||||
public class JfLanguageAdapter implements LanguageAdapter {
|
|
||||||
@Override
|
|
||||||
public native <T> T create(net.fabricmc.loader.api.ModContainer mod, String value, Class<T> type);
|
|
||||||
|
|
||||||
static {
|
|
||||||
DynamicEntry.execute(Libjf.MOD_ID + ":config", JfConfig.class, s -> {
|
|
||||||
Libjf.registerConfig(s.modId(), s.instance().getClass());
|
|
||||||
Libjf.LOGGER.info("Registering config for " + s.modId());
|
|
||||||
});
|
|
||||||
DynamicEntry.execute(Libjf.MOD_ID + ":early", UltraEarlyInit.class, s -> s.instance().init());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package io.gitlab.jfronny.libjf.entry;
|
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.Libjf;
|
|
||||||
import io.gitlab.jfronny.libjf.config.Config;
|
|
||||||
import io.gitlab.jfronny.libjf.config.Entry;
|
|
||||||
import io.gitlab.jfronny.libjf.config.EntryInfo;
|
|
||||||
import io.gitlab.jfronny.libjf.gson.GsonHidden;
|
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
|
||||||
|
|
||||||
public class LibjfClient implements ClientModInitializer {
|
|
||||||
@Override
|
|
||||||
public void onInitializeClient() {
|
|
||||||
for (Config config : Libjf.getConfigs().values()) {
|
|
||||||
Libjf.LOGGER.info("Registring config UI for " + config.modid);
|
|
||||||
for (EntryInfo info : config.entries) {
|
|
||||||
if (info.field.isAnnotationPresent(Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))
|
|
||||||
try {
|
|
||||||
config.initClient(info);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
package io.gitlab.jfronny.libjf.entry;
|
package io.gitlab.jfronny.libjf.entry;
|
||||||
|
|
||||||
public interface UltraEarlyInit {
|
public interface UltraEarlyInit extends io.gitlab.jfronny.libjf.unsafe.UltraEarlyInit {
|
||||||
void init();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
package io.gitlab.jfronny.libjf.mixin;
|
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.data.WrappedPack;
|
|
||||||
import net.fabricmc.fabric.impl.resource.loader.FabricModResourcePack;
|
|
||||||
import net.fabricmc.fabric.impl.resource.loader.ModNioResourcePack;
|
|
||||||
import net.minecraft.resource.ReloadableResourceManagerImpl;
|
|
||||||
import net.minecraft.resource.ResourcePack;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
|
||||||
|
|
||||||
@Mixin(ReloadableResourceManagerImpl.class)
|
|
||||||
public class ReloadableResourceManagerImplMixin {
|
|
||||||
@ModifyVariable(method = "addPack(Lnet/minecraft/resource/ResourcePack;)V", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
|
||||||
private ResourcePack modifyPack(ResourcePack pack) {
|
|
||||||
if (pack instanceof WrappedPack || pack instanceof ModNioResourcePack || pack instanceof FabricModResourcePack) { //TODO use ASM for this to allow mod resource packs
|
|
||||||
return pack;
|
|
||||||
}
|
|
||||||
return WrappedPack.create(pack);
|
|
||||||
}
|
|
||||||
}
|
|
BIN
src/main/resources/assets/libjf/icon.png
Normal file
BIN
src/main/resources/assets/libjf/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
|
@ -15,22 +15,14 @@
|
||||||
"icon": "assets/libjf/icon.png",
|
"icon": "assets/libjf/icon.png",
|
||||||
"environment": "*",
|
"environment": "*",
|
||||||
"entrypoints": {
|
"entrypoints": {
|
||||||
"client": [
|
"main": ["io.gitlab.jfronny.libjf.Libjf"]
|
||||||
"io.gitlab.jfronny.libjf.entry.LibjfClient"
|
|
||||||
],
|
|
||||||
"modmenu": [
|
|
||||||
"io.gitlab.jfronny.libjf.config.ModMenu"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"languageAdapters": {
|
|
||||||
"libjf": "io.gitlab.jfronny.libjf.entry.JfLanguageAdapter"
|
|
||||||
},
|
|
||||||
"mixins": [
|
|
||||||
"libjf.mixins.json"
|
|
||||||
],
|
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.11.3",
|
"fabricloader": ">=0.12.0",
|
||||||
"minecraft": "*"
|
"minecraft": "*",
|
||||||
|
"libjf-config-v0": "${version}",
|
||||||
|
"libjf-data-v0": "${version}",
|
||||||
|
"libjf-base": "${version}"
|
||||||
},
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"modmenu": {
|
"modmenu": {
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package io.gitlab.jfronny.libjf.test;
|
|
||||||
|
|
||||||
import io.gitlab.jfronny.libjf.data.UserResourceEvents;
|
|
||||||
import net.fabricmc.api.ModInitializer;
|
|
||||||
import net.minecraft.resource.AbstractFileResourcePack;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
|
|
||||||
public class Entrypoint implements ModInitializer {
|
|
||||||
@Override
|
|
||||||
public void onInitialize() {
|
|
||||||
// This should prevent resource packs from doing anything if my hooks are working and
|
|
||||||
UserResourceEvents.OPEN.register((type, id, previous, pack) -> {
|
|
||||||
if (TestMod.disablePacks && pack.getUnderlying() instanceof AbstractFileResourcePack) {
|
|
||||||
throw new FileNotFoundException();
|
|
||||||
}
|
|
||||||
return previous;
|
|
||||||
});
|
|
||||||
UserResourceEvents.CONTAINS.register((type, id, previous, pack) -> {
|
|
||||||
if (TestMod.disablePacks && pack.getUnderlying() instanceof AbstractFileResourcePack) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return previous;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"libjf-testmod.jfconfig.title": "JfConfig example",
|
|
||||||
"libjf-testmod.jfconfig.disablePacks": "Disable resource packs",
|
|
||||||
"libjf-testmod.jfconfig.intTest": "Int Test",
|
|
||||||
"libjf-testmod.jfconfig.decimalTest": "Decimal Test",
|
|
||||||
"libjf-testmod.jfconfig.dieStr": "String Test",
|
|
||||||
"libjf-testmod.jfconfig.enumTest": "Enum Test",
|
|
||||||
"libjf-testmod.jfconfig.enumTest.tooltip": "Enum Test Tooltip",
|
|
||||||
"libjf-testmod.jfconfig.enum.Test.Test": "Test",
|
|
||||||
"libjf-testmod.jfconfig.enum.Test.ER": "ER"
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user