Merge branch 'refactor' into 'master'
Refactor See merge request jfmods/LibJF!1
This commit is contained in:
commit
d90671c005
|
@ -117,3 +117,8 @@ run/
|
|||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
.classpath
|
||||
bin
|
||||
.project
|
||||
.settings
|
||||
|
|
|
@ -9,21 +9,23 @@ before_script:
|
|||
build_test:
|
||||
stage: deploy
|
||||
script:
|
||||
- gradle --build-cache assemble
|
||||
- gradle --build-cache build publish -PpublicMaven -Ppipeline=$CI_PIPELINE_ID
|
||||
- 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 tmp.zip latest-dev.jar
|
||||
- mv dev.zip latest-dev.jar
|
||||
artifacts:
|
||||
paths:
|
||||
- build/libs
|
||||
- latest.jar
|
||||
- latest-dev.jar
|
||||
only:
|
||||
- master
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
when: manual
|
||||
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 {
|
||||
maven {
|
||||
name = 'TerraformersMC'
|
||||
url = 'https://maven.terraformersmc.com/'
|
||||
plugins {
|
||||
id "java"
|
||||
id "idea"
|
||||
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 {
|
||||
testmod {
|
||||
compileClasspath += main.compileClasspath + main.output
|
||||
runtimeClasspath += main.runtimeClasspath + main.output
|
||||
}
|
||||
testmod
|
||||
}
|
||||
|
||||
def devOnlyModules = [
|
||||
"libjf-devutil-v0"
|
||||
]
|
||||
|
||||
dependencies {
|
||||
//to change the versions see the gradle.properties file
|
||||
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}"
|
||||
afterEvaluate {
|
||||
subprojects.each {
|
||||
api project(path: ":${it.name}", configuration: "dev")
|
||||
|
||||
include modImplementation(fabricApi.module("fabric-tag-extensions-v0", "0.40.1+1.17"))
|
||||
include modImplementation(fabricApi.module("fabric-resource-loader-v0", "0.40.1+1.17"))
|
||||
modImplementation "com.terraformersmc:modmenu:2.0.10"
|
||||
}
|
||||
if (!(it.name in devOnlyModules)) {
|
||||
include it
|
||||
}
|
||||
|
||||
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
|
||||
testmodImplementation it.sourceSets.testmod.output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
minecraft_version=1.17.1
|
||||
yarn_mappings=build.61
|
||||
loader_version=0.11.6
|
||||
loader_version=0.12.1
|
||||
# Mod Properties
|
||||
mod_version=1.2.3
|
||||
mod_version=2.0
|
||||
maven_group=io.gitlab.jfronny
|
||||
archives_base_name=libjf
|
||||
|
||||
modrinth_id=WKwQAwke
|
||||
modrinth_required_dependencies=
|
||||
modrinth_optional_dependencies=
|
||||
curseforge_id=482600
|
||||
curseforge_required_dependencies=
|
||||
curseforge_optional_dependencies=
|
||||
fabric_version=0.40.6+1.17
|
Binary file not shown.
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
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
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -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");
|
||||
# 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
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
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.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
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 [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
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."
|
||||
fi
|
||||
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.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
|
@ -106,80 +140,95 @@ location of your Java installation."
|
|||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
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" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --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
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
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" "$@"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
archivesBaseName = "libjf-base"
|
||||
|
||||
dependencies {
|
||||
}
|
|
@ -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;
|
||||
|
||||
public class HiddenAnnotationExclusionStrategy implements ExclusionStrategy {
|
||||
public boolean shouldSkipClass(Class<?> clazz) { return false; }
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
|
||||
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;
|
||||
}
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "libjf-base-testmod",
|
||||
"version": "1.0",
|
||||
"environment": "*"
|
||||
}
|
|
@ -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.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.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.ModMenuApi;
|
||||
import io.gitlab.jfronny.libjf.Libjf;
|
||||
import io.gitlab.jfronny.libjf.config.gui.TinyConfigScreen;
|
||||
import io.gitlab.jfronny.libjf.config.impl.gui.TinyConfigScreen;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -12,8 +12,8 @@ public class ModMenu implements ModMenuApi {
|
|||
@Override
|
||||
public Map<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
|
||||
Map<String, ConfigScreenFactory<?>> factories = new HashMap<>();
|
||||
for (Map.Entry<String, Config> entry : Libjf.getConfigs().entrySet()) {
|
||||
if (!Libjf.MOD_ID.equals(entry.getKey()))
|
||||
for (Map.Entry<String, Config> entry : ConfigHolder.getConfigs().entrySet()) {
|
||||
if (!LibJf.MOD_ID.equals(entry.getKey()))
|
||||
factories.put(entry.getKey(), buildFactory(entry.getValue()));
|
||||
}
|
||||
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.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 net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.gui.widget.*;
|
||||
import net.minecraft.text.*;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** Based on https://github.com/TeamMidnightDust/MidnightLib which is based on https://github.com/Minenash/TinyConfig
|
||||
* Credits to TeamMidnightDust and Minenash */
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Config {
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class EntryInfoWidgetBuilder {
|
||||
private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)");
|
||||
private static final Pattern DECIMAL_ONLY = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)");
|
||||
|
||||
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))
|
||||
public static void initConfig(Config config) {
|
||||
for (EntryInfo info : config.entries) {
|
||||
if (info.field.isAnnotationPresent(Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))
|
||||
try {
|
||||
info.defaultValue = field.get(null);
|
||||
} catch (IllegalAccessException ignored) {}
|
||||
entries.add(info);
|
||||
initEntry(config, info);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
Libjf.GSON.fromJson(Files.newBufferedReader(path), config); }
|
||||
catch (Exception e) { write(); }
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public void initClient(EntryInfo info) {
|
||||
if (!(info.field.isAnnotationPresent(Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))) return;
|
||||
private static void initEntry(Config config, EntryInfo info) {
|
||||
if (!(info.field.isAnnotationPresent(io.gitlab.jfronny.libjf.config.api.Entry.class) || info.field.isAnnotationPresent(GsonHidden.class))) return;
|
||||
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;
|
||||
|
||||
if (e == null) return;
|
||||
|
||||
if (type == int.class) textField(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 == String.class) textField(info, String::length, null, Math.min(e.min(),0), Math.max(e.max(),1),true);
|
||||
if (type == int.class) textField(config, info, Integer::parseInt, INTEGER_ONLY, e.min(), e.max(), true);
|
||||
else if (type == double.class) textField(config, info, Double::parseDouble, DECIMAL_ONLY, e.min(), e.max(),false);
|
||||
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) {
|
||||
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.value = !(Boolean) info.value;
|
||||
button.setMessage(func.apply(info.value));
|
||||
}, func);
|
||||
} else if (type.isEnum()) {
|
||||
List<?> values = Arrays.asList(info.field.getType().getEnumConstants());
|
||||
Function<Object,Text> func = value -> new TranslatableText(modid + ".jfconfig." + "enum." + type.getSimpleName() + "." + info.value.toString());
|
||||
info.widget = new AbstractMap.SimpleEntry<ButtonWidget.PressAction, Function<Object,Text>>( button -> {
|
||||
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 -> {
|
||||
int index = values.indexOf(info.value) + 1;
|
||||
info.value = values.get(index >= values.size()? 0 : index);
|
||||
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;
|
||||
info.widget = (BiFunction<TextFieldWidget, ButtonWidget, Predicate<String>>) (t, b) -> s -> {
|
||||
s = s.trim();
|
||||
|
@ -107,7 +92,7 @@ public class Config {
|
|||
info.tempValue = s;
|
||||
t.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777);
|
||||
info.inLimits = inLimits;
|
||||
b.active = entries.stream().allMatch(e -> e.inLimits);
|
||||
b.active = config.entries.stream().allMatch(e -> e.inLimits);
|
||||
|
||||
if (inLimits)
|
||||
info.value = isNumber? value : s;
|
||||
|
@ -115,14 +100,4 @@ public class Config {
|
|||
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.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.config.Config;
|
||||
import io.gitlab.jfronny.libjf.config.Entry;
|
||||
import io.gitlab.jfronny.libjf.config.EntryInfo;
|
||||
import io.gitlab.jfronny.libjf.config.gui.ButtonEntry;
|
||||
import io.gitlab.jfronny.libjf.config.gui.MidnightConfigListWidget;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.config.impl.Config;
|
||||
import io.gitlab.jfronny.libjf.config.api.Entry;
|
||||
import io.gitlab.jfronny.libjf.config.impl.EntryInfo;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.ScreenTexts;
|
||||
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 -> {
|
||||
try {
|
||||
Libjf.GSON.fromJson(Files.newBufferedReader(config.path), config.configClass); }
|
||||
LibJf.GSON.fromJson(Files.newBufferedReader(config.path), config.configClass); }
|
||||
catch (Exception e) { config.write(); }
|
||||
|
||||
for (EntryInfo info : config.entries) {
|
|
@ -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.JfConfig;
|
||||
import io.gitlab.jfronny.libjf.config.api.JfConfig;
|
||||
import io.gitlab.jfronny.libjf.config.api.Entry;
|
||||
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 int intTest = 20;
|
||||
@Entry public static double decimalTest = 20;
|
||||
@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";
|
||||
@Entry public static Test enumTest = Test.Test;
|
||||
@Entry
|
||||
public static Test enumTest = Test.Test;
|
||||
|
||||
public enum Test {
|
||||
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"
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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.gson.JsonObject;
|
||||
import io.gitlab.jfronny.libjf.Libjf;
|
||||
import io.gitlab.jfronny.libjf.data.RecipeUtil;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.data.manipulation.api.RecipeUtil;
|
||||
import net.minecraft.recipe.Recipe;
|
||||
import net.minecraft.recipe.RecipeManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
@ -19,15 +18,15 @@ import java.util.Map;
|
|||
|
||||
@Mixin(RecipeManager.class)
|
||||
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) {
|
||||
ArrayList<Map.Entry<Identifier, JsonObject>> replacement = new ArrayList<>();
|
||||
while(iterator.hasNext()) {
|
||||
Map.Entry<Identifier, JsonObject> cur = iterator.next();
|
||||
Identifier recipeId = cur.getKey();
|
||||
|
||||
if (RecipeUtil.getIdentifiersForRemoval().contains(recipeId.toString())) {
|
||||
Libjf.LOGGER.info("Blocking recipe by identifier: " + recipeId);
|
||||
if (RecipeUtil.isIdBlocked(recipeId)) {
|
||||
LibJf.LOGGER.info("Blocking recipe by identifier: " + recipeId);
|
||||
} else {
|
||||
replacement.add(cur);
|
||||
}
|
||||
|
@ -36,16 +35,14 @@ public class RecipeManagerMixin {
|
|||
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) {
|
||||
Identifier id = (Identifier) key;
|
||||
Recipe<?> recipe = (Recipe<?>) value;
|
||||
|
||||
for (ItemStack stack : RecipeUtil.getRecipesForRemoval()) {
|
||||
if (recipe.getOutput().isItemEqual(stack)) {
|
||||
Libjf.LOGGER.info("Blocked recipe by predicate: " + recipe.getId());
|
||||
return builder;
|
||||
}
|
||||
|
||||
if (RecipeUtil.isOutputBlocked(recipe.getOutput())) {
|
||||
LibJf.LOGGER.info("Blocked recipe by predicate: " + recipe.getId());
|
||||
return builder;
|
||||
}
|
||||
|
||||
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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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 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 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 net.minecraft.item.ItemStack;
|
|
@ -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,
|
||||
"minVersion": "0.8",
|
||||
"package": "io.gitlab.jfronny.libjf.mixin",
|
||||
"package": "io.gitlab.jfronny.libjf.data.mixin",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
"EntityMixin",
|
||||
"RecipeManagerMixin",
|
||||
"ReloadableResourceManagerImplMixin",
|
||||
"ShulkerBoxBlockEntityMixin",
|
||||
"ShulkerBoxSlotMixin"
|
||||
],
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"minecraft:netherite_helmet",
|
||||
"minecraft:netherite_chestplate",
|
||||
"minecraft:netherite_leggings",
|
||||
"minecraft:netherite_boots"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "libjf-data-v0-testmod",
|
||||
"version": "1.0",
|
||||
"environment": "*"
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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 net.fabricmc.loader.ModContainer;
|
||||
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.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -46,9 +46,8 @@ public class DynamicEntry {
|
|||
|
||||
private static ReferenceArrayList<EntrypointContainer> getEntrypointTargets(final String entrypoint) {
|
||||
final ReferenceArrayList<EntrypointContainer> entrypoints = ReferenceArrayList.wrap(new EntrypointContainer[1], 0);
|
||||
|
||||
for (final ModContainer mod : (Collection<ModContainer>) (Object) FabricLoader.getInstance().getAllMods()) {
|
||||
final List<EntrypointMetadata> modEntrypoints = mod.getInfo().getEntrypoints(entrypoint);
|
||||
for (final ModContainer mod : FabricLoader.getInstance().getAllMods()) {
|
||||
final List<EntrypointMetadata> modEntrypoints = ((ModContainerImpl)mod).getInfo().getEntrypoints(entrypoint);
|
||||
|
||||
if (modEntrypoints != null) {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.gitlab.jfronny.libjf.config.Config;
|
||||
import io.gitlab.jfronny.libjf.gson.HiddenAnnotationExclusionStrategy;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import io.gitlab.jfronny.libjf.config.impl.ConfigHolder;
|
||||
import io.gitlab.jfronny.libjf.data.WrappedPack;
|
||||
import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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
|
||||
//TODO remove for 1.18
|
||||
@Deprecated(forRemoval = true)
|
||||
public class Libjf implements ModInitializer {
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void registerConfig(String modId, Class<?> config) {
|
||||
if (!isRegistered(config))
|
||||
configs.put(modId, new Config(modId, config));
|
||||
ConfigHolder.registerConfig(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;
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
UserResourceEvents.CONTAINS.register((type, id, previous, pack) ->
|
||||
io.gitlab.jfronny.libjf.data.UserResourceEvents.CONTAINS.invoker().contains(type, id, previous, WrappedPack.create(pack)));
|
||||
UserResourceEvents.FIND_RESOURCE.register((type, namespace, prefix, maxDepth, pathFilter, previous, pack) ->
|
||||
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) ->
|
||||
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;
|
||||
|
||||
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 java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
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) {
|
||||
removalsByIdentifier.add(id);
|
||||
RecipeUtil.removeRecipe(id);
|
||||
}
|
||||
|
||||
public static void removeRecipeFor(ItemStack product) {
|
||||
recipesForRemoval.add(product);
|
||||
RecipeUtil.removeRecipeFor(product);
|
||||
}
|
||||
|
||||
public static Iterable<ItemStack> getRecipesForRemoval() {
|
||||
return recipesForRemoval;
|
||||
return RecipeUtil.getRecipesForRemoval();
|
||||
}
|
||||
|
||||
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.util.Identifier;
|
||||
|
||||
public class ResourcePath implements AutoCloseable {
|
||||
private final ResourceType type;
|
||||
private final Identifier id;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public class ResourcePath extends io.gitlab.jfronny.libjf.ResourcePath {
|
||||
public ResourcePath(ResourceType type, Identifier id) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
super(type, 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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.function.Predicate;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public class UserResourceEvents {
|
||||
public static final Event<Contains> CONTAINS = EventFactory.createArrayBacked(Contains.class,
|
||||
(listeners) -> (type, id, previous, pack) -> {
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.gitlab.jfronny.libjf.data;
|
|||
import io.gitlab.jfronny.libjf.data.wrappedPackImpl.WrappedResourcePack;
|
||||
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 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.function.Predicate;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public class WrappedResourcePack extends WrappedPack {
|
||||
ResourcePack pack;
|
||||
SafeWrappedResourcePack safeWrappedResourcePack;
|
||||
public WrappedResourcePack(ResourcePack pack) {
|
||||
this.pack = pack;
|
||||
safeWrappedResourcePack = new SafeWrappedResourcePack(pack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourcePack getUnderlying() {
|
||||
return safeWrappedResourcePack;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InputStream openRoot(String fileName) throws IOException {
|
||||
return EventCallImpl.hookOpenRoot(this, fileName);
|
||||
return pack.openRoot(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream open(ResourceType type, Identifier id) throws IOException {
|
||||
return EventCallImpl.hookOpen(this, type, id);
|
||||
return pack.open(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public boolean contains(ResourceType type, Identifier id) {
|
||||
return EventCallImpl.hookContains(this, type, id);
|
||||
return pack.contains(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,8 +68,4 @@ public class WrappedResourcePack extends WrappedPack {
|
|||
public void 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;
|
||||
|
||||
public interface UltraEarlyInit {
|
||||
void init();
|
||||
public interface UltraEarlyInit extends io.gitlab.jfronny.libjf.unsafe.UltraEarlyInit {
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
|
@ -15,22 +15,14 @@
|
|||
"icon": "assets/libjf/icon.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
"io.gitlab.jfronny.libjf.entry.LibjfClient"
|
||||
],
|
||||
"modmenu": [
|
||||
"io.gitlab.jfronny.libjf.config.ModMenu"
|
||||
]
|
||||
"main": ["io.gitlab.jfronny.libjf.Libjf"]
|
||||
},
|
||||
"languageAdapters": {
|
||||
"libjf": "io.gitlab.jfronny.libjf.entry.JfLanguageAdapter"
|
||||
},
|
||||
"mixins": [
|
||||
"libjf.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.11.3",
|
||||
"minecraft": "*"
|
||||
"fabricloader": ">=0.12.0",
|
||||
"minecraft": "*",
|
||||
"libjf-config-v0": "${version}",
|
||||
"libjf-data-v0": "${version}",
|
||||
"libjf-base": "${version}"
|
||||
},
|
||||
"custom": {
|
||||
"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