diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0cdf7d6d..f419860a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -716,6 +716,42 @@ jobs: path: fabric/1.21.2/build/libs/AdvancedBackups-fabric-1.21.2-${{ inputs.version }}.jar + fabric-26-2: + runs-on: ubuntu-latest + needs: core + + steps: + + - uses: actions/checkout@v3 + with: + ref: main + + - name: Download corelib + uses: actions/download-artifact@v4 + with: + name: advancedbackups-corelib.jar + path: core/build/libs/ + + - name: Set up JDK 25 + uses: actions/setup-java@v3 + with: + java-version: 25 + distribution: 'temurin' + + - name: Build Mod + run: | + cd fabric/26.2 + ./gradlew -Pversion=${{ inputs.version }} build + #run: ls -la advancedbackups-corelib.jar + + + - name: Upload Mod + uses: actions/upload-artifact@v4 + with: + name: AdvancedBackups-fabric-26.2-${{ inputs.version }}.jar + path: fabric/26.2/build/libs/AdvancedBackups-fabric-26.2-${{ inputs.version }}.jar + + neoforge-1-20-2: runs-on: ubuntu-latest needs: core @@ -940,12 +976,89 @@ jobs: name: AdvancedBackups-spigot-1.21-${{ inputs.version }}.jar path: spigot/1.21/build/libs/AdvancedBackups-spigot-1.21-${{ inputs.version }}.jar + + neoforge-26-2: + runs-on: ubuntu-latest + needs: core + + steps: + + - uses: actions/checkout@v3 + with: + ref: main + + - name: Download corelib + uses: actions/download-artifact@v4 + with: + name: advancedbackups-corelib.jar + path: core/build/libs/ + + - name: Set up JDK 25 + uses: actions/setup-java@v3 + with: + java-version: 25 + distribution: 'temurin' + + - name: Build Mod + run: | + cd neoforge/26.2 + ./gradlew -Pversion=${{ inputs.version }} build + #run: ls -la advancedbackups-corelib.jar + + + - name: Upload Mod + uses: actions/upload-artifact@v4 + with: + name: AdvancedBackups-neoforge-26.2-${{ inputs.version }}.jar + path: neoforge/26.2/build/libs/AdvancedBackups-neoforge-26.2-${{ inputs.version }}.jar + + + spigot-26-2: + runs-on: ubuntu-latest + needs: core + + steps: + + - uses: actions/checkout@v3 + with: + ref: main + + - name: Download corelib + uses: actions/download-artifact@v4 + with: + name: advancedbackups-corelib.jar + path: core/build/libs/ + + - name: Set up JDK 25 + uses: actions/setup-java@v3 + with: + java-version: 25 + distribution: 'temurin' + + - name: Setup Buildtools + run: | + wget -O BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar + java -jar BuildTools.jar --nogui --rev 26.2 --remapped + + - name: Build Mod + run: | + cd spigot/26.2 + ./gradlew -Pversion=${{ inputs.version }} build + #run: ls -la advancedbackups-corelib.jar + + + - name: Upload Mod + uses: actions/upload-artifact@v4 + with: + name: AdvancedBackups-spigot-26.2-${{ inputs.version }}.jar + path: spigot/26.2/build/libs/AdvancedBackups-spigot-26.2-${{ inputs.version }}.jar + bundle-archives: runs-on: ubuntu-latest - needs: [core, forge-1-7-10, forge-1-12-2, forge-1-16, forge-1-18, fabric-1-18, forge-1-19-2, + needs: [core, forge-1-7-10, forge-1-12-2, forge-1-16, forge-1-18, fabric-1-18, forge-1-19-2, forge-1-19-3, fabric-1-19-2, fabric-1-19-3, forge-1-20, forge-1-20-2, fabric-1-20, fabric-1-20-2, neoforge-1-20-2, - forge-1-20-4, forge-1-20-6, fabric-1-20-4, fabric-1-21, fabric-1-21-2, neoforge-1-20-4, fabric-1-20-6, neoforge-1-20-6, neoforge-1-21, neoforge-1-21-2, - spigot-1-21] + forge-1-20-4, forge-1-20-6, fabric-1-20-4, fabric-1-21, fabric-1-21-2, fabric-26-2, neoforge-1-20-4, fabric-1-20-6, neoforge-1-20-6, neoforge-1-21, neoforge-1-21-2, + spigot-1-21, neoforge-26-2, spigot-26-2] steps: @@ -1064,6 +1177,12 @@ jobs: name: AdvancedBackups-fabric-1.21.2-${{ inputs.version }}.jar path: archives/ + - name: Download Fabric-26-2 + uses: actions/download-artifact@v4 + with: + name: AdvancedBackups-fabric-26.2-${{ inputs.version }}.jar + path: archives/ + - name: Download Neoforge-1-20-2 uses: actions/download-artifact@v4 with: @@ -1099,7 +1218,19 @@ jobs: with: name: AdvancedBackups-spigot-1.21-${{ inputs.version }}.jar path: archives/ - + + - name: Download Neoforge-26-2 + uses: actions/download-artifact@v4 + with: + name: AdvancedBackups-neoforge-26.2-${{ inputs.version }}.jar + path: archives/ + + - name: Download Spigot-26-2 + uses: actions/download-artifact@v4 + with: + name: AdvancedBackups-spigot-26.2-${{ inputs.version }}.jar + path: archives/ + - name: Cleanup Old Archives uses: geekyeggo/delete-artifact@v2 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 042d0d24..7b8264ed 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -37,12 +37,15 @@ on: - Fabric 1.20.6 - Fabric 1.21 - Fabric 1.21.2 + - Fabric 26.2 - Neoforge 1.20.2 - Neoforge 1.20.4 - Neoforge 1.20.6 - Neoforge 1.21 - Neoforge 1.21.2 + - Neoforge 26.2 - Spigot 1.21 + - Spigot 26.2 jobs: @@ -620,7 +623,7 @@ jobs: loaders: | fabric quilt - game-versions: | + game-versions: | 1.21.2 1.21.3 1.21.4 @@ -629,6 +632,34 @@ jobs: changelog: ${{ vars.CHANGELOG }} + - name: Upload Fabric 26.2 + + if: inputs.version == 'Fabric 26.2' || inputs.version == 'All' + uses: Kir-Antipov/mc-publish@v3.3 + with: + #CF specific + curseforge-id: 876284 + curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} + #MR specific + modrinth-id: Jrmoreqs + modrinth-token: ${{ secrets.MODRINTH_TOKEN }} + #Modrinth's auto-feature works nicely, so we're going to keep using it. + modrinth-featured: false + #General + files: | + **/AdvancedBackups-fabric-26.2-${{ steps.version.outputs.VERSION }}.jar + name: Fabric 26.2 - ${{ steps.version.outputs.VERSION }} + version: ${{ steps.version.outputs.VERSION }} + version-type: release + loaders: | + fabric + game-versions: | + 26.2 + java: | + Java 25 + changelog: ${{ vars.CHANGELOG }} + + - name: Upload Neoforge 1.20.2 if: inputs.version == 'Neoforge 1.20.2' || inputs.version == 'All' @@ -762,7 +793,7 @@ jobs: version-type: release loaders: | neoforge - game-versions: | + game-versions: | 1.21.2 1.21.3 1.21.4 @@ -771,6 +802,34 @@ jobs: changelog: ${{ vars.CHANGELOG }} + - name: Upload Neoforge 26.2 + + if: inputs.version == 'Neoforge 26.2' || inputs.version == 'All' + uses: Kir-Antipov/mc-publish@v3.3 + with: + #CF specific + curseforge-id: 876284 + curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} + #MR specific + modrinth-id: Jrmoreqs + modrinth-token: ${{ secrets.MODRINTH_TOKEN }} + #Modrinth's auto-feature works nicely, so we're going to keep using it. + modrinth-featured: false + #General + files: | + **/AdvancedBackups-neoforge-26.2-${{ steps.version.outputs.VERSION }}.jar + name: Neoforge 26.2 - ${{ steps.version.outputs.VERSION }} + version: ${{ steps.version.outputs.VERSION }} + version-type: release + loaders: | + neoforge + game-versions: | + 26.2 + java: | + Java 25 + changelog: ${{ vars.CHANGELOG }} + + - name: Upload Spigot 1.21 if: inputs.version == 'Spigot 1.21' || inputs.version == 'All' @@ -799,3 +858,32 @@ jobs: Java 21 changelog: ${{ vars.CHANGELOG }} + + - name: Upload Spigot 26.2 + + if: inputs.version == 'Spigot 26.2' || inputs.version == 'All' + uses: Kir-Antipov/mc-publish@v3.3 + with: + #CF specific + curseforge-id: 876284 + curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} + #MR specific + modrinth-id: Jrmoreqs + modrinth-token: ${{ secrets.MODRINTH_TOKEN }} + #Modrinth's auto-feature works nicely, so we're going to keep using it. + modrinth-featured: false + #General + files: | + **/AdvancedBackups-spigot-26.2-${{ steps.version.outputs.VERSION }}.jar + name: Spigot 26.2 - ${{ steps.version.outputs.VERSION }} + version: ${{ steps.version.outputs.VERSION }} + version-type: release + loaders: | + spigot + paper + game-versions: | + 26.2 + java: | + Java 25 + changelog: ${{ vars.CHANGELOG }} + diff --git a/fabric/26.2/README.md b/fabric/26.2/README.md new file mode 100644 index 00000000..e71edfdb --- /dev/null +++ b/fabric/26.2/README.md @@ -0,0 +1,6 @@ +# Fabric - 26.2 + +This is the branch specifically for fabric 26.2. +Any differences will be listed below. For full documentation, see the `core` branch. + +Minecraft 26.2 ships unobfuscated, so this module builds against Mojang's official mappings directly (no Yarn) using the non-remapping `net.fabricmc.fabric-loom` plugin, and targets Java 25. diff --git a/fabric/26.2/build.gradle b/fabric/26.2/build.gradle new file mode 100644 index 00000000..d3d7798a --- /dev/null +++ b/fabric/26.2/build.gradle @@ -0,0 +1,99 @@ +plugins { + id 'net.fabricmc.fabric-loom' version "${loom_version}" + id 'maven-publish' +} + +apply from: '../../global.properties' + +group = project.maven_group +base { + archivesName = 'AdvancedBackups-' + modloaderName + "-" + minecraftVersion +} + + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +configurations { + // configuration that holds jars to include in the jar + extraLibs +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + implementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + // Uncomment the following line to enable the deprecated Fabric API modules. + // These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time. + + // implementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}" + + implementation files(abCoreLibPath) + extraLibs files(abCoreLibPath) +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.release = 25 +} + +java { + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() + + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 +} + +jar { + manifest { + attributes([ + "Main-Class" : "computer.heather.advancedbackups.cli.AdvancedBackupsCLI", + "Specification-Title" : "advancedbackups", + "Specification-Vendor" : "Heather White", + "Specification-Version" : "1", // We are version 1 of ourselves + "Implementation-Title" : project.name, + "Implementation-Version" : project.jar.archiveVersion, + "Implementation-Vendor" : "Heather White", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") + ]) + } + from { + configurations.extraLibs.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/fabric/26.2/gradle.properties b/fabric/26.2/gradle.properties new file mode 100644 index 00000000..620e6779 --- /dev/null +++ b/fabric/26.2/gradle.properties @@ -0,0 +1,24 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349 +org.gradle.configuration-cache=false + +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=26.2 +loader_version=0.19.5 +loom_version=1.17-SNAPSHOT + +# Mod Properties +version=2.0 +mod_version=2.0 +maven_group=computer.heather.advancedbackups +archives_base_name=advancedbackups +modloaderName =fabric + +minecraftVersion =26.2 + +# Dependencies +fabric_version=0.160.0+26.2 diff --git a/fabric/26.2/gradle/wrapper/gradle-wrapper.jar b/fabric/26.2/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..a4b76b95 Binary files /dev/null and b/fabric/26.2/gradle/wrapper/gradle-wrapper.jar differ diff --git a/fabric/26.2/gradle/wrapper/gradle-wrapper.properties b/fabric/26.2/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..df6a6ad7 --- /dev/null +++ b/fabric/26.2/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,9 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip +networkTimeout=10000 +retries=0 +retryBackOffMs=500 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/fabric/26.2/gradlew b/fabric/26.2/gradlew new file mode 100644 index 00000000..f5feea6d --- /dev/null +++ b/fabric/26.2/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + MSYS* | 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 + if ! command -v java >/dev/null 2>&1 + then + 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 +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# 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. + +# 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 + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# 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" "$@" diff --git a/fabric/26.2/gradlew.bat b/fabric/26.2/gradlew.bat new file mode 100644 index 00000000..9d21a218 --- /dev/null +++ b/fabric/26.2/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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=. +@rem This is normally unused +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% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +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% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/fabric/26.2/settings.gradle b/fabric/26.2/settings.gradle new file mode 100644 index 00000000..a4ef71df --- /dev/null +++ b/fabric/26.2/settings.gradle @@ -0,0 +1,12 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + mavenCentral() + gradlePluginPortal() + } +} + +rootProject.name = 'fabric-26.2' diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackups.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackups.java new file mode 100644 index 00000000..48b78400 --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackups.java @@ -0,0 +1,130 @@ +package computer.heather.advancedbackups; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import computer.heather.advancedbackups.client.ClientContactor; +import computer.heather.advancedbackups.core.ABCore; +import computer.heather.advancedbackups.core.backups.BackupTimer; +import computer.heather.advancedbackups.core.backups.BackupWrapper; +import computer.heather.advancedbackups.core.config.ConfigManager; +import computer.heather.advancedbackups.network.PacketBackupStatus; +import computer.heather.advancedbackups.network.PacketToastSubscribe; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.fabricmc.loader.impl.FabricLoaderImpl; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.LevelResource; + +public class AdvancedBackups implements ModInitializer { + // This logger is used to write text to the console and the log file. + // It is considered best practice to use your mod id as the logger's name. + // That way, it's clear which mod wrote info, warnings, and errors. + + public static final Logger LOGGER = LoggerFactory.getLogger("advanced-backups"); + + public static final Consumer infoLogger = LOGGER::info; + public static final Consumer warningLogger = LOGGER::warn; + public static final Consumer errorLogger = LOGGER::error; + + public static final ArrayList players = new ArrayList<>(); + + public static MinecraftServer server; + + @Override + public void onInitialize() { + + ServerLifecycleEvents.SERVER_STARTING.register((server) -> { + AdvancedBackups.server = server; + ABCore.worldName = server.getWorldData().getLevelName(); + ABCore.worldDir = server.getWorldPath(LevelResource.ROOT); + + ABCore.disableSaving = AdvancedBackups::disableSaving; + ABCore.enableSaving = AdvancedBackups::enableSaving; + ABCore.saveOnce = AdvancedBackups::saveOnce; + + ABCore.infoLogger = infoLogger; + ABCore.warningLogger = warningLogger; + ABCore.errorLogger = errorLogger; + + ABCore.resetActivity = AdvancedBackups::resetActivity; + + ABCore.clientContactor = new ClientContactor(); + ABCore.modJar = new File(FabricLoaderImpl.INSTANCE.getModContainer("advancedbackups").get().getOrigin().getPaths().get(0).toAbsolutePath().toString()); + + + ConfigManager.loadOrCreateConfig(); + LOGGER.info("Config loaded!!"); + }); + + ServerLifecycleEvents.SERVER_STARTED.register((server) -> { + BackupWrapper.checkStartupBackups(); + }); + ServerLifecycleEvents.SERVER_STOPPING.register((server) -> { + BackupWrapper.checkShutdownBackups(); + }); + + ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { + ABCore.setActivity(true); + }); + + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + AdvancedBackupsCommand.register(dispatcher); + }); + + + ServerTickEvents.END_SERVER_TICK.register((server) -> { + BackupTimer.check(); + }); + + PayloadTypeRegistry.clientboundPlay().register(PacketBackupStatus.ID, PacketBackupStatus.CODEC); + PayloadTypeRegistry.serverboundPlay().register(PacketToastSubscribe.ID, PacketToastSubscribe.CODEC); + + ServerPlayNetworking.registerGlobalReceiver(PacketToastSubscribe.ID, PacketToastSubscribe::handle); + + + } + + + + + public static void disableSaving() { + MinecraftServer server = AdvancedBackups.server; + for (ServerLevel level : server.getAllLevels()) { + if (level != null && !level.noSave) { + level.noSave = true; + } + } + } + + public static void enableSaving() { + MinecraftServer server = AdvancedBackups.server; + for (ServerLevel level : server.getAllLevels()) { + if (level != null && level.noSave) { + level.noSave = false; + } + } + } + + public static void saveOnce(boolean flush) { + MinecraftServer server = AdvancedBackups.server; + server.saveEverything(true, flush, true); + } + + public static void resetActivity() { + List players = server.getPlayerList().getPlayers(); + ABCore.setActivity(!players.isEmpty()); + } +} diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackupsCommand.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackupsCommand.java new file mode 100644 index 00000000..746efd86 --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackupsCommand.java @@ -0,0 +1,69 @@ +package computer.heather.advancedbackups; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.arguments.StringArgumentType; + +import computer.heather.advancedbackups.core.CoreCommandSystem; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.Component; +import net.minecraft.server.permissions.Permissions; + +public class AdvancedBackupsCommand { + public static void register(CommandDispatcher stack) { + stack.register(Commands.literal("backup").requires((runner) -> { + return !AdvancedBackups.server.isDedicatedServer() || runner.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER); + }).then(Commands.literal("start").executes((runner) -> { + CoreCommandSystem.startBackup((response) -> { + runner.getSource().sendSuccess(() -> Component.literal(response), true); + }); + return 1; + })) + + .then(Commands.literal("reload-config").executes((runner) -> { + CoreCommandSystem.reloadConfig((response) -> { + runner.getSource().sendSuccess(() -> Component.literal(response), true); + }); + return 1; + })) + + .then(Commands.literal("reset-chain").executes((runner) -> { + CoreCommandSystem.resetChainLength((response) -> { + runner.getSource().sendSuccess(() -> Component.literal(response), true); + }); + return 1; + })) + + .then(Commands.literal("snapshot").executes((runner) -> { + CoreCommandSystem.snapshot((response) -> { + runner.getSource().sendSuccess(() -> Component.literal(response), true); + }, "snapshot"); + return 1; + }) + .then(Commands.argument("name", StringArgumentType.greedyString()).executes((runner) -> { + ParseResults parseResults = stack.parse(StringArgumentType.getString(runner, "name"), runner.getSource()); + String snapshotName = parseResults.getReader().getString(); + CoreCommandSystem.snapshot((response) -> { + runner.getSource().sendSuccess(() -> Component.literal(response), true); + }, snapshotName); + return 1; + }))) + + .then(Commands.literal("cancel").executes((runner) -> { + CoreCommandSystem.cancelBackup((response) -> { + runner.getSource().sendSuccess(() -> Component.literal(response), true); + }); + return 1; + })) + + .then(Commands.literal("reload-client-config").executes((runner) -> { + runner.getSource().sendSuccess(() -> Component.literal("This command can only be ran on the client!"), true); + return 1; + })) + + ); + } + + +} diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/AdvancedBackupsClientCommand.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/AdvancedBackupsClientCommand.java new file mode 100644 index 00000000..e52d9cf2 --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/AdvancedBackupsClientCommand.java @@ -0,0 +1,61 @@ +package computer.heather.advancedbackups.client; + + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.arguments.StringArgumentType; + +import computer.heather.advancedbackups.core.CoreCommandSystem; +import net.fabricmc.fabric.api.client.command.v2.ClientCommands; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; + +public class AdvancedBackupsClientCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(ClientCommands.literal("backup").requires((runner) -> { + return true; + }).then(ClientCommands.literal("start").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup start"); + return 1; + })) + + .then(ClientCommands.literal("reload-config").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup reload-config"); + return 1; + })) + + .then(ClientCommands.literal("reset-chain").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup reset-chain"); + return 1; + })) + + .then(ClientCommands.literal("snapshot").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup snapshot"); + return 1; + }) + + .then(ClientCommands.argument("name", StringArgumentType.greedyString()).executes((runner) -> { + ParseResults parseResults = dispatcher.parse(StringArgumentType.getString(runner, "name"), runner.getSource()); + String snapshotName = parseResults.getReader().getString(); + Minecraft.getInstance().player.connection.sendCommand("backup snapshot " + snapshotName); + return 1; + }))) + + .then(ClientCommands.literal("cancel").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup cancel"); + return 1; + })) + + .then(ClientCommands.literal("reload-client-config").executes((runner) -> { + CoreCommandSystem.reloadClientConfig((response) -> { + runner.getSource().sendFeedback(Component.literal(response)); + }); + return 1; + })) + + ); + } + + +} \ No newline at end of file diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/BackupToast.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/BackupToast.java new file mode 100644 index 00000000..ed4b620d --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/BackupToast.java @@ -0,0 +1,143 @@ +package computer.heather.advancedbackups.client; + + +import computer.heather.advancedbackups.core.config.ClientConfigManager; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.toasts.Toast; +import net.minecraft.client.gui.components.toasts.ToastManager; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.resources.Identifier; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class BackupToast implements Toast { + + public static boolean starting; + public static boolean started; + public static boolean failed; + public static boolean finished; + public static boolean cancelled; + + public static int progress; + public static int max; + + public static boolean exists = false; + + private static long time; + private static boolean timeSet = false; + + public static final ItemStack stack = new ItemStack(Items.PAPER); + private static final Identifier TEXTURE = Identifier.parse("toast/advancement"); + + public static String title = "You shouldn't see this!"; + public static int textColour = 0; + + private Visibility visibility = Visibility.SHOW; + + @Override + public void extractRenderState(GuiGraphicsExtractor context, Font renderer, long startTime) { + context.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, 0, 0, 0F, (float) (ClientConfigManager.darkMode.get() ? 0 : this.height()), this.width(), this.height(), this.width(), this.height() * 2); + + context.item(stack, 8, 8); + + float percent = finished ? 100 : (float) progress / (float) max; + + context.fill(4, 28, 156, 29, ColourHelper.colour + (255, (int) ClientConfigManager.progressBackgroundRed.get(), (int) ClientConfigManager.progressBackgroundGreen.get(), (int) ClientConfigManager.progressBackgroundBlue.get())); + + float f = Math.min(156, ( + 156 * percent + )); + + if (!exists) { + if (title.equals(I18n.get("advancedbackups.backup_finished"))){ + textColour = ColourHelper.colour(255, (int) ClientConfigManager.progressTextRed.get(), (int) ClientConfigManager.progressTextGreen.get(), (int) ClientConfigManager.progressTextBlue.get()); + context.text(renderer, I18n.get(title), 25, 11, textColour, false); + context.fill(4, 28, 156, 29, ColourHelper.colour + (255, (int) ClientConfigManager.progressBarRed.get(), (int) ClientConfigManager.progressBarGreen.get(), (int) ClientConfigManager.progressBarBlue.get())); + } + else { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.errorTextRed.get(), (int) ClientConfigManager.errorTextGreen.get(), (int) ClientConfigManager.errorTextBlue.get()); + context.text(renderer, I18n.get(title), 25, 11, textColour, false); + } + visibility = Visibility.HIDE; + return; + } + + title = "You shouldn't see this!"; + + + if (starting) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.progressTextRed.get(), (int) ClientConfigManager.progressTextGreen.get(), (int) ClientConfigManager.progressTextBlue.get()); + title = I18n.get("advancedbackups.backup_starting"); + } + else if (started) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.progressTextRed.get(), (int) ClientConfigManager.progressTextGreen.get(), (int) ClientConfigManager.progressTextBlue.get()); + title = I18n.get("advancedbackups.progress", round(percent * 100)); + } + else if (failed) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.errorTextRed.get(), (int) ClientConfigManager.errorTextGreen.get(), (int) ClientConfigManager.errorTextBlue.get()); + title = I18n.get("advancedbackups.backup_failed"); + if (!timeSet) { + time = System.currentTimeMillis(); + timeSet = true; + } + } + else if (finished) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.progressTextRed.get(), (int) ClientConfigManager.progressTextGreen.get(), (int) ClientConfigManager.progressTextBlue.get()); + title = I18n.get("advancedbackups.backup_finished"); + if (!timeSet) { + time = System.currentTimeMillis(); + timeSet = true; + } + } + else if (cancelled) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.errorTextRed.get(), (int) ClientConfigManager.errorTextGreen.get(), (int) ClientConfigManager.errorTextBlue.get()); + title = I18n.get("advancedbackups.backup_cancelled"); + if (!timeSet) { + time = System.currentTimeMillis(); + timeSet = true; + } + } + + context.text(renderer, title, 25, 11, textColour, false); + + if (timeSet && System.currentTimeMillis() >= time + 5000) { + starting = false; + started = false; + failed = false; + finished = false; + progress = 0; + max = 0; + timeSet = false; + exists = false; + visibility = Visibility.HIDE; + return; + } + + context.fill(4, 28, Math.max(4, (int) f), 29, ColourHelper.colour + (255, (int) ClientConfigManager.progressBarRed.get(), (int) ClientConfigManager.progressBarGreen.get(), (int) ClientConfigManager.progressBarBlue.get())); + + visibility = Visibility.SHOW; + } + + + private static String round (float value) { + return String.format("%.1f", value); + } + + + @Override + public Visibility getWantedVisibility() { + return visibility; + } + + + @Override + public void update(ToastManager manager, long time) { + // does this need to do anything? + } + +} diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/ClientContactor.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/ClientContactor.java new file mode 100644 index 00000000..13d5a1da --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/ClientContactor.java @@ -0,0 +1,79 @@ +package computer.heather.advancedbackups.client; + +import java.util.List; + +import computer.heather.advancedbackups.AdvancedBackups; +import computer.heather.advancedbackups.interfaces.IClientContactor; +import computer.heather.advancedbackups.network.NetworkHandler; +import computer.heather.advancedbackups.network.PacketBackupStatus; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; + +public class ClientContactor implements IClientContactor { + + @Override + public void backupComplete(boolean all) { + MinecraftServer server = AdvancedBackups.server; + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(false, false, false, true, false, 0, 0); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } + + @Override + public void backupFailed(boolean all) { + MinecraftServer server = AdvancedBackups.server; + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(false, false, true, false, false, 0, 0); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } + + @Override + public void backupProgress(int progress, int max, boolean all) { + MinecraftServer server = AdvancedBackups.server; + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(false, true, false, false, false, progress, max); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } + + @Override + public void backupStarting(boolean all) { + MinecraftServer server = AdvancedBackups.server; + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(true, false, false, false, false, 0, 0); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } + + @Override + public void backupCancelled(boolean all) { + MinecraftServer server = AdvancedBackups.server; + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(false, false, false, false, true, 0, 0); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } +} diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/ClientWrapper.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/ClientWrapper.java new file mode 100644 index 00000000..5bab6301 --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/ClientWrapper.java @@ -0,0 +1,76 @@ +package computer.heather.advancedbackups.client; + +import computer.heather.advancedbackups.AdvancedBackups; +import computer.heather.advancedbackups.core.ABCore; +import computer.heather.advancedbackups.core.config.ClientConfigManager; +import computer.heather.advancedbackups.network.PacketBackupStatus; +import computer.heather.advancedbackups.network.PacketToastSubscribe; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.minecraft.client.Minecraft; + +public class ClientWrapper implements ClientModInitializer { + + @Override + public void onInitializeClient() { + + ABCore.infoLogger = AdvancedBackups.infoLogger; + ABCore.warningLogger = AdvancedBackups.warningLogger; + ABCore.errorLogger = AdvancedBackups.errorLogger; + + ClientLifecycleEvents.CLIENT_STARTED.register((client) -> { + ClientConfigManager.loadOrCreateConfig(); + }); + + ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { + PacketToastSubscribe packet = new PacketToastSubscribe(ClientConfigManager.showProgress.get()); + if (ClientPlayNetworking.canSend(packet.type())) { + //Make sure a server can receive the packet before trying to send! + ClientPlayNetworking.send(packet); + } + else { + ABCore.warningLogger.accept("Refusing to send packet " + packet + " as the server cannot accept it!"); + } + }); + + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + AdvancedBackupsClientCommand.register(dispatcher); + + }); + + + ClientPlayNetworking.registerGlobalReceiver(PacketBackupStatus.ID, ClientWrapper::handle); + + + } + + + + public static void handle(PacketBackupStatus message, ClientPlayNetworking.Context context) { + + + + Minecraft.getInstance().execute(() -> { + BackupToast.starting = message.starting(); + BackupToast.started = message.started(); + BackupToast.failed = message.failed(); + BackupToast.finished = message.finished(); + BackupToast.cancelled = message.cancelled(); + + BackupToast.progress = message.progress(); + BackupToast.max = message.max(); + + if (!BackupToast.exists) { + BackupToast.exists = true; + Minecraft.getInstance().gui.toastManager().addToast(new BackupToast()); + } + + }); + + + } + +} diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/ColourHelper.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/ColourHelper.java new file mode 100644 index 00000000..5619961d --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/client/ColourHelper.java @@ -0,0 +1,31 @@ +package computer.heather.advancedbackups.client; + +//A colour helper, loosely based on that of vanilla 1.16. +//What is the modern equivalent..? +public class ColourHelper { + + public static int alpha(int in) { + return in >>> 24; + } + + public static int red(int in) { + return in >> 16 & 255; + } + + public static int green(int in) { + return in >> 8 & 255; + } + + public static int blue(int in) { + return in & 255; + } + + public static int colour(int a, int r, int g, int b) { + return a << 24 | r << 16 | g << 8 | b; + } + + public static int colour(int a, long r, long g, long b) { + return colour(a, (int) r, (int) g, (int) b); + } + +} \ No newline at end of file diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/network/NetworkHandler.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/network/NetworkHandler.java new file mode 100644 index 00000000..54349211 --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/network/NetworkHandler.java @@ -0,0 +1,16 @@ +package computer.heather.advancedbackups.network; + +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.server.level.ServerPlayer; + +public class NetworkHandler { + + public static void sendToClient(ServerPlayer player, PacketBackupStatus packet) { + + ServerPlayNetworking.send(player, packet); + + } + + + +} diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/network/PacketBackupStatus.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/network/PacketBackupStatus.java new file mode 100644 index 00000000..eb0bd543 --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/network/PacketBackupStatus.java @@ -0,0 +1,39 @@ +package computer.heather.advancedbackups.network; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; + +public record PacketBackupStatus(boolean starting, boolean started, boolean failed, boolean finished, boolean cancelled, int progress, int max) implements CustomPacketPayload { + + public static final Type ID = new CustomPacketPayload.Type(Identifier.parse("advancedbackups:backup_status")); + + public static final StreamCodec CODEC = StreamCodec.of((buf, packet) -> { + buf.writeBoolean(packet.starting); + buf.writeBoolean(packet.started); + buf.writeBoolean(packet.failed); + buf.writeBoolean(packet.finished); + buf.writeBoolean(packet.cancelled); + buf.writeInt(packet.progress); + buf.writeInt(packet.max); + }, + + buf -> new PacketBackupStatus( + buf.readBoolean(), + buf.readBoolean(), + buf.readBoolean(), + buf.readBoolean(), + buf.readBoolean(), + buf.readInt(), + buf.readInt() + )); + + + @Override + public Type type() { + return ID; + } + + +} diff --git a/fabric/26.2/src/main/java/computer/heather/advancedbackups/network/PacketToastSubscribe.java b/fabric/26.2/src/main/java/computer/heather/advancedbackups/network/PacketToastSubscribe.java new file mode 100644 index 00000000..ae0842a4 --- /dev/null +++ b/fabric/26.2/src/main/java/computer/heather/advancedbackups/network/PacketToastSubscribe.java @@ -0,0 +1,45 @@ +package computer.heather.advancedbackups.network; + +import computer.heather.advancedbackups.AdvancedBackups; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; +import net.minecraft.server.level.ServerPlayer; + +public record PacketToastSubscribe(boolean enable) implements CustomPacketPayload { + + public static final Type ID = new CustomPacketPayload.Type(Identifier.parse("advancedbackups:toast_subscribe")); + + public PacketToastSubscribe(boolean enable) { + this.enable = enable; + } + + public static final StreamCodec CODEC = StreamCodec.composite(ByteBufCodecs.BOOL, PacketToastSubscribe::enable, PacketToastSubscribe::new); + + + public static void handle(PacketToastSubscribe message, ServerPlayNetworking.Context context) { + + ServerPlayer player = context.player(); + + if (message.enable() && !AdvancedBackups.players.contains(player.getStringUUID())) { + AdvancedBackups.players.add(player.getStringUUID()); + } + else if (!message.enable()) { + AdvancedBackups.players.remove(player.getStringUUID()); + } + + } + + + + @Override + public Type type() { + return ID; + } + + + +} \ No newline at end of file diff --git a/fabric/26.2/src/main/resources/fabric.mod.json b/fabric/26.2/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..5d9b810a --- /dev/null +++ b/fabric/26.2/src/main/resources/fabric.mod.json @@ -0,0 +1,30 @@ +{ + "schemaVersion": 1, + "id": "advancedbackups", + "version": "${version}", + "name": "Advanced Backups", + "description": "An extremely advanced backup mod.\n\nSupports many backup types.\n\nConfig file and github contain documentation.", + "authors": [ + "Mommy Heather" + ], + "contact": { + "homepage": "https://github.com/HeatherComputer/advancedbackups" + }, + "license": "BSD", + "icon": "assets/advanced-backups/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "computer.heather.advancedbackups.AdvancedBackups" + ], + "client": [ + "computer.heather.advancedbackups.client.ClientWrapper" + ] + }, + "depends": { + "fabricloader": ">=0.19.5", + "minecraft": ">=26.2", + "java": ">=25", + "fabric-api": "*" + } +} \ No newline at end of file diff --git a/fabric/26.2/src/main/resources/pack.mcmeta b/fabric/26.2/src/main/resources/pack.mcmeta new file mode 100644 index 00000000..877382f4 --- /dev/null +++ b/fabric/26.2/src/main/resources/pack.mcmeta @@ -0,0 +1,8 @@ +{ + "pack": { + "description": "advancedbackups resources", + "pack_format": 9, + "forge:resource_pack_format": 8, + "forge:data_pack_format": 9 + } +} diff --git a/neoforge/26.2/README.md b/neoforge/26.2/README.md new file mode 100644 index 00000000..15bb9746 --- /dev/null +++ b/neoforge/26.2/README.md @@ -0,0 +1,6 @@ +# Neoforge - 26.2 + +This is the branch specifically for neoforge 26.2. +Any differences will be listed below. For full documentation, see the `core` branch. + +Minecraft 26.2 ships unobfuscated, so `ResourceLocation` is now `Identifier` and the toast rendering API uses the new `extractRenderState`/`GuiGraphicsExtractor` pattern. Toolchain uses ModDevGradle (`net.neoforged.moddev`) and targets Java 25. diff --git a/neoforge/26.2/build.gradle b/neoforge/26.2/build.gradle new file mode 100644 index 00000000..5afb90f4 --- /dev/null +++ b/neoforge/26.2/build.gradle @@ -0,0 +1,155 @@ +plugins { + id 'java-library' + id 'eclipse' + id 'idea' + id 'maven-publish' + id 'net.neoforged.moddev' version '2.0.147' +} + + +tasks.named('wrapper', Wrapper).configure { + // Define wrapper values here so as to not have to always do so when updating gradlew.properties. + // Switching this to Wrapper.DistributionType.ALL will download the full gradle sources that comes with + // documentation attached on cursor hover of gradle classes and methods. However, this comes with increased + // file size for Gradle. If you do switch this to ALL, run the Gradle wrapper task twice afterwards. + // (Verify by checking gradle/wrapper/gradle-wrapper.properties to see if distributionUrl now points to `-all`) + distributionType = Wrapper.DistributionType.BIN +} + +apply from: '../../global.properties' + +group = mod_group_id + +version = "${version != 'unspecified' ? version : '0.1-dev'}" //debuggins sake + +configurations { + // configuration that holds jars to include in the jar + extraLibs +} + +base { + archivesName = 'AdvancedBackups-' + modloaderName + "-" + minecraftVersion +} + +// Mojang ships Java 25 to end users in 26.2, so your mod should target Java 25. +java.toolchain.languageVersion = JavaLanguageVersion.of(25) + +neoForge { + // Specify the version of NeoForge to use. + version = project.neo_version + + // Access Transformers are automatically detected from src/main/resources/META-INF/accesstransformer.cfg + + // Default run configurations. + // These can be tweaked, removed, or duplicated as needed. + runs { + client { + client() + // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. + systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id + } + + server { + server() + programArgument '--nogui' + systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id + } + + // This run config launches GameTestServer and runs all registered gametests, then exits. + // By default, the server will crash when no gametests are provided. + // The gametest system is also enabled by default for other run configs under the /test command. + gameTestServer { + type = "gameTestServer" + systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id + } + + // applies to all the run configs above + configureEach { + // Recommended logging data for a userdev environment + // The markers can be added/remove as needed separated by commas. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + systemProperty 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + logLevel = org.slf4j.event.Level.DEBUG + } + } + + mods { + // define mod <-> source bindings + // these are used to tell the game which sources are for which mod + "${mod_id}" { + sourceSet(sourceSets.main) + } + } +} + +// Include resources generated by data generators. +sourceSets.main.resources { srcDir 'src/generated/resources' } + +dependencies { + // Specify the version of Minecraft to use. + implementation "net.neoforged:neoforge:${neo_version}" + + implementation files(abCoreLibPath) + extraLibs files(abCoreLibPath) +} + +// This block of code expands all declared replace properties in the specified resource targets. +// A missing property will result in an error. Properties are expanded using ${} Groovy notation. +// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments. +// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html +tasks.withType(ProcessResources).configureEach { + var replaceProperties = [ + minecraft_version : minecraft_version, minecraft_version_range: minecraft_version_range, + neo_version : neo_version, + mod_id : mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: version, + mod_authors : mod_authors, mod_description: mod_description, pack_format_number: pack_format_number, + ] + inputs.properties replaceProperties + + filesMatching(['META-INF/neoforge.mods.toml']) { + expand replaceProperties + [project: project] + } +} + +// Example for how to get properties into the manifest for reading at runtime. +tasks.named('jar', Jar).configure { + manifest { + attributes([ + "Main-Class" : "computer.heather.advancedbackups.cli.AdvancedBackupsCLI", + "Specification-Title" : "advancedbackups", + "Specification-Vendor" : "Heather White", + "Specification-Version" : "1", // We are version 1 of ourselves + "Implementation-Title" : project.name, + "Implementation-Version" : project.jar.archiveVersion, + "Implementation-Vendor" : "Heather White", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") + ]) + } + from { + configurations.extraLibs.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +// Example configuration to allow publishing using the maven-publish plugin +publishing { + publications { + register('mavenJava', MavenPublication) { + from components.java + } + } + repositories { + maven { + url "file://${project.projectDir}/repo" + } + } +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation +} diff --git a/neoforge/26.2/gradle.properties b/neoforge/26.2/gradle.properties new file mode 100644 index 00000000..c3ef5998 --- /dev/null +++ b/neoforge/26.2/gradle.properties @@ -0,0 +1,43 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false +org.gradle.debug=false + +# Environment Properties +# You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge +# The Minecraft version must agree with the Neo version to get a valid artifact +minecraft_version=26.2 +# The Minecraft version range can use any release version of Minecraft as bounds. +# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly +# as they do not follow standard versioning conventions. +minecraft_version_range=[26.2] +# The Neo version must agree with the Minecraft version to get a valid artifact +neo_version=26.2.0.87 + + + +## Mod Properties + +# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} +# Must match the String constant located in the main mod class annotated with @Mod. +mod_id=advancedbackups +# The human-readable display name for the mod. +mod_name=Advanced Backups +# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. +mod_license=BSD +# The mod version. See https://semver.org/ +mod_version=2.1.2 +# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. +# This should match the base package used for the mod sources. +# See https://maven.apache.org/guides/mini/guide-naming-conventions.html +mod_group_id=computer.heather.advancedbackups +# The authors of the mod. This is a simple text string that is used for display purposes in the mod list. +mod_authors=Heather White +# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. +mod_description=A highly advanced and configurable backup mod.\Github contains documentation. +# Pack version - this changes each minecraft release, in general. +pack_format_number=18 + + +modloaderName =neoforge +minecraftVersion =26.2 diff --git a/neoforge/26.2/gradle/wrapper/gradle-wrapper.jar b/neoforge/26.2/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..a4b76b95 Binary files /dev/null and b/neoforge/26.2/gradle/wrapper/gradle-wrapper.jar differ diff --git a/neoforge/26.2/gradle/wrapper/gradle-wrapper.properties b/neoforge/26.2/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..23449a2b --- /dev/null +++ b/neoforge/26.2/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/neoforge/26.2/gradlew b/neoforge/26.2/gradlew new file mode 100644 index 00000000..f5feea6d --- /dev/null +++ b/neoforge/26.2/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + MSYS* | 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 + if ! command -v java >/dev/null 2>&1 + then + 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 +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# 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. + +# 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 + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# 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" "$@" diff --git a/neoforge/26.2/gradlew.bat b/neoforge/26.2/gradlew.bat new file mode 100644 index 00000000..9d21a218 --- /dev/null +++ b/neoforge/26.2/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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=. +@rem This is normally unused +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% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +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% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/neoforge/26.2/settings.gradle b/neoforge/26.2/settings.gradle new file mode 100644 index 00000000..168e7113 --- /dev/null +++ b/neoforge/26.2/settings.gradle @@ -0,0 +1,11 @@ +pluginManagement { + repositories { + gradlePluginPortal() + } +} + +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +} + +rootProject.name = 'neoforge-26.2' diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackups.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackups.java new file mode 100644 index 00000000..42b4a302 --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackups.java @@ -0,0 +1,149 @@ +package computer.heather.advancedbackups; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.slf4j.Logger; + +import com.mojang.logging.LogUtils; + +import computer.heather.advancedbackups.client.ClientContactor; +import computer.heather.advancedbackups.client.ClientWrapper; +import computer.heather.advancedbackups.core.ABCore; +import computer.heather.advancedbackups.core.backups.BackupTimer; +import computer.heather.advancedbackups.core.backups.BackupWrapper; +import computer.heather.advancedbackups.core.config.ConfigManager; +import computer.heather.advancedbackups.network.NetworkHandler; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.storage.LevelResource; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModList; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.event.server.ServerStartingEvent; +import net.neoforged.neoforge.event.server.ServerStoppingEvent; +import net.neoforged.neoforge.event.tick.ServerTickEvent; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + +@Mod("advancedbackups") +public class AdvancedBackups +{ + + private static final Logger LOGGER = LogUtils.getLogger(); + + public static final Consumer infoLogger = LOGGER::info; + public static final Consumer warningLogger = LOGGER::warn; + public static final Consumer errorLogger = LOGGER::error; + + public static final ArrayList players = new ArrayList<>(); + + + public AdvancedBackups(IEventBus modEventBus) + { + // Register ourselves for server and other game events we are interested in + NeoForge.EVENT_BUS.register(this); + modEventBus.addListener(NetworkHandler::onRegisterPayloadHandler); + modEventBus.addListener(this::clientSetup); + + ABCore.infoLogger = infoLogger; + ABCore.warningLogger = warningLogger; + ABCore.errorLogger = errorLogger; + } + + @SubscribeEvent + public void onServerStarting(ServerStartingEvent event) + { + // Do something when the server starts + ABCore.worldName = event.getServer().getWorldData().getLevelName(); + ABCore.worldDir = event.getServer().getWorldPath(LevelResource.ROOT); + + ABCore.disableSaving = AdvancedBackups::disableSaving; + ABCore.enableSaving = AdvancedBackups::enableSaving; + ABCore.saveOnce = AdvancedBackups::saveOnce; + + ABCore.resetActivity = AdvancedBackups::resetActivity; + + ABCore.clientContactor = new ClientContactor(); + ABCore.modJar = ModList.get().getModFileById("advancedbackups").getFile().getFilePath().toFile(); + + + ConfigManager.loadOrCreateConfig(); + LOGGER.info("Config loaded!!"); + + } + + public void clientSetup(FMLClientSetupEvent e) { + ClientWrapper.init(e); + } + + + @SubscribeEvent + public void onServerStarted(ServerStartedEvent event) { + BackupWrapper.checkStartupBackups(); + } + + @SubscribeEvent + public void onServerStopping(ServerStoppingEvent event) { + BackupWrapper.checkShutdownBackups(); + } + + + @SubscribeEvent + public void onPlayerJoined(PlayerEvent.PlayerLoggedInEvent event) { + ABCore.setActivity(true); + } + + @SubscribeEvent + public void registerCommands(RegisterCommandsEvent event){ + AdvancedBackupsCommand.register(event.getDispatcher()); + } + + + @SubscribeEvent + public void onPostTick(ServerTickEvent.Post event) { + BackupTimer.check(); + } + + + + + + public static void disableSaving() { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + for (ServerLevel level : server.getAllLevels()) { + if (level != null && !level.noSave) { + level.noSave = true; + } + } + } + + public static void enableSaving() { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + for (ServerLevel level : server.getAllLevels()) { + if (level != null && level.noSave) { + level.noSave = false; + } + } + } + + public static void saveOnce(boolean flush) { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + server.saveEverything(true, flush, true); + } + + + public static void resetActivity() { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + List players = server.getPlayerList().getPlayers(); + ABCore.setActivity(!players.isEmpty()); + } + +} diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackupsCommand.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackupsCommand.java new file mode 100644 index 00000000..cf6fa05a --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/AdvancedBackupsCommand.java @@ -0,0 +1,83 @@ +package computer.heather.advancedbackups; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; + +import computer.heather.advancedbackups.core.CoreCommandSystem; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.Component; +import net.minecraft.server.permissions.Permissions; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + +public class AdvancedBackupsCommand { + public static void register(CommandDispatcher stack) { + stack.register(Commands.literal("backup").requires((runner) -> { + return !ServerLifecycleHooks.getCurrentServer().isDedicatedServer() || runner.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER); + }).then(Commands.literal("start").executes((runner) -> { + CoreCommandSystem.startBackup((response) -> { + runner.getSource().sendSuccess(() -> { + return Component.literal(response) ; + }, true); + }); + return 1; + })) + + .then(Commands.literal("reload-config").executes((runner) -> { + CoreCommandSystem.reloadConfig((response) -> { + runner.getSource().sendSuccess(() -> { + return Component.literal(response) ; + }, true); + }); + return 1; + })) + + .then(Commands.literal("reset-chain").executes((runner) -> { + CoreCommandSystem.resetChainLength((response) -> { + runner.getSource().sendSuccess(() -> { + return Component.literal(response) ; + }, true); + }); + return 1; + })) + + .then(Commands.literal("snapshot").executes((runner) -> { + CoreCommandSystem.snapshot((response) -> { + runner.getSource().sendSuccess(() -> { + return Component.literal(response) ; + }, true); + }, "snapshot"); + return 1; + }) + + .then(Commands.argument("name", StringArgumentType.greedyString()).executes((runner) -> { + String name = StringArgumentType.getString(runner, "name"); + CoreCommandSystem.snapshot((response) -> { + runner.getSource().sendSuccess(() -> { + return Component.literal(response) ; + }, true); + }, name); + return 1; + }))) + + .then(Commands.literal("cancel").executes((runner) -> { + CoreCommandSystem.cancelBackup((response) -> { + runner.getSource().sendSuccess(() -> { + return Component.literal(response) ; + }, true); + }); + return 1; + })) + + .then(Commands.literal("reload-client-config").executes((runner) -> { + runner.getSource().sendSuccess(() -> { + return Component.literal("This command can only be ran on the client!") ; + }, true); + return 1; + })) + + ); + } + + +} diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/AdvancedBackupsClientCommand.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/AdvancedBackupsClientCommand.java new file mode 100644 index 00000000..62e0063e --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/AdvancedBackupsClientCommand.java @@ -0,0 +1,66 @@ +package computer.heather.advancedbackups.client; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; + +import computer.heather.advancedbackups.core.CoreCommandSystem; +import net.minecraft.client.Minecraft; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.Component; + +public class AdvancedBackupsClientCommand { + public static void register(CommandDispatcher commandDispatcher) { + commandDispatcher.register(literal("backup").requires((runner) -> { + return true; + }).then(literal("start").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup start"); + return 1; + })) + + .then(literal("reload-config").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup reload-config"); + return 1; + })) + + .then(literal("reset-chain").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup reset-chain"); + return 1; + })) + + .then(literal("snapshot").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup snapshot"); + return 1; + }) + + .then(Commands.argument("name", StringArgumentType.greedyString()).executes((runner) -> { + String name = StringArgumentType.getString(runner, "name"); + Minecraft.getInstance().player.connection.sendCommand("backup snapshot " + name); + return 1; + }))) + + .then(literal("cancel").executes((runner) -> { + Minecraft.getInstance().player.connection.sendCommand("backup cancel"); + return 1; + })) + + .then(literal("reload-client-config").executes((runner) -> { + CoreCommandSystem.reloadClientConfig((response) -> { + runner.getSource().sendSuccess(() -> { + return Component.literal(response); + }, true); + }); + return 1; + })) + + ); + } + + //I'm no fan of having this here but it seems required + public static LiteralArgumentBuilder literal(String literal) { + return LiteralArgumentBuilder.literal(literal); + } + + +} diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/BackupToast.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/BackupToast.java new file mode 100644 index 00000000..4522dc4b --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/BackupToast.java @@ -0,0 +1,143 @@ +package computer.heather.advancedbackups.client; + +import computer.heather.advancedbackups.core.config.ClientConfigManager; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.toasts.Toast; +import net.minecraft.client.gui.components.toasts.ToastManager; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.resources.Identifier; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class BackupToast implements Toast { + + public static boolean starting; + public static boolean started; + public static boolean failed; + public static boolean finished; + public static boolean cancelled; + + public static int progress; + public static int max; + + public static boolean exists = false; + + private static long time; + private static boolean timeSet = false; + + public static final ItemStack stack = new ItemStack(Items.PAPER); + private static final Identifier TEXTURE = Identifier.parse("toast/advancement"); + + private int textColour; + private String title = "You shouldn't see this!"; + + private Visibility visibility = Visibility.SHOW; + + + @Override + public void extractRenderState(GuiGraphicsExtractor graphics, Font font, long delta) { + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, TEXTURE, 0, ClientConfigManager.darkMode.get() ? 0 : this.height(), this.width(), this.height()); + graphics.fakeItem(stack, 8, 8); + + + float percent = finished ? 100 : (float) progress / (float) max; + + graphics.fill(4, 28, 156, 29, ColourHelper.colour + (255, (int) ClientConfigManager.progressBackgroundRed.get(), (int) ClientConfigManager.progressBackgroundGreen.get(), (int) ClientConfigManager.progressBackgroundBlue.get())); + + float f = Math.min(156, ( + 156 * percent + )); + + if (!exists) { + if (title.equals(I18n.get("advancedbackups.backup_finished"))){ + textColour = ColourHelper.colour(255, (int) ClientConfigManager.progressTextRed.get(), (int) ClientConfigManager.progressTextGreen.get(), (int) ClientConfigManager.progressTextBlue.get()); + graphics.text(font, I18n.get(title), 25, 11, textColour); + graphics.fill(3, 28, 156, 29, ColourHelper.colour + (255, (int) ClientConfigManager.progressBarRed.get(), (int) ClientConfigManager.progressBarGreen.get(), (int) ClientConfigManager.progressBarBlue.get())); + } + else { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.errorTextRed.get(), (int) ClientConfigManager.errorTextGreen.get(), (int) ClientConfigManager.errorTextBlue.get()); + graphics.text(font, I18n.get(title), 25, 11, textColour); + } + visibility = Visibility.HIDE; + return; + } + + + if (starting) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.progressTextRed.get(), (int) ClientConfigManager.progressTextGreen.get(), (int) ClientConfigManager.progressTextBlue.get()); + title = I18n.get("advancedbackups.backup_starting"); + } + else if (started) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.progressTextRed.get(), (int) ClientConfigManager.progressTextGreen.get(), (int) ClientConfigManager.progressTextBlue.get()); + title = I18n.get("advancedbackups.progress", round(percent * 100)); + } + else if (failed) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.errorTextRed.get(), (int) ClientConfigManager.errorTextGreen.get(), (int) ClientConfigManager.errorTextBlue.get()); + title = I18n.get("advancedbackups.backup_failed"); + if (!timeSet) { + time = System.currentTimeMillis(); + timeSet = true; + } + } + else if (finished) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.progressTextRed.get(), (int) ClientConfigManager.progressTextGreen.get(), (int) ClientConfigManager.progressTextBlue.get()); + title = I18n.get("advancedbackups.backup_finished"); + if (!timeSet) { + time = System.currentTimeMillis(); + timeSet = true; + } + } + else if (cancelled) { + textColour = ColourHelper.colour(255, (int) ClientConfigManager.errorTextRed.get(), (int) ClientConfigManager.errorTextGreen.get(), (int) ClientConfigManager.errorTextBlue.get()); + title = I18n.get("advancedbackups.backup_cancelled"); + if (!timeSet) { + time = System.currentTimeMillis(); + timeSet = true; + } + } + + graphics.text(font, title, 25, 11, textColour); + + if (timeSet && System.currentTimeMillis() >= time + 5000) { + starting = false; + started = false; + failed = false; + finished = false; + progress = 0; + max = 0; + timeSet = false; + exists = false; + visibility = Visibility.HIDE; + return; + } + + graphics.fill(4, 28, Math.max(4, (int) f), 29, ColourHelper.colour + (255, (int) ClientConfigManager.progressBarRed.get(), (int) ClientConfigManager.progressBarGreen.get(), (int) ClientConfigManager.progressBarBlue.get())); + + visibility = Visibility.SHOW; + return; + + } + + + private static String round (float value) { + return String.format("%.1f", value); + } + + + @Override + public Visibility getWantedVisibility() { + return visibility; + } + + + @Override + public void update(ToastManager p_361832_, long p_362759_) { + // unused... + } + +} diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/ClientContactor.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/ClientContactor.java new file mode 100644 index 00000000..89631385 --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/ClientContactor.java @@ -0,0 +1,80 @@ +package computer.heather.advancedbackups.client; + +import java.util.List; + +import computer.heather.advancedbackups.AdvancedBackups; +import computer.heather.advancedbackups.interfaces.IClientContactor; +import computer.heather.advancedbackups.network.NetworkHandler; +import computer.heather.advancedbackups.network.PacketBackupStatus; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + +public class ClientContactor implements IClientContactor { + + @Override + public void backupComplete(boolean all) { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(false, false, false, true, false, 0, 0); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } + + @Override + public void backupFailed(boolean all) { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(false, false, true, false, false, 0, 0); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } + + @Override + public void backupProgress(int progress, int max, boolean all) { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(false, true, false, false, false, progress, max); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } + + @Override + public void backupStarting(boolean all) { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(true, false, false, false, false, 0, 0); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } + + @Override + public void backupCancelled(boolean all) { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + List players = server.getPlayerList().getPlayers(); + PacketBackupStatus packet = new PacketBackupStatus(false, false, false, false, true, 0, 0); + for (ServerPlayer player : players) { + if (!AdvancedBackups.players.contains(player.getStringUUID())) continue; + if (!server.isDedicatedServer() || player.permissions().hasPermission(Permissions.COMMANDS_ADMIN) || all) { + NetworkHandler.sendToClient(player, packet); + } + } + } +} diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/ClientWrapper.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/ClientWrapper.java new file mode 100644 index 00000000..f1f90326 --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/ClientWrapper.java @@ -0,0 +1,68 @@ +package computer.heather.advancedbackups.client; + +import java.util.Objects; + +import computer.heather.advancedbackups.core.ABCore; +import computer.heather.advancedbackups.core.config.ClientConfigManager; +import computer.heather.advancedbackups.network.PacketBackupStatus; +import computer.heather.advancedbackups.network.PacketToastSubscribe; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; +import net.neoforged.neoforge.client.event.RegisterClientCommandsEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.registration.NetworkRegistry; + +public class ClientWrapper { + + public static void handle(PacketBackupStatus packet, IPayloadContext context) { + BackupToast.starting = packet.starting(); + BackupToast.started = packet.started(); + BackupToast.failed = packet.failed(); + BackupToast.finished = packet.finished(); + BackupToast.cancelled = packet.cancelled(); + + BackupToast.progress = packet.progress(); + BackupToast.max = packet.max(); + + if (!BackupToast.exists) { + BackupToast.exists = true; + Minecraft.getInstance().gui.toastManager().addToast(new BackupToast()); + } + } + + public static void init(FMLClientSetupEvent e) { + NeoForge.EVENT_BUS.addListener(ClientWrapper::registerClientCommands); + NeoForge.EVENT_BUS.addListener(ClientWrapper::onServerConnected); + ClientConfigManager.loadOrCreateConfig(); + } + + public static void registerClientCommands(RegisterClientCommandsEvent event) { + AdvancedBackupsClientCommand.register(event.getDispatcher()); + } + + public static void onServerConnected(ClientPlayerNetworkEvent.LoggingIn event) { + sendToServer(new PacketToastSubscribe(ClientConfigManager.showProgress.get())); + } + + + public static void sendToServer(MSG message) { + //We do this to implement custom checks! + ServerboundCustomPayloadPacket packet = new ServerboundCustomPayloadPacket(message); + + ClientPacketListener listener = Objects.requireNonNull(Minecraft.getInstance().getConnection()); + try { + NetworkRegistry.checkPacket(packet, listener); + } + catch (UnsupportedOperationException e) { + ABCore.warningLogger.accept("Refusing to send packet " + message + " to server as the serve cannot receive it."); + return; + } + listener.connection.send(packet); + } + +} diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/ColourHelper.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/ColourHelper.java new file mode 100644 index 00000000..29e0ef46 --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/client/ColourHelper.java @@ -0,0 +1,31 @@ +package computer.heather.advancedbackups.client; + +//A colour helper, loosely based on that of vanilla 1.16. +//Where is it in modern? +public class ColourHelper { + + public static int alpha(int in) { + return in >>> 24; + } + + public static int red(int in) { + return in >> 16 & 255; + } + + public static int green(int in) { + return in >> 8 & 255; + } + + public static int blue(int in) { + return in & 255; + } + + public static int colour(int a, int r, int g, int b) { + return a << 24 | r << 16 | g << 8 | b; + } + + public static int colour(int a, long r, long g, long b) { + return colour(a, (int) r, (int) g, (int) b); + } + +} \ No newline at end of file diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/network/NetworkHandler.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/network/NetworkHandler.java new file mode 100644 index 00000000..f4a5b371 --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/network/NetworkHandler.java @@ -0,0 +1,25 @@ +package computer.heather.advancedbackups.network; + + +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.PacketDistributor; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.registration.PayloadRegistrar; + +public class NetworkHandler { + + + public static void onRegisterPayloadHandler(RegisterPayloadHandlersEvent event) { + final PayloadRegistrar registrar = event.registrar("1").optional(); //this .optional() seems to be required, but will it be a problem for our packets with non-neo servers...? + + registrar.commonToClient(PacketBackupStatus.ID, PacketBackupStatus.CODEC, PacketBackupStatus::handle); + registrar.commonToServer(PacketToastSubscribe.ID, PacketToastSubscribe.CODEC, PacketToastSubscribe::handle); + } + + public static void sendToClient(ServerPlayer player, MSG message) { + PacketDistributor.sendToPlayer(player, message); + } + + +} diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/network/PacketBackupStatus.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/network/PacketBackupStatus.java new file mode 100644 index 00000000..9f43dac9 --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/network/PacketBackupStatus.java @@ -0,0 +1,48 @@ +package computer.heather.advancedbackups.network; + +import computer.heather.advancedbackups.client.BackupToast; +import computer.heather.advancedbackups.client.ClientWrapper; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public record PacketBackupStatus(boolean starting, boolean started, boolean failed, boolean finished, boolean cancelled, int progress, int max) implements CustomPacketPayload { + + public static final CustomPacketPayload.Type ID = new CustomPacketPayload.Type(Identifier.parse("advancedbackups:backup_status")); + + public static final StreamCodec CODEC = StreamCodec.of((buf, packet) -> { + buf.writeBoolean(packet.starting); + buf.writeBoolean(packet.started); + buf.writeBoolean(packet.failed); + buf.writeBoolean(packet.finished); + buf.writeBoolean(packet.cancelled); + buf.writeInt(packet.progress); + buf.writeInt(packet.max); + }, + + buf -> new PacketBackupStatus( + buf.readBoolean(), + buf.readBoolean(), + buf.readBoolean(), + buf.readBoolean(), + buf.readBoolean(), + buf.readInt(), + buf.readInt() + )); + + + @Override + public CustomPacketPayload.Type type() { + return ID; + } + + + public static void handle(PacketBackupStatus packet, IPayloadContext context) { + ClientWrapper.handle(packet, context); + } + + +} diff --git a/neoforge/26.2/src/main/java/computer/heather/advancedbackups/network/PacketToastSubscribe.java b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/network/PacketToastSubscribe.java new file mode 100644 index 00000000..4271d2d6 --- /dev/null +++ b/neoforge/26.2/src/main/java/computer/heather/advancedbackups/network/PacketToastSubscribe.java @@ -0,0 +1,45 @@ +package computer.heather.advancedbackups.network; + +import computer.heather.advancedbackups.AdvancedBackups; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; +import net.minecraft.world.entity.player.Player; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public record PacketToastSubscribe(boolean enable) implements CustomPacketPayload { + + public static final CustomPacketPayload.Type ID = new CustomPacketPayload.Type<>(Identifier.parse("advancedbackups:toast_subscribe")); + + public PacketToastSubscribe(boolean enable) { + this.enable = enable; + } + + public static final StreamCodec CODEC = StreamCodec.composite(ByteBufCodecs.BOOL, PacketToastSubscribe::enable, PacketToastSubscribe::new); + + + public static void handle(PacketToastSubscribe message, IPayloadContext context) { + + Player player = context.player(); + + if (message.enable() && !AdvancedBackups.players.contains(player.getStringUUID())) { + AdvancedBackups.players.add(player.getStringUUID()); + } + else if (!message.enable()) { + AdvancedBackups.players.remove(player.getStringUUID()); + } + + } + + + + @Override + public CustomPacketPayload.Type type() { + return ID; + } + + + +} \ No newline at end of file diff --git a/neoforge/26.2/src/main/resources/META-INF/accesstransformer.cfg b/neoforge/26.2/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 00000000..a21689a8 --- /dev/null +++ b/neoforge/26.2/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1 @@ +public net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl connection # connection diff --git a/neoforge/26.2/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/26.2/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..7d5a0181 --- /dev/null +++ b/neoforge/26.2/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,62 @@ +# This is an example mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml +# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. +# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. +license="${mod_license}" +# A URL to refer people to when problems occur with this mod +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory +# The modid of the mod +modId="${mod_id}" #mandatory +# The version number of the mod +version="${mod_version}" #mandatory +# A display name for the mod +displayName="${mod_name}" #mandatory +# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/ +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional +# A URL for the "homepage" for this mod, displayed in the mod UI +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional +# A file name (in the root of the mod JAR) containing a logo for display +#logoFile="examplemod.png" #optional +# A text field displayed in the mod UI +#credits="" #optional +# A text field displayed in the mod UI +authors="${mod_authors}" #optional + +[[accessTransformers]] +file="META-INF/accesstransformer.cfg" + +# The description text for the mod (multi line!) (#mandatory) +description='''${mod_description}''' +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies.${mod_id}]] #optional + # the modid of the dependency + modId="neoforge" #mandatory + # The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). + type="required" #mandatory + # The version range of the dependency + versionRange="[${neo_version},)" #mandatory + # An ordering relationship for the dependency - BEFORE or AFTER required if the dependency is not mandatory + # BEFORE - This mod is loaded BEFORE the dependency + # AFTER - This mod is loaded AFTER the dependency + ordering="NONE" + # Side this dependency is applied on - BOTH, CLIENT, or SERVER + side="BOTH" +# Here's another dependency +[[dependencies.${mod_id}]] + modId="minecraft" + type="required" + # This version range declares a minimum of the current minecraft version up to but not including the next major version + versionRange="${minecraft_version_range}" + ordering="NONE" + side="BOTH" + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features.${mod_id}] +#openGLVersion="[3.2,)" diff --git a/neoforge/26.2/src/main/resources/pack.mcmeta b/neoforge/26.2/src/main/resources/pack.mcmeta new file mode 100644 index 00000000..877382f4 --- /dev/null +++ b/neoforge/26.2/src/main/resources/pack.mcmeta @@ -0,0 +1,8 @@ +{ + "pack": { + "description": "advancedbackups resources", + "pack_format": 9, + "forge:resource_pack_format": 8, + "forge:data_pack_format": 9 + } +} diff --git a/spigot/26.2/build.gradle b/spigot/26.2/build.gradle new file mode 100644 index 00000000..360ba4f5 --- /dev/null +++ b/spigot/26.2/build.gradle @@ -0,0 +1,82 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + id 'java-library' + id 'maven-publish' +} + +apply from: '../../global.properties' + +group = 'computer.heather.AdvancedBackups' +description = 'AdvancedBackups' +java.sourceCompatibility = JavaVersion.VERSION_25 + +base { + archivesName = 'AdvancedBackups-' + modloaderName + "-" + minecraftVersion +} + +repositories { + maven { + url = uri('https://hub.spigotmc.org/nexus/content/repositories/snapshots/') + } + + maven { + url = uri('https://repo.maven.apache.org/maven2/') + } + mavenCentral() + mavenLocal() +} + +configurations { + // configuration that holds jars to include in the jar + extraLibs +} + +dependencies { + compileOnly("org.spigotmc:spigot:26.2-R0.1-SNAPSHOT") + + implementation files(abCoreLibPath) + extraLibs files(abCoreLibPath) +} + + + +tasks.withType(ProcessResources).configureEach { + var replaceProperties = [ + version: version + ] + inputs.properties replaceProperties + + filesMatching(['plugin.yml']) { + expand replaceProperties + [project: project] + } +} + +// Example for how to get properties into the manifest for reading at runtime. +tasks.named('jar', Jar).configure { + manifest { + attributes([ + "Main-Class" : "computer.heather.advancedbackups.cli.AdvancedBackupsCLI", + "Specification-Title" : "advancedbackups", + "Specification-Vendor" : "Heather White", + "Specification-Version" : "1", // We are version 1 of ourselves + "Implementation-Title" : project.name, + "Implementation-Version" : project.jar.archiveVersion, + "Implementation-Vendor" : "Heather White", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") + ]) + } + from { + configurations.extraLibs.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +publishing { + publications { + maven(MavenPublication) { + from(components.java) + } + } +} diff --git a/spigot/26.2/gradle.properties b/spigot/26.2/gradle.properties new file mode 100644 index 00000000..e8da44ad --- /dev/null +++ b/spigot/26.2/gradle.properties @@ -0,0 +1,5 @@ +version = 0.1-DEV + + +modloaderName =spigot +minecraftVersion =26.2 \ No newline at end of file diff --git a/spigot/26.2/gradle/wrapper/gradle-wrapper.jar b/spigot/26.2/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..a4b76b95 Binary files /dev/null and b/spigot/26.2/gradle/wrapper/gradle-wrapper.jar differ diff --git a/spigot/26.2/gradle/wrapper/gradle-wrapper.properties b/spigot/26.2/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..23449a2b --- /dev/null +++ b/spigot/26.2/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/spigot/26.2/gradlew b/spigot/26.2/gradlew new file mode 100644 index 00000000..f5feea6d --- /dev/null +++ b/spigot/26.2/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + MSYS* | 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 + if ! command -v java >/dev/null 2>&1 + then + 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 +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# 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. + +# 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 + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# 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" "$@" diff --git a/spigot/26.2/gradlew.bat b/spigot/26.2/gradlew.bat new file mode 100644 index 00000000..9d21a218 --- /dev/null +++ b/spigot/26.2/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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=. +@rem This is normally unused +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% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +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% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/spigot/26.2/settings.gradle b/spigot/26.2/settings.gradle new file mode 100644 index 00000000..4ba85991 --- /dev/null +++ b/spigot/26.2/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'spigot-26.2' diff --git a/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/AdvancedBackups.java b/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/AdvancedBackups.java new file mode 100644 index 00000000..2b5f6105 --- /dev/null +++ b/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/AdvancedBackups.java @@ -0,0 +1,151 @@ +package computer.heather.AdvancedBackups; + +import java.io.File; +import java.util.ArrayList; +import java.util.function.Consumer; + +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.server.ServerLoadEvent; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +import computer.heather.advancedbackups.core.ABCore; +import computer.heather.advancedbackups.core.backups.BackupTimer; +import computer.heather.advancedbackups.core.backups.BackupWrapper; +import computer.heather.advancedbackups.core.config.ConfigManager; +import computer.heather.AdvancedBackups.network.ClientContactor; +import computer.heather.AdvancedBackups.network.PacketListener; + +public class AdvancedBackups extends JavaPlugin implements Listener { + + private static boolean enabled = false; + public static Server server = null; + + public static Consumer infoLogger; + public static Consumer warningLogger; + public static Consumer errorLogger; + + //List of players that have asked to see packets + public static final ArrayList players = new ArrayList<>(); + + @Override + public void onEnable() { + enabled = true; + + ABCore.disableSaving = AdvancedBackups::disableSaving; + ABCore.enableSaving = AdvancedBackups::enableSaving; + ABCore.saveOnce = AdvancedBackups::saveOnce; + + AdvancedBackups.server = getServer(); + + ABCore.worldName = getServer().getWorlds().get(0).getName(); + ABCore.worldDir = new File(getServer().getWorlds().get(0).getWorldFolder(), "./").toPath(); //need to have this parent because of some forge compatability bullshit.. if i knew how much I'd need to do this I really would've just called getParent on the offending versions + ABCore.modJar = getFile().getAbsoluteFile(); + + infoLogger = getLogger()::info; + warningLogger = getLogger()::warning; + errorLogger = getLogger()::severe; + + ABCore.infoLogger = infoLogger; + ABCore.warningLogger = warningLogger; + ABCore.errorLogger = errorLogger; + + this.getCommand("backup").setExecutor(new AdvancedBackupsCommand()); + getServer().getPluginManager().registerEvents(this, this); + + ConfigManager.loadOrCreateConfig(); + + server.getMessenger().registerIncomingPluginChannel(this, "advancedbackups:toast_subscribe", new PacketListener()); + server.getMessenger().registerOutgoingPluginChannel(this, "advancedbackups:backup_status"); + + + ABCore.clientContactor = new ClientContactor(); + + ABCore.resetActivity = AdvancedBackups::resetActivity; + + new BukkitRunnable() { + @Override + public void run() { + AdvancedBackups.this.onTickEnd(); + } + }.runTaskTimer(this, 0, 0); + } + + @Override + public void onDisable() { + enabled = false; + BackupWrapper.checkShutdownBackups(); + } + + public boolean getEnabled() { + return enabled; + } + + @EventHandler + public void onServerLoad(ServerLoadEvent event) { + BackupWrapper.checkStartupBackups(); + } + + + /* handled in onDisable + @EventHandler + public void onServerStop( event) { + BackupWrapper.checkShutdownBackups();; + }*/ + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + /*Might not need to do this + //Gets us the actual player class, not bukkit's strange reimpl + ServerPlayer player = MinecraftServer.getServer().getPlayerList().getPlayer(event.getPlayer().getUniqueId()); + player.connection.networkmanager*/ + + ABCore.setActivity(true); + } + + public void onTickEnd() { + BackupTimer.check(); + } + + + + public static void disableSaving() { + for (World level : server.getWorlds()) { + level.setAutoSave(false); + } + } + + public static void enableSaving() { + for (World level : server.getWorlds()) { + level.setAutoSave(true); + } + } + + public static void saveOnce(boolean unused) { + + server.savePlayers(); + boolean flag; + + for (World level : server.getWorlds()) { + flag = level.isAutoSave(); + level.setAutoSave(false); + level.save(); + level.setAutoSave(flag); + } + + } + + + public static void resetActivity() { + ABCore.setActivity(!server.getOnlinePlayers().isEmpty()); + } + + + + + +} \ No newline at end of file diff --git a/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/AdvancedBackupsCommand.java b/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/AdvancedBackupsCommand.java new file mode 100644 index 00000000..122ae128 --- /dev/null +++ b/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/AdvancedBackupsCommand.java @@ -0,0 +1,115 @@ +package computer.heather.AdvancedBackups; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.util.StringUtil; + +import computer.heather.advancedbackups.core.CoreCommandSystem; + + +// /backup start | reload-config | reload-client-config | snapshot | reset-chain +public class AdvancedBackupsCommand implements CommandExecutor, TabCompleter { + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length == 0) + { + return false; + } + else if ("start".equals(args[0])) + { + Start.execute(sender); + } + else if ("reload-config".equals(args[0])) + { + Reload.execute(sender); + } + else if ("reload-client-config".equals(args[0])) + { + ReloadClient.execute(sender); + } + else if ("reset-chain".equals(args[0])) { + ResetChain.execute(sender); + } + else if ("snapshot".equals(args[0])) { + Snapshot.execute(sender, args); + } + else if ("cancel".equals(args[0])) { + Cancel.execute(sender); + } + else + { + return false; + } + return true; + } + + public static class Reload { + public static void execute(CommandSender sender) { + CoreCommandSystem.reloadConfig((response) -> { + sender.sendMessage(response); + }); + } + } + + public static class ReloadClient { + public static void execute(CommandSender sender) { + sender.sendMessage("This can only be ran on the client!"); + } + } + + public static class Start { + public static void execute(CommandSender sender) { + CoreCommandSystem.startBackup((response) -> { + sender.sendMessage(response); + }); + } + } + + public static class ResetChain { + public static void execute(CommandSender sender) { + CoreCommandSystem.resetChainLength((response) -> { + sender.sendMessage(response); + }); + } + } + + public static class Snapshot { + public static void execute(CommandSender sender, String[] args) { + String name; + if (args.length > 1) { + name = String.join(" ", Arrays.copyOfRange(args, 1, args.length)); + } + else name = "snapshot"; + CoreCommandSystem.snapshot((response) -> { + sender.sendMessage(response); + }, name); + } + + } + + public static class Cancel { + public static void execute(CommandSender sender) { + CoreCommandSystem.cancelBackup((response) -> { + sender.sendMessage(response); + }); + } + + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + final List completions = new ArrayList<>(); + StringUtil.copyPartialMatches(args[0], Arrays.asList(new String[] {"start", "reload-config", "reload-client-config", "reset-chain", "snapshot", "cancel"}), completions); + + return completions; + } + + +} diff --git a/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/network/ClientContactor.java b/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/network/ClientContactor.java new file mode 100644 index 00000000..30322442 --- /dev/null +++ b/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/network/ClientContactor.java @@ -0,0 +1,81 @@ +package computer.heather.AdvancedBackups.network; + +import computer.heather.advancedbackups.interfaces.IClientContactor; +import computer.heather.AdvancedBackups.AdvancedBackups; + +public class ClientContactor implements IClientContactor { + + private static byte[] toBytes(boolean starting, boolean started, boolean failed, boolean finished, boolean cancelled, int progress, int max) { + byte[] ret = new byte[13]; + + ret[0] = (byte) (starting ? 1 : 0); + ret[1] = (byte) (started ? 1 : 0); + ret[2] = (byte) (failed ? 1 : 0); + ret[3] = (byte) (finished ? 1 : 0); + ret[4] = (byte) (cancelled ? 1 : 0); + + ret[5] = (byte) ((progress & 0xFF000000) >> 24); + ret[6] = (byte) ((progress & 0x00FF0000) >> 16); + ret[7] = (byte) ((progress & 0x0000FF00) >> 8); + ret[8] = (byte) ((progress & 0x000000FF) >> 0); + + ret[9] = (byte) ((max & 0xFF000000) >> 24); + ret[10] = (byte) ((max & 0x00FF0000) >> 16); + ret[11] = (byte) ((max & 0x0000FF00) >> 8); + ret[12] = (byte) ((max & 0x000000FF) >> 0); + + return ret; + } + + //starting, started, failed, finished, cancelled, progress, max + @Override + public void backupComplete(boolean all) { + byte[] bytes = toBytes(false, false, false, true, false, 0, 0); + + AdvancedBackups.server.getOnlinePlayers().forEach((player) -> { + if (!AdvancedBackups.players.contains(player.getUniqueId().toString())) return; + if (player.isOp() || all) player.sendPluginMessage(AdvancedBackups.getPlugin(AdvancedBackups.class), "advancedbackups:backup_status", bytes); + }); + } + + @Override + public void backupFailed(boolean all) { + byte[] bytes = toBytes(false, false, true, false, false, 0, 0); + + AdvancedBackups.server.getOnlinePlayers().forEach((player) -> { + if (!AdvancedBackups.players.contains(player.getUniqueId().toString())) return; + if (player.isOp() || all) player.sendPluginMessage(AdvancedBackups.getPlugin(AdvancedBackups.class), "advancedbackups:backup_status", bytes); + }); + } + + @Override + public void backupProgress(int progress, int max, boolean all) { + byte[] bytes = toBytes(false, true, false, false, false, progress, max); + + AdvancedBackups.server.getOnlinePlayers().forEach((player) -> { + if (!AdvancedBackups.players.contains(player.getUniqueId().toString())) return; + if (player.isOp() || all) player.sendPluginMessage(AdvancedBackups.getPlugin(AdvancedBackups.class), "advancedbackups:backup_status", bytes); + }); + } + + @Override + public void backupStarting(boolean all) { + byte[] bytes = toBytes(true, false, false, false, false, 0, 0); + + AdvancedBackups.server.getOnlinePlayers().forEach((player) -> { + if (!AdvancedBackups.players.contains(player.getUniqueId().toString())) return; + if (player.isOp() || all) player.sendPluginMessage(AdvancedBackups.getPlugin(AdvancedBackups.class), "advancedbackups:backup_status", bytes); + }); + } + + @Override + public void backupCancelled(boolean all) { + byte[] bytes = toBytes(false, false, false, false, true, 0, 0); + + AdvancedBackups.server.getOnlinePlayers().forEach((player) -> { + if (!AdvancedBackups.players.contains(player.getUniqueId().toString())) return; + if (player.isOp() || all) player.sendPluginMessage(AdvancedBackups.getPlugin(AdvancedBackups.class), "advancedbackups:backup_status", bytes); + }); + } + +} diff --git a/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/network/PacketListener.java b/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/network/PacketListener.java new file mode 100644 index 00000000..6e50ada4 --- /dev/null +++ b/spigot/26.2/src/main/java/computer/heather/AdvancedBackups/network/PacketListener.java @@ -0,0 +1,25 @@ +package computer.heather.AdvancedBackups.network; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.messaging.PluginMessageListener; + +import computer.heather.advancedbackups.core.ABCore; +import computer.heather.AdvancedBackups.AdvancedBackups; + +public class PacketListener implements PluginMessageListener { + + @Override + public void onPluginMessageReceived(String channel, Player player, byte[] message) { + String uuid = player.getUniqueId().toString(); + Boolean subscribe = message[0] != 0; + ABCore.infoLogger.accept("Player has chosen to " + (subscribe ? "accept" : "reject") + " progress updates."); + + if (!AdvancedBackups.players.contains(uuid) && subscribe) { + AdvancedBackups.players.add(uuid); + } + else if (!subscribe) { + AdvancedBackups.players.remove(uuid); + } + } + +} diff --git a/spigot/26.2/src/main/resources/plugin.yml b/spigot/26.2/src/main/resources/plugin.yml new file mode 100644 index 00000000..993ecdda --- /dev/null +++ b/spigot/26.2/src/main/resources/plugin.yml @@ -0,0 +1,9 @@ +name: AdvancedBackups +version: ${version} +api-version: '26.2' +main: computer.heather.AdvancedBackups.AdvancedBackups +commands: + backup: + description: Commands for Advanced Backups. + usage: /backup start | reload-config | reload-client-config | snapshot | reset-chain + permission: advancedbackups.backup \ No newline at end of file