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
+ }
+}