Initial Commit
This commit is contained in:
parent
4d6fa9c83d
commit
e90f127b03
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Disable autocrlf on generated files, they always generate with LF
|
||||||
|
# Add any extra files or paths here to make git stop saying they
|
||||||
|
# are changed when only line endings change.
|
||||||
|
src/generated/**/.cache/cache text eol=lf
|
||||||
|
src/generated/**/*.json text eol=lf
|
||||||
207
build.gradle
Normal file
207
build.gradle
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
plugins {
|
||||||
|
id 'eclipse'
|
||||||
|
id 'idea'
|
||||||
|
id 'maven-publish'
|
||||||
|
id 'net.minecraftforge.gradle' version '[6.0,6.2)'
|
||||||
|
}
|
||||||
|
|
||||||
|
version = mod_version
|
||||||
|
group = mod_group_id
|
||||||
|
|
||||||
|
base {
|
||||||
|
archivesName = mod_id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17.
|
||||||
|
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
|
||||||
|
println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}"
|
||||||
|
minecraft {
|
||||||
|
// The mappings can be changed at any time and must be in the following format.
|
||||||
|
// Channel: Version:
|
||||||
|
// official MCVersion Official field/method names from Mojang mapping files
|
||||||
|
// parchment YYYY.MM.DD-MCVersion Open community-sourced parameter names and javadocs layered on top of official
|
||||||
|
//
|
||||||
|
// You must be aware of the Mojang license when using the 'official' or 'parchment' mappings.
|
||||||
|
// See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md
|
||||||
|
//
|
||||||
|
// Parchment is an unofficial project maintained by ParchmentMC, separate from MinecraftForge
|
||||||
|
// Additional setup is needed to use their mappings: https://parchmentmc.org/docs/getting-started
|
||||||
|
//
|
||||||
|
// Use non-default mappings at your own risk. They may not always work.
|
||||||
|
// Simply re-run your setup task after changing the mappings to update your workspace.
|
||||||
|
mappings channel: mapping_channel, version: mapping_version
|
||||||
|
|
||||||
|
// When true, this property will have all Eclipse/IntelliJ IDEA run configurations run the "prepareX" task for the given run configuration before launching the game.
|
||||||
|
// In most cases, it is not necessary to enable.
|
||||||
|
// enableEclipsePrepareRuns = true
|
||||||
|
// enableIdeaPrepareRuns = true
|
||||||
|
|
||||||
|
// This property allows configuring Gradle's ProcessResources task(s) to run on IDE output locations before launching the game.
|
||||||
|
// It is REQUIRED to be set to true for this template to function.
|
||||||
|
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
|
||||||
|
copyIdeResources = true
|
||||||
|
|
||||||
|
// When true, this property will add the folder name of all declared run configurations to generated IDE run configurations.
|
||||||
|
// The folder name can be set on a run configuration using the "folderName" property.
|
||||||
|
// By default, the folder name of a run configuration is the name of the Gradle project containing it.
|
||||||
|
// generateRunFolders = true
|
||||||
|
|
||||||
|
// This property enables access transformers for use in development.
|
||||||
|
// They will be applied to the Minecraft artifact.
|
||||||
|
// The access transformer file can be anywhere in the project.
|
||||||
|
// However, it must be at "META-INF/accesstransformer.cfg" in the final mod jar to be loaded by Forge.
|
||||||
|
// This default location is a best practice to automatically put the file in the right place in the final jar.
|
||||||
|
// See https://docs.minecraftforge.net/en/latest/advanced/accesstransformers/ for more information.
|
||||||
|
// accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||||
|
|
||||||
|
// Default run configurations.
|
||||||
|
// These can be tweaked, removed, or duplicated as needed.
|
||||||
|
runs {
|
||||||
|
// applies to all the run configs below
|
||||||
|
configureEach {
|
||||||
|
workingDirectory project.file('run')
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
property '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
|
||||||
|
property 'forge.logging.console.level', 'debug'
|
||||||
|
|
||||||
|
mods {
|
||||||
|
"${mod_id}" {
|
||||||
|
source sourceSets.main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client {
|
||||||
|
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
|
||||||
|
property 'forge.enabledGameTestNamespaces', mod_id
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
property 'forge.enabledGameTestNamespaces', mod_id
|
||||||
|
args '--nogui'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
property 'forge.enabledGameTestNamespaces', mod_id
|
||||||
|
}
|
||||||
|
|
||||||
|
data {
|
||||||
|
// example of overriding the workingDirectory set in configureEach above
|
||||||
|
workingDirectory project.file('run-data')
|
||||||
|
|
||||||
|
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
||||||
|
args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include resources generated by data generators.
|
||||||
|
sourceSets.main.resources { srcDir 'src/generated/resources' }
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
// Put repositories for dependencies here
|
||||||
|
// ForgeGradle automatically adds the Forge maven and Maven Central for you
|
||||||
|
|
||||||
|
// If you have mod jar dependencies in ./libs, you can declare them as a repository like so.
|
||||||
|
// See https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver
|
||||||
|
// flatDir {
|
||||||
|
// dir 'libs'
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Specify the version of Minecraft to use.
|
||||||
|
// Any artifact can be supplied so long as it has a "userdev" classifier artifact and is a compatible patcher artifact.
|
||||||
|
// The "userdev" classifier will be requested and setup by ForgeGradle.
|
||||||
|
// If the group id is "net.minecraft" and the artifact id is one of ["client", "server", "joined"],
|
||||||
|
// then special handling is done to allow a setup of a vanilla dependency without the use of an external repository.
|
||||||
|
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
|
||||||
|
|
||||||
|
// Example mod dependency with JEI - using fg.deobf() ensures the dependency is remapped to your development mappings
|
||||||
|
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
|
||||||
|
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}-common-api:${jei_version}")
|
||||||
|
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}")
|
||||||
|
// runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}")
|
||||||
|
|
||||||
|
// Example mod dependency using a mod jar from ./libs with a flat dir repository
|
||||||
|
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
|
||||||
|
// The group id is ignored when searching -- in this case, it is "blank"
|
||||||
|
// implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}")
|
||||||
|
|
||||||
|
// For more info:
|
||||||
|
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||||
|
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.named('processResources', ProcessResources).configure {
|
||||||
|
var replaceProperties = [
|
||||||
|
minecraft_version: minecraft_version, minecraft_version_range: minecraft_version_range,
|
||||||
|
forge_version: forge_version, forge_version_range: forge_version_range,
|
||||||
|
loader_version_range: loader_version_range,
|
||||||
|
mod_id: mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version,
|
||||||
|
mod_authors: mod_authors, mod_description: mod_description,
|
||||||
|
]
|
||||||
|
inputs.properties replaceProperties
|
||||||
|
|
||||||
|
filesMatching(['META-INF/mods.toml', 'pack.mcmeta']) {
|
||||||
|
expand replaceProperties + [project: project]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example for how to get properties into the manifest for reading at runtime.
|
||||||
|
tasks.named('jar', Jar).configure {
|
||||||
|
manifest {
|
||||||
|
attributes([
|
||||||
|
'Specification-Title' : mod_id,
|
||||||
|
'Specification-Vendor' : mod_authors,
|
||||||
|
'Specification-Version' : '1', // We are version 1 of ourselves
|
||||||
|
'Implementation-Title' : project.name,
|
||||||
|
'Implementation-Version' : project.jar.archiveVersion,
|
||||||
|
'Implementation-Vendor' : mod_authors,
|
||||||
|
'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the preferred method to reobfuscate your jar file
|
||||||
|
finalizedBy 'reobfJar'
|
||||||
|
}
|
||||||
|
|
||||||
|
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing:
|
||||||
|
// tasks.named('publish').configure {
|
||||||
|
// dependsOn 'reobfJar'
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Example configuration to allow publishing using the maven-publish plugin
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
register('mavenJava', MavenPublication) {
|
||||||
|
artifact jar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url "file://${project.projectDir}/mcmodsrepo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||||
|
}
|
||||||
18
gradle.properties
Normal file
18
gradle.properties
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx3G
|
||||||
|
org.gradle.daemon=false
|
||||||
|
|
||||||
|
minecraft_version=1.20.1
|
||||||
|
minecraft_version_range=[1.20.1,1.21)
|
||||||
|
forge_version=47.4.10
|
||||||
|
forge_version_range=[47,)
|
||||||
|
loader_version_range=[47,)
|
||||||
|
mapping_channel=official
|
||||||
|
mapping_version=1.20.1
|
||||||
|
|
||||||
|
mod_id=rustybeds
|
||||||
|
mod_name=Rusty Beds
|
||||||
|
mod_license=MIT
|
||||||
|
mod_version=1.0.0
|
||||||
|
mod_group_id=com.rustybeds
|
||||||
|
mod_authors=Barry
|
||||||
|
mod_description=Track your last 5 beds and choose where to respawn when you die.
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
245
gradlew
vendored
Executable file
245
gradlew
vendored
Executable file
@ -0,0 +1,245 @@
|
|||||||
|
#!/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.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# 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/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
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##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || 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
|
||||||
|
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" && ! "$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=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=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, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# 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" "$@"
|
||||||
92
gradlew.bat
vendored
Normal file
92
gradlew.bat
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
@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=.
|
||||||
|
@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.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% 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
|
||||||
13
settings.gradle
Normal file
13
settings.gradle
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven {
|
||||||
|
name = 'MinecraftForge'
|
||||||
|
url = 'https://maven.minecraftforge.net/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
|
||||||
|
}
|
||||||
20
src/main/java/com/rustybeds/RustyBeds.java
Normal file
20
src/main/java/com/rustybeds/RustyBeds.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package com.rustybeds;
|
||||||
|
|
||||||
|
import com.rustybeds.network.ModNetwork;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||||
|
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||||
|
|
||||||
|
@Mod(RustyBeds.MOD_ID)
|
||||||
|
public class RustyBeds {
|
||||||
|
|
||||||
|
public static final String MOD_ID = "rustybeds";
|
||||||
|
|
||||||
|
public RustyBeds() {
|
||||||
|
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commonSetup(FMLCommonSetupEvent event) {
|
||||||
|
event.enqueueWork(ModNetwork::register);
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/main/java/com/rustybeds/capability/BedEntry.java
Normal file
84
src/main/java/com/rustybeds/capability/BedEntry.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package com.rustybeds.capability;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
public class BedEntry {
|
||||||
|
|
||||||
|
private final BlockPos pos;
|
||||||
|
private final ResourceKey<Level> dimension;
|
||||||
|
private String name;
|
||||||
|
private boolean locked;
|
||||||
|
|
||||||
|
public BedEntry(BlockPos pos, ResourceKey<Level> dimension) {
|
||||||
|
this(pos, dimension, "", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BedEntry(BlockPos pos, ResourceKey<Level> dimension, String name, boolean locked) {
|
||||||
|
this.pos = pos;
|
||||||
|
this.dimension = dimension;
|
||||||
|
this.name = name != null ? name : "";
|
||||||
|
this.locked = locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos pos() { return pos; }
|
||||||
|
public ResourceKey<Level> dimension() { return dimension; }
|
||||||
|
public String name() { return name; }
|
||||||
|
public boolean locked() { return locked; }
|
||||||
|
public void setName(String name) { this.name = name != null ? name : ""; }
|
||||||
|
public void setLocked(boolean locked) { this.locked = locked; }
|
||||||
|
|
||||||
|
public boolean matches(BlockPos otherPos, ResourceKey<Level> otherDim) {
|
||||||
|
return pos.equals(otherPos) && dimension.equals(otherDim);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundTag toNbt() {
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
tag.putInt("x", pos.getX());
|
||||||
|
tag.putInt("y", pos.getY());
|
||||||
|
tag.putInt("z", pos.getZ());
|
||||||
|
tag.putString("dim", dimension.location().toString());
|
||||||
|
tag.putString("name", name);
|
||||||
|
tag.putBoolean("locked", locked);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BedEntry fromNbt(CompoundTag tag) {
|
||||||
|
BlockPos pos = new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
|
||||||
|
ResourceKey<Level> dim = ResourceKey.create(Registries.DIMENSION,
|
||||||
|
new ResourceLocation(tag.getString("dim")));
|
||||||
|
String name = tag.getString("name");
|
||||||
|
boolean locked = tag.getBoolean("locked");
|
||||||
|
return new BedEntry(pos, dim, name, locked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(FriendlyByteBuf buf) {
|
||||||
|
buf.writeBlockPos(pos);
|
||||||
|
buf.writeResourceLocation(dimension.location());
|
||||||
|
buf.writeUtf(name);
|
||||||
|
buf.writeBoolean(locked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BedEntry decode(FriendlyByteBuf buf) {
|
||||||
|
BlockPos pos = buf.readBlockPos();
|
||||||
|
ResourceLocation dim = buf.readResourceLocation();
|
||||||
|
String name = buf.readUtf();
|
||||||
|
boolean locked = buf.readBoolean();
|
||||||
|
return new BedEntry(pos, ResourceKey.create(Registries.DIMENSION, dim), name, locked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String dimensionDisplayName() {
|
||||||
|
String path = dimension.location().getPath();
|
||||||
|
return switch (path) {
|
||||||
|
case "overworld" -> "Overworld";
|
||||||
|
case "the_nether" -> "The Nether";
|
||||||
|
case "the_end" -> "The End";
|
||||||
|
default -> dimension.location().toString();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
119
src/main/java/com/rustybeds/capability/BedHistory.java
Normal file
119
src/main/java/com/rustybeds/capability/BedHistory.java
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package com.rustybeds.capability;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.BedBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||||
|
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BedHistory {
|
||||||
|
|
||||||
|
public static final Capability<BedHistory> CAPABILITY = CapabilityManager.get(new CapabilityToken<>() {});
|
||||||
|
private static final int MAX_BEDS = 5;
|
||||||
|
|
||||||
|
private final List<BedEntry> beds = new ArrayList<>();
|
||||||
|
|
||||||
|
public void addBed(BedEntry entry) {
|
||||||
|
for (int i = 0; i < beds.size(); i++) {
|
||||||
|
BedEntry existing = beds.get(i);
|
||||||
|
if (existing.matches(entry.pos(), entry.dimension())) {
|
||||||
|
beds.remove(i);
|
||||||
|
beds.add(0, existing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (beds.size() < MAX_BEDS) {
|
||||||
|
beds.add(0, entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = beds.size() - 1; i >= 0; i--) {
|
||||||
|
if (!beds.get(i).locked()) {
|
||||||
|
beds.remove(i);
|
||||||
|
beds.add(0, entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBed(BlockPos pos, ResourceKey<Level> dimension) {
|
||||||
|
beds.removeIf(e -> e.matches(pos, dimension));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renameBed(BlockPos pos, ResourceKey<Level> dimension, String name) {
|
||||||
|
for (BedEntry e : beds) {
|
||||||
|
if (e.matches(pos, dimension)) {
|
||||||
|
e.setName(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleLock(BlockPos pos, ResourceKey<Level> dimension) {
|
||||||
|
for (BedEntry e : beds) {
|
||||||
|
if (e.matches(pos, dimension)) {
|
||||||
|
e.setLocked(!e.locked());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BedEntry> getBeds() {
|
||||||
|
return Collections.unmodifiableList(beds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BedEntry> getValidBeds(MinecraftServer server) {
|
||||||
|
List<BedEntry> valid = new ArrayList<>();
|
||||||
|
Iterator<BedEntry> iter = beds.iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
BedEntry entry = iter.next();
|
||||||
|
ServerLevel level = server.getLevel(entry.dimension());
|
||||||
|
if (level != null) {
|
||||||
|
BlockState state = level.getBlockState(entry.pos());
|
||||||
|
if (state.getBlock() instanceof BedBlock) {
|
||||||
|
valid.add(entry);
|
||||||
|
} else {
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyFrom(BedHistory other) {
|
||||||
|
beds.clear();
|
||||||
|
beds.addAll(other.beds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundTag serializeNBT() {
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
ListTag list = new ListTag();
|
||||||
|
for (BedEntry entry : beds) {
|
||||||
|
list.add(entry.toNbt());
|
||||||
|
}
|
||||||
|
tag.put("beds", list);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deserializeNBT(CompoundTag tag) {
|
||||||
|
beds.clear();
|
||||||
|
ListTag list = tag.getList("beds", Tag.TAG_COMPOUND);
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
beds.add(BedEntry.fromNbt(list.getCompound(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.rustybeds.capability;
|
||||||
|
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class BedHistoryProvider implements ICapabilitySerializable<CompoundTag> {
|
||||||
|
|
||||||
|
private final BedHistory bedHistory = new BedHistory();
|
||||||
|
private final LazyOptional<BedHistory> lazy = LazyOptional.of(() -> bedHistory);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
|
||||||
|
return cap == BedHistory.CAPABILITY ? lazy.cast() : LazyOptional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag serializeNBT() {
|
||||||
|
return bedHistory.serializeNBT();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deserializeNBT(CompoundTag tag) {
|
||||||
|
bedHistory.deserializeNBT(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate() {
|
||||||
|
lazy.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
199
src/main/java/com/rustybeds/client/BedPickerScreen.java
Normal file
199
src/main/java/com/rustybeds/client/BedPickerScreen.java
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
package com.rustybeds.client;
|
||||||
|
|
||||||
|
import com.rustybeds.capability.BedEntry;
|
||||||
|
import com.rustybeds.network.ModNetwork;
|
||||||
|
import com.rustybeds.network.ModifyBedPacket;
|
||||||
|
import com.rustybeds.network.SelectBedPacket;
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.components.EditBox;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BedPickerScreen extends Screen {
|
||||||
|
|
||||||
|
private static final int MAIN_W = 230;
|
||||||
|
private static final int SMALL_W = 20;
|
||||||
|
private static final int BTN_H = 20;
|
||||||
|
private static final int GAP = 2;
|
||||||
|
private static final int ROW_GAP = 4;
|
||||||
|
|
||||||
|
private final List<BedEntry> beds;
|
||||||
|
private final boolean editOnly;
|
||||||
|
private int editingIndex = -1;
|
||||||
|
private EditBox editBox;
|
||||||
|
|
||||||
|
public BedPickerScreen(List<BedEntry> beds, boolean editOnly) {
|
||||||
|
super(Component.literal(editOnly ? "Manage Beds" : "Choose Your Respawn Bed"));
|
||||||
|
this.beds = new ArrayList<>(beds);
|
||||||
|
this.editOnly = editOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
editBox = null;
|
||||||
|
int rowWidth = MAIN_W + 3 * (SMALL_W + GAP);
|
||||||
|
int rowLeft = (this.width - rowWidth) / 2;
|
||||||
|
int totalHeight = beds.size() * (BTN_H + ROW_GAP) + BTN_H + 20;
|
||||||
|
int startY = Math.max(55, (this.height - totalHeight) / 2);
|
||||||
|
|
||||||
|
for (int i = 0; i < beds.size(); i++) {
|
||||||
|
BedEntry bed = beds.get(i);
|
||||||
|
int y = startY + i * (BTN_H + ROW_GAP);
|
||||||
|
int x = rowLeft;
|
||||||
|
final int idx = i;
|
||||||
|
|
||||||
|
if (editingIndex == i) {
|
||||||
|
editBox = new EditBox(this.font, x, y, MAIN_W, BTN_H, Component.literal("Name"));
|
||||||
|
editBox.setMaxLength(32);
|
||||||
|
editBox.setValue(bed.name());
|
||||||
|
addRenderableWidget(editBox);
|
||||||
|
this.setInitialFocus(editBox);
|
||||||
|
|
||||||
|
x += MAIN_W + GAP;
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("Ok"), btn -> confirmRename(idx))
|
||||||
|
.bounds(x, y, SMALL_W, BTN_H).build());
|
||||||
|
} else if (editOnly) {
|
||||||
|
Button labelBtn = Button.builder(buildLabel(bed, i == 0), btn -> {})
|
||||||
|
.bounds(x, y, MAIN_W, BTN_H).build();
|
||||||
|
labelBtn.active = false;
|
||||||
|
addRenderableWidget(labelBtn);
|
||||||
|
|
||||||
|
x += MAIN_W + GAP;
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("E"), btn -> startRename(idx))
|
||||||
|
.bounds(x, y, SMALL_W, BTN_H).build());
|
||||||
|
} else {
|
||||||
|
addRenderableWidget(Button.builder(buildLabel(bed, i == 0), btn -> {
|
||||||
|
ModNetwork.sendToServer(new SelectBedPacket(idx));
|
||||||
|
onClose();
|
||||||
|
}).bounds(x, y, MAIN_W, BTN_H).build());
|
||||||
|
|
||||||
|
x += MAIN_W + GAP;
|
||||||
|
addRenderableWidget(Button.builder(Component.literal("E"), btn -> startRename(idx))
|
||||||
|
.bounds(x, y, SMALL_W, BTN_H).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
x += SMALL_W + GAP;
|
||||||
|
Component lockLabel = bed.locked()
|
||||||
|
? Component.literal("L").withStyle(ChatFormatting.GREEN)
|
||||||
|
: Component.literal("L");
|
||||||
|
addRenderableWidget(Button.builder(lockLabel, btn -> toggleLock(idx))
|
||||||
|
.bounds(x, y, SMALL_W, BTN_H).build());
|
||||||
|
|
||||||
|
x += SMALL_W + GAP;
|
||||||
|
addRenderableWidget(Button.builder(
|
||||||
|
Component.literal("X").withStyle(ChatFormatting.RED), btn -> removeBed(idx))
|
||||||
|
.bounds(x, y, SMALL_W, BTN_H).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
int stayY = startY + beds.size() * (BTN_H + ROW_GAP) + 12;
|
||||||
|
Component closeLabel = editOnly ? Component.literal("Done") : Component.literal("Stay Here");
|
||||||
|
addRenderableWidget(Button.builder(closeLabel, btn -> onClose())
|
||||||
|
.bounds(centerX(120), stayY, 120, BTN_H).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
|
||||||
|
renderBackground(graphics);
|
||||||
|
graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, 0xFFFFFF);
|
||||||
|
Component hint = editOnly
|
||||||
|
? Component.literal("E = rename L = lock X = remove")
|
||||||
|
: Component.literal("\u2605 = most recent ")
|
||||||
|
.append(Component.literal("L").withStyle(ChatFormatting.GREEN))
|
||||||
|
.append(Component.literal(" = locked"));
|
||||||
|
graphics.drawCenteredString(this.font, hint, this.width / 2, 32, 0xAAAAAA);
|
||||||
|
super.render(graphics, mouseX, mouseY, partialTick);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||||
|
if (editingIndex >= 0) {
|
||||||
|
if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) {
|
||||||
|
confirmRename(editingIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
||||||
|
editingIndex = -1;
|
||||||
|
rebuildWidgets();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPauseScreen() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component buildLabel(BedEntry bed, boolean mostRecent) {
|
||||||
|
MutableComponent label = Component.empty().copy();
|
||||||
|
if (bed.locked()) {
|
||||||
|
label.append(Component.literal("[L] ").withStyle(ChatFormatting.GREEN));
|
||||||
|
}
|
||||||
|
if (!bed.name().isEmpty()) {
|
||||||
|
label.append(bed.name() + " ");
|
||||||
|
}
|
||||||
|
label.append(Component.literal(
|
||||||
|
bed.dimensionDisplayName() + " (" +
|
||||||
|
bed.pos().getX() + ", " + bed.pos().getY() + ", " + bed.pos().getZ() + ")")
|
||||||
|
.withStyle(ChatFormatting.GRAY));
|
||||||
|
if (mostRecent) {
|
||||||
|
label.append(Component.literal(" \u2605").withStyle(ChatFormatting.GOLD));
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startRename(int index) {
|
||||||
|
editingIndex = index;
|
||||||
|
rebuildWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void confirmRename(int index) {
|
||||||
|
if (editBox != null && index >= 0 && index < beds.size()) {
|
||||||
|
String newName = editBox.getValue().trim();
|
||||||
|
BedEntry bed = beds.get(index);
|
||||||
|
bed.setName(newName);
|
||||||
|
ModNetwork.sendToServer(new ModifyBedPacket(
|
||||||
|
ModifyBedPacket.Action.RENAME, bed.pos(), bed.dimension(), newName));
|
||||||
|
}
|
||||||
|
editingIndex = -1;
|
||||||
|
rebuildWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleLock(int index) {
|
||||||
|
editingIndex = -1;
|
||||||
|
if (index >= 0 && index < beds.size()) {
|
||||||
|
BedEntry bed = beds.get(index);
|
||||||
|
bed.setLocked(!bed.locked());
|
||||||
|
ModNetwork.sendToServer(new ModifyBedPacket(
|
||||||
|
ModifyBedPacket.Action.TOGGLE_LOCK, bed.pos(), bed.dimension()));
|
||||||
|
}
|
||||||
|
rebuildWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBed(int index) {
|
||||||
|
editingIndex = -1;
|
||||||
|
if (index >= 0 && index < beds.size()) {
|
||||||
|
BedEntry bed = beds.get(index);
|
||||||
|
ModNetwork.sendToServer(new ModifyBedPacket(
|
||||||
|
ModifyBedPacket.Action.REMOVE, bed.pos(), bed.dimension()));
|
||||||
|
beds.remove(index);
|
||||||
|
}
|
||||||
|
if (beds.isEmpty()) {
|
||||||
|
onClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rebuildWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int centerX(int elementWidth) {
|
||||||
|
return (this.width - elementWidth) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/main/java/com/rustybeds/client/ClientForgeEvents.java
Normal file
26
src/main/java/com/rustybeds/client/ClientForgeEvents.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package com.rustybeds.client;
|
||||||
|
|
||||||
|
import com.rustybeds.RustyBeds;
|
||||||
|
import com.rustybeds.network.ModNetwork;
|
||||||
|
import com.rustybeds.network.RequestBedPickerPacket;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.event.TickEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
|
||||||
|
@Mod.EventBusSubscriber(modid = RustyBeds.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT)
|
||||||
|
public class ClientForgeEvents {
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onClientTick(TickEvent.ClientTickEvent event) {
|
||||||
|
if (event.phase != TickEvent.Phase.END) return;
|
||||||
|
|
||||||
|
Minecraft mc = Minecraft.getInstance();
|
||||||
|
if (mc.screen != null || mc.player == null) return;
|
||||||
|
|
||||||
|
while (RustyBedsKeybinds.OPEN_BED_MENU.consumeClick()) {
|
||||||
|
ModNetwork.sendToServer(RequestBedPickerPacket.INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/main/java/com/rustybeds/client/ClientModEvents.java
Normal file
16
src/main/java/com/rustybeds/client/ClientModEvents.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package com.rustybeds.client;
|
||||||
|
|
||||||
|
import com.rustybeds.RustyBeds;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.client.event.RegisterKeyMappingsEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
|
||||||
|
@Mod.EventBusSubscriber(modid = RustyBeds.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
|
||||||
|
public class ClientModEvents {
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onRegisterKeyMappings(RegisterKeyMappingsEvent event) {
|
||||||
|
event.register(RustyBedsKeybinds.OPEN_BED_MENU);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/main/java/com/rustybeds/client/ClientPacketHandler.java
Normal file
13
src/main/java/com/rustybeds/client/ClientPacketHandler.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package com.rustybeds.client;
|
||||||
|
|
||||||
|
import com.rustybeds.capability.BedEntry;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ClientPacketHandler {
|
||||||
|
|
||||||
|
public static void openBedPicker(List<BedEntry> beds, boolean editOnly) {
|
||||||
|
Minecraft.getInstance().setScreen(new BedPickerScreen(beds, editOnly));
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/main/java/com/rustybeds/client/RustyBedsKeybinds.java
Normal file
20
src/main/java/com/rustybeds/client/RustyBedsKeybinds.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package com.rustybeds.client;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.InputConstants;
|
||||||
|
import com.rustybeds.RustyBeds;
|
||||||
|
import net.minecraft.client.KeyMapping;
|
||||||
|
import net.minecraftforge.client.settings.KeyConflictContext;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
public final class RustyBedsKeybinds {
|
||||||
|
|
||||||
|
public static final KeyMapping OPEN_BED_MENU = new KeyMapping(
|
||||||
|
"key." + RustyBeds.MOD_ID + ".open_bed_menu",
|
||||||
|
KeyConflictContext.IN_GAME,
|
||||||
|
InputConstants.Type.KEYSYM,
|
||||||
|
GLFW.GLFW_KEY_B,
|
||||||
|
"key.categories." + RustyBeds.MOD_ID
|
||||||
|
);
|
||||||
|
|
||||||
|
private RustyBedsKeybinds() {}
|
||||||
|
}
|
||||||
98
src/main/java/com/rustybeds/event/ForgeBusEvents.java
Normal file
98
src/main/java/com/rustybeds/event/ForgeBusEvents.java
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package com.rustybeds.event;
|
||||||
|
|
||||||
|
import com.rustybeds.RustyBeds;
|
||||||
|
import com.rustybeds.capability.BedEntry;
|
||||||
|
import com.rustybeds.capability.BedHistory;
|
||||||
|
import com.rustybeds.capability.BedHistoryProvider;
|
||||||
|
import com.rustybeds.network.ModNetwork;
|
||||||
|
import com.rustybeds.network.OpenBedPickerPacket;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||||
|
import net.minecraftforge.event.TickEvent;
|
||||||
|
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||||
|
import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
import net.minecraftforge.server.ServerLifecycleHooks;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Mod.EventBusSubscriber(modid = RustyBeds.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
|
||||||
|
public class ForgeBusEvents {
|
||||||
|
|
||||||
|
private static final ResourceLocation CAP_ID = new ResourceLocation(RustyBeds.MOD_ID, "bed_history");
|
||||||
|
|
||||||
|
private record PendingBed(BlockPos pos, ResourceKey<Level> dimension) {}
|
||||||
|
private static final Map<UUID, PendingBed> pendingBeds = new HashMap<>();
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onAttachCapabilities(AttachCapabilitiesEvent<Entity> event) {
|
||||||
|
if (event.getObject() instanceof Player) {
|
||||||
|
event.addCapability(CAP_ID, new BedHistoryProvider());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onPlayerClone(PlayerEvent.Clone event) {
|
||||||
|
event.getOriginal().reviveCaps();
|
||||||
|
try {
|
||||||
|
event.getOriginal().getCapability(BedHistory.CAPABILITY).ifPresent(oldCap ->
|
||||||
|
event.getEntity().getCapability(BedHistory.CAPABILITY).ifPresent(newCap ->
|
||||||
|
newCap.copyFrom(oldCap)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
event.getOriginal().invalidateCaps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onPlayerSleep(PlayerSleepInBedEvent event) {
|
||||||
|
if (!(event.getEntity() instanceof ServerPlayer player)) return;
|
||||||
|
if (event.getResultStatus() != null) return;
|
||||||
|
|
||||||
|
pendingBeds.put(player.getUUID(),
|
||||||
|
new PendingBed(event.getPos(), player.level().dimension()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onServerTick(TickEvent.ServerTickEvent event) {
|
||||||
|
if (event.phase != TickEvent.Phase.END || pendingBeds.isEmpty()) return;
|
||||||
|
|
||||||
|
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||||
|
if (server == null) return;
|
||||||
|
|
||||||
|
Iterator<Map.Entry<UUID, PendingBed>> iter = pendingBeds.entrySet().iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Map.Entry<UUID, PendingBed> entry = iter.next();
|
||||||
|
iter.remove();
|
||||||
|
ServerPlayer player = server.getPlayerList().getPlayer(entry.getKey());
|
||||||
|
if (player != null && player.isSleeping()) {
|
||||||
|
PendingBed bed = entry.getValue();
|
||||||
|
player.getCapability(BedHistory.CAPABILITY).ifPresent(cap ->
|
||||||
|
cap.addBed(new BedEntry(bed.pos(), bed.dimension()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
|
||||||
|
if (!(event.getEntity() instanceof ServerPlayer player)) return;
|
||||||
|
if (event.isEndConquered()) return;
|
||||||
|
|
||||||
|
player.getCapability(BedHistory.CAPABILITY).ifPresent(cap -> {
|
||||||
|
List<BedEntry> validBeds = cap.getValidBeds(player.getServer());
|
||||||
|
if (validBeds.size() > 1) {
|
||||||
|
ModNetwork.sendToPlayer(new OpenBedPickerPacket(validBeds), player);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/main/java/com/rustybeds/event/ModBusEvents.java
Normal file
16
src/main/java/com/rustybeds/event/ModBusEvents.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package com.rustybeds.event;
|
||||||
|
|
||||||
|
import com.rustybeds.RustyBeds;
|
||||||
|
import com.rustybeds.capability.BedHistory;
|
||||||
|
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
|
||||||
|
@Mod.EventBusSubscriber(modid = RustyBeds.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||||
|
public class ModBusEvents {
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) {
|
||||||
|
event.register(BedHistory.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/main/java/com/rustybeds/network/ModNetwork.java
Normal file
57
src/main/java/com/rustybeds/network/ModNetwork.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package com.rustybeds.network;
|
||||||
|
|
||||||
|
import com.rustybeds.RustyBeds;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraftforge.network.NetworkDirection;
|
||||||
|
import net.minecraftforge.network.NetworkRegistry;
|
||||||
|
import net.minecraftforge.network.PacketDistributor;
|
||||||
|
import net.minecraftforge.network.simple.SimpleChannel;
|
||||||
|
|
||||||
|
public class ModNetwork {
|
||||||
|
|
||||||
|
private static final String PROTOCOL = "4";
|
||||||
|
private static SimpleChannel CHANNEL;
|
||||||
|
private static int id = 0;
|
||||||
|
|
||||||
|
public static void register() {
|
||||||
|
CHANNEL = NetworkRegistry.ChannelBuilder
|
||||||
|
.named(new ResourceLocation(RustyBeds.MOD_ID, "main"))
|
||||||
|
.networkProtocolVersion(() -> PROTOCOL)
|
||||||
|
.clientAcceptedVersions(PROTOCOL::equals)
|
||||||
|
.serverAcceptedVersions(PROTOCOL::equals)
|
||||||
|
.simpleChannel();
|
||||||
|
|
||||||
|
CHANNEL.messageBuilder(OpenBedPickerPacket.class, id++, NetworkDirection.PLAY_TO_CLIENT)
|
||||||
|
.encoder(OpenBedPickerPacket::encode)
|
||||||
|
.decoder(OpenBedPickerPacket::decode)
|
||||||
|
.consumerMainThread(OpenBedPickerPacket::handle)
|
||||||
|
.add();
|
||||||
|
|
||||||
|
CHANNEL.messageBuilder(SelectBedPacket.class, id++, NetworkDirection.PLAY_TO_SERVER)
|
||||||
|
.encoder(SelectBedPacket::encode)
|
||||||
|
.decoder(SelectBedPacket::decode)
|
||||||
|
.consumerMainThread(SelectBedPacket::handle)
|
||||||
|
.add();
|
||||||
|
|
||||||
|
CHANNEL.messageBuilder(ModifyBedPacket.class, id++, NetworkDirection.PLAY_TO_SERVER)
|
||||||
|
.encoder(ModifyBedPacket::encode)
|
||||||
|
.decoder(ModifyBedPacket::decode)
|
||||||
|
.consumerMainThread(ModifyBedPacket::handle)
|
||||||
|
.add();
|
||||||
|
|
||||||
|
CHANNEL.messageBuilder(RequestBedPickerPacket.class, id++, NetworkDirection.PLAY_TO_SERVER)
|
||||||
|
.encoder(RequestBedPickerPacket::encode)
|
||||||
|
.decoder(RequestBedPickerPacket::decode)
|
||||||
|
.consumerMainThread(RequestBedPickerPacket::handle)
|
||||||
|
.add();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendToPlayer(Object msg, ServerPlayer player) {
|
||||||
|
CHANNEL.send(PacketDistributor.PLAYER.with(() -> player), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendToServer(Object msg) {
|
||||||
|
CHANNEL.sendToServer(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/main/java/com/rustybeds/network/ModifyBedPacket.java
Normal file
63
src/main/java/com/rustybeds/network/ModifyBedPacket.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package com.rustybeds.network;
|
||||||
|
|
||||||
|
import com.rustybeds.capability.BedHistory;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ModifyBedPacket {
|
||||||
|
|
||||||
|
public enum Action { REMOVE, RENAME, TOGGLE_LOCK }
|
||||||
|
|
||||||
|
private final Action action;
|
||||||
|
private final BlockPos pos;
|
||||||
|
private final ResourceKey<Level> dimension;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public ModifyBedPacket(Action action, BlockPos pos, ResourceKey<Level> dimension) {
|
||||||
|
this(action, pos, dimension, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModifyBedPacket(Action action, BlockPos pos, ResourceKey<Level> dimension, String name) {
|
||||||
|
this.action = action;
|
||||||
|
this.pos = pos;
|
||||||
|
this.dimension = dimension;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void encode(ModifyBedPacket msg, FriendlyByteBuf buf) {
|
||||||
|
buf.writeByte(msg.action.ordinal());
|
||||||
|
buf.writeBlockPos(msg.pos);
|
||||||
|
buf.writeResourceLocation(msg.dimension.location());
|
||||||
|
buf.writeUtf(msg.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModifyBedPacket decode(FriendlyByteBuf buf) {
|
||||||
|
Action action = Action.values()[buf.readByte()];
|
||||||
|
BlockPos pos = buf.readBlockPos();
|
||||||
|
ResourceLocation dim = buf.readResourceLocation();
|
||||||
|
String name = buf.readUtf();
|
||||||
|
return new ModifyBedPacket(action, pos, ResourceKey.create(Registries.DIMENSION, dim), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handle(ModifyBedPacket msg, Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ServerPlayer player = ctx.get().getSender();
|
||||||
|
if (player == null) return;
|
||||||
|
|
||||||
|
player.getCapability(BedHistory.CAPABILITY).ifPresent(cap -> {
|
||||||
|
switch (msg.action) {
|
||||||
|
case REMOVE -> cap.removeBed(msg.pos, msg.dimension);
|
||||||
|
case RENAME -> cap.renameBed(msg.pos, msg.dimension, msg.name);
|
||||||
|
case TOGGLE_LOCK -> cap.toggleLock(msg.pos, msg.dimension);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/main/java/com/rustybeds/network/OpenBedPickerPacket.java
Normal file
52
src/main/java/com/rustybeds/network/OpenBedPickerPacket.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package com.rustybeds.network;
|
||||||
|
|
||||||
|
import com.rustybeds.capability.BedEntry;
|
||||||
|
import com.rustybeds.client.ClientPacketHandler;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.fml.DistExecutor;
|
||||||
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class OpenBedPickerPacket {
|
||||||
|
|
||||||
|
private final List<BedEntry> beds;
|
||||||
|
/** If true (keybind menu): rename / lock / remove only — no teleport. */
|
||||||
|
private final boolean editOnly;
|
||||||
|
|
||||||
|
public OpenBedPickerPacket(List<BedEntry> beds) {
|
||||||
|
this(beds, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenBedPickerPacket(List<BedEntry> beds, boolean editOnly) {
|
||||||
|
this.beds = beds;
|
||||||
|
this.editOnly = editOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void encode(OpenBedPickerPacket msg, FriendlyByteBuf buf) {
|
||||||
|
buf.writeBoolean(msg.editOnly);
|
||||||
|
buf.writeInt(msg.beds.size());
|
||||||
|
for (BedEntry entry : msg.beds) {
|
||||||
|
entry.encode(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OpenBedPickerPacket decode(FriendlyByteBuf buf) {
|
||||||
|
boolean editOnly = buf.readBoolean();
|
||||||
|
int size = buf.readInt();
|
||||||
|
List<BedEntry> beds = new ArrayList<>(size);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
beds.add(BedEntry.decode(buf));
|
||||||
|
}
|
||||||
|
return new OpenBedPickerPacket(beds, editOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handle(OpenBedPickerPacket msg, Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
DistExecutor.unsafeRunWhenOn(Dist.CLIENT,
|
||||||
|
() -> () -> ClientPacketHandler.openBedPicker(msg.beds, msg.editOnly));
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.rustybeds.network;
|
||||||
|
|
||||||
|
import com.rustybeds.capability.BedHistory;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public final class RequestBedPickerPacket {
|
||||||
|
|
||||||
|
public static final RequestBedPickerPacket INSTANCE = new RequestBedPickerPacket();
|
||||||
|
|
||||||
|
private RequestBedPickerPacket() {}
|
||||||
|
|
||||||
|
public static void encode(RequestBedPickerPacket msg, FriendlyByteBuf buf) {}
|
||||||
|
|
||||||
|
public static RequestBedPickerPacket decode(FriendlyByteBuf buf) {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handle(RequestBedPickerPacket msg, Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ServerPlayer player = ctx.get().getSender();
|
||||||
|
if (player == null) return;
|
||||||
|
|
||||||
|
player.getCapability(BedHistory.CAPABILITY).ifPresent(cap -> {
|
||||||
|
var beds = cap.getValidBeds(player.getServer());
|
||||||
|
if (beds.isEmpty()) {
|
||||||
|
player.sendSystemMessage(Component.literal("Rusty Beds: no beds stored yet."));
|
||||||
|
} else {
|
||||||
|
ModNetwork.sendToPlayer(new OpenBedPickerPacket(beds, true), player);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/main/java/com/rustybeds/network/SelectBedPacket.java
Normal file
54
src/main/java/com/rustybeds/network/SelectBedPacket.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package com.rustybeds.network;
|
||||||
|
|
||||||
|
import com.rustybeds.capability.BedEntry;
|
||||||
|
import com.rustybeds.capability.BedHistory;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class SelectBedPacket {
|
||||||
|
|
||||||
|
private final int index;
|
||||||
|
|
||||||
|
public SelectBedPacket(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void encode(SelectBedPacket msg, FriendlyByteBuf buf) {
|
||||||
|
buf.writeInt(msg.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectBedPacket decode(FriendlyByteBuf buf) {
|
||||||
|
return new SelectBedPacket(buf.readInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handle(SelectBedPacket msg, Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ServerPlayer player = ctx.get().getSender();
|
||||||
|
if (player == null) return;
|
||||||
|
|
||||||
|
player.getCapability(BedHistory.CAPABILITY).ifPresent(cap -> {
|
||||||
|
List<BedEntry> validBeds = cap.getValidBeds(player.getServer());
|
||||||
|
if (msg.index < 0 || msg.index >= validBeds.size()) return;
|
||||||
|
|
||||||
|
BedEntry bed = validBeds.get(msg.index);
|
||||||
|
ServerLevel level = player.getServer().getLevel(bed.dimension());
|
||||||
|
if (level == null) return;
|
||||||
|
|
||||||
|
Optional<Vec3> safePos = Player.findRespawnPositionAndUseSpawnBlock(
|
||||||
|
level, bed.pos(), 0F, false, true);
|
||||||
|
if (safePos.isPresent()) {
|
||||||
|
Vec3 p = safePos.get();
|
||||||
|
player.teleportTo(level, p.x, p.y, p.z, player.getYRot(), player.getXRot());
|
||||||
|
player.setRespawnPosition(bed.dimension(), bed.pos(), 0F, false, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/main/resources/META-INF/mods.toml
Normal file
24
src/main/resources/META-INF/mods.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
modLoader="javafml"
|
||||||
|
loaderVersion="${loader_version_range}"
|
||||||
|
license="${mod_license}"
|
||||||
|
|
||||||
|
[[mods]]
|
||||||
|
modId="${mod_id}"
|
||||||
|
version="${mod_version}"
|
||||||
|
displayName="${mod_name}"
|
||||||
|
authors="${mod_authors}"
|
||||||
|
description='''${mod_description}'''
|
||||||
|
|
||||||
|
[[dependencies.${mod_id}]]
|
||||||
|
modId="forge"
|
||||||
|
mandatory=true
|
||||||
|
versionRange="${forge_version_range}"
|
||||||
|
ordering="NONE"
|
||||||
|
side="BOTH"
|
||||||
|
|
||||||
|
[[dependencies.${mod_id}]]
|
||||||
|
modId="minecraft"
|
||||||
|
mandatory=true
|
||||||
|
versionRange="${minecraft_version_range}"
|
||||||
|
ordering="NONE"
|
||||||
|
side="BOTH"
|
||||||
4
src/main/resources/assets/rustybeds/lang/en_us.json
Normal file
4
src/main/resources/assets/rustybeds/lang/en_us.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"key.rustybeds.open_bed_menu": "Open bed menu",
|
||||||
|
"key.categories.rustybeds": "Rusty Beds"
|
||||||
|
}
|
||||||
6
src/main/resources/pack.mcmeta
Normal file
6
src/main/resources/pack.mcmeta
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"pack": {
|
||||||
|
"description": "Rusty Beds resources",
|
||||||
|
"pack_format": 15
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user