diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be328f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# gradle + +.gradle/ +build/ +out/ +classes/ + +# idea + +.idea/ +*.iml +*.ipr +*.iws + +# vscode + +.settings/ +.vscode/ +bin/ +.classpath +.project + +# fabric + +run/ +backup/ +/deps/ +/remappedSrc/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..760a05e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ + | GlobalSpawn Changelog | + +Version 1.0.0 - Minecraft 1.16.4 + Public Release \o/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a397638 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +## About GlobalSpawn + +Global spawn lets you set a default spawn for all players. +Unlike vanialla that spawn can even be in a separate dimension. +Useful for modpacks that start in the Nether or other dimensions. + +## Features + +Configure the global spawn via: +- Config file +- Mod Menu +- or "/globalspawn" command \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..361701d --- /dev/null +++ b/build.gradle @@ -0,0 +1,92 @@ +plugins { + id 'fabric-loom' version '0.5-SNAPSHOT' + id 'maven-publish' +} + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + +archivesBaseName = project.archives_base_name +version = project.mod_version +group = project.maven_group + +minecraft { + //accessWidener = file("src/main/resources/starry_sky.accesswidener") +} + +repositories { + maven { + name = "Ladysnake Libs" + url = 'https://dl.bintray.com/ladysnake/libs' + } + maven { + url 'https://maven.fabricmc.net/io/github/prospector/modmenu/' + } + maven { + name "cotton-config" + url 'http://server.bbkr.space:8081/artifactory/libs-release' + } + +} + +dependencies { + //to change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + modImplementation ("net.fabricmc.fabric-api:fabric-api:${project.fabric_version}") { + force = true //Force the version we want rather than trusting Gradle to pick it over transitive suggestions + } + + modImplementation ("me.shedaniel.cloth:config-2:${project.cloth_config_version}"){ + exclude module: 'fabric-api' + } + include ("me.shedaniel.cloth:config-2:${project.cloth_config_version}"){ + exclude module: 'fabric-api' + } + + modImplementation ("me.sargunvohra.mcmods:autoconfig1u:${project.auto_config_version}"){ + exclude module: 'fabric-api' + } + include ("me.sargunvohra.mcmods:autoconfig1u:${project.auto_config_version}"){ + exclude module: 'fabric-api' + } + + modImplementation ("io.github.prospector:modmenu:${project.mod_menu_version}"){ + exclude module: 'fabric-api' + } + +} + +processResources { + inputs.property "version", project.version + + from(sourceSets.main.resources.srcDirs) { + include "fabric.mod.json" + expand "version": project.version + } + + from(sourceSets.main.resources.srcDirs) { + exclude "fabric.mod.json" + } +} + +// ensure that the encoding is set to UTF-8, no matter what the system default is +// this fixes some edge cases with special characters not displaying correctly +// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task +// if it is present. +// If you remove this task, sources will not be generated. +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = "sources" + from sourceSets.main.allSource +} + +jar { + from "LICENSE" +} diff --git a/client.launch b/client.launch new file mode 100644 index 0000000..832ed45 --- /dev/null +++ b/client.launch @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..bb9fa30 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,20 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs = -Xmx2G + +# Fabric Properties +# check these on https://fabricmc.net/use +minecraft_version = 1.16.4 +yarn_mappings = 1.16.4+build.7 +loader_version = 0.10.8 + +# Mod Properties +mod_version = 1.0.0-1.16.4 +maven_group = de.dafuqs.globalspawn +archives_base_name = globalspawn + +# Dependencies +# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api +fabric_version = 0.26.0+1.16 +auto_config_version = 3.2.0-unstable +cloth_config_version = 4.7.0-unstable +mod_menu_version = 1.14.6+build.31 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..5c2d1cf Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..4b7e1f3 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..8e25e6c --- /dev/null +++ b/gradlew @@ -0,0 +1,188 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9618d8d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +: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 %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/server.launch b/server.launch new file mode 100644 index 0000000..832ed45 --- /dev/null +++ b/server.launch @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..5b60df3 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + jcenter() + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/de/dafuqs/globalspawn/GlobalSpawnCommon.java b/src/main/java/de/dafuqs/globalspawn/GlobalSpawnCommon.java new file mode 100644 index 0000000..aa2b594 --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/GlobalSpawnCommon.java @@ -0,0 +1,40 @@ +package de.dafuqs.globalspawn; + +import de.dafuqs.globalspawn.command.GlobalSpawnCommand; +import de.dafuqs.globalspawn.config.GlobalSpawnConfig; +import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; +import me.sargunvohra.mcmods.autoconfig1u.ConfigHolder; +import me.sargunvohra.mcmods.autoconfig1u.ConfigManager; +import me.sargunvohra.mcmods.autoconfig1u.serializer.JanksonConfigSerializer; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class GlobalSpawnCommon implements ModInitializer { + + public static final String MOD_ID = "globalspawn"; + public static ConfigManager GLOBAL_SPAWN_CONFIG_MANAGER; + public static GlobalSpawnConfig GLOBAL_SPAWN_CONFIG; + public static final Logger LOGGER = LogManager.getLogger(MOD_ID); + + @Override + public void onInitialize() { + //Set up config + LOGGER.info("Loading config file..."); + ConfigHolder configHolder = AutoConfig.register(GlobalSpawnConfig.class, JanksonConfigSerializer::new); + GLOBAL_SPAWN_CONFIG_MANAGER = ((ConfigManager) configHolder); + GLOBAL_SPAWN_CONFIG = AutoConfig.getConfigHolder(GlobalSpawnConfig.class).getConfig(); + LOGGER.info("Finished loading config file."); + + GlobalSpawnCommand.initialize(); + GlobalSpawnManager.initialize(); + + + + ServerWorldEvents.LOAD.register((server, world) -> { + GlobalSpawnManager.addWorld(world); + }); + } + +} diff --git a/src/main/java/de/dafuqs/globalspawn/GlobalSpawnManager.java b/src/main/java/de/dafuqs/globalspawn/GlobalSpawnManager.java new file mode 100644 index 0000000..859da46 --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/GlobalSpawnManager.java @@ -0,0 +1,79 @@ +package de.dafuqs.globalspawn; + +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.World; + +import java.util.HashMap; + +public class GlobalSpawnManager { + + private static HashMap, World> dimensions = new HashMap<>(); + private static boolean active; + private static GlobalSpawnPoint activeSpawnPointDefinition; + + public static void unset() { + activeSpawnPointDefinition = null; + active = false; + updateConfigFile(); + } + + public static void set(GlobalSpawnPoint globalSpawnPoint) { + activeSpawnPointDefinition = globalSpawnPoint; + updateConfigFile(); + } + + private static void updateConfigFile() { + GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.active = active; + if(activeSpawnPointDefinition != null) { + GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.spawnDimension = activeSpawnPointDefinition.spawnPointDimension.getValue().toString(); + GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.spawnX = activeSpawnPointDefinition.spawnPointPosition.getX(); + GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.spawnY = activeSpawnPointDefinition.spawnPointPosition.getY(); + GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.spawnZ = activeSpawnPointDefinition.spawnPointPosition.getZ(); + } + + GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG_MANAGER.save(); + } + + public static GlobalSpawnPoint get() { + return activeSpawnPointDefinition; + } + + public static boolean isActive() { + if(active && activeSpawnPointDefinition != null) { + if (existsWorld(activeSpawnPointDefinition.spawnPointDimension)) { + return true; + } else { + GlobalSpawnCommon.LOGGER.warn("Spawn dimension " + activeSpawnPointDefinition.spawnPointDimension + " is not loaded. GlobalSpawn is disabled"); + return false; + } + } else { + return false; + } + } + + private static boolean existsWorld(RegistryKey registryKey) { + return dimensions.containsKey(registryKey); + } + + public static void initialize() { + active = GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.active; + } + + public static void addWorld(World world) { + dimensions.put(world.getRegistryKey(), world); + + boolean shouldBeActive = GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.active; + if(shouldBeActive) { + Identifier identifier = new Identifier(GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.spawnDimension); + if(world.getRegistryKey().getValue().equals(identifier)) { + int x = GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.spawnX; + int y = GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.spawnY; + int z = GlobalSpawnCommon.GLOBAL_SPAWN_CONFIG.spawnZ; + activeSpawnPointDefinition = new GlobalSpawnPoint(world.getRegistryKey(), new BlockPos(x, y, z)); + } + } + } + +} diff --git a/src/main/java/de/dafuqs/globalspawn/GlobalSpawnMixinHandler.java b/src/main/java/de/dafuqs/globalspawn/GlobalSpawnMixinHandler.java new file mode 100644 index 0000000..d418779 --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/GlobalSpawnMixinHandler.java @@ -0,0 +1,74 @@ +package de.dafuqs.globalspawn; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.World; + +import java.util.Optional; + +public class GlobalSpawnMixinHandler { + + /** + * Handles checks for getRespawnDimension and getRespawnPosition + * @param spawnPointDimension + * @param spawnPointPosition + * @param spawnPointSet + * @return + */ + public static GlobalSpawnPoint getRespawnData(RegistryKey spawnPointDimension, BlockPos spawnPointPosition, boolean spawnPointSet) { + if(GlobalSpawnManager.isActive()) { + if (spawnPointDimension == World.OVERWORLD && spawnPointPosition == null) { + return GlobalSpawnManager.get(); + } + } + return null; + } + + /** + * Handles the search for respawn position in the world itself + * @param optional + * @return + */ + public static Optional getRespawnPlayer(Optional optional) { + if(GlobalSpawnManager.isActive()) { + if (!optional.isPresent()) { + Vec3d vec3d = GlobalSpawnManager.get().getSpawnVec3D(); + return Optional.of(vec3d); + } + } + return optional; + } + + /** + * Sets compound tags for the respawn position of new players + * @param compoundTag + * @return + */ + public static CompoundTag modifySpawnRegistry(CompoundTag compoundTag) { + // only for new players + if(GlobalSpawnManager.isActive()) { + if (compoundTag == null) { + return GlobalSpawnManager.get().getSpawnCompoundTag(); + } + } + return compoundTag; + } + + /** + * Moving a newly joined player to the world spawn + * @param serverPlayerEntity The player + */ + public static boolean moveToSpawn(ServerPlayerEntity serverPlayerEntity) { + if(GlobalSpawnManager.isActive()) { + BlockPos spawnBlockPos = GlobalSpawnManager.get().getSpawnBlockPos(); + serverPlayerEntity.refreshPositionAndAngles(spawnBlockPos, 0.0F, 0.0F); + serverPlayerEntity.updatePosition(spawnBlockPos.getX(), spawnBlockPos.getY(), spawnBlockPos.getZ()); + return true; + } + return false; + } + +} diff --git a/src/main/java/de/dafuqs/globalspawn/GlobalSpawnPoint.java b/src/main/java/de/dafuqs/globalspawn/GlobalSpawnPoint.java new file mode 100644 index 0000000..ef9152c --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/GlobalSpawnPoint.java @@ -0,0 +1,43 @@ +package de.dafuqs.globalspawn; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.DoubleTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.World; + +public class GlobalSpawnPoint { + + public RegistryKey spawnPointDimension; + public BlockPos spawnPointPosition; + + + public GlobalSpawnPoint(RegistryKey spawnPointDimension, BlockPos spawnPointPosition) { + this.spawnPointDimension = spawnPointDimension; + this.spawnPointPosition = spawnPointPosition; + } + + public CompoundTag getSpawnCompoundTag() { + CompoundTag compoundTag1 = new CompoundTag(); + compoundTag1.putString("Dimension", spawnPointDimension.getValue().toString()); + + ListTag listTag = new ListTag(); + listTag.addTag(0, DoubleTag.of(spawnPointPosition.getX())); + listTag.addTag(1, DoubleTag.of(spawnPointPosition.getY())); + listTag.addTag(2, DoubleTag.of(spawnPointPosition.getZ())); + + compoundTag1.put("Pos", listTag); + return compoundTag1; + } + + public Vec3d getSpawnVec3D() { + return new Vec3d(spawnPointPosition.getX(), spawnPointPosition.getY(), spawnPointPosition.getZ()); + } + + public BlockPos getSpawnBlockPos() { + return new BlockPos(spawnPointPosition.getX(), spawnPointPosition.getY(), spawnPointPosition.getZ()); + } + +} \ No newline at end of file diff --git a/src/main/java/de/dafuqs/globalspawn/command/GlobalSpawnCommand.java b/src/main/java/de/dafuqs/globalspawn/command/GlobalSpawnCommand.java new file mode 100644 index 0000000..2412c5a --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/command/GlobalSpawnCommand.java @@ -0,0 +1,64 @@ +package de.dafuqs.globalspawn.command; + +import de.dafuqs.globalspawn.GlobalSpawnManager; +import de.dafuqs.globalspawn.GlobalSpawnPoint; +import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.math.BlockPos; + +public class GlobalSpawnCommand { + + enum Action { + QUERY, + SET, + UNSET + } + + public static void initialize() { + CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> { + dispatcher.register(CommandManager.literal("globalspawnpoint") + .requires((source) -> source.hasPermissionLevel(0)) + .executes((commandContext) -> { + return GlobalSpawnCommand.execute(commandContext.getSource(), GlobalSpawnCommand.Action.QUERY, null, null); + }) + .then(CommandManager.literal("query").executes((commandContext) -> { + return GlobalSpawnCommand.execute(commandContext.getSource(), GlobalSpawnCommand.Action.QUERY, null, null); + })).then(CommandManager.literal("unset").executes((commandContext) -> { + return GlobalSpawnCommand.execute(commandContext.getSource(), GlobalSpawnCommand.Action.UNSET, null, null); + })).then(CommandManager.literal("set").executes((commandContext) -> { + return GlobalSpawnCommand.execute(commandContext.getSource(), GlobalSpawnCommand.Action.SET, commandContext.getSource().getWorld(), new BlockPos((commandContext.getSource()).getPosition())); + })) + ); + }); + } + + static int execute(ServerCommandSource source, Action action, ServerWorld serverWorld, BlockPos blockPos) { + GlobalSpawnPoint globalSpawnPoint; + switch (action) { + case QUERY: + globalSpawnPoint = GlobalSpawnManager.get(); + if(globalSpawnPoint == null) { + source.sendFeedback(new TranslatableText("commands.globalspawn.globalspawnpoint.query_not_set"), false); + } else { + source.sendFeedback(new TranslatableText("commands.globalspawn.globalspawnpoint.query_set_at", globalSpawnPoint.spawnPointDimension.getValue(), globalSpawnPoint.spawnPointPosition.getX(), globalSpawnPoint.spawnPointPosition.getY(), globalSpawnPoint.spawnPointPosition.getZ()), false); + } + break; + case SET: + globalSpawnPoint = new GlobalSpawnPoint(serverWorld.getRegistryKey(), blockPos); + GlobalSpawnManager.set(globalSpawnPoint); + source.sendFeedback(new TranslatableText("commands.globalspawn.globalspawnpoint.set_to", serverWorld.getRegistryKey().getValue(), blockPos.getX(), blockPos.getY(), blockPos.getZ()), true); + break; + case UNSET: + GlobalSpawnManager.unset(); + source.sendFeedback(new TranslatableText("commands.globalspawn.globalspawnpoint.unset"), true); + break; + } + + return 1; + } + + +} \ No newline at end of file diff --git a/src/main/java/de/dafuqs/globalspawn/config/GlobalSpawnConfig.java b/src/main/java/de/dafuqs/globalspawn/config/GlobalSpawnConfig.java new file mode 100644 index 0000000..165effa --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/config/GlobalSpawnConfig.java @@ -0,0 +1,15 @@ +package de.dafuqs.globalspawn.config; + +import me.sargunvohra.mcmods.autoconfig1u.ConfigData; +import me.sargunvohra.mcmods.autoconfig1u.annotation.Config; + +@Config(name = "GlobalSpawn") +public class GlobalSpawnConfig implements ConfigData { + + public boolean active = false; + public String spawnDimension = "minecraft:overworld"; + public int spawnX = 50; + public int spawnY = 80; + public int spawnZ = 50; + +} diff --git a/src/main/java/de/dafuqs/globalspawn/config/ModMenuConfig.java b/src/main/java/de/dafuqs/globalspawn/config/ModMenuConfig.java new file mode 100644 index 0000000..9f29099 --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/config/ModMenuConfig.java @@ -0,0 +1,18 @@ +package de.dafuqs.globalspawn.config; + + +import io.github.prospector.modmenu.api.ConfigScreenFactory; +import io.github.prospector.modmenu.api.ModMenuApi; +import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +@Environment(EnvType.CLIENT) +public class ModMenuConfig implements ModMenuApi { + + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return parent -> AutoConfig.getConfigScreen(GlobalSpawnConfig.class, parent).get(); + } + +} \ No newline at end of file diff --git a/src/main/java/de/dafuqs/globalspawn/mixin/PlayerEntityMixin.java b/src/main/java/de/dafuqs/globalspawn/mixin/PlayerEntityMixin.java new file mode 100644 index 0000000..1923522 --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/mixin/PlayerEntityMixin.java @@ -0,0 +1,37 @@ +package de.dafuqs.globalspawn.mixin; + +import de.dafuqs.globalspawn.GlobalSpawnMixinHandler; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.math.Vec3d; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +import java.util.Optional; + +@Mixin(PlayerEntity.class) +public abstract class PlayerEntityMixin { + + /** + * Called when a player makes it's first connection ever + * @param x + * @return + */ + @ModifyVariable(method = "onPlayerConnect", at = @At("STORE"), ordinal = 0) + private CompoundTag onPlayerConnect(CompoundTag x) { + return GlobalSpawnMixinHandler.modifySpawnRegistry(x); + } + + /** + * Called when pressing "respawn" after death + * Searching for bed, respawn anchor, ... at respawn point position + * @param optional + * @return + */ + @ModifyVariable(method = "respawnPlayer", at = @At("STORE"), ordinal = 0) + public Optional respawnPlayer(Optional optional) { + return GlobalSpawnMixinHandler.getRespawnPlayer(optional); + } + +} \ No newline at end of file diff --git a/src/main/java/de/dafuqs/globalspawn/mixin/PlayerManagerMixin.java b/src/main/java/de/dafuqs/globalspawn/mixin/PlayerManagerMixin.java new file mode 100644 index 0000000..f2da5bc --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/mixin/PlayerManagerMixin.java @@ -0,0 +1,37 @@ +package de.dafuqs.globalspawn.mixin; + +import de.dafuqs.globalspawn.GlobalSpawnMixinHandler; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.PlayerManager; +import net.minecraft.util.math.Vec3d; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +import java.util.Optional; + +@Mixin(PlayerManager.class) +public abstract class PlayerManagerMixin { + + /** + * Called when a player makes it's first connection ever + * @param x + * @return + */ + @ModifyVariable(method = "onPlayerConnect", at = @At("STORE"), ordinal = 0) + private CompoundTag onPlayerConnect(CompoundTag x) { + return GlobalSpawnMixinHandler.modifySpawnRegistry(x); + } + + /** + * Called when pressing "respawn" after death + * Searching for bed, respawn anchor, ... at respawn point position + * @param optional + * @return + */ + @ModifyVariable(method = "respawnPlayer", at = @At("STORE"), ordinal = 0) + public Optional respawnPlayer(Optional optional) { + return GlobalSpawnMixinHandler.getRespawnPlayer(optional); + } + +} \ No newline at end of file diff --git a/src/main/java/de/dafuqs/globalspawn/mixin/ServerPlayerEntityMixin.java b/src/main/java/de/dafuqs/globalspawn/mixin/ServerPlayerEntityMixin.java new file mode 100644 index 0000000..e1d75af --- /dev/null +++ b/src/main/java/de/dafuqs/globalspawn/mixin/ServerPlayerEntityMixin.java @@ -0,0 +1,61 @@ +package de.dafuqs.globalspawn.mixin; + +import de.dafuqs.globalspawn.GlobalSpawnMixinHandler; +import de.dafuqs.globalspawn.GlobalSpawnPoint; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ServerPlayerEntity.class) +public abstract class ServerPlayerEntityMixin { + + @Shadow + private RegistryKey spawnPointDimension; + @Shadow + private BlockPos spawnPointPosition; + @Shadow + private boolean spawnPointSet; + @Shadow + @Final + public MinecraftServer server; + + @Inject(method = "getSpawnPointDimension", at = @At("HEAD"), cancellable = true) + private void getSpawnPointDimension(CallbackInfoReturnable> cir) { + GlobalSpawnPoint globalSpawnPoint = GlobalSpawnMixinHandler.getRespawnData(spawnPointDimension, spawnPointPosition, spawnPointSet); + if(globalSpawnPoint != null) { + spawnPointDimension = globalSpawnPoint.spawnPointDimension; + spawnPointPosition = globalSpawnPoint.spawnPointPosition; + cir.setReturnValue(globalSpawnPoint.spawnPointDimension); + } + } + + @Inject(method = "getSpawnPointPosition", at = @At("HEAD"), cancellable = true) + public void getSpawnPointPosition(CallbackInfoReturnable cir) { + GlobalSpawnPoint globalSpawnPoint = GlobalSpawnMixinHandler.getRespawnData(spawnPointDimension, spawnPointPosition, spawnPointSet); + if(globalSpawnPoint != null) { + spawnPointDimension = globalSpawnPoint.spawnPointDimension; + spawnPointPosition = globalSpawnPoint.spawnPointPosition; + cir.setReturnValue(globalSpawnPoint.spawnPointPosition); + } + } + + // on first connect + @Inject(method = "moveToSpawn", at = @At("HEAD"), cancellable = true) + private void moveToSpawn(ServerWorld world, CallbackInfo ci) { + boolean set = GlobalSpawnMixinHandler.moveToSpawn((ServerPlayerEntity) (Object) this); + if(set) { + ci.cancel(); + } + } + +} diff --git a/src/main/resources/assets/globalspawn/icon.png b/src/main/resources/assets/globalspawn/icon.png new file mode 100644 index 0000000..7e8ae6e Binary files /dev/null and b/src/main/resources/assets/globalspawn/icon.png differ diff --git a/src/main/resources/assets/globalspawn/lang/en_us.json b/src/main/resources/assets/globalspawn/lang/en_us.json new file mode 100644 index 0000000..a760378 --- /dev/null +++ b/src/main/resources/assets/globalspawn/lang/en_us.json @@ -0,0 +1,12 @@ +{ + "commands.globalspawn.globalspawnpoint.query_set_at": "Global spawn is set in dimension %s at x:%d y:%d z:%d.", + "commands.globalspawn.globalspawnpoint.query_not_set": "Global spawn is not set.", + "commands.globalspawn.globalspawnpoint.set_to": "Set global spawn in dimension %s at x:%d y:%d z:%d.", + "commands.globalspawn.globalspawnpoint.unset": "Global spawn removed.", + + "text.autoconfig.GlobalSpawn.option.overrideSpawn": "Active (Override vanilla spawn)", + "text.autoconfig.GlobalSpawn.option.spawnDimension": "Dimension", + "text.autoconfig.GlobalSpawn.option.spawnX": "X coordinate", + "text.autoconfig.GlobalSpawn.option.spawnY": "Y coordinate", + "text.autoconfig.GlobalSpawn.option.spawnZ": "Z coordinate" +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..40edb5f --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,37 @@ +{ + "schemaVersion": 1, + "id": "globalspawn", + "version": "${version}", + + "name": "GlobalSpawn", + "description": "API for setting player spawn position server wide", + "authors": [ + "DaFuqs" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/DaFuqs/GlobalSpawn" + }, + + "license": "MIT", + "icon": "assets/globalspawn/icon.png", + + "environment": "*", + "entrypoints": { + "main": [ + "de.dafuqs.globalspawn.GlobalSpawnCommon" + ], + "modmenu": [ + "de.dafuqs.globalspawn.config.ModMenuConfig" + ] + }, + "mixins": [ + "globalspawn.mixins.json" + ], + "depends": { + "fabricloader": ">=0.10.8", + "fabric": "*", + "minecraft": ">=1.16.4" + }, + "suggests": { } +} diff --git a/src/main/resources/globalspawn.mixins.json b/src/main/resources/globalspawn.mixins.json new file mode 100644 index 0000000..e2811f5 --- /dev/null +++ b/src/main/resources/globalspawn.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "de.dafuqs.globalspawn.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "PlayerManagerMixin", + "ServerPlayerEntityMixin" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +}