Compare commits
No commits in common. "master" and "v1.7.1" have entirely different histories.
27
.gitlab-ci.yml
Normal file
27
.gitlab-ci.yml
Normal file
@ -0,0 +1,27 @@
|
||||
image: gradle:jdk16
|
||||
|
||||
variables:
|
||||
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
|
||||
|
||||
before_script:
|
||||
- export GRADLE_USER_HOME=`pwd`/.gradle
|
||||
|
||||
build_test:
|
||||
stage: deploy
|
||||
script:
|
||||
- gradle --build-cache assemble
|
||||
- cp build/libs/* ./
|
||||
- rm *-dev.jar
|
||||
- mv *.jar latest.jar
|
||||
artifacts:
|
||||
paths:
|
||||
- build/libs
|
||||
- latest.jar
|
||||
only:
|
||||
- master
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
when: manual
|
||||
script:
|
||||
- gradle --build-cache publishModrinth
|
@ -1 +0,0 @@
|
||||
#link https://pages.frohnmeyer-wds.de/scripts/jfmod.yml
|
67
README.md
67
README.md
@ -1,67 +0,0 @@
|
||||
Resclone automatically downloads and updates resource and data packs specified in its config.
|
||||
Packs are updated on startup whenever possible, meaning that very large packs will slow down your startup.
|
||||
|
||||
Example config:
|
||||
```
|
||||
{
|
||||
// The packs to be loaded by resclone
|
||||
"packs": [
|
||||
{
|
||||
"fetcher": "file",
|
||||
"source": "https://github.com/FaithfulTeam/Faithful/raw/releases/1.16.zip",
|
||||
"name": "Faithful",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "seasnail8169/SnailPack",
|
||||
"name": "SnailPack",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "spiralhalo/LumiLights/release",
|
||||
"name": "LumiLights",
|
||||
"forceDownload": true,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "spiralhalo/LumiPBRExt/tag/v0.7",
|
||||
"name": "LumiPBRExt",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "github",
|
||||
"source": "FaithfulTeam/Faithful/branch/1.16",
|
||||
"name": "Faithful",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "file",
|
||||
"source": "https://media.forgecdn.net/files/3031/178/BattyCoordinates_005.zip",
|
||||
"name": "Battys Coordinates",
|
||||
"forceDownload": false,
|
||||
"forceEnable": false
|
||||
},
|
||||
{
|
||||
"fetcher": "curseforge",
|
||||
"source": "325017",
|
||||
"name": "Vanilla Additions",
|
||||
"forceDownload": false,
|
||||
"forceEnable": true
|
||||
},
|
||||
{
|
||||
"fetcher": "modrinth",
|
||||
"source": "new-in-town",
|
||||
"name": "New in Town"
|
||||
}
|
||||
],
|
||||
// Whether to prune unused packs from the cache
|
||||
"pruneUnused": true
|
||||
}
|
||||
```
|
92
build.gradle
Normal file
92
build.gradle
Normal file
@ -0,0 +1,92 @@
|
||||
apply from: "https://gitlab.com/-/snippets/2121059/raw/master/jfbase.gradle"
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = 'TerraformersMC'
|
||||
url = 'https://maven.terraformersmc.com/'
|
||||
}
|
||||
}
|
||||
|
||||
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}"
|
||||
|
||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||
modImplementation "com.terraformersmc:modmenu:2.0.0-beta.7"
|
||||
}
|
||||
|
||||
/*plugins {
|
||||
id 'fabric-loom' version '0.7-SNAPSHOT'
|
||||
id 'maven-publish'
|
||||
id "com.modrinth.minotaur" version "1.1.0"
|
||||
}
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
archivesBaseName = project.archives_base_name
|
||||
version = project.mod_version
|
||||
group = project.maven_group
|
||||
|
||||
repositories {
|
||||
maven { url = "https://maven.terraformersmc.com/"; name = "ModMenu" }
|
||||
}
|
||||
|
||||
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}"
|
||||
|
||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||
modImplementation "com.terraformersmc:modmenu:1.16.9"
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
include "fabric.mod.json"
|
||||
expand "version": project.version
|
||||
}
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
exclude "fabric.mod.json"
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||
// this fixes some edge cases with special characters not displaying correctly
|
||||
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
|
||||
// if it is present.
|
||||
// If you remove this task, sources will not be generated.
|
||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
classifier = "sources"
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
jar {
|
||||
from "LICENSE"
|
||||
}
|
||||
|
||||
import com.modrinth.minotaur.TaskModrinthUpload
|
||||
|
||||
task publishModrinth (type: TaskModrinthUpload){
|
||||
token = System.getenv("MODRINTH_API_TOKEN") // Use an environment property!
|
||||
projectId = 'kVAQyCLX'
|
||||
versionNumber = "${project.mod_version}"
|
||||
uploadFile = remapJar // This is the java jar task
|
||||
addGameVersion("${project.minecraft_version}")
|
||||
addLoader('fabric')
|
||||
versionName = "[${project.minecraft_version}] ${project.mod_version}"
|
||||
afterEvaluate {
|
||||
tasks.publishModrinth.dependsOn(remapJar)
|
||||
tasks.publishModrinth.dependsOn(sourcesJar)
|
||||
}
|
||||
}
|
||||
*/
|
@ -1,47 +0,0 @@
|
||||
plugins {
|
||||
id("jfmod") version "1.6-SNAPSHOT"
|
||||
}
|
||||
|
||||
loom {
|
||||
accessWidenerPath.set(file("src/main/resources/resclone.accesswidener"))
|
||||
}
|
||||
|
||||
allprojects { group = "io.gitlab.jfronny" }
|
||||
base.archivesName = "resclone"
|
||||
|
||||
val modmenuVersion = "11.0.0-beta.1"
|
||||
val commonsVersion = "2.0.0-SNAPSHOT"
|
||||
jfMod {
|
||||
minecraftVersion = "1.21"
|
||||
yarn("build.1")
|
||||
loaderVersion = "0.15.11"
|
||||
libJfVersion = "3.16.0"
|
||||
fabricApiVersion = "0.100.1+1.21"
|
||||
|
||||
modrinth {
|
||||
projectId = "resclone"
|
||||
optionalDependencies.add("fabric-api")
|
||||
}
|
||||
curseforge {
|
||||
projectId = "839008"
|
||||
optionalDependencies.add("fabric-api")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
include(modImplementation("io.gitlab.jfronny.libjf:libjf-base")!!) // for JfCommons
|
||||
include(modImplementation("io.gitlab.jfronny.libjf:libjf-config-core-v2")!!) // for JfCommons
|
||||
include(modImplementation("net.fabricmc.fabric-api:fabric-resource-loader-v0")!!)
|
||||
|
||||
compileOnly("io.gitlab.jfronny:commons-serialize-generator-annotations:$commonsVersion")
|
||||
annotationProcessor("io.gitlab.jfronny:commons-serialize-generator:$commonsVersion")
|
||||
|
||||
// Dev env
|
||||
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-config-ui-tiny")
|
||||
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil")
|
||||
modLocalRuntime("com.terraformersmc:modmenu:$modmenuVersion")
|
||||
// for modmenu
|
||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-resource-loader-v0")
|
||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-screen-api-v1")
|
||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-key-binding-api-v1")
|
||||
}
|
22
gradle.properties
Normal file
22
gradle.properties
Normal file
@ -0,0 +1,22 @@
|
||||
# Done to increase the memory available to gradle.
|
||||
org.gradle.jvmargs=-Xmx1G
|
||||
# Fabric Properties
|
||||
# check these on https://modmuss50.me/fabric.html
|
||||
minecraft_version=1.17
|
||||
yarn_mappings=build.1
|
||||
loader_version=0.11.3
|
||||
# Mod Properties
|
||||
mod_version=1.7.1
|
||||
maven_group=io.gitlab.jfronny
|
||||
archives_base_name=resclone
|
||||
# Dependencies
|
||||
# check this on https://modmuss50.me/fabric.html
|
||||
fabric_version=0.34.9+1.17
|
||||
|
||||
modrinth_id=kVAQyCLX
|
||||
modrinth_required_dependencies=
|
||||
modrinth_optional_dependencies=
|
||||
curseforge_id=none
|
||||
curseforge_required_dependencies=
|
||||
curseforge_optional_dependencies=
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
185
gradlew
vendored
Normal file
185
gradlew
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# 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
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$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"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 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
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
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"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
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
|
||||
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" ;;
|
||||
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, 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"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
@ -1,9 +0,0 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven("https://maven.fabricmc.net/") // FabricMC
|
||||
maven("https://maven.frohnmeyer-wds.de/artifacts") // scripts
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "resclone"
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "io.gitlab.jfronny.resclone.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"client": [
|
||||
"GameOptionsMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
@ -1,176 +1,164 @@
|
||||
package io.gitlab.jfronny.resclone;
|
||||
|
||||
import io.gitlab.jfronny.commons.logger.SystemLoggerPlus;
|
||||
import com.google.gson.Gson;
|
||||
import io.gitlab.jfronny.resclone.api.PackFetcher;
|
||||
import io.gitlab.jfronny.resclone.api.RescloneApi;
|
||||
import io.gitlab.jfronny.resclone.api.RescloneEntry;
|
||||
import io.gitlab.jfronny.resclone.data.PackMetaLoaded;
|
||||
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
|
||||
import io.gitlab.jfronny.resclone.fetchers.*;
|
||||
import io.gitlab.jfronny.resclone.processors.*;
|
||||
import io.gitlab.jfronny.resclone.api.PackProcessor;
|
||||
import io.gitlab.jfronny.resclone.processors.RemoveEmptyProcessor;
|
||||
import io.gitlab.jfronny.resclone.processors.RootPathProcessor;
|
||||
import io.gitlab.jfronny.resclone.util.PackUrlCache;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import io.gitlab.jfronny.resclone.util.Result;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Resclone implements ModInitializer {
|
||||
public static final Map<String, PackFetcher> FETCHER_INSTANCES = new LinkedHashMap<>();
|
||||
public static final Set<PackProcessor> PROCESSORS = new LinkedHashSet<>();
|
||||
public static final Set<PackMetaLoaded> DOWNLOADED_PACKS = new LinkedHashSet<>();
|
||||
public static final Set<PackMetaLoaded> NEW_PACKS = new LinkedHashSet<>(); // Client-only!
|
||||
public class Resclone implements ModInitializer, RescloneApi {
|
||||
public static final Set<PackMetaUnloaded> conf = new LinkedHashSet<>();
|
||||
public static final Map<String, PackFetcher> fetcherInstances = new LinkedHashMap<>();
|
||||
public static final Set<PackProcessor> processors = new LinkedHashSet<>();
|
||||
public static final Set<PackMetaLoaded> downloadedPacks = new LinkedHashSet<>();
|
||||
public static final Set<PackMetaLoaded> newPacks = new LinkedHashSet<>();
|
||||
public static final Gson gson = new Gson();
|
||||
|
||||
public static final String MOD_ID = "resclone";
|
||||
public static final SystemLoggerPlus LOGGER = SystemLoggerPlus.forName(MOD_ID);
|
||||
public static final String USER_AGENT = "jfmods/" + MOD_ID + "/" + FabricLoader.getInstance()
|
||||
.getModContainer(MOD_ID).orElseThrow()
|
||||
.getMetadata()
|
||||
.getVersion()
|
||||
.getFriendlyString();
|
||||
public static final Logger LOGGER = LogManager.getLogger(MOD_ID);
|
||||
|
||||
public static PackUrlCache urlCache;
|
||||
|
||||
public static int packCount = 0;
|
||||
public static int COUNT = 0;
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
LOGGER.info("Initialising Resclone.");
|
||||
|
||||
urlCache = new PackUrlCache(getConfigPath().resolve("urlCache.properties"));
|
||||
FETCHER_INSTANCES.clear();
|
||||
PROCESSORS.clear();
|
||||
DOWNLOADED_PACKS.clear();
|
||||
conf.clear();
|
||||
fetcherInstances.clear();
|
||||
processors.clear();
|
||||
downloadedPacks.clear();
|
||||
|
||||
addProcessor(new RootPathProcessor()); //This should be run before any other processor to make sure the root is correct
|
||||
addFetcher(new BasicFileFetcher());
|
||||
addFetcher(new GitHubFetcher());
|
||||
addFetcher(new CurseforgeFetcher());
|
||||
addFetcher(new ModrinthFetcher());
|
||||
if (RescloneConfig.filterPacks) {
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) addProcessor(new PruneVanillaProcessor());
|
||||
addProcessor(new RemoveEmptyProcessor());
|
||||
addProcessor(new RootPathProcessor()); //This should be run before any other processor to make sure the path is valid
|
||||
for (RescloneEntry entry : FabricLoader.getInstance().getEntrypoints(MOD_ID, RescloneEntry.class)) {
|
||||
try {
|
||||
entry.init(this);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
addProcessor(new RemoveEmptyProcessor());
|
||||
reload();
|
||||
|
||||
LOGGER.info("Installed {0} resource pack{1}.", packCount, packCount == 1 ? "" : "s");
|
||||
LOGGER.info("Installed {} resource pack{}.", COUNT, COUNT == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFetcher(PackFetcher fetcher) {
|
||||
FETCHER_INSTANCES.put(fetcher.getSourceTypeName(), fetcher);
|
||||
fetcherInstances.put(fetcher.getSourceTypeName(), fetcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProcessor(PackProcessor processor) {
|
||||
PROCESSORS.add(processor);
|
||||
processors.add(processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPack(String fetcher, String pack, String name) {
|
||||
addPack(fetcher, pack, name, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPack(String fetcher, String pack, String name, boolean forceRedownload) {
|
||||
addPack(fetcher, pack, name, forceRedownload, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPack(String fetcher, String pack, String name, boolean forceRedownload, boolean forceEnable) {
|
||||
RescloneConfig.packs.add(new PackMetaUnloaded(fetcher, pack, name, forceRedownload, forceEnable));
|
||||
conf.add(new PackMetaUnloaded(fetcher, pack, name, forceRedownload, forceEnable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
Set<PackMetaLoaded> metas = new LinkedHashSet<>();
|
||||
if (RescloneConfig.packs.isEmpty()) {
|
||||
LOGGER.info("No resclone pack was specified, add one");
|
||||
}
|
||||
else {
|
||||
try (ExecutorService pool = Executors.newFixedThreadPool(RescloneConfig.packs.size())) {
|
||||
for (PackMetaUnloaded s : RescloneConfig.packs) {
|
||||
pool.submit(generateTask(s, metas));
|
||||
}
|
||||
pool.shutdown();
|
||||
if (!pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS)) {
|
||||
LOGGER.error("Download timed out. This shouldn't be possible");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error("Could not execute pack download task", e);
|
||||
try {
|
||||
ExecutorService pool = Executors.newFixedThreadPool(conf.size());
|
||||
for (PackMetaUnloaded s : conf) {
|
||||
pool.submit(generateTask(s, metas));
|
||||
}
|
||||
pool.shutdown();
|
||||
if (!pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS)) {
|
||||
LOGGER.error("Download timed out. This shouldn't be possible");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
urlCache.save();
|
||||
DOWNLOADED_PACKS.clear();
|
||||
DOWNLOADED_PACKS.addAll(metas);
|
||||
|
||||
if (RescloneConfig.pruneUnused) pruneCache();
|
||||
downloadedPacks.clear();
|
||||
downloadedPacks.addAll(metas);
|
||||
}
|
||||
|
||||
private Runnable generateTask(PackMetaUnloaded meta, Set<PackMetaLoaded> metas) {
|
||||
return () -> {
|
||||
try {
|
||||
if (!FETCHER_INSTANCES.containsKey(meta.fetcher()))
|
||||
throw new Exception("Invalid fetcher: " + meta.fetcher());
|
||||
if (!fetcherInstances.containsKey(meta.fetcher))
|
||||
throw new Exception("Invalid fetcher: " + meta.fetcher);
|
||||
Path cacheDir = getConfigPath().resolve("cache");
|
||||
PackMetaLoaded p;
|
||||
try {
|
||||
boolean isNew = !urlCache.containsKey(meta.source());
|
||||
boolean isNew = !urlCache.containsKey(meta.source);
|
||||
//Download
|
||||
PackFetcher.Result fr = FETCHER_INSTANCES.get(meta.fetcher()).get(meta.source(), cacheDir, meta.forceDownload());
|
||||
p = new PackMetaLoaded(fr.downloadPath(), meta.name(), meta.forceEnable());
|
||||
Result fr = fetcherInstances.get(meta.fetcher).get(meta.source, cacheDir, meta.forceDownload);
|
||||
p = new PackMetaLoaded(fr.downloadPath, meta.name, meta.forceEnable);
|
||||
metas.add(p);
|
||||
if (isNew && FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) NEW_PACKS.add(p);
|
||||
if (fr.freshDownload()) {
|
||||
if (isNew)
|
||||
newPacks.add(p);
|
||||
if (fr.freshDownload) {
|
||||
//Process
|
||||
Map<String, String> props = new HashMap<>();
|
||||
props.put("create", "false");
|
||||
URI zipfile = URI.create("jar:" + p.zipPath().toUri());
|
||||
URI zipfile = URI.create("jar:" + p.zipPath.toUri());
|
||||
try (FileSystem zipfs = FileSystems.newFileSystem(zipfile, props)) {
|
||||
for (PackProcessor processor : PROCESSORS) {
|
||||
for (PackProcessor processor : processors) {
|
||||
processor.process(zipfs);
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("Could not run pack processors on " + p.zipPath(), e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new Exception("Failed to download pack", e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("Encountered issue while preparing " + meta.name(), e);
|
||||
LOGGER.error("Encountered issue while preparing " + meta.name, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void pruneCache() {
|
||||
Set<Path> loadedPacks = DOWNLOADED_PACKS.stream().map(PackMetaLoaded::zipPath).collect(Collectors.toUnmodifiableSet());
|
||||
Set<Path> toDelete = new HashSet<>();
|
||||
try (Stream<Path> cacheEntries = Files.list(getConfigPath().resolve("cache"))) {
|
||||
cacheEntries
|
||||
.filter(s -> !Files.isRegularFile(s) || !loadedPacks.contains(s))
|
||||
.forEach(toDelete::add);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Could find cache entries to prune", e);
|
||||
}
|
||||
if (!toDelete.isEmpty()) {
|
||||
LOGGER.info("Pruning " + toDelete.size() + " unused cache entries");
|
||||
for (Path path : toDelete) {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Could not delete unused cache entry: " + path, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Path getConfigPath() {
|
||||
Path configPath = FabricLoader.getInstance().getConfigDir().resolve(MOD_ID);
|
||||
@Override
|
||||
public Path getConfigPath() {
|
||||
Path configPath = FabricLoader.getInstance().getConfigDir().resolve("resclone");
|
||||
if (!Files.isDirectory(configPath.resolve("cache"))) {
|
||||
try {
|
||||
Files.createDirectories(configPath.resolve("cache"));
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Could not create cache directory", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return configPath;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
package io.gitlab.jfronny.resclone;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
|
||||
import io.gitlab.jfronny.commons.serialize.Token;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonWriter;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.JfCustomConfig;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.dsl.DSL;
|
||||
import io.gitlab.jfronny.resclone.data.GC_PackMetaUnloaded;
|
||||
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
|
||||
import io.gitlab.jfronny.resclone.util.ListAdaptation;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class RescloneConfig implements JfCustomConfig {
|
||||
public static Set<PackMetaUnloaded> packs;
|
||||
public static boolean pruneUnused;
|
||||
public static boolean filterPacks;
|
||||
public static boolean logProcessing;
|
||||
|
||||
private static final String ERR_DUPLICATE = "Unexpected duplicate \"%s\" in Resclone config";
|
||||
private static final String PACKS = "packs";
|
||||
private static final String PRUNE_UNUSED = "pruneUnused";
|
||||
private static final String FILTER_PACKS = "filterPacks";
|
||||
private static final String LOG_PROCESSING = "logProcessing";
|
||||
|
||||
private static void load(Path path) throws IOException {
|
||||
if (!Files.exists(path)) {
|
||||
packs = new HashSet<>();
|
||||
pruneUnused = true;
|
||||
filterPacks = true;
|
||||
logProcessing = false;
|
||||
write(path);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean updateRequired = false;
|
||||
try (BufferedReader br = Files.newBufferedReader(path);
|
||||
JsonReader reader = LibJf.LENIENT_TRANSPORT.createReader(br)) {
|
||||
if (reader.peek() == Token.BEGIN_ARRAY) {
|
||||
// Legacy format compatibility
|
||||
packs = ListAdaptation.deserializeSet(reader, GC_PackMetaUnloaded::deserialize);
|
||||
updateRequired = true;
|
||||
} else if (reader.peek() == Token.BEGIN_OBJECT) {
|
||||
// New format
|
||||
reader.beginObject();
|
||||
Set<PackMetaUnloaded> packs = null;
|
||||
Boolean pruneUnused = null;
|
||||
Boolean filterPacks = null;
|
||||
Boolean logProcessing = null;
|
||||
while (reader.peek() != Token.END_OBJECT) {
|
||||
final String name = reader.nextName();
|
||||
switch (name) {
|
||||
case PACKS -> {
|
||||
if (packs != null) throw new MalformedDataException(ERR_DUPLICATE.formatted(PACKS));
|
||||
if (reader.peek() == Token.BEGIN_ARRAY) {
|
||||
packs = ListAdaptation.deserializeSet(reader, GC_PackMetaUnloaded::deserialize);
|
||||
} else {
|
||||
packs = Set.of(GC_PackMetaUnloaded.deserialize(reader));
|
||||
}
|
||||
}
|
||||
case PRUNE_UNUSED -> {
|
||||
if (pruneUnused != null) throw new MalformedDataException(ERR_DUPLICATE.formatted(PRUNE_UNUSED));
|
||||
pruneUnused = reader.nextBoolean();
|
||||
}
|
||||
case FILTER_PACKS -> {
|
||||
if (filterPacks != null) throw new MalformedDataException(ERR_DUPLICATE.formatted(FILTER_PACKS));
|
||||
filterPacks = reader.nextBoolean();
|
||||
}
|
||||
case LOG_PROCESSING -> {
|
||||
if (logProcessing != null) throw new MalformedDataException(ERR_DUPLICATE.formatted(LOG_PROCESSING));
|
||||
logProcessing = reader.nextBoolean();
|
||||
}
|
||||
default -> throw new MalformedDataException("Unexpected element: \"" + name + "\" in Resclone config");
|
||||
}
|
||||
}
|
||||
reader.endObject();
|
||||
if (packs == null) throw new MalformedDataException("Expected Resclone config object to contain packs");
|
||||
if (pruneUnused == null) {
|
||||
pruneUnused = true;
|
||||
updateRequired = true;
|
||||
}
|
||||
if (filterPacks == null) {
|
||||
filterPacks = true;
|
||||
updateRequired = true;
|
||||
}
|
||||
if (logProcessing == null) {
|
||||
logProcessing = false;
|
||||
updateRequired = true;
|
||||
}
|
||||
RescloneConfig.packs = packs;
|
||||
RescloneConfig.pruneUnused = pruneUnused;
|
||||
RescloneConfig.filterPacks = filterPacks;
|
||||
RescloneConfig.logProcessing = logProcessing;
|
||||
} else throw new MalformedDataException("Expected Resclone config to be an object or array");
|
||||
}
|
||||
if (updateRequired) write(path);
|
||||
}
|
||||
|
||||
private static void write(Path path) throws IOException {
|
||||
try (BufferedWriter bw = Files.newBufferedWriter(path);
|
||||
JsonWriter writer = LibJf.LENIENT_TRANSPORT.createWriter(bw)) {
|
||||
writer.beginObject()
|
||||
.comment("The packs to be loaded by resclone")
|
||||
.name(PACKS)
|
||||
.beginArray();
|
||||
for (PackMetaUnloaded pack : packs) {
|
||||
GC_PackMetaUnloaded.serialize(pack, writer);
|
||||
}
|
||||
writer.endArray()
|
||||
.comment("Automatically remove all downloaded packs that are not in the config to free up unneeded space")
|
||||
.name(PRUNE_UNUSED)
|
||||
.value(pruneUnused)
|
||||
.comment("Whether to filter packs to remove files unchanged from vanilla and empty directories")
|
||||
.name(FILTER_PACKS)
|
||||
.value(filterPacks)
|
||||
.comment("Log automatic processing steps applied to downloaded packs")
|
||||
.name(LOG_PROCESSING)
|
||||
.value(logProcessing)
|
||||
.endObject();
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
Path path = Resclone.getConfigPath().resolve("config.json");
|
||||
DSL.create(Resclone.MOD_ID).register(builder ->
|
||||
builder.setLoadMethod(configInstance -> {
|
||||
try {
|
||||
load(path);
|
||||
} catch (IOException e) {
|
||||
Resclone.LOGGER.error("Could not load config", e);
|
||||
}
|
||||
}).setWriteMethod(configInstance -> {
|
||||
try {
|
||||
write(path);
|
||||
} catch (IOException e) {
|
||||
Resclone.LOGGER.error("Could not write config", e);
|
||||
}
|
||||
}).setPath(path)
|
||||
.<Set<PackMetaUnloaded>>value(PACKS, new HashSet<>(), Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, io.gitlab.jfronny.libjf.config.api.v2.type.Type.ofClass(new TypeToken<Set<PackMetaUnloaded>>(){}.getType()), 100, () -> packs, p -> packs = p)
|
||||
.value(PRUNE_UNUSED, pruneUnused, () -> pruneUnused, p -> pruneUnused = p)
|
||||
.value(FILTER_PACKS, filterPacks, () -> filterPacks, p -> filterPacks = p)
|
||||
.value(LOG_PROCESSING, logProcessing, () -> logProcessing, p -> logProcessing = p)
|
||||
).load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(DSL.Defaulted dsl) {
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.gitlab.jfronny.resclone;
|
||||
|
||||
import io.gitlab.jfronny.resclone.api.RescloneApi;
|
||||
import io.gitlab.jfronny.resclone.api.RescloneEntry;
|
||||
import io.gitlab.jfronny.resclone.util.ConfigLoader;
|
||||
import io.gitlab.jfronny.resclone.fetchers.BasicFileFetcher;
|
||||
import io.gitlab.jfronny.resclone.fetchers.CurseforgeFetcher;
|
||||
import io.gitlab.jfronny.resclone.fetchers.GitHubFetcher;
|
||||
import io.gitlab.jfronny.resclone.processors.PruneVanillaProcessor;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
public class RescloneEntryDefault implements RescloneEntry {
|
||||
|
||||
@Override
|
||||
public void init(RescloneApi api) {
|
||||
api.addFetcher(new BasicFileFetcher());
|
||||
api.addFetcher(new GitHubFetcher());
|
||||
api.addFetcher(new CurseforgeFetcher());
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
||||
api.addProcessor(new PruneVanillaProcessor());
|
||||
ConfigLoader.load(api);
|
||||
}
|
||||
|
||||
}
|
@ -3,57 +3,27 @@ package io.gitlab.jfronny.resclone;
|
||||
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.metadata.ModMetadata;
|
||||
import net.minecraft.resource.*;
|
||||
import net.minecraft.resource.ZipResourcePack;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RescloneResourcePack extends ZipResourcePack implements ModResourcePack {
|
||||
private static final ModMetadata METADATA = FabricLoader.getInstance().getModContainer(Resclone.MOD_ID).orElseThrow().getMetadata();
|
||||
private final ResourcePackInfo info;
|
||||
private final ZipFileWrapper file;
|
||||
|
||||
RescloneResourcePack(ZipFileWrapper file, ResourcePackInfo info, String overlay) {
|
||||
super(info, file, overlay);
|
||||
this.info = info;
|
||||
this.file = file;
|
||||
private final String name;
|
||||
|
||||
public RescloneResourcePack(File file, String name) {
|
||||
super(file);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModMetadata getFabricModMetadata() {
|
||||
return METADATA;
|
||||
return FabricLoader.getInstance().getModContainer(Resclone.MOD_ID).get().getMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModResourcePack createOverlay(String overlay) {
|
||||
return new RescloneResourcePack(this.file, this.info, overlay);
|
||||
}
|
||||
|
||||
public static class Factory implements ResourcePackProfile.PackFactory {
|
||||
private final File file;
|
||||
private final ResourcePackInfo info;
|
||||
|
||||
public Factory(File file, ResourcePackInfo info) {
|
||||
this.file = file;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourcePack open(ResourcePackInfo info) {
|
||||
ZipFileWrapper zipFileWrapper = new ZipFileWrapper(this.file);
|
||||
return new RescloneResourcePack(zipFileWrapper, this.info, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourcePack openWithOverlays(ResourcePackInfo info, ResourcePackProfile.Metadata metadata) {
|
||||
ZipFileWrapper zipFileWrapper = new ZipFileWrapper(this.file);
|
||||
ZipResourcePack resourcePack = new RescloneResourcePack(zipFileWrapper, this.info, "");
|
||||
List<String> overlays = metadata.overlays();
|
||||
if (overlays.isEmpty()) return resourcePack;
|
||||
List<ResourcePack> overlayPacks = new ArrayList<>(overlays.size());
|
||||
for (String string : overlays) overlayPacks.add(new RescloneResourcePack(zipFileWrapper, this.info, string));
|
||||
return new OverlayResourcePack(resourcePack, overlayPacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package io.gitlab.jfronny.resclone.fetchers;
|
||||
package io.gitlab.jfronny.resclone.api;
|
||||
|
||||
import io.gitlab.jfronny.resclone.util.Result;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface PackFetcher {
|
||||
Result get(String baseUrl, Path targetDir, boolean forceDownload) throws Exception;
|
||||
String getSourceTypeName(); // The name for users to specify in the config
|
||||
|
||||
record Result(Path downloadPath, boolean freshDownload) { }
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package io.gitlab.jfronny.resclone.processors;
|
||||
package io.gitlab.jfronny.resclone.api;
|
||||
|
||||
import java.nio.file.FileSystem;
|
||||
|
||||
public interface PackProcessor {
|
||||
void process(FileSystem p) throws Exception;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package io.gitlab.jfronny.resclone.api;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface RescloneApi {
|
||||
|
||||
void addFetcher(PackFetcher fetcher);
|
||||
|
||||
void addProcessor(PackProcessor processor);
|
||||
|
||||
void addPack(String fetcher, String pack, String name);
|
||||
|
||||
void addPack(String fetcher, String pack, String name, boolean forceRedownload);
|
||||
|
||||
void addPack(String fetcher, String pack, String name, boolean forceRedownload, boolean forceEnable);
|
||||
|
||||
void reload();
|
||||
|
||||
Path getConfigPath();
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package io.gitlab.jfronny.resclone.api;
|
||||
|
||||
public interface RescloneEntry {
|
||||
void init(RescloneApi api) throws Exception;
|
||||
}
|
11
src/main/java/io/gitlab/jfronny/resclone/data/CfAddon.java
Normal file
11
src/main/java/io/gitlab/jfronny/resclone/data/CfAddon.java
Normal file
@ -0,0 +1,11 @@
|
||||
package io.gitlab.jfronny.resclone.data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class CfAddon {
|
||||
|
||||
public String downloadUrl;
|
||||
public String fileDate;
|
||||
public Set<String> gameVersion;
|
||||
|
||||
}
|
@ -2,5 +2,14 @@ package io.gitlab.jfronny.resclone.data;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public record PackMetaLoaded(Path zipPath, String name, boolean forceEnable) {
|
||||
}
|
||||
public class PackMetaLoaded {
|
||||
public final Path zipPath;
|
||||
public final String name;
|
||||
public final boolean forceEnable;
|
||||
|
||||
public PackMetaLoaded(Path zipPath, String name, boolean forceEnable) {
|
||||
this.zipPath = zipPath;
|
||||
this.name = name;
|
||||
this.forceEnable = forceEnable;
|
||||
}
|
||||
}
|
@ -1,7 +1,17 @@
|
||||
package io.gitlab.jfronny.resclone.data;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
||||
public class PackMetaUnloaded {
|
||||
public final String fetcher;
|
||||
public final String source;
|
||||
public final String name;
|
||||
public final boolean forceDownload;
|
||||
public final boolean forceEnable;
|
||||
|
||||
@GSerializable
|
||||
public record PackMetaUnloaded(String fetcher, String source, String name, boolean forceDownload, boolean forceEnable) {
|
||||
public PackMetaUnloaded(String fetcher, String source, String name, boolean forceDownload, boolean forceEnable) {
|
||||
this.fetcher = fetcher;
|
||||
this.source = source;
|
||||
this.name = name;
|
||||
this.forceDownload = forceDownload;
|
||||
this.forceEnable = forceEnable;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
package io.gitlab.jfronny.resclone.data.curseforge;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@GSerializable
|
||||
public class GetModFilesResponse {
|
||||
public List<Data> data;
|
||||
|
||||
@GSerializable
|
||||
public static class Data {
|
||||
public String downloadUrl;
|
||||
public Date fileDate;
|
||||
public List<String> gameVersions;
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package io.gitlab.jfronny.resclone.data.curseforge;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
||||
|
||||
@GSerializable
|
||||
public class GetModResponse {
|
||||
public Data data;
|
||||
|
||||
@GSerializable
|
||||
public static class Data {
|
||||
public Boolean allowModDistribution;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package io.gitlab.jfronny.resclone.data.github;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@GSerializable
|
||||
public class Release {
|
||||
public List<Asset> assets;
|
||||
public String zipball_url;
|
||||
|
||||
@GSerializable
|
||||
public static class Asset {
|
||||
public String name;
|
||||
public String content_type;
|
||||
public String browser_download_url;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package io.gitlab.jfronny.resclone.data.github;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
||||
|
||||
@GSerializable
|
||||
public class Repository {
|
||||
public String default_branch;
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package io.gitlab.jfronny.resclone.data.modrinth;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.annotations.SerializedName;
|
||||
import io.gitlab.jfronny.commons.serialize.generator.annotations.GSerializable;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@GSerializable
|
||||
public class Version {
|
||||
public String name;
|
||||
public String version_number;
|
||||
@Nullable public String changelog;
|
||||
public List<Dependency> dependencies;
|
||||
public List<String> game_versions;
|
||||
public VersionType version_type;
|
||||
public List<String> loaders;
|
||||
public Boolean featured;
|
||||
public Status status;
|
||||
@Nullable public Status requested_status;
|
||||
public String id;
|
||||
public String project_id;
|
||||
public String author_id;
|
||||
public Date date_published;
|
||||
public Integer downloads;
|
||||
public List<File> files;
|
||||
|
||||
@GSerializable
|
||||
public static class Dependency {
|
||||
@Nullable public String version_id;
|
||||
@Nullable public String project_id;
|
||||
@Nullable public String file_name;
|
||||
public Type dependency_type;
|
||||
|
||||
public enum Type {
|
||||
required, optional, incompatible, embedded
|
||||
}
|
||||
}
|
||||
|
||||
public enum VersionType {
|
||||
release, beta, alpha
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
listed, archived, draft, unlisted, scheduled, unknown
|
||||
}
|
||||
|
||||
@GSerializable
|
||||
public static class File {
|
||||
public Hashes hashes;
|
||||
public String url;
|
||||
public String filename;
|
||||
public Boolean primary;
|
||||
public Integer size;
|
||||
@Nullable public Type file_type;
|
||||
|
||||
@GSerializable
|
||||
public static class Hashes {
|
||||
public String sha512;
|
||||
public String sha1;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
@SerializedName("required-resource-pack") REQUIRED_RESOURCE_PACK,
|
||||
@SerializedName("optional-resource-pack") OPTIONAL_RESOURCE_PACK
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package io.gitlab.jfronny.resclone.fetchers;
|
||||
|
||||
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.api.PackFetcher;
|
||||
import io.gitlab.jfronny.resclone.util.Result;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@ -17,7 +20,7 @@ public abstract class BasePackFetcher implements PackFetcher {
|
||||
Resclone.urlCache.set(baseUrl, url);
|
||||
} catch (Exception e) {
|
||||
if (Resclone.urlCache.containsKey(baseUrl)) {
|
||||
Resclone.LOGGER.error("Could not get download URL for " + baseUrl + ", using cached", e);
|
||||
e.printStackTrace();
|
||||
url = Resclone.urlCache.get(baseUrl);
|
||||
} else {
|
||||
throw e;
|
||||
@ -26,14 +29,14 @@ public abstract class BasePackFetcher implements PackFetcher {
|
||||
Path p = targetDir.resolve(Integer.toHexString(url.hashCode()));
|
||||
|
||||
if (!forceDownload && Files.exists(p)) {
|
||||
Resclone.packCount++;
|
||||
Resclone.COUNT++;
|
||||
return new Result(p, false);
|
||||
}
|
||||
|
||||
Resclone.LOGGER.info("Downloading pack: " + url);
|
||||
|
||||
try (InputStream is = HttpClient.get(url).userAgent(Resclone.USER_AGENT).sendInputStream();
|
||||
OutputStream os = Files.newOutputStream(p)) {
|
||||
try (InputStream is = new URL(url).openStream()) {
|
||||
FileOutputStream os = new FileOutputStream(p.toFile());
|
||||
byte[] dataBuffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = is.read(dataBuffer, 0, 1024)) != -1) {
|
||||
@ -41,9 +44,9 @@ public abstract class BasePackFetcher implements PackFetcher {
|
||||
}
|
||||
Resclone.LOGGER.info("Finished downloading.");
|
||||
} catch (Throwable e) {
|
||||
throw new IOException("Could not download pack", e);
|
||||
throw new Exception("Could not download pack", e);
|
||||
}
|
||||
Resclone.packCount++;
|
||||
Resclone.COUNT++;
|
||||
return new Result(p, true);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package io.gitlab.jfronny.resclone.fetchers;
|
||||
|
||||
public class BasicFileFetcher extends BasePackFetcher {
|
||||
|
||||
@Override
|
||||
public String getSourceTypeName() {
|
||||
return "file";
|
||||
@ -10,4 +11,5 @@ public class BasicFileFetcher extends BasePackFetcher {
|
||||
public String getDownloadUrl(String baseUrl) {
|
||||
return baseUrl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +1,14 @@
|
||||
package io.gitlab.jfronny.resclone.fetchers;
|
||||
|
||||
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingFunction;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.data.curseforge.GC_GetModFilesResponse;
|
||||
import io.gitlab.jfronny.resclone.data.curseforge.GC_GetModResponse;
|
||||
import io.gitlab.jfronny.resclone.data.curseforge.GetModFilesResponse;
|
||||
import io.gitlab.jfronny.resclone.data.curseforge.GetModResponse;
|
||||
import io.gitlab.jfronny.resclone.data.CfAddon;
|
||||
import io.gitlab.jfronny.resclone.util.UrlUtils;
|
||||
import net.minecraft.MinecraftVersion;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
|
||||
public class CurseforgeFetcher extends BasePackFetcher {
|
||||
// So you found the API key.
|
||||
// Please be aware that CurseForge requires you to change this if you make any kind of derivative work
|
||||
// Creating your own API key is relatively simple, so please don't abuse this
|
||||
private static final String API_KEY = new String(unsalt(new byte[] {
|
||||
-30, 50, -60, -121, 62, -31, 35, 17, 16, -53,
|
||||
-53, -88, 21, -21, 15, -105, -115, -108, 114, -50,
|
||||
-49, -4, 56, -65, -70, 108, -65, -3, -55, -4,
|
||||
36, -86, -40, 116, 71, -5, 75, -9, -43, 4,
|
||||
91, -91, -29, 40, 66, 87, -80, -74, 71, 41,
|
||||
76, -96, 108, -61, -113, 118, 7, -39, -116, -120
|
||||
}, 1024));
|
||||
|
||||
@Override
|
||||
public String getSourceTypeName() {
|
||||
@ -39,49 +18,34 @@ public class CurseforgeFetcher extends BasePackFetcher {
|
||||
@Override
|
||||
public String getDownloadUrl(String baseUrl) throws Exception {
|
||||
try {
|
||||
GetModResponse response = GET(baseUrl, GC_GetModResponse::deserialize);
|
||||
if (!response.data.allowModDistribution)
|
||||
throw new Exception("The author of " + baseUrl + " disabled access to this pack outside of the curseforge launcher");
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'");
|
||||
|
||||
String version = MinecraftVersion.CURRENT.getName();
|
||||
String version = MinecraftVersion.GAME_VERSION.getName();
|
||||
|
||||
GetModFilesResponse.Data latest = null;
|
||||
CfAddon latest = null;
|
||||
Date latestDate = null;
|
||||
boolean foundMatchingVersion = false;
|
||||
|
||||
for (GetModFilesResponse.Data addon : GET(baseUrl + "/files", GC_GetModFilesResponse::deserialize).data) {
|
||||
if (foundMatchingVersion && !addon.gameVersions.contains(version)) continue;
|
||||
if (!foundMatchingVersion && addon.gameVersions.contains(version)) {
|
||||
for (CfAddon addon : UrlUtils.readJsonFromURLSet("https://addons-ecs.forgesvc.net/api/v2/addon/" + baseUrl + "/files", CfAddon.class)) {
|
||||
Date d = df.parse(addon.fileDate);
|
||||
if (foundMatchingVersion && !addon.gameVersion.contains(version))
|
||||
continue;
|
||||
if (!foundMatchingVersion && addon.gameVersion.contains(version)) {
|
||||
foundMatchingVersion = true;
|
||||
latest = null;
|
||||
}
|
||||
if (latest == null || addon.fileDate.after(latestDate)) {
|
||||
if (latest == null || d.after(latestDate)) {
|
||||
latest = addon;
|
||||
latestDate = addon.fileDate;
|
||||
latestDate = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (latest == null) throw new FileNotFoundException("Could not identify valid version");
|
||||
if (!foundMatchingVersion) Resclone.LOGGER.error("Could not find matching version of " + baseUrl + ", using latest");
|
||||
if (!foundMatchingVersion)
|
||||
Resclone.LOGGER.error("Could not find pack for matching version, using latest");
|
||||
return latest.downloadUrl;
|
||||
} catch (Throwable e) {
|
||||
throw new IOException("Could not get CurseForge download for " + baseUrl, e);
|
||||
throw new Exception("Could not get CF download for " + baseUrl, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T GET(String suffix, ThrowingFunction<JsonReader, T, IOException> klazz) throws URISyntaxException, IOException {
|
||||
try (Reader r = HttpClient.get("https://api.curseforge.com/v1/mods/" + suffix).header("x-api-key", API_KEY).sendReader();
|
||||
JsonReader jr = LibJf.LENIENT_TRANSPORT.createReader(r)) {
|
||||
return klazz.apply(jr);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] unsalt(byte[] data, int salt) {
|
||||
byte[] result = new byte[data.length];
|
||||
new Random(salt).nextBytes(result);
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
result[i] ^= data[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,12 @@
|
||||
package io.gitlab.jfronny.resclone.fetchers;
|
||||
|
||||
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.data.github.GC_Release;
|
||||
import io.gitlab.jfronny.resclone.data.github.GC_Repository;
|
||||
import io.gitlab.jfronny.resclone.data.github.Release;
|
||||
import io.gitlab.jfronny.resclone.util.UrlUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class GitHubFetcher extends BasePackFetcher {
|
||||
@Override
|
||||
@ -43,21 +38,21 @@ public class GitHubFetcher extends BasePackFetcher {
|
||||
|
||||
//"user/repo/release" - Gets from the latest release.
|
||||
else if (parts[2].equalsIgnoreCase("release")) {
|
||||
try (Reader r = HttpClient.get("https://api.github.com/repos/" + parts[0] + "/" + parts[1] + "/releases/latest").sendReader();
|
||||
JsonReader jr = LibJf.LENIENT_TRANSPORT.createReader(r)) {
|
||||
Release latestRelease = GC_Release.deserialize(jr);
|
||||
try {
|
||||
JsonObject latestRelease = UrlUtils.readJsonFromURL("https://api.github.com/repos/" + parts[0] + "/" + parts[1] + "/releases/latest", JsonObject.class);
|
||||
String res = null;
|
||||
|
||||
for (Release.Asset asset : latestRelease.assets) {
|
||||
if ("application/x-zip-compressed".equals(asset.content_type) || asset.name.endsWith(".zip")) {
|
||||
res = asset.browser_download_url;
|
||||
for (JsonElement element : latestRelease.get("assets").getAsJsonArray()) {
|
||||
JsonObject o = element.getAsJsonObject();
|
||||
if ("application/x-zip-compressed".equals(o.get("content_type").getAsString()) || o.get("name").getAsString().endsWith(".zip")) {
|
||||
res = o.get("browser_download_url").getAsString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Resclone.LOGGER.info("Getting from latest release.");
|
||||
|
||||
if (res == null) return latestRelease.zipball_url;
|
||||
if (res == null) return latestRelease.get("zipball_url").getAsString();
|
||||
else return res;
|
||||
|
||||
} catch (Throwable e) {
|
||||
@ -78,10 +73,9 @@ public class GitHubFetcher extends BasePackFetcher {
|
||||
|
||||
private String getFromBranch(String repo, @Nullable String branch) {
|
||||
if (branch == null) {
|
||||
try (Reader r = HttpClient.get("https://api.github.com/repos/" + repo).sendReader();
|
||||
JsonReader jr = LibJf.LENIENT_TRANSPORT.createReader(r)) {
|
||||
branch = GC_Repository.deserialize(jr).default_branch;
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
try {
|
||||
branch = UrlUtils.readJsonFromURL("https://api.github.com/repos/" + repo, JsonObject.class).get("default_branch").getAsString();
|
||||
} catch (IOException e) {
|
||||
Resclone.LOGGER.error("Failed to fetch branch for " + repo + ". Choosing \"main\"", e);
|
||||
branch = "main";
|
||||
}
|
||||
@ -94,4 +88,4 @@ public class GitHubFetcher extends BasePackFetcher {
|
||||
Resclone.LOGGER.info("Getting from tag " + tag + ".");
|
||||
return "https://codeload.github.com/" + repo + "/legacy.zip/refs/tags/" + tag;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package io.gitlab.jfronny.resclone.fetchers;
|
||||
|
||||
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.data.modrinth.GC_Version;
|
||||
import io.gitlab.jfronny.resclone.data.modrinth.Version;
|
||||
import io.gitlab.jfronny.resclone.util.ListAdaptation;
|
||||
import net.minecraft.MinecraftVersion;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class ModrinthFetcher extends BasePackFetcher {
|
||||
@Override
|
||||
public String getSourceTypeName() {
|
||||
return "modrinth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadUrl(String baseUrl) throws Exception {
|
||||
try {
|
||||
String version = MinecraftVersion.CURRENT.getName();
|
||||
|
||||
Version latest = null;
|
||||
Date latestDate = null;
|
||||
boolean foundMatchingVersion = false;
|
||||
|
||||
List<Version> versions;
|
||||
try (Reader r = HttpClient.get("https://api.modrinth.com/v2/project/" + baseUrl + "/version")
|
||||
.userAgent(Resclone.USER_AGENT)
|
||||
.sendReader();
|
||||
JsonReader jr = LibJf.LENIENT_TRANSPORT.createReader(r)) {
|
||||
versions = ListAdaptation.deserializeList(jr, GC_Version::deserialize);
|
||||
}
|
||||
for (Version ver : versions) {
|
||||
if (foundMatchingVersion && !ver.game_versions.contains(version)) continue;
|
||||
if (ver.files.isEmpty()) continue;
|
||||
if (!foundMatchingVersion && ver.game_versions.contains(version)) {
|
||||
foundMatchingVersion = true;
|
||||
latest = null;
|
||||
}
|
||||
if (latest == null || ver.date_published.after(latestDate)) {
|
||||
latest = ver;
|
||||
latestDate = ver.date_published;
|
||||
}
|
||||
}
|
||||
|
||||
if (latest == null) throw new FileNotFoundException("Could not identify valid version");
|
||||
if (!foundMatchingVersion) Resclone.LOGGER.error("Could not find matching version of " + baseUrl + ", using latest");
|
||||
|
||||
for (Version.File file : latest.files) {
|
||||
if (file.primary) return file.url;
|
||||
}
|
||||
Resclone.LOGGER.error("Could not identify primary file of " + baseUrl + ", attempting identification by file_type");
|
||||
for (Version.File file : latest.files) {
|
||||
if (file.file_type == Version.File.Type.REQUIRED_RESOURCE_PACK) return file.url;
|
||||
}
|
||||
Resclone.LOGGER.error("Identification failed, using first file of " + baseUrl);
|
||||
return latest.files.getFirst().url;
|
||||
} catch (Throwable e) {
|
||||
throw new IOException("Could not get Modrinth download for " + baseUrl, e);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,43 +3,38 @@ package io.gitlab.jfronny.resclone.mixin;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.RescloneResourcePack;
|
||||
import io.gitlab.jfronny.resclone.data.PackMetaLoaded;
|
||||
import net.minecraft.resource.*;
|
||||
import net.minecraft.text.Text;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import net.minecraft.resource.FileResourcePackProvider;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
import net.minecraft.resource.ResourcePackSource;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(FileResourcePackProvider.class)
|
||||
public class FileResourcePackProviderMixin {
|
||||
@Shadow @Final private ResourcePackSource source;
|
||||
@Shadow @Final private ResourceType type;
|
||||
@Shadow
|
||||
@Final
|
||||
private ResourcePackSource field_25345;
|
||||
|
||||
@Inject(at = @At("TAIL"), method = "register(Ljava/util/function/Consumer;)V")
|
||||
public void registerExtra(Consumer<ResourcePackProfile> consumer, CallbackInfo info) {
|
||||
for (PackMetaLoaded meta : Resclone.DOWNLOADED_PACKS) {
|
||||
ResourcePackInfo ifo = new ResourcePackInfo(
|
||||
"resclone/" + meta.name(),
|
||||
Text.literal(meta.name()),
|
||||
source,
|
||||
Optional.empty()
|
||||
);
|
||||
ResourcePackProfile resourcePackProfile = ResourcePackProfile.create(
|
||||
ifo,
|
||||
new RescloneResourcePack.Factory(meta.zipPath().toFile(), ifo),
|
||||
type,
|
||||
new ResourcePackPosition(
|
||||
meta.forceEnable(),
|
||||
ResourcePackProfile.InsertionPosition.TOP,
|
||||
false
|
||||
)
|
||||
@Inject(at = @At("TAIL"), method = "register(Ljava/util/function/Consumer;Lnet/minecraft/resource/ResourcePackProfile$Factory;)V")
|
||||
public void registerExtra(Consumer<ResourcePackProfile> consumer, ResourcePackProfile.Factory factory, CallbackInfo info) {
|
||||
for (PackMetaLoaded meta : Resclone.downloadedPacks) {
|
||||
ResourcePackProfile resourcePackProfile = ResourcePackProfile.of(
|
||||
"resclone/" + meta.name,
|
||||
meta.forceEnable,
|
||||
() -> new RescloneResourcePack(meta.zipPath.toFile(), meta.name),
|
||||
factory,
|
||||
ResourcePackProfile.InsertionPosition.TOP,
|
||||
this.field_25345
|
||||
);
|
||||
if (resourcePackProfile != null) {
|
||||
consumer.accept(resourcePackProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -19,12 +19,12 @@ public abstract class GameOptionsMixin {
|
||||
|
||||
@Inject(at = @At("TAIL"), method = "load()V")
|
||||
public void load(CallbackInfo ci) {
|
||||
for (PackMetaLoaded meta : Resclone.NEW_PACKS) {
|
||||
Resclone.LOGGER.info(Resclone.MOD_ID + "/" + meta.name());
|
||||
resourcePacks.add(Resclone.MOD_ID + "/" + meta.name());
|
||||
for (PackMetaLoaded meta : Resclone.newPacks) {
|
||||
Resclone.LOGGER.info(Resclone.MOD_ID + "/" + meta.name);
|
||||
resourcePacks.add(Resclone.MOD_ID + "/" + meta.name);
|
||||
}
|
||||
if (!Resclone.NEW_PACKS.isEmpty())
|
||||
if (!Resclone.newPacks.isEmpty())
|
||||
write();
|
||||
Resclone.NEW_PACKS.clear();
|
||||
Resclone.newPacks.clear();
|
||||
}
|
||||
}
|
@ -1,19 +1,21 @@
|
||||
package io.gitlab.jfronny.resclone.processors;
|
||||
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.RescloneConfig;
|
||||
import io.gitlab.jfronny.resclone.api.PackProcessor;
|
||||
import io.gitlab.jfronny.resclone.util.io.PathPruneVisitor;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class PruneVanillaProcessor implements PackProcessor {
|
||||
|
||||
@Override
|
||||
public void process(FileSystem p) throws Exception {
|
||||
ClassLoader cl = MinecraftServer.class.getClassLoader();
|
||||
ClassLoader cl = MinecraftClient.class.getClassLoader();
|
||||
try {
|
||||
if (Files.isDirectory(p.getPath("/assets/minecraft"))) {
|
||||
Files.walkFileTree(p.getPath("/assets/minecraft"), new PathPruneVisitor((s) -> {
|
||||
@ -23,14 +25,11 @@ public class PruneVanillaProcessor implements PackProcessor {
|
||||
InputStream vn = cl.getResourceAsStream(p.getPath("/").relativize(s).toString());
|
||||
if (vn != null) {
|
||||
try (InputStream pk = Files.newInputStream(s, StandardOpenOption.READ)) {
|
||||
if (IOUtils.contentEquals(vn, pk)) {
|
||||
if (RescloneConfig.logProcessing) Resclone.LOGGER.info("Pruning file unchanged from vanilla: {}", s);
|
||||
return true;
|
||||
}
|
||||
return IOUtils.contentEquals(vn, pk);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Resclone.LOGGER.error("Could not prune unchanged assets", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
@ -39,4 +38,5 @@ public class PruneVanillaProcessor implements PackProcessor {
|
||||
throw new Exception("Could not prune vanilla files", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
package io.gitlab.jfronny.resclone.processors;
|
||||
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.RescloneConfig;
|
||||
import io.gitlab.jfronny.resclone.api.PackProcessor;
|
||||
import io.gitlab.jfronny.resclone.util.io.PathPruneVisitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class RemoveEmptyProcessor implements PackProcessor {
|
||||
|
||||
@Override
|
||||
public void process(FileSystem p) throws Exception {
|
||||
if (Files.exists(p.getPath("/assets"))) {
|
||||
@ -15,12 +18,9 @@ public class RemoveEmptyProcessor implements PackProcessor {
|
||||
Files.walkFileTree(p.getPath("/assets"), new PathPruneVisitor(s -> {
|
||||
if (Files.isDirectory(s)) {
|
||||
try (DirectoryStream<Path> paths = Files.newDirectoryStream(s)) {
|
||||
if (!paths.iterator().hasNext()) {
|
||||
if (RescloneConfig.logProcessing) Resclone.LOGGER.info("Pruning empty directory: {}", s);
|
||||
return true;
|
||||
}
|
||||
return !paths.iterator().hasNext();
|
||||
} catch (IOException e) {
|
||||
Resclone.LOGGER.error("Could not check whether directory has entries", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -30,4 +30,5 @@ public class RemoveEmptyProcessor implements PackProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
package io.gitlab.jfronny.resclone.processors;
|
||||
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.RescloneConfig;
|
||||
import io.gitlab.jfronny.resclone.api.PackProcessor;
|
||||
import io.gitlab.jfronny.resclone.util.io.MoveDirVisitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
|
||||
public class RootPathProcessor implements PackProcessor {
|
||||
|
||||
@Override
|
||||
public void process(FileSystem p) throws Exception {
|
||||
if (!Files.exists(p.getPath("/pack.mcmeta"))) {
|
||||
@ -16,7 +16,6 @@ public class RootPathProcessor implements PackProcessor {
|
||||
try (DirectoryStream<Path> paths = Files.newDirectoryStream(root)) {
|
||||
for (Path path : paths) {
|
||||
if (Files.isDirectory(path) && Files.exists(path.resolve("pack.mcmeta"))) {
|
||||
if (RescloneConfig.logProcessing) Resclone.LOGGER.info("Moving discovered root out of: {}", path);
|
||||
Files.walkFileTree(path, new MoveDirVisitor(path, root, StandardCopyOption.REPLACE_EXISTING));
|
||||
}
|
||||
}
|
||||
@ -26,4 +25,5 @@ public class RootPathProcessor implements PackProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package io.gitlab.jfronny.resclone.util;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
import io.gitlab.jfronny.resclone.api.RescloneApi;
|
||||
import io.gitlab.jfronny.resclone.data.PackMetaUnloaded;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class ConfigLoader {
|
||||
|
||||
public static void load(RescloneApi api) {
|
||||
Path configPath = api.getConfigPath().resolve("config.json");
|
||||
if (!Files.exists(configPath)) {
|
||||
save(api, new HashSet<>());
|
||||
}
|
||||
try {
|
||||
StringBuilder text = new StringBuilder();
|
||||
for (String s : Files.readAllLines(configPath)) {
|
||||
text.append("\r\n");
|
||||
text.append(s);
|
||||
}
|
||||
Set<PackMetaUnloaded> data = Resclone.gson.fromJson(text.toString(), new TypeToken<Set<PackMetaUnloaded>>(){}.getType());
|
||||
for (PackMetaUnloaded meta : data) {
|
||||
api.addPack(meta.fetcher, meta.source, meta.name, meta.forceDownload, meta.forceEnable);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void save(RescloneApi api, Set<PackMetaUnloaded> data) {
|
||||
Path configPath = api.getConfigPath().resolve("config.json");
|
||||
Set<String> text = new HashSet<>();
|
||||
text.add(Resclone.gson.toJson(data));
|
||||
try {
|
||||
Files.write(configPath, text);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package io.gitlab.jfronny.resclone.util;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingFunction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ListAdaptation {
|
||||
public static <T, TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> List<T> deserializeList(Reader reader, ThrowingFunction<Reader, T, TEx> deserializeOne) throws TEx {
|
||||
List<T> result = new ArrayList<>();
|
||||
reader.beginArray();
|
||||
while (reader.hasNext()) {
|
||||
result.add(deserializeOne.apply(reader));
|
||||
}
|
||||
reader.endArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T, TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Set<T> deserializeSet(Reader reader, ThrowingFunction<Reader, T, TEx> serializeOne) throws TEx {
|
||||
Set<T> result = new LinkedHashSet<>();
|
||||
reader.beginArray();
|
||||
while (reader.hasNext()) {
|
||||
result.add(serializeOne.apply(reader));
|
||||
}
|
||||
reader.endArray();
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
package io.gitlab.jfronny.resclone.util;
|
||||
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Properties;
|
||||
|
||||
public class PackUrlCache {
|
||||
|
||||
private final Path file;
|
||||
Properties properties = new Properties();
|
||||
|
||||
@ -17,7 +18,7 @@ public class PackUrlCache {
|
||||
try (BufferedReader r = Files.newBufferedReader(file)) {
|
||||
properties.load(r);
|
||||
} catch (IOException e) {
|
||||
Resclone.LOGGER.error("Could not load pack URL cache");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26,7 +27,7 @@ public class PackUrlCache {
|
||||
try (BufferedWriter w = Files.newBufferedWriter(file)) {
|
||||
properties.store(w, "This is an internal file used for offline pack loading, do not edit");
|
||||
} catch (IOException e) {
|
||||
Resclone.LOGGER.error("Could not write pack URL cache");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,4 +42,5 @@ public class PackUrlCache {
|
||||
public void set(String key, String value) {
|
||||
properties.setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
15
src/main/java/io/gitlab/jfronny/resclone/util/Result.java
Normal file
15
src/main/java/io/gitlab/jfronny/resclone/util/Result.java
Normal file
@ -0,0 +1,15 @@
|
||||
package io.gitlab.jfronny.resclone.util;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class Result {
|
||||
|
||||
public final Path downloadPath;
|
||||
public final boolean freshDownload;
|
||||
|
||||
public Result(Path downloadPath, boolean freshDownload) {
|
||||
this.downloadPath = downloadPath;
|
||||
this.freshDownload = freshDownload;
|
||||
}
|
||||
|
||||
}
|
41
src/main/java/io/gitlab/jfronny/resclone/util/UrlUtils.java
Normal file
41
src/main/java/io/gitlab/jfronny/resclone/util/UrlUtils.java
Normal file
@ -0,0 +1,41 @@
|
||||
package io.gitlab.jfronny.resclone.util;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.gitlab.jfronny.resclone.Resclone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
|
||||
public class UrlUtils {
|
||||
|
||||
public static boolean urlValid(String url) {
|
||||
try {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).toURI().toURL().openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.connect();
|
||||
return connection.getResponseCode() == 200;
|
||||
} catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String readStringFromURL(String requestURL) throws IOException {
|
||||
try (Scanner scanner = new Scanner(new URL(requestURL).openStream(), StandardCharsets.UTF_8.toString())) {
|
||||
scanner.useDelimiter("\\A");
|
||||
return scanner.hasNext() ? scanner.next() : "";
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T readJsonFromURL(String requestUrl, Class<T> classOfT) throws IOException {
|
||||
return Resclone.gson.fromJson(readStringFromURL(requestUrl), classOfT);
|
||||
}
|
||||
|
||||
public static <T> Set<T> readJsonFromURLSet(String requestUrl, Class<T> classOfT) throws IOException {
|
||||
return Resclone.gson.fromJson(readStringFromURL(requestUrl), TypeToken.getParameterized(Set.class, classOfT).getType());
|
||||
}
|
||||
|
||||
}
|
@ -5,6 +5,7 @@ import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
public class MoveDirVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
private final Path fromPath;
|
||||
private final Path toPath;
|
||||
private final CopyOption copyOption;
|
||||
@ -35,4 +36,5 @@ public class MoveDirVisitor extends SimpleFileVisitor<Path> {
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +1,16 @@
|
||||
package io.gitlab.jfronny.resclone.util.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class PathPruneVisitor extends SimpleFileVisitor<Path> {
|
||||
private final Predicate<Path> removalSelector;
|
||||
|
||||
Predicate<Path> removalSelector;
|
||||
|
||||
public PathPruneVisitor(Predicate<Path> removalSelector) {
|
||||
this.removalSelector = removalSelector;
|
||||
@ -23,4 +27,5 @@ public class PathPruneVisitor extends SimpleFileVisitor<Path> {
|
||||
if (removalSelector.test(dir)) Files.walkFileTree(dir, new RemoveDirVisitor());
|
||||
return super.postVisitDirectory(dir, exc);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,14 @@
|
||||
package io.gitlab.jfronny.resclone.util.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
public class RemoveDirVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
@ -16,4 +20,5 @@ public class RemoveDirVisitor extends SimpleFileVisitor<Path> {
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"resclone.jfconfig.title": "Resclone",
|
||||
"resclone.jfconfig.packs": "Packs",
|
||||
"resclone.jfconfig.packs.tooltip": "The packs to download and add",
|
||||
"resclone.jfconfig.pruneUnused": "Prune Unused",
|
||||
"resclone.jfconfig.pruneUnused.tooltip": "Automatically remove all downloaded packs that are not in the config to free up unneeded space",
|
||||
"resclone.jfconfig.filterPacks": "Filter Packs",
|
||||
"resclone.jfconfig.filterPacks.tooltip": "Whether to filter packs to remove files unchanged from vanilla and empty directories",
|
||||
"resclone.jfconfig.logProcessing": "Log Processing",
|
||||
"resclone.jfconfig.logProcessing.tooltip": "Log automatic processing steps applied to downloaded packs"
|
||||
}
|
@ -1,36 +1,30 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "resclone",
|
||||
"name": "Resclone",
|
||||
"version": "${version}",
|
||||
"description": "Downloads and updates resource packs",
|
||||
"authors": ["JFronny"],
|
||||
"contact": {
|
||||
"email": "projects.contact@frohnmeyer-wds.de",
|
||||
"homepage": "https://jfronny.gitlab.io",
|
||||
"issues": "https://git.frohnmeyer-wds.de/JfMods/Resclone/issues",
|
||||
"sources": "https://git.frohnmeyer-wds.de/JfMods/Resclone"
|
||||
},
|
||||
"name": "Resclone",
|
||||
"description": "Downloads and updates resourcepacks.",
|
||||
"authors": [
|
||||
"JFronny"
|
||||
],
|
||||
"contact": {},
|
||||
"license": "MIT",
|
||||
"icon": "assets/resclone/icon.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": ["io.gitlab.jfronny.resclone.Resclone"],
|
||||
"libjf:config": [
|
||||
"io.gitlab.jfronny.resclone.RescloneConfig"
|
||||
"main": [
|
||||
"io.gitlab.jfronny.resclone.Resclone"
|
||||
],
|
||||
"resclone": [
|
||||
"io.gitlab.jfronny.resclone.RescloneEntryDefault"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"resclone.mixins.json",
|
||||
{
|
||||
"config": "resclone.client.mixins.json",
|
||||
"environment": "client"
|
||||
}
|
||||
"resclone.mixins.json"
|
||||
],
|
||||
"accessWidener": "resclone.accesswidener",
|
||||
"depends": {
|
||||
"fabricloader": ">=0.12.0",
|
||||
"minecraft": "*",
|
||||
"libjf-base": "*"
|
||||
"fabricloader": ">=0.10.8",
|
||||
"fabric": "*",
|
||||
"minecraft": "*"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
accessWidener v2 named
|
||||
|
||||
accessible class net/minecraft/resource/ZipResourcePack$ZipFileWrapper
|
||||
accessible method net/minecraft/resource/ZipResourcePack <init> (Lnet/minecraft/resource/ResourcePackInfo;Lnet/minecraft/resource/ZipResourcePack$ZipFileWrapper;Ljava/lang/String;)V
|
||||
accessible method net/minecraft/resource/ZipResourcePack$ZipFileWrapper <init> (Ljava/io/File;)V
|
@ -2,8 +2,10 @@
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "io.gitlab.jfronny.resclone.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"FileResourcePackProviderMixin"
|
||||
"FileResourcePackProviderMixin",
|
||||
"GameOptionsMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
Loading…
Reference in New Issue
Block a user