First one
Some checks failed
build / build (21) (push) Has been cancelled

This commit is contained in:
Jkibbels 2024-10-28 17:47:51 -04:00
commit 95c68935f3
92 changed files with 8093 additions and 0 deletions

9
.gitattributes vendored Normal file
View File

@ -0,0 +1,9 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf

37
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,37 @@
# Automatically build the project and run any configured tests for every push
# and submitted pull request. This can help catch issues that only occur on
# certain platforms or Java versions, and provides a first line of defence
# against bad commits.
name: build
on: [pull_request, push]
jobs:
build:
strategy:
matrix:
# Use these Java versions
java: [
21, # Current Java LTS
]
runs-on: ubuntu-22.04
steps:
- name: checkout repository
uses: actions/checkout@v4
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v2
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'microsoft'
- name: make gradle wrapper executable
run: chmod +x ./gradlew
- name: build
run: ./gradlew build
- name: capture build artifacts
if: ${{ matrix.java == '21' }} # Only upload artifacts built from latest java
uses: actions/upload-artifact@v4
with:
name: Artifacts
path: build/libs/

44
.gitignore vendored Normal file
View File

@ -0,0 +1,44 @@
# gradle
.gradle/
build/
out/
classes/
# eclipse
*.launch
# Doxygen ignores
**html/*
**latex/*
# idea
.idea/
*.iml
*.ipr
*.iws
# vscode
.settings/
.vscode/
bin/
.classpath
.project
# macos
*.DS_Store
# fabric
run/
# java
hs_err_*.log
replay_*.log
*.hprof
*.jfr

2658
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

121
LICENSE Normal file
View File

@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

95
build.gradle Normal file
View File

@ -0,0 +1,95 @@
plugins {
id 'fabric-loom' version '1.7-SNAPSHOT'
id 'maven-publish'
}
version = project.mod_version
group = project.maven_group
base {
archivesName = project.archives_base_name
}
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
// maven {
// url "https://maven.squiddev.cc"
// content {
// includeGroup("cc.tweaked")
// }
// }
}
loom {
splitEnvironmentSourceSets()
mods {
"keeblarcraft" {
sourceSet sourceSets.main
sourceSet sourceSets.client
}
}
}
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}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
//modCompileOnly "cc.tweaked:cc-tweaked-1.20-fabric-api:1.105.0"
//modRuntimeOnly "cc.tweaked:cc-tweaked-1.20-fabric-api:1.105.0"
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
tasks.withType(JavaCompile).configureEach {
it.options.release = 17
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
jar {
from("LICENSE") {
rename { "${it}_${project.base.archivesName.get()}"}
}
}
// configure the maven publication
publishing {
publications {
create("mavenJava", MavenPublication) {
artifactId = project.archives_base_name
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}

17
gradle.properties Normal file
View File

@ -0,0 +1,17 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
org.gradle.parallel=true
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.20
yarn_mappings=1.20+build.1
loader_version=0.15.11
# Mod Properties
mod_version=0.0.1
maven_group=jesse.keeblarcraft
archives_base_name=keeblarcraft
# Dependencies
fabric_version=0.83.0+1.20

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

249
gradlew vendored Executable file
View File

@ -0,0 +1,249 @@
#!/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/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && 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
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
gradlew.bat vendored Normal file
View 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. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

4
installmod.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
rm ~/.minecraft/mods/keeblarcraft* # What else could be named this? Really now?
cp build/devlibs/*sources.jar ~/.minecraft/mods
echo Moved minecraft to mods dir

10
settings.gradle Normal file
View File

@ -0,0 +1,10 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
mavenCentral()
gradlePluginPortal()
}
}

View File

@ -0,0 +1,21 @@
package jesse.keeblarcraft;
import jesse.keeblarcraft.gui.ClientHandlers;
import jesse.keeblarcraft.gui.ScreenManager;
import jesse.keeblarcraft.gui.widgets.TreeWidget;
import jesse.keeblarcraft.Shortcuts.ShortcutManager;
import net.fabricmc.api.ClientModInitializer;
public class KeeblarcraftClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
ShortcutManager.RegisterKeybinds();
ClientHandlers.RegisterHandlers();
ScreenManager.GetInstance();
ScreenManager.AddWidget(TreeWidget.class, 10);
}
}

View File

@ -0,0 +1,97 @@
/*
*
* ShortcutManager
*
* This is the client-side shortcut keybind manager
*
*
*/
package jesse.keeblarcraft.Shortcuts;
import org.lwjgl.glfw.GLFW;
// import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.gui.ScreenManager;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.util.InputUtil;
import net.minecraft.text.Text;
public class ShortcutManager {
private static KeyBinding skillTreeShortcut;
private static KeyBinding globalConfig;
private static Boolean toggleTreeGui = true; // Initialized to true for first time pressing keybind to actually display gui
// private static ScreenManager treeGui = null;
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterKeybinds
///
/// @brief Registers all player keybinds and callbacks to them
/////////////////////////////////////////////////////////////////////////////
// All callbacks for keyboard shortcuts are put here
public static void RegisterKeybinds() {
// Keybindings are registered here
skillTreeShortcut = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.keeblarcraft.treemap",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_K,
"category.keeblarcraft.test"
));
globalConfig = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.keeblarcraft.globalconfig",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_M,
"category.keeblarcraft.test"
));
/// Callbacks are down here
ClientTickEvents.END_CLIENT_TICK.register(client -> {
// The keybinding util does 2 things in one, if I check the state of the key press it flips a bit and deducts from
// the actual pressed amount - which we don't want to do. We will call the callbacks for keybinding functions here,
// and let them check the keybind themselves. NOTE: These functions are called at the end of every tick! Future us
// should see if there is a less expensive way to check this on just the keybind itself being flipped
DrawTreeGui(client);
if (globalConfig.wasPressed()) {
client.player.sendMessage(Text.of("Keeblarcraft recognizes you pressed the config button, but this option doesn't do anything yet"), false);
}
});
}
/////////////////////////////////////////////////////////////////////////////
/// @fn DrawTreeGui
///
/// @arg[in] client is the minecraft client instance that is passed in
/// so we have context of the client itself
///
/// @brief Draws the attribute tree GUI
/////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("resource")
public static void DrawTreeGui(MinecraftClient client) {
while (skillTreeShortcut.wasPressed()) { // Constant loop required to redraw screen continuously
System.out.println("Key pressed detected"); /// TODO: When we figure out how to get key presses to still work when a screen opens; then we can toggle menus with buttons...
if (toggleTreeGui) {
// There is a technical resource leak that current screen is never closed when defining it here; which is ok I think because we want to return to it anyways
Screen currentScreen = MinecraftClient.getInstance().currentScreen;
client.player.sendMessage(Text.of("You pressed the GUI key!"));
// treeGui = new ScreenManager(Keeblarcraft.TREE_HANDLER.create(0, client.player.getInventory()), client.player.getInventory(), Text.of("Test"));
// treeGui.AddParent(currentScreen); ///TODO: Put this in the constructor when you figure out how the hell the magic is happening with registration
ScreenManager treeGui = ScreenManager.GetInstance();
treeGui.AddParent(currentScreen);
MinecraftClient.getInstance().setScreen(treeGui);
// toggleTreeGui intentionally never bit-flipped to false as a slight implementation bug exists such that pressing the keybind again
// does NOT call this callback function until the previous screen is CLOSED (which is why this isn't resource leaking...). This will
// need a slight rework or an extra subcall - but is fine for now since this is just how screens work.
}
}
}
}

View File

@ -0,0 +1,14 @@
/*
*
* IndividualAttribute
*
* This will be the handler class for each little attribute on the attribute tree itself.
* It will be fully implemented in the ticket that finishes the TreeWidget ticket (NOT GuiMgr)
*/
package jesse.keeblarcraft.gui.AttributeTree;
// This represents a single node in the tree of attributes drawn to the gui!!!
public class IndividualAttribute {
}

View File

@ -0,0 +1,11 @@
package jesse.keeblarcraft.gui;
import jesse.keeblarcraft.Keeblarcraft;
import net.minecraft.client.gui.screen.ingame.HandledScreens;
public class ClientHandlers {
public static void RegisterHandlers() {
System.out.println("Registering tree handler screen");
HandledScreens.register(Keeblarcraft.TREE_HANDLER, ScreenManager::new);
}
}

View File

@ -0,0 +1,74 @@
/*
*
* GenericLayerT
*
* GenericLayerT is the class that all layers must inherit from in order to be drawn. This allows for the GUI to properly
* draw the layers on which you want your canvas to exist!
*
*
*/
package jesse.keeblarcraft.gui;
import jesse.keeblarcraft.Utils.CommonStructures.Position2d;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.text.Text;
abstract public class GenericLayerT extends ClickableWidget {
// These are the box bounds of the widget
public int startX;
public int startY;
public int endX;
public int endY;
public GenericLayerT() {
// default constructor
super(0, 0, 0, 0, Text.of(""));
}
/////////////////////////////////////////////////////////////////////////////
/// @fn GenericLayerT
///
/// @brief Constructor for class. This is setup directly by the screen
/// manager object and it is not recommended to change the x/y
/// positions or you may break the relation of your widget to
/// others on the screen at the same time!
/////////////////////////////////////////////////////////////////////////////
public GenericLayerT(int x, int y, int width, int height, Text message) {
super(x, y, width, height, message);
}
/////////////////////////////////////////////////////////////////////////////
/// @fn DrawLayer
///
/// @brief Draw on a layer! This is an early implementation of being
/// able to choose the layer you are drawing on and making sure
/// you can customize which layer your context will draw on.
/// This will be called by the overall screen manager so the
/// layer id will be passed in. You will need to handle the layer
/// id yourself.
///
/// @note It is possible to get passed any layer id and you handle each
/// layer respectively; however, it is possible to draw on the
/// wrong layer if handled wrong. For example, you could be
/// passed layer '3' but this function is called for every layer
/// drawn on screen; so you must handle this to only draw on a
/// specific layer. Be careful!
/////////////////////////////////////////////////////////////////////////////
public abstract void DrawLayer(DrawContext context, int layer);
public void UpdateAnchorValues(int x, int y) {
this.startX = x;
this.startY = y;
}
// Calculated based on center of box values
public Position2d GetScreenCenter() {
return new Position2d((this.endX - this.startX) / 2, (this.endY - this.startY) / 2);
}
public abstract void renderButton(DrawContext context, int mouseX, int mouseY, float delta);
protected abstract void appendClickableNarrations(NarrationMessageBuilder builder);
}

View File

@ -0,0 +1,381 @@
/*
*
* ScreenManager
*
* This is the screen manager for the global screen handler of `TreeHandler` type
* Please note: `TreeHandler` is suited for a more broad range of things; however
* its initial implementation is for the skill tree. It will be renamed to a more
* generic name so this class is more obviously re-usable after its implementation
* is complete AND we verify this class can be made more generic for it. Since this
* is unknown, it will remain the name it has currently.
*
*
*/
package jesse.keeblarcraft.gui;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.GuiMgr.TreeHandler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text;
// Client side renderer
public class ScreenManager extends HandledScreen<TreeHandler> {
private static ScreenManager static_inst;
@SuppressWarnings("resource")
private static PlayerInventory static_inventory = new PlayerInventory(null);// = MinecraftClient.getInstance().player.getInventory();
public static ScreenManager GetInstance() {
if (static_inst == null) {
static_inst = new ScreenManager(Keeblarcraft.TREE_HANDLER.create(0, static_inventory), static_inventory, Text.of("Test"));
}
return static_inst;
}
private static HashMap<Integer, ArrayList<Class<? extends GenericLayerT>>> layerMap = new HashMap<Integer, ArrayList<Class<? extends GenericLayerT>>>();
private static HashMap<Integer, ArrayList<GenericLayerT>> layers = new HashMap<Integer, ArrayList<GenericLayerT>>(); // key: layer id; value: list of classes to draw
// private TreeWidget treeWidget = null;
private Screen parent;
/////////////////////////////////////////////////////////////////////////////
/// @fn ScreenManager
///
/// @arg[in] handler is the TreeHandler (ScreenHandler) object
///
/// @arg[in] inventory is the players inventory. Required by HandledScreen
/// object however is unused currently in this Screen
///
/// @arg[in] title is the title of the screen window
///
/// @brief Class constructor
/////////////////////////////////////////////////////////////////////////////
public ScreenManager(TreeHandler handler, PlayerInventory inventory, Text title) {
super(handler, inventory, title);
System.out.println("Called constructor of screen manager!");
// Initialize layers in map
// for (int i = 0; i < NUMBER_DRAW_LAYERS; i++) {
// layers.put(i, new ArrayList<Class<? extends GenericLayerT>>());
// }
}
/////////////////////////////////////////////////////////////////////////////
/// @fn AddParent
///
/// @arg[in] parent is the parent screen object
///
/// @brief Add a parent screen to the screen object. This is useful if
/// you want to return to the previous screen when this one gets
/// closed by the user
/////////////////////////////////////////////////////////////////////////////
public void AddParent(Screen parent) {
this.parent = parent;
}
public static void AddWidget(Class<? extends GenericLayerT> widget, int drawLayer) {
if (layerMap.containsKey(drawLayer)) {
// Just append the widget to the draw layer
var layerList = layerMap.get(drawLayer);
layerList.add(widget);
} else {
// Brand new layer!
layerMap.put(drawLayer, new ArrayList<>(List.of(widget)));
}
}
/////////////////////////////////////////////////////////////////////////////
/// @fn init
///
/// @brief Initialize method; called one-time to setup class variables
/////////////////////////////////////////////////////////////////////////////
@Override
public void init() {
// initialize screen size to the global background picture
this.width = MinecraftClient.getInstance().getWindow().getScaledWidth();
this.height = MinecraftClient.getInstance().getWindow().getScaledHeight();
// Let's go through and initialize all the screen types now in our active memory
for (Entry<Integer, ArrayList<Class<? extends GenericLayerT>>> layerEntry : layerMap.entrySet()) {
var layerList = layerEntry.getValue();
layers.put(layerEntry.getKey(), new ArrayList<>());
var activeLayerList = layers.get(layerEntry.getKey());
for (int i = 0; i < layerList.size(); i++) {
try {
System.out.println("Attempting to initialize widget with information: LAYER I-VAL W H: " + layerEntry.getKey() + " " + i + " " + this.width + " " + this.height);
GenericLayerT initializedWidget = layerList.get(i).getDeclaredConstructor(int.class, int.class, int.class, int.class, Text.class).newInstance(0, 0, this.width, this.height, Text.of(""));
activeLayerList.add(initializedWidget);
} catch (InstantiationException e) {
System.out.println("Could not initialize GenericLayerT class (" + layerList.get(i).getClass().toString() + ") because of IntantiationException");
e.printStackTrace();
} catch (IllegalAccessException e) {
System.out.println("Could not initialize GenericLayerT class (" + layerList.get(i).getClass().toString() + ") because of IllegalAccessException");
e.printStackTrace();
} catch (IllegalArgumentException e) {
System.out.println("Could not initialize GenericLayerT class (" + layerList.get(i).getClass().toString() + ") because of IllegalArgumentException");
e.printStackTrace();
} catch (InvocationTargetException e) {
System.out.println("Could not initialize GenericLayerT class (" + layerList.get(i).getClass().toString() + ") because of InvocationTargetException");
e.printStackTrace();
} catch (NoSuchMethodException e) {
System.out.println("Could not initialize GenericLayerT class (" + layerList.get(i).getClass().toString() + ") because of NoSuchMethodException");
e.printStackTrace();
} catch (SecurityException e) {
System.out.println("Could not initialize GenericLayerT class (" + layerList.get(i).getClass().toString() + ") because of SecurityException");
e.printStackTrace();
}
}
}
// Initialize child widgets with correct screen values so they can draw themselves in the right area on the screen (duh!)
// treeWidget = new TreeWidget(GLOBAL_SCREEN_START_X + 24, GLOBAL_SCREEN_START_Y + 24, GLOBAL_SCREEN_WIDTH - 24, GLOBAL_SCREEN_HEIGHT - 24);
// this.addDrawableChild(treeWidget);
}
/////////////////////////////////////////////////////////////////////////////
/// @fn render
///
/// @arg[in] context is the drawing context of super class
///
/// @arg[in] mouseX is passed to parent class but unused here
///
/// @arg[in] mouseY is passed to parent class but unused here
///
/// @brief Render is called every frame while the screen is open
///
/// @note This is a pure abstract in parent and is required
/////////////////////////////////////////////////////////////////////////////
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta); // This takes care of calling drawBackground which calls DrawMainScreen
}
/////////////////////////////////////////////////////////////////////////////
/// @fn drawForeground
///
/// @arg[in] context is the drawing context of super class
///
/// @arg[in] mouseX is unused
///
/// @arg[in] mouseY is unused
///
/// @brief Draw foreground exists to draw the titles; however we
/// intentionally override it so the superclass object does not
/// draw the overlay over the background screen
/////////////////////////////////////////////////////////////////////////////
@Override
public void drawForeground(DrawContext context, int mouseX, int mouseY) {
// We override this function to intentionally do nothing
// If in the future we want, we would draw the foreground and TITLES with this!
}
/////////////////////////////////////////////////////////////////////////////
/// @fn close
///
/// @brief Called when the screen closes
///
/// @note This is a pure abstract in parent and is required
/////////////////////////////////////////////////////////////////////////////
@Override
public void close() {
this.client.setScreen(parent); // return to previous screen or null
}
/////////////////////////////////////////////////////////////////////////////
/// @fn mouseDragged
///
/// @arg[in] mouseX is x-axis position of original mouse click
///
/// @arg[in] mouseY is y-axis position of original mouse click
///
/// @arg[in] button is the int value of button pressed for mouse dragging
///
/// @arg[in] deltaX is the change in the X position from the previous
/// mouse click
///
/// @arg[in] deltaY is the change in the Y position from the previous
/// mouse click
///
/// @brief The drag event is called on all widgets on the screen so
/// long as the initial position of the drag is within the
/// bounds of the widget box itself. Widgets themselves will need
/// to handle any sub-widgets since the bound check is only
/// there to verify if the event happened ANYWHERE within a
/// widget box
/////////////////////////////////////////////////////////////////////////////
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
// UpdateAnchorValues();
for (Entry<Integer, ArrayList<GenericLayerT>> layerEntry : layers.entrySet()) {
var layerList = layerEntry.getValue();
for (var layerListIterator = 0; layerListIterator < layerList.size(); layerListIterator++) {
var layer = layerList.get(layerListIterator);
// Check to make sure scroll is within the context of the widget then deliver information
if (InBounds(layer.startX, layer.startY, layer.endX, layer.endY, (int) mouseX, (int) mouseY)) {
layer.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn mouseScrolled
///
/// @arg[in] mouseX is the initial X position of the cursor on a scroll
///
/// @arg[in] mouseY is the initial Y position of the cursor on a scroll
///
/// @arg[in] amount is a normalized value that indicates scroll direction
///
/// @brief The scroll event is called on all widgets on the screen so
/// long as the initial position of the scroll is within the
/// bounds of the widget box itself. Widgets themselves will need
/// to handle any sub-widgets since the bound check is only
/// there to verify if the event happened ANYWHERE within a
/// widget box
///
/////////////////////////////////////////////////////////////////////////////
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
super.mouseScrolled(mouseX, mouseY, amount);
for (Entry<Integer, ArrayList<GenericLayerT>> layerEntry : layers.entrySet()) {
var layerList = layerEntry.getValue();
for (var layerListIterator = 0; layerListIterator < layerList.size(); layerListIterator++) {
var layer = layerList.get(layerListIterator);
// Check to make sure scroll is within the context of the widget then deliver information
if (InBounds(layer.startX, layer.startY, layer.endX, layer.endY, (int) mouseX, (int) mouseY)) {
layer.mouseScrolled(mouseX, mouseY, amount);
}
}
}
return true; // The parent function defines this to be boolean; but I have no idea when I would want to return false from this
}
/////////////////////////////////////////////////////////////////////////////
/// @fn mouseClicked
///
/// @arg[in] mouseX is the initial X position of the mouse click event
///
/// @arg[in] mouseY is the initial Y position of the mouse click event
///
/// @arg[in] button is the mouse click button (left/right click value)
///
/// @brief The mouse click is called on all widgets on the screen so
/// long as the initial position of the click is within the
/// bounds of the widget box itself. Widgets themselves will need
/// to handle any sub-widgets since the bound check is only
/// there to verify if the event happened ANYWHERE within a
/// widget box
///
/////////////////////////////////////////////////////////////////////////////
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
super.mouseClicked(mouseX, mouseY, button);
for (Entry<Integer, ArrayList<GenericLayerT>> layerEntry : layers.entrySet()) {
var layerList = layerEntry.getValue();
for (var layerListIterator = 0; layerListIterator < layerList.size(); layerListIterator++) {
var layer = layerList.get(layerListIterator);
// Check to make sure scroll is within the context of the widget then deliver information
if (InBounds(layer.startX, layer.startY, layer.endX, layer.endY, (int) mouseX, (int) mouseY)) {
layer.mouseClicked(mouseX, mouseY, button);
}
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn DrawLayers
///
/// @arg[in] context is the global drawing context for the screen
///
/// @arg[in] delta is passed in from background draw
///
/// @brief This is the primary drawing function so that all the texture
/// draws can be done in one place in this class. We want as
/// little distance as possible between redraws when possible!
///
/// @note Currently the foreground is not drawn in the custom screen
/// manager. This is because the foreground features the general
/// inventory manager that this screen handler is based on and we
/// do not want to see those text pop ups.
/////////////////////////////////////////////////////////////////////////////
public void DrawLayers(DrawContext context, float delta) {
for (Entry<Integer, ArrayList<GenericLayerT>> layerEntry : layers.entrySet()) {
var layerList = layerEntry.getValue();
for (var layerListIterator = 0; layerListIterator < layerList.size(); layerListIterator++) {
var layer = layerList.get(layerListIterator);
System.out.println("Drawing layer " + layerEntry.getKey() + " for class type " + layer.getClass().toString());
layer.DrawLayer(context, layerEntry.getKey());
}
}
}
/////////////////////////////////////////////////////////////////////////////
/// @fn InBounds
///
/// @arg[in] initX is initial position of X axis
///
/// @arg[in] initY is initial position of Y axis
///
/// @arg[in] endX is the end position of X axis
///
/// @arg[in] endY is the end position of Y axis
///
/// @arg[in] x is the current X value we are comparing in the X axis
///
/// @arg[in] y is the current Y value we are comparing in the Y axis
///
/// @brief Checks if an x,y coordinate position falls within the bounds
/// of a bounded box
/////////////////////////////////////////////////////////////////////////////
private boolean InBounds(int initX, int initY, int endX, int endY, int x, int y) {
return (x >= initX && x <= endX) && (y >= initY && y <= endY);
}
/////////////////////////////////////////////////////////////////////////////
/// @fn drawBackground
///
/// @arg[in] context is the drawing context of super class
///
/// @arg[in] delta is the change in background draw
///
/// @arg[in] mouseX is the mouse x-axis position
///
/// @arg[in] mouseY is the mouse y-axis position
///
/// @brief This function is an abstract parent class and must be
/// implemented. This is "hijacked" and just being used as our
/// main drawing method of all the background images. There isn't
/// a huge difference of drawing our stuff in background vs the
/// foreground - except possibly foreground is drawn first.
/////////////////////////////////////////////////////////////////////////////
@Override
protected void drawBackground(DrawContext context, float delta, int mouseX, int mouseY) {
DrawLayers(context, delta);
}
}

View File

@ -0,0 +1,165 @@
/*
*
* TreeWidget
*
* Handles the skill tree widget
*
*
*/
package jesse.keeblarcraft.gui.widgets;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.Utils.CommonStructures.Position2d;
import jesse.keeblarcraft.gui.GenericLayerT;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
public class TreeWidget extends GenericLayerT {
private static Identifier BACKGROUND_TEXTURE = new Identifier(Keeblarcraft.MOD_ID + ":" + "textures/gui/attribute_tree_background.png");
private static int maxHeight = 320;
private static int maxLength = 640;
private int zoomScale = 1;
// private static Identifier FLIGHT_ATTRIBUTE = new Identifier(Keeblarcraft.MOD_ID + ":" + "textures/gui/attribute_flight.png");
///TODO: Make this THE root node in the attribute tree! Rename in future
// private static Identifier EXAMPLE_NODE = new Identifier(Keeblarcraft.MOD_ID + ":" + "textures/gui/attribute_tree_example_node.png"); ///TODO: Make a way to make this programmatic (Proabably extend AbstractNode to carry this var)
/////////////////////////////////////////////////////////////////////////////
/// @fn TreeWidget
///
/// @brief Class constructor for constructing a tree widget. This will
/// be deprecated in a future version but exists for testing
/////////////////////////////////////////////////////////////////////////////
public TreeWidget() {
this(0, 0, 0, 0, Text.of("Test"));
System.out.println("Calling empty tree constructor");
}
/////////////////////////////////////////////////////////////////////////////
/// @fn TreeWidget
///
/// @brief Class constructor for constructing a tree widget
/////////////////////////////////////////////////////////////////////////////
public TreeWidget(int x, int y, int width, int height, Text message) {
super(x, y, width, height, message);
this.startX = x;
this.startY = y;
this.endX = x + width;
this.endY = y + height;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn renderButton
///
/// @param[in] context is the drawing pane
///
/// @param[in] x is the X position to draw at
///
/// @param[in] y is the Y position to draw at
///
/// @param[in] delta is unused in this version
///
/// @brief Primary call to draw the GUI for this widget
/////////////////////////////////////////////////////////////////////////////
@Override
public void renderButton(DrawContext context, int x, int y, float delta) {
context.drawTexture(BACKGROUND_TEXTURE, x, y, 0, 0, maxLength, maxHeight, maxLength, maxHeight);
}
/////////////////////////////////////////////////////////////////////////////
/// @fn appendClickableNarrations
///
/// @param[in] builder is the narration builder. This is pure virtual in
/// the parent but is unused in this widget currently
///
/// @brief Handles the narrator
/////////////////////////////////////////////////////////////////////////////
@Override
protected void appendClickableNarrations(NarrationMessageBuilder builder) {
return;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn mouseClicked
///
/// @param[in] mouseX is where on the x-axis the mouse was clicked
///
/// @param[in] mouseY is where on the y-axis the mouse was clicked
///
/// @param[in] button is the button clicked with (think of a mouse...)
///
/// @brief Handler for mouse click events
/////////////////////////////////////////////////////////////////////////////
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
return true;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn mouseDragged
///
/// @param[in] mouseX is where on the x-axis the mouse was dragged
///
/// @param[in] mouseY is where on the y-axis the mouse was dragged
///
/// @param[in] button is the button dragged with (think of a mouse...)
///
/// @brief Handler for mouse drag events. delta's unused currently
/////////////////////////////////////////////////////////////////////////////
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
boolean ret = false;
if (this.isValidClickButton(button)) {
// Do camera panning magic stuff here
ret = true;
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn mouseDragged
///
/// @param[in] mouseX is where on the x-axis the mouse was dragged
///
/// @param[in] mouseY is where on the y-axis the mouse was dragged
///
/// @param[in] amount represents scroll direction. If the value is negative
/// we scale out. If positive, we scale in
///
/// @brief Handler for mouse scroll events
/////////////////////////////////////////////////////////////////////////////
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
// Zooming INWARDS on scroll wheel produces 1.0 (which means zoom in)
// Zooming BACKWARDS on scroll wheel produces -1.0 (which means zoom out)
// We enforce a max scroll of 10 in either direction here
if (amount > 0 && zoomScale <= 10) {
// Zoom in
zoomScale++;
} else if (amount < 0 && zoomScale >= -10) {
// Zoom out
zoomScale--;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn DrawLayer
///
/// @param[in] context is the drawing pane
///
/// @param[in] layer is the layer in which this widget is being drawn in
///
/// @brief This calls renderButton and gives it the valid drawing
/// context to use. This function is called by a ScreenManager<T>
/////////////////////////////////////////////////////////////////////////////
@Override
public void DrawLayer(DrawContext context, int layer) {
Position2d pos = GetScreenCenter();
this.renderButton(context, pos.x - (maxLength / 2), pos.y - (maxHeight / 2), 0);
}
}

View File

@ -0,0 +1,17 @@
package jesse;
import net.minecraft.server.MinecraftServer;
public class CommonServerUtils {
//the config is gonna hold the server instance change it if you want but dont reject my code cause its here
private static MinecraftServer server;
public void SetServerInstance(MinecraftServer inputServer) {
server = inputServer;
}
public static MinecraftServer GetServerInstance() {
return server;
}
}

View File

@ -0,0 +1,81 @@
package jesse.keeblarcraft.Armor;
import jesse.keeblarcraft.Keeblarcraft;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.ArmorMaterial;
import net.minecraft.item.Items;
import net.minecraft.recipe.Ingredient;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
public class MetalJacketArmor implements ArmorMaterial {
// All references to this class must refer to this
public static final MetalJacketArmor INSTANCE = new MetalJacketArmor();
int defaultArmorModifier = 3;
/// FUNCTIONS BELOW
public MetalJacketArmor() {
System.out.println("Constructor for armor called");
}
@Override
public int getDurability(ArmorItem.Type type) {
System.out.println("durability for armor called");
// Replace this multiplier by a constant value for the durability of the armor.
// For reference, diamond uses 33 for all armor pieces, whilst leather uses 5.
return switch (type) { // All from ArmorItem.Type
case BOOTS -> Integer.MAX_VALUE;
case LEGGINGS -> Integer.MAX_VALUE;
case CHESTPLATE -> Integer.MAX_VALUE;
case HELMET -> Integer.MAX_VALUE;
};
}
@Override
public int getProtection(ArmorItem.Type type) {
System.out.println("Protection called");
// Protection values for all the slots.
// For reference, diamond uses 3 for boots, 6 for leggings, 8 for chestplate, and 3 for helmet,
// whilst leather uses 1, 2, 3 and 1 respectively.
return switch (type) {
case BOOTS -> defaultArmorModifier;
case HELMET -> defaultArmorModifier;
case LEGGINGS -> defaultArmorModifier;
case CHESTPLATE -> defaultArmorModifier;
};
}
@Override
public int getEnchantability() {
return Integer.MAX_VALUE;
}
@Override
public SoundEvent getEquipSound() {
// Example for Iron Armor
return SoundEvents.ITEM_ARMOR_EQUIP_IRON;
}
@Override
public Ingredient getRepairIngredient() {
return Ingredient.ofItems(Items.BEDROCK); // prayfully impossible repair or just not worth it
}
@Override
public String getName() {
return Keeblarcraft.MOD_ID + ":" + "metaljacket";
}
@Override
public float getToughness() {
// Toughness is the actual "resistance" the armor provides to HIGH damage attacks
return (float) defaultArmorModifier;
}
@Override
public float getKnockbackResistance() {
return 0;
}
}

View File

@ -0,0 +1,118 @@
/*
*
* AttributeMgr
*
* Central point for the attribute skill system in the mod
*
*
*/
package jesse.keeblarcraft.AttributeMgr;
import java.util.HashMap;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.AttributeMgr.AttributeNodes.AbstractNode;
import jesse.keeblarcraft.AttributeMgr.AttributeNodes.AttributeFlight;
import jesse.keeblarcraft.AttributeMgr.AttributeNodes.AttributeMetalJacket;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.Utils.ChatUtil;
public class AttributeMgr {
ConfigManager config;
// Global list of attributes
// Key: node title
// Val: The node class itself
public static final HashMap<String, Class<? extends AbstractNode>> attributes = new HashMap<String, Class<? extends AbstractNode>>();
// This is a list of all logged in player tree's. These are kept in memory until either the mod is torn down or
// players log out for optimization reasons
public static final HashMap<String, AttributeTree> activeTrees = new HashMap<String, AttributeTree>();
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterAttributeClass
///
/// @brief All generic class objects for attributes should be
/// registered via this function
/////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("deprecation")
public static void RegisterAttributeClass(Class<? extends AbstractNode> classObj) {
AbstractNode verifyNode = null;
try {
// We spin up an instance of this node; but it will be dead after this function. We need this for name
verifyNode = classObj.newInstance();
} catch (Exception e) {
Keeblarcraft.LOGGER.error("Attempted to assign AbstractNode class type when registering object but could not call .newInstance()! Constructs must be empty");
e.printStackTrace();
}
ChatUtil.LoggerColored("Registring attribute called", ChatUtil.CONSOLE_COLOR.CYAN, Keeblarcraft.LOGGER);
try {
if (attributes.containsKey(verifyNode.GetNodeTitle())) {
Keeblarcraft.LOGGER.warn("Could not register attribute with duplicate name '" + verifyNode.GetNodeTitle() + "'");
} else {
ChatUtil.LoggerColored("REGISTERING ATTRIBUTE " + verifyNode.GetNodeTitle(), ChatUtil.CONSOLE_COLOR.YELLOW, Keeblarcraft.LOGGER);
attributes.put(verifyNode.GetNodeTitle(), classObj);
}
} catch (Exception e) {} // Left empty since previous try-catch will throw error-message
}
/////////////////////////////////////////////////////////////////////////////
/// @fn ApplyAttribute
///
/// @arg[in] uuid is the players uuid
///
/// @arg[in] attributeName is the node title of the attribute class object
///
/// @brief Used to apply an attribute to a player's attribute tree
/////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("deprecation")
public static String ApplyAttribute(String uuid, String attributeName) {
String msg = "";
if (attributes.containsKey(attributeName)) {
AttributeTree playerTree = activeTrees.get(uuid);
AbstractNode node = null;
try {
node = attributes.get(attributeName).newInstance();
} catch (Exception e) {
Keeblarcraft.LOGGER.error("Could not successfully apply attribute. String of attribute name could not be successfully cast to actual java object");
}
if (playerTree != null && node != null) {
///////////
// debug testing
String isNull = (node == null ? "NULL" : "NOT NULL");
System.out.println("Node is " + isNull);
if (isNull.equals("NOT NULL")) { System.out.println("Node name: " + node.GetNodeTitle()); }
// end debug testing
///////////
playerTree.AddNewNode(node, 1, null, null);
msg = "Applied attribute '" + attributeName + "' successfully";
} else {
msg = "Player tree not found!";
}
} else {
msg = "Could not apply attribute, attribute name does not exist!";
}
return msg;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterAttributes
///
/// @brief This registers all attribute classes to the global attribute
/// class. Individual classes can be added here that call the
/// more braod "RegisterAttributeClass"
/// @see RegisterAttributeClass
/////////////////////////////////////////////////////////////////////////////
public static void RegisterAttributes() {
// Manually register all attribute node classes here
/// TODO: Find a better way to do this more dynamically in the future
/// hint: make it an API for other modders to add to
RegisterAttributeClass(AttributeFlight.class);
RegisterAttributeClass(AttributeMetalJacket.class);
}
}

View File

@ -0,0 +1,51 @@
/*
*
* AbstractNode
*
* This is the general definition of everything that is allowed inside a node and is called by our system
*
*
*/
package jesse.keeblarcraft.AttributeMgr.AttributeNodes;
import java.util.HashMap;
import java.util.List;
abstract public class AbstractNode {
/////////////////////////////////////////////////////////////////////////////
/// @fn GetNodeTitle
///
/// @brief The title of your node/skill!
/////////////////////////////////////////////////////////////////////////////
public abstract String GetNodeTitle();
/////////////////////////////////////////////////////////////////////////////
/// @fn GetNodeDescription
///
/// @brief This will become the hover-over text display of a skill in
/// the skill tree
/////////////////////////////////////////////////////////////////////////////
public abstract String GetNodeDescription();
/////////////////////////////////////////////////////////////////////////////
/// @fn GetDetails
///
/// @brief This is the general details that may be displayed inside the
/// GUI when the skill tree becomes available to a player. The
/// object is suggested to be treated as such:
///
/// KEY (String) -> The title of an effect/attribute
/// VAL (List<String>) -> Treated as a list of description
/// attributes. 1 string per description
/////////////////////////////////////////////////////////////////////////////
public abstract HashMap<String, List<String>> GetDetails();
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterCallbacks
///
/// @brief If your node has responsive callbacks; then this is the area
/// you will register your callbacks for everything
/////////////////////////////////////////////////////////////////////////////
public abstract void RegisterCallbacks();
}

View File

@ -0,0 +1,68 @@
/*
*
* AttributeFlight
*
* The flight attribute
*
*
*/
package jesse.keeblarcraft.AttributeMgr.AttributeNodes;
import java.util.HashMap;
import java.util.List;
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
import net.minecraft.block.BlockState;
import net.minecraft.util.ActionResult;
public class AttributeFlight extends AbstractNode {
Boolean registeredHitCallback = false;
public AttributeFlight() {
}
@Override
public String GetNodeTitle() {
return "attribute_low_health_flight";
}
@Override
public String GetNodeDescription() {
return "Gives player flight with low health";
}
@Override
public HashMap<String, List<String>> GetDetails() {
HashMap<String, List<String>> ret = new HashMap<String, List<String>>();
// Filling out description item stuff here
ret.put("Flight", List.of (
"Gives a player natural flight if they have less than or equal to two hearts remaining"
));
return ret;
}
@Override
public void RegisterCallbacks() {
// Register events here
if (registeredHitCallback == false) {
AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> {
BlockState state = world.getBlockState(pos);
// Manual spectator check is necessary because AttackBlockCallbacks fire before the spectator check
if (!player.isSpectator() && player.getMainHandStack().isEmpty() && state.isToolRequired()) {
player.damage(world.getDamageSources().generic(), 1.0F);
}
return ActionResult.PASS;
});
}
registeredHitCallback = true;
}
}

View File

@ -0,0 +1,56 @@
package jesse.keeblarcraft.AttributeMgr.AttributeNodes;
import java.util.HashMap;
import java.util.List;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.Armor.MetalJacketArmor;
import jesse.keeblarcraft.CustomItems.ItemManager;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.Item;
public final class AttributeMetalJacket extends AbstractNode {
// This is the custom armor set that players will receive if no armor is on & attribute is equipped
public final Item jacketHelm = new ArmorItem(MetalJacketArmor.INSTANCE, ArmorItem.Type.HELMET, new Item.Settings());
public final Item jacketChest = new ArmorItem(MetalJacketArmor.INSTANCE, ArmorItem.Type.CHESTPLATE, new Item.Settings());
public final Item jacketLegs = new ArmorItem(MetalJacketArmor.INSTANCE, ArmorItem.Type.LEGGINGS, new Item.Settings());
public final Item jacketBoots = new ArmorItem(MetalJacketArmor.INSTANCE, ArmorItem.Type.BOOTS, new Item.Settings());
public AttributeMetalJacket() {
// Finally register items with game
Keeblarcraft.LOGGER.debug("Registering metaljackets");
ItemManager.RegisterItem("metaljacket_helmet", jacketHelm);
ItemManager.RegisterItem("metaljacket_chestplate", jacketChest);
ItemManager.RegisterItem("metaljacket_leggings", jacketLegs);
ItemManager.RegisterItem("metaljacket_boots", jacketBoots);
}
@Override
public String GetNodeTitle() {
return "MetalJacket";
}
// Short description of node on hover-event in GUI
@Override
public String GetNodeDescription() {
return "MetalJacket affects armor value modifiers or gives player base armor when none is worn";
}
// Detailed description of node on click-event in GUI
@Override
public HashMap<String, List<String>> GetDetails() {
HashMap<String, List<String>> ret = new HashMap<String, List<String>>();
// Filling out description item stuff here
ret.put("durability", List.of (
"Gives player a base armor set with 3 protection and resistance",
"If armor is being worn, this attribute multiplies each pieces armor value by 120% (rounding up to nearest integer)"
));
return ret;
}
@Override
public void RegisterCallbacks() {
}
}

View File

@ -0,0 +1,322 @@
/*
*
* AttributeTree
*
* Handles a players individual attribute tree
*
*
*/
package jesse.keeblarcraft.AttributeMgr;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.HashMap;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.AttributeMgr.AttributeNodes.AbstractNode;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import jesse.keeblarcraft.Utils.CustomExceptions.FILE_WRITE_EXCEPTION;
public class AttributeTree {
PlayerTree playerAttributeTree = new PlayerTree();
ConfigManager config = new ConfigManager();
private AbstractNode root = new RootNode();
/////////////////////////////////////////////////////////////////////////////
/// @class TreeNode
///
/// @brief This is an individual node that goes within the larger
/// PlayerTree class object
/////////////////////////////////////////////////////////////////////////////
private class TreeNode {
public TreeNode(AbstractNode node, Integer maxLevel, List<String> parents, List<String> children) {
thisNode = node;
parentNodes = parents;
childrenNodes = children;
maxNodeLevel = maxLevel;
currentNodeLevel = 1;
}
AbstractNode thisNode;
Integer currentNodeLevel;
Integer maxNodeLevel;
// Store the names of the parent and children nodes; this lets a node have any number of parents and children
// NOTE TO DEVELOPERS: Be aware! The more nodes the harrier the tree will look in the GUI!!! Always test your
// code before just adding stuff willy-nilly!
List<String> parentNodes;
List<String> childrenNodes;
}
/////////////////////////////////////////////////////////////////////////////
/// @class PlayerTree
///
/// @brief This is the tree object of a given player. It contains all
/// valid attribute information for a player that is statically
/// stored inside the AttributeMgr class
/////////////////////////////////////////////////////////////////////////////
private class PlayerTree {
String uuid;
// Key = name of AbstractNode
// Val = The attribute itself
HashMap<String, TreeNode> tree = new HashMap<String, TreeNode>();
}
/////////////////////////////////////////////////////////////////////////////
/// @class RootNode
///
/// @brief This is the default root node of a tree. It is mostly left
/// blank and used to code around as an "anchor" object
/////////////////////////////////////////////////////////////////////////////
private class RootNode extends AbstractNode {
@Override
public String GetNodeTitle() {
return "root";
}
@Override
public String GetNodeDescription() {
return "This is the players tree root! All attributes extend from here";
}
@Override
public HashMap<String, List<String>> GetDetails() {
HashMap<String, List<String>> ret = new HashMap<String, List<String>>();
ret.put("First Attribute", List.of("This is your skill tree", "All attributes grow from this root! Unlocking nodes requires all nodes connected to that previously to be unlocked first"));
return ret;
}
@Override
public void RegisterCallbacks() {
}
}
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterActiveCallbacks
///
/// @brief This will register all the active callbacks on all nodes that
/// are unlocked within a players tree instance
/////////////////////////////////////////////////////////////////////////////
public void RegisterActiveCallbacks() {
for(Entry<String, TreeNode> tree : playerAttributeTree.tree.entrySet()) {
if (tree.getValue().currentNodeLevel != 0) {
// We need to construct the node object if it's null. If it's not null; we assume
// the callback registration process has already happened
if (tree.getValue().thisNode == null) {
System.out.println("REGISTERACTIVECALLBACKS - NULL NODE");
tree.getValue().thisNode.RegisterCallbacks();
} else {
System.out.println("REGISTERACTIVECALLBACKS - NOT NULL NODE");
}
}
}
}
/////////////////////////////////////////////////////////////////////////////
/// @fn AddNewNode
///
/// @arg[in] newNode is the attribute node to be added to the player tree
///
/// @arg[in] maxNodeLevel is the max level of the node
///
/// @arg[in] parents is the list of parent attributes. If empty,
/// root is default
///
/// @arg[in] children is the list of children attributes.
/// Root cannot be child
///
/// @brief Add a new node to the tree arbitrarily. Root can never be a
/// child node parents can never be empty or null (null or empty
/// would imply you are adding root)
///
/// @note developer warning: you must personally verify your nodes are
/// in the tree. anywhere is fine, and the tree is very dynamic
/// (which is why self verification is required). When the tree
/// is drawn in the GUI, it starts from the root node. As long as
/// your node is attached to this node anywhere in the indirected
/// graph; then your attribute will be drawn in the gui
/////////////////////////////////////////////////////////////////////////////
public Boolean AddNewNode(AbstractNode newNode, Integer maxNodeLevel, List<String> parents, List<String> children) {
Boolean ret = false;
TreeNode nodeReference = playerAttributeTree.tree.get(newNode.GetNodeTitle());
System.out.println("Is node reference null? -> " + (nodeReference == null ? "YES":"NO"));
System.out.println("Is root reference null? -> " + (root == null ? "YES":"NO"));
System.out.println("Is parents null? -> " + (parents == null ? "YES":"NO"));
// Some special handling is required on checking child list in case it is null
Boolean validChildren = true;
if (children != null) {
validChildren = !children.contains(root.GetNodeTitle());
}
if (nodeReference == null && validChildren) {
// Since these values can be left as null, we want to guarentee they are at least initialized here
maxNodeLevel = (maxNodeLevel == null ? 1 : maxNodeLevel);
parents = (parents == null ? new ArrayList<String>() : parents);
children = (children == null ? new ArrayList<String>() : children);
if (parents.size() == 0) {
parents.add(root.GetNodeTitle()); // No disconnected nodes allowed! All parentless nodes are adopted by root by default
}
// Node implementation here
playerAttributeTree.tree.put(newNode.GetNodeTitle(), new TreeNode(newNode, maxNodeLevel, parents, children));
// if the new nodes level is not 0 we need to manually register the callbacks
if (playerAttributeTree.tree.get(newNode.GetNodeTitle()).currentNodeLevel != 0) {
System.out.println("Registering new callback for new node");
playerAttributeTree.tree.get(newNode.GetNodeTitle()).thisNode.RegisterCallbacks();
}
} else {
// Some fancy error handling for console log
if (nodeReference != null) {
Keeblarcraft.LOGGER.error("Attribute with name " + newNode.GetNodeTitle() + " was already found within the tree! Rename your node to add it");
} else {
Keeblarcraft.LOGGER.error("The attribute you attempted to add (name: " + newNode.GetNodeTitle() + "), has root in the children");
}
ret = false;
}
FlashConfig();
return ret;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn ChangeNodeLevel
///
/// @arg[in] nodeName is the attribute node title
///
/// @arg[in] newLevel is the new level you wish to grant the node
///
/// @brief If you wish to change the level of an attribute, you can use
/// this function to do so
/////////////////////////////////////////////////////////////////////////////
public void ChangeNodeLevel(String nodeName, Integer newLevel) {
TreeNode nodeReference = playerAttributeTree.tree.get(nodeName);
if (nodeReference != null) {
if (newLevel <= nodeReference.maxNodeLevel) {
nodeReference.currentNodeLevel = newLevel;
}
}
FlashConfig();
}
/////////////////////////////////////////////////////////////////////////////
/// @fn DeleteNode
///
/// @arg[in] nodeName is the attribute node title
///
/// @brief Delete a node via the name
/////////////////////////////////////////////////////////////////////////////
public void DeleteNode(String nodeName) {
// Do not delete root!
if (nodeName != root.GetNodeTitle()) {
playerAttributeTree.tree.remove(nodeName);
}
FlashConfig();
}
/////////////////////////////////////////////////////////////////////////////
/// @fn GetNodeDetails
///
/// @arg[in] nodeName is the attribute node title
///
/// @brief Returns the detail map from the attribute if it has any
/////////////////////////////////////////////////////////////////////////////
public HashMap<String, List<String>> GetNodeDetails(String nodeName) {
HashMap<String, List<String>> ret = null;
TreeNode nodeReference = playerAttributeTree.tree.get(nodeName);
if (nodeReference != null) {
ret = nodeReference.thisNode.GetDetails();
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn GetPlayerTree
///
/// @brief Returns the player tree. Will be deprecated in use of the
/// static active user map list instead for performance reasons
/////////////////////////////////////////////////////////////////////////////
@Deprecated
public PlayerTree GetPlayerTree() {
return playerAttributeTree;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn AttributeTree
///
/// @arg[in] uuid is the player uuid
///
/// @brief Constructor for class
/////////////////////////////////////////////////////////////////////////////
public AttributeTree(String uuid) {
// DEVELOPER NOTE:
// If you are testing this part of the code, please be reminded that anonymous testing starts a new
// player instance everytime you launch. This means the UUID CAN CHANGE when you launch the
// game! This is not an issue with proper registered accounts in production
Boolean existingFile = false;
try {
playerAttributeTree = config.GetJsonObjectFromFile("attributes/" + uuid + ".json", PlayerTree.class);
existingFile = true;
} catch (Exception e) {
// Do nothing. This means the file does not exist
}
// In the event the above code failed out, this means a new file has to be created for the player's uuid
if (!existingFile)
{
System.out.println(ChatUtil.ColoredString("Trying to create new file", CONSOLE_COLOR.BLUE));
try {
playerAttributeTree.uuid = uuid;
FlashConfig();
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Could not write to file", CONSOLE_COLOR.RED));
}
} else {
System.out.println(ChatUtil.ColoredString("Moving on", CONSOLE_COLOR.BLUE));
}
// It's possible the above code will return a blank class if a file doesn't exist. This will make
// a new file with this players uuid
if ("".equals(playerAttributeTree.uuid)) {
System.out.println(ChatUtil.ColoredString("Assigning new config file for this uuid. No previous existing", CONSOLE_COLOR.BLUE));
playerAttributeTree.uuid = uuid;
}
// If the tree is empty or missing root, add it!
if (playerAttributeTree.tree.size() == 0 || playerAttributeTree.tree.containsKey(root.GetNodeTitle()) == false) {
playerAttributeTree.tree.put(root.GetNodeTitle(), new TreeNode(root, 1, null, null));
}
RegisterActiveCallbacks();
}
/////////////////////////////////////////////////////////////////////////////
/// @fn FlashConfig
///
/// @brief Flashes the config to the disk
/////////////////////////////////////////////////////////////////////////////
public void FlashConfig() {
try {
config.WriteToJsonFile("attributes/" + playerAttributeTree.uuid + ".json", playerAttributeTree);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
}
}
}

View File

@ -0,0 +1,256 @@
package jesse.keeblarcraft.BankMgr;
import java.util.HashMap;
import java.util.Map.Entry;
import jesse.CommonServerUtils;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import jesse.keeblarcraft.Utils.CustomExceptions.FILE_WRITE_EXCEPTION;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
public class BankManager {
BankManagerFile bankInfo = new BankManagerFile(); // not sure why we make another one but i guess so we
ConfigManager config = new ConfigManager(); // for read and write privs
CommonServerUtils commonServerUtils = new CommonServerUtils();
public BankManager(String uuid) {
Boolean existingFile = false;
try {
bankInfo = config.GetJsonObjectFromFile("bank/" + uuid + ".json", BankManagerFile.class);
existingFile = true;
} catch (Exception e) {
// Do nothing. This means the file does not exist
}
// In the event the above code failed out, this means a new file has to be
// created for the player's uuid
if (!existingFile) {
System.out.println(ChatUtil.ColoredString("Trying to create new file", CONSOLE_COLOR.BLUE));
try {
FlashConfig(bankInfo.uuid);
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Could not write to file", CONSOLE_COLOR.RED));
}
} else {
System.out.println(ChatUtil.ColoredString("Moving on", CONSOLE_COLOR.BLUE));
}
if ("".equals(bankInfo.uuid)) {
System.out.println(ChatUtil.ColoredString("Assigning new config file for this uuid. No previous existing",
CONSOLE_COLOR.BLUE));
bankInfo.uuid = uuid;
}
}
//this class is the structure for the hashmap in BankManagerFile
private class BankManagerMetaData {
public BankManagerMetaData(long money, String reason, long payment, String otherParty, Integer time) {
this.balance = money;
this.reason = reason;
this.payment = payment;
this.otherParty = otherParty;
this.time = time;
}
long balance = 0;
String reason; //not sure why my compiler is saying unused
long payment;
String otherParty;
Integer time;
}
// This is the general bank account of the read-in config for the player uuid ||| the class that gets converted into a json for the players file
public class BankManagerFile {
// Players uuid is the name of the file
String uuid;
// Contents of file
/*
* Example:
* player_uuid_here:
* {
* "1":
* {
* "balance": "10";
* "reason": "tax evasion";
* "payment": $-44
* "other party": "jt";
* "time": "30";
* }
* "2":
* {
* Etc.
* }
* }
*/
public HashMap<String, BankManagerMetaData> bank = new HashMap<String, BankManagerMetaData>();
}
public long GetBalance() {
long ret = 0;
for (Entry<String, BankManagerMetaData> entry : bankInfo.bank.entrySet()) {
ret = entry.getValue().balance;
}
return ret;
}
//https://maven.fabricmc.net/docs/fabric-api-0.34.8+1.17/net/fabricmc/fabric/api/networking/v1/PlayerLookup.html maybe this for getting the players im still not sure
public void SetBalance(Integer newBalance, String reason, String otherParty) {
Integer transactionNumber = bankInfo.bank.size();
bankInfo.bank.put(transactionNumber.toString(),
new BankManagerMetaData(newBalance, reason, newBalance, otherParty, 0));
FlashConfig(PlayerListNameChecker(otherParty));
}
public void AddMoney(String reason, long payment, String otherParty) {
if (bankInfo.bank.size() > 0) {
for (Entry<String, BankManagerMetaData> entry : bankInfo.bank.entrySet()) {
entry.getValue().balance += payment;
entry.getValue().reason = "SERVER: " + reason;
entry.getValue().payment = payment;
entry.getValue().otherParty = otherParty;
entry.getValue().time = 0;
}
} else {
bankInfo.bank.put(bankInfo.uuid, new BankManagerMetaData(payment, reason, payment, otherParty, 0));
}
FlashConfig(PlayerListNameChecker(otherParty));
}
public void SubtractBalance(String reason, long payment, String otherParty) {
if (bankInfo.bank.size() > 0) {
for (Entry<String, BankManagerMetaData> entry : bankInfo.bank.entrySet()) {
entry.getValue().balance -= payment;//not working?
entry.getValue().reason = "SERVER: " + reason;
entry.getValue().payment = payment;
entry.getValue().otherParty = otherParty;
entry.getValue().time = 0;
}
} else {
bankInfo.bank.put(bankInfo.uuid, new BankManagerMetaData(0, reason, payment, otherParty, 0));
}
FlashConfig(PlayerListNameChecker(otherParty));
}
public void Wire(String reason, long payment, String otherParty) {
if (bankInfo.bank.size() > 0) {
for (Entry<String, BankManagerMetaData> entry : bankInfo.bank.entrySet()) {
entry.getValue().balance -= payment;
entry.getValue().reason = reason;
entry.getValue().payment = payment;
entry.getValue().otherParty = otherParty;
entry.getValue().time = 0;
if (payment <= 0) {
// add a error for the PLAYER not the server
return;
}
}
// make a server instance
MinecraftServer server = CommonServerUtils.GetServerInstance();
String[] playerList = server.getPlayerNames();
//NOT SURE IF THIS FOR LOOP IS ONE OFF COULD POSSIBLEY BE SO IF NO ONE IS GETTING MONEY DOUBLE CHECK FOR LOOP
for (int i = 0; i < playerList.length; i++) {
System.out.println(ChatUtil.ColoredString("PLAYERS: " + playerList, CONSOLE_COLOR.YELLOW));
if (playerList[i] == otherParty) {
System.out.println(ChatUtil.ColoredString("Found Player: " + otherParty, CONSOLE_COLOR.GREEN));
// we will use getuuidbyname then set the other VALID players bank BE SURE TO
// MAKE THEM A BANK FIRST IF THEY DONT HAVE ONE fortnite forever
if (config.GetFile("bank/" + GetUuidByName(server, otherParty) + ".json").size() < 1) {
BankManagerFile newBankInfo = new BankManagerFile();
BankManagerMetaData newBank = new BankManagerMetaData(0, "Account Created", 0, "Server", 0);
newBankInfo.bank.put(GetUuidByName(server, otherParty).toString(), newBank);
try {
config.WriteToJsonFile("bank/" + newBankInfo.uuid + ".json", newBankInfo);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
return;
}
}
//since the other player has a account now and we have access to it we can start fucking around
BankManagerFile newBankInfo = new BankManagerFile();
newBankInfo = config.GetJsonObjectFromFile("bank/" + GetUuidByName(server, otherParty) + ".json", BankManagerFile.class);
// for now we will only use adding valance and not subtracting since that dosent make any sense
for (Entry<String, BankManagerMetaData> entry : newBankInfo.bank.entrySet()) {
entry.getValue().balance += payment;
entry.getValue().reason = reason;
entry.getValue().payment = payment;
entry.getValue().otherParty = otherParty;
entry.getValue().time = 0;
}
//cannot use regular flash config since the hard coded values need to add agurment for the uuid
//needs to be inside the player check
FlashConfig(newBankInfo.uuid);
try {
config.WriteToJsonFile("bank/" + newBankInfo.uuid + ".json", bankInfo);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
}
} else {
System.out.println(ChatUtil.ColoredString("Player Not Found: " + otherParty, CONSOLE_COLOR.RED));
return;
}
}
} else {
System.out.println(ChatUtil.ColoredString("You need to finance better", CONSOLE_COLOR.RED));
return;
}
}
String GetUuidByName(MinecraftServer server, String playerName) {
PlayerManager playerManager = server.getPlayerManager();
ServerPlayerEntity player = playerManager.getPlayer(playerName);
if (player.getUuid() != null) {
return player.getUuidAsString();
} else {
return "";
}
}
String PlayerListNameChecker(String otherParty) {
MinecraftServer server = CommonServerUtils.GetServerInstance();
String[] playerList = server.getPlayerNames();
for (int i = 0; i < playerList.length; i++) {
System.out.println(ChatUtil.ColoredString("PLAYERS: " + playerList, CONSOLE_COLOR.YELLOW));
if (playerList[i] == otherParty) {
System.out.println(ChatUtil.ColoredString("Found Player: " + otherParty, CONSOLE_COLOR.GREEN));
// we will use getuuidbyname then set the other VALID players bank BE SURE TO
// MAKE THEM A BANK FIRST IF THEY DONT HAVE ONE fortnite forever
if (config.GetFile("bank/" + GetUuidByName(server, otherParty) + ".json").size() < 1) {
BankManagerFile newBankInfo = new BankManagerFile();
BankManagerMetaData newBank = new BankManagerMetaData(0, "Account Created", 0, "Server", 0);
newBankInfo.bank.put(GetUuidByName(server, otherParty).toString(), newBank);
try {
config.WriteToJsonFile("bank/" + newBankInfo.uuid + ".json", newBankInfo);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
return "";
}
}
else {
System.out.println(ChatUtil.ColoredString("Bank Account Found", CONSOLE_COLOR.GREEN));
}
return GetUuidByName(server, otherParty);
}
}
System.out.println(ChatUtil.ColoredString("For Loop condition bypassed Null Playerlist or Null Server instance", CONSOLE_COLOR.RED));
return "";
}
public void FlashConfig(String uuid) {
try {
config.WriteToJsonFile("bank/" + uuid + ".json", bankInfo);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
}
}
}

View File

@ -0,0 +1,5 @@
package jesse.keeblarcraft.CCTweakedExtension;
public class CCTweakedTest {
}

View File

@ -0,0 +1,113 @@
/*
*
* AttributeCommands
*
* This class handles all the possible in-game commands for the attribute system
*
*
*/
package jesse.keeblarcraft.Commands;
import java.util.Map.Entry;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.AttributeMgr.AttributeMgr;
import jesse.keeblarcraft.AttributeMgr.AttributeNodes.AbstractNode;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
public class AttributeCommands {
public void RegisterCommands() {
// Command Root: "/attributes"
// Subcommands: "apply <name> <attributename>", "remove <name> <attributename>"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
var attributeNode = CommandManager.literal("attributes").build();
var applyNode = CommandManager.literal("apply").build();
var removeNode = CommandManager.literal("delete").build();
var listNode = CommandManager.literal("list").executes(context -> ListAttributes(context)).build();
var playerArgAdd = CommandManager.argument("targetPlayer", EntityArgumentType.player()).build();
var playerArgRemove = CommandManager.argument("targetPlayer", EntityArgumentType.player()).build();
var attributeNameAdd = CommandManager.argument("attributeName", StringArgumentType.greedyString())
.executes(context -> ApplyAttribute
(
EntityArgumentType.getPlayer(context, "targetPlayer"),
StringArgumentType.getString(context, "attributeName"),
context)
)
.build();
var attributeNameDelete = CommandManager.argument("attributeName", StringArgumentType.greedyString())
.executes(context -> DeleteAttribute
(
EntityArgumentType.getPlayer(context, "targetPlayer"),
StringArgumentType.getString(context, "attributeName"),
context)
)
.build();
// Build out the argument tree here
dispatcher.getRoot().addChild(attributeNode);
attributeNode.addChild(applyNode);
attributeNode.addChild(listNode);
attributeNode.addChild(removeNode);
// Subcommands "/apply playerArg", "/remove playerArg"
applyNode.addChild(playerArgAdd);
removeNode.addChild(playerArgRemove);
// name argument
playerArgAdd.addChild(attributeNameAdd);
playerArgRemove.addChild(attributeNameDelete);
});
}
public int ApplyAttribute(ServerPlayerEntity targetPlayer, String attributeName, CommandContext<ServerCommandSource> context) {
int ret = -1;
System.out.println("Applying attribute");
if (context.getSource().isExecutedByPlayer()) {
System.out.println("Executed by player");
String result = AttributeMgr.ApplyAttribute(targetPlayer.getUuidAsString(), attributeName);
Keeblarcraft.LOGGER.info("[ApplyAttribute] -> " + result);
context.getSource().getPlayer().sendMessage(Text.of(result));
ret = 0;
}
return ret;
}
public int DeleteAttribute(ServerPlayerEntity username, String attributeName, CommandContext<ServerCommandSource> context) {
int ret = -1;
return ret;
}
public int ListAttributes(CommandContext<ServerCommandSource> context) {
int ret = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
for (Entry<String, Class<? extends AbstractNode>> entry : AttributeMgr.attributes.entrySet()) {
Keeblarcraft.LOGGER.debug("ATTR-LIST: " + entry.getKey() + " LINKS " + entry.getValue());
player.sendMessage(Text.of(entry.getKey()));
}
}
return ret;
}
}

View File

@ -0,0 +1,155 @@
package jesse.keeblarcraft.Commands;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.LongArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import jesse.keeblarcraft.BankMgr.BankManager;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
public class BankCommands {
ConfigManager config = new ConfigManager();
public void RegisterCommands(){
// Command: "/getbalance"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("getbalance")
.executes(context -> GetBalance(context)));
});
// Command: "/setbalance || rootSetBalance"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final var rootSetBalance = dispatcher.register(CommandManager.literal("setbalance")
.then(CommandManager.argument("newBalance", IntegerArgumentType.integer())
.then(CommandManager.argument("reason", StringArgumentType.string())
.then(CommandManager.argument("otherParty", StringArgumentType.string())
.executes(context -> SetBalance(
IntegerArgumentType.getInteger(context, "newBalance"),
StringArgumentType.getString(context, "reason"),
StringArgumentType.getString(context, "otherParty"),
context))))));
dispatcher.register(CommandManager.literal("setbalance").redirect(rootSetBalance));
});
// Command: "/AddMoney"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("AddMoney")
.then(CommandManager.argument("reason", StringArgumentType.string())
.then(CommandManager.argument("payment", LongArgumentType.longArg())
.then(CommandManager.argument("otherParty", StringArgumentType.string())
.executes(context -> AddMoney(
StringArgumentType.getString(context, "reason"),
LongArgumentType.getLong(context, "payment"),
StringArgumentType.getString(context, "otherParty"),
context))))));
});
// Command: "/subtractbalance || /SubtractBalance"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final var SubtractBalance = dispatcher.register(CommandManager.literal("subtractbalance")
.then(CommandManager.argument("reason", StringArgumentType.string())
.then(CommandManager.argument("payment", LongArgumentType.longArg())
.then(CommandManager.argument("otherParty", StringArgumentType.string())
.executes(context -> SubtractBalance(
StringArgumentType.getString(context, "reason"),
LongArgumentType.getLong(context, "payment"),
StringArgumentType.getString(context, "otherParty"),
context))))));
dispatcher.register(CommandManager.literal("subtractbalance").redirect(SubtractBalance));
});
// Command: "/wire || /sendmoney"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final var sendmoney = dispatcher.register(CommandManager.literal("wire")
.then(CommandManager.argument("reason", StringArgumentType.string())
.then(CommandManager.argument("payment", LongArgumentType.longArg())
.then(CommandManager.argument("otherParty", StringArgumentType.string())
.executes(context -> Wire(
StringArgumentType.getString(context, "reason"),
LongArgumentType.getLong(context, "payment"),
StringArgumentType.getString(context, "otherParty"),
context))))));
dispatcher.register(CommandManager.literal("wire").redirect(sendmoney));
});
}
public Integer GetBalance(CommandContext<ServerCommandSource> context) {
Integer ret = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
BankManager playerBank = new BankManager(player.getUuidAsString());
player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance())));
return 0;
}
return ret;
}
//SetBalance will be a ServerCommand only Perm level Op
public Integer SetBalance(Integer newBalance, String reason, String otherParty, CommandContext<ServerCommandSource> context) {
Integer ret = -1;
if (context.getSource().hasPermissionLevel(1)) {
ServerPlayerEntity player = context.getSource().getPlayer();
BankManager playerBank = new BankManager(player.getUuidAsString());
playerBank.SetBalance(newBalance, reason, otherParty);
player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance())));
}
return ret;
}
//AddMoney will be a ServerCommand Only Perm level Op
public Integer AddMoney(String reason, long payment, String otherParty, CommandContext<ServerCommandSource> context) {
Integer ret = -1;
if (context.getSource().hasPermissionLevel(1)) {
ret = 0;
ServerPlayerEntity player = context.getSource().getPlayer();
BankManager playerBank = new BankManager(player.getUuidAsString());
playerBank.AddMoney(reason, payment, otherParty);
player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance())));
}
return ret;
}
//SubtractBalance will be a ServerCommand Perm leve Op
public Integer SubtractBalance(String reason, long payment, String otherParty, CommandContext<ServerCommandSource> context) {
Integer ret = -1;
if (context.getSource().hasPermissionLevel(1)) {
ret = 0;
ServerPlayerEntity player = context.getSource().getPlayer();
BankManager playerBank = new BankManager(player.getUuidAsString());
playerBank.SubtractBalance(reason, payment, otherParty);
player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance())));
}
return ret;
}
public Integer Wire(String reason, long payment, String otherParty, CommandContext<ServerCommandSource> context) {
Integer ret = -1;
if (context.getSource().isExecutedByPlayer()) {
ret = 0;
ServerPlayerEntity player = context.getSource().getPlayer();
BankManager playerBank = new BankManager(player.getUuidAsString());
playerBank.Wire(reason, payment, otherParty);
player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance())));
}
return ret;
}
}

View File

@ -0,0 +1,34 @@
/*
*
* CommandManager
*
* This class maintains all custom commands and executions throughout the mod
*
*/
package jesse.keeblarcraft.Commands;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
public class CustomCommandManager {
// Intentionally empty constructor since at object definition time it may not be possible to register commands
public CustomCommandManager() {}
// Registers all custom command classes here
public void RegisterCustomCommands() {
// CUSTOM COMMAND CLASS OBJECTS BELOW
ShortcutCommands shortcuts = new ShortcutCommands();
NoteCommands noteCommands = new NoteCommands();
BankCommands bankCommands = new BankCommands();
AttributeCommands attributeCommands = new AttributeCommands();
// REGISTER COMMANDS BELOW
System.out.println(ChatUtil.ColoredString("REGISTERING CUSTOM COMMAND EXTENSIONS BELOW", CONSOLE_COLOR.BLUE));
shortcuts.RegisterShortcutCommands();
noteCommands.RegisterNoteCommands();
bankCommands.RegisterCommands();
attributeCommands.RegisterCommands();
}
}

View File

@ -0,0 +1,132 @@
package jesse.keeblarcraft.Commands;
public class FactionCommands {
// Register function for commands
public void RegisterFactionCommands() {
}
/// PRIVATE HANDLERS BELOW
private int CreateFaction() {
int retValue = -1;
return retValue;
}
private int DeleteFaction() {
int retValue = -1;
return retValue;
}
private int AddPlayerToFaction() {
int retValue = -1;
return retValue;
}
private int KickPlayerFromFaction() {
int retValue = -1;
return retValue;
}
private int PromotePlayerInFaction() {
int retValue = -1;
return retValue;
}
private int DemotePlayerInFaction() {
int retValue = -1;
return retValue;
}
private int SetPlayerRoleInFaction() {
int retValue = -1;
return retValue;
}
private int DeclareFactionBase() {
int retValue = -1;
return retValue;
}
// admin only
private int SetFactionPower() {
int retValue = -1;
return retValue;
}
private int DeclareFactionEvent() {
int retValue = -1;
return retValue;
}
private int DeclareEnemyFaction() {
int retValue = -1;
return retValue;
}
private int DeclareAlliedFaction() {
int retValue = -1;
return retValue;
}
private int DeclareNeutralFaction() {
int retValue = -1;
return retValue;
}
private int ListAllFactions() {
int retValue = -1;
return retValue;
}
private int RegisterFactionChatChannel() {
int retValue = -1;
return retValue;
}
// admin only
private int DeleteFactionChatChannel() {
int retValue = -1;
return retValue;
}
private int DepositToFactionBank() {
int retValue = -1;
return retValue;
}
private int WithdrawFromFactionBank() {
int retValue = -1;
return retValue;
}
private int FactionBankBalance() {
int retValue = -1;
return retValue;
}
private int ListFactionBankTransactions() {
int retValue = -1;
return retValue;
}
}

View File

@ -0,0 +1,301 @@
package jesse.keeblarcraft.Commands;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.JsonClassObjects.PlayerNote;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import jesse.keeblarcraft.Utils.CustomExceptions.DIRECTORY_CREATE_EXCEPTION;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
public class NoteCommands {
/// Class Variables
ConfigManager notesConfig = new ConfigManager();
String NOTES_GLOBAL_DIRECTORY = "notes"; // The overall "notes" dir inside cfg folder
/////////////////////////////////////////////////////////////////////////////
/// @fn NoteCommands
///
/// @brief This classes non-trivial constructor. Ensures creation
// of notes directory exists before commands can be ran
/////////////////////////////////////////////////////////////////////////////
public NoteCommands() {
// Check if directory exists
if (notesConfig.DoesDirectoryExist(NOTES_GLOBAL_DIRECTORY) == false) {
// Attempt to create the directory
try {
if (notesConfig.CreateDirectory(NOTES_GLOBAL_DIRECTORY) == true) {
System.out.println(ChatUtil.ColoredString("Created notes directory successfully!", CONSOLE_COLOR.BLUE)); //TODO: Success!
} else {
System.out.println(ChatUtil.ColoredString("ERROR: Notes directory FAILED to create!! Either the directory already exists or we are missing permissions!", CONSOLE_COLOR.RED)); //TODO: Critical failure --not specfic enough to mark it as a red or blue
}
} catch (DIRECTORY_CREATE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Directory creation failed", CONSOLE_COLOR.RED));
}
} else {
System.out.println(ChatUtil.ColoredString("Notes directory already exists. Skipping creation...", CONSOLE_COLOR.BLUE)); //TODO: Success!
}
}
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterNoteCommands
///
/// @brief Registers all the commands for this class
/////////////////////////////////////////////////////////////////////////////
public void RegisterNoteCommands() {
// Command: "/addnote note goes here"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("addnote")
.then(CommandManager.argument("value", StringArgumentType.greedyString())
.executes(context -> AddNote(StringArgumentType.getString(context, "value"), context))));
});
// Command: "/delnote noteIdHere"
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// dispatcher.register(CommandManager.literal("delnote")
// .then(CommandManager.argument("value", StringArgumentType.greedyString())
// .executes(context -> AddNote(StringArgumentType.getString(context, "value"), context))));
// });
// Command: "/purgenotes"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("purgenotes")
.executes(context -> PurgeAllNotes(context)));
});
// Command: "/modifynote noteIdHere new_note_string_here"
// Alises: "/editnote"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final var mNote = dispatcher.register(CommandManager.literal("editnote")
.then(CommandManager.argument("note_id", IntegerArgumentType.integer())
.then(CommandManager.argument("new_note", StringArgumentType.string())
.executes(context -> ModifyNote(
IntegerArgumentType.getInteger(context, "note_id"),
StringArgumentType.getString(context, "new_note"),
context)))));
dispatcher.register(CommandManager.literal("editnote").redirect(mNote));
});
// Command Root: "/delnote noteIdHere"
// Aliases: "/rmnote", "/deletenote"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final var rootDeleteCmd = dispatcher.register(CommandManager.literal("delnote")
.then(CommandManager.argument("value", IntegerArgumentType.integer())
.executes(context -> DeleteNote(IntegerArgumentType.getInteger(context, "value"), context))));
// Alias redirects
dispatcher.register(CommandManager.literal("rmnote").redirect(rootDeleteCmd));
dispatcher.register(CommandManager.literal("deletenote").redirect(rootDeleteCmd));
});
// Command Root: "/notegui"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("notegui")
.executes(context -> { OpenNoteGui(context);
return 0;
}));
});
// Command Root: "/notelist"
// Aliases: "/listnotes"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final var rootListNotes = dispatcher.register(CommandManager.literal("notelist")
.executes(context -> { ListNotes(context);
return 0;
}));
dispatcher.register(CommandManager.literal("listnotes").redirect(rootListNotes));
});
}
/////////////////////////////////////////////////////////////////////////////
/// @fn AddNote
///
/// @brief Adds a new note to the players notebook
///
/// @arg[in] value is the new note to be added
///
/// @arg[in] context is the context of the ServerCommandSource object
/// the command was run with
///
/// @return 0 if success, -1 if not
/////////////////////////////////////////////////////////////////////////////
private Integer AddNote(String value, CommandContext<ServerCommandSource> context) {
Integer ret = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
// Note mgmt
PlayerNote note = new PlayerNote(player.getUuidAsString());
note.AddNote(value, 1, 1, 1, 1);
ChatUtil.SendPlayerMsg(player, "New note logged to entry! View notes any time with /notegui");
ret = 0;
} else {
System.out.println(ChatUtil.ColoredString("Only a player can execute this command!", CONSOLE_COLOR.RED));
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn DeleteNote
///
/// @brief Deletes a note by id
///
/// @arg[in] value is the integer ID of the note to be deleted
///
/// @arg[in] context is the context of the ServerCommandSource object
/// the command was run with
///
/// @return 0 if success, -1 if not
/////////////////////////////////////////////////////////////////////////////
private int DeleteNote(int value, CommandContext<ServerCommandSource> context) {
int ret = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
PlayerNote note = new PlayerNote(player.getUuidAsString());
ChatUtil.SendPlayerMsg(player, "Deleted note entry. View notes any time with /notegui");
ret = 0;
note.DeleteNote(value);
} else {
System.out.println("Only a player can execute this command!");
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn ModifyNote
///
/// @brief Modifies a single note by id value
///
/// @arg[in] value is the integer ID of the note to be modified
///
/// @arg[in] newNote is the new version of the edited note
///
/// @arg[in] context is the context of the ServerCommandSource object
/// the command was run with
///
/// @return 0 if success, -1 if not
/////////////////////////////////////////////////////////////////////////////
private int ModifyNote(Integer value, String newNote, CommandContext<ServerCommandSource> context) {
int ret = -1;
if (context.getSource().isExecutedByPlayer() && value > 0) {
ServerPlayerEntity player = context.getSource().getPlayer();
PlayerNote note = new PlayerNote(player.getUuidAsString());
long time = context.getSource().getWorld().getTime();
// long day = ; ///TODO: Docs lack this for some reason? Add in future
long epochTime = System.currentTimeMillis();
long storyChapter = -1; // Intentional garbage until story is fleshed out later (TODO)
long storyPart = -1; // Intentional garbage until story is fleshed out later (TODO)
note.ModifyNote(value, newNote, epochTime, storyChapter, storyPart);
ChatUtil.SendPlayerMsg(player, "Modified note entry. View notes any time with /notegui");
ret = 0;
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn PurgeAllNotes
///
/// @brief Removes all notes from a players note file
///
/// @arg[in] context is the context of the ServerCommandSource object
/// the command was run with
///
/// @return 0 if success, -1 if not
/////////////////////////////////////////////////////////////////////////////
private int PurgeAllNotes(CommandContext<ServerCommandSource> context) {
int ret = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
PlayerNote note = new PlayerNote(player.getUuidAsString());
note.PurgeAllNotes();
ChatUtil.SendPlayerMsg(player, "Purged all notes. View notes any time with /notegui");
ret = 0;
} else {
System.out.println("Only a player can execute this command!");
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn ListNotes
///
/// @brief Lists notes in pages in the players active chat
///
/// @arg[in] context is the context of the ServerCommandSource object
/// the command was run with
///
/// @return 0 if success, -1 if not
/////////////////////////////////////////////////////////////////////////////
private int ListNotes(CommandContext<ServerCommandSource> context) {
int ret = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
PlayerNote notes = new PlayerNote(player.getUuidAsString());
ChatUtil.SendPlayerMsg(player, "Listing all notes...");
for (int i = 0; i <= notes.GetNotebookSize(); i++) {
String individualNote = notes.GetNoteString(i);
if (individualNote != "") {
ChatUtil.SendPlayerMsg(player, "Note " + i + ": " + individualNote);
}
}
ret = 0;
} else {
System.out.println("Only a player can execute this command!");
}
return ret;
}
///TODO: Blocked until GUI manager is available
/////////////////////////////////////////////////////////////////////////////
/// @fn OpenNoteGui
///
/// @brief Opens up the graphical display of the note manager
///
/// @arg[in] context is the context of the ServerCommandSource object
/// the command was run with
///
/// @return 0 if success, -1 if not
/////////////////////////////////////////////////////////////////////////////
private int OpenNoteGui(CommandContext<ServerCommandSource> context) {
int ret = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
ret = 0;
} else {
System.out.println("Only a player can execute this command!");
}
return ret;
}
}

View File

@ -0,0 +1,259 @@
/*
*
* ShortcutCommands
*
* A class that simplifies some of the current vanilla commands with shortcut commands in the game. You may remember an old plugin
* called "Essentials" (Or EssentialsEx in a later version) -> This file is re-creating a portion of that plugin but in mod-format
*
*
*/
package jesse.keeblarcraft.Commands;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.context.CommandContext;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.entity.player.PlayerAbilities;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.command.TimeCommand;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
public class ShortcutCommands {
float DEFAULT_FLIGHT_SPEED = 0.05f; // Minecraft operates on a 0.0 -> 1.0 scale; with each .01 making a difference in speed. 0.05 is creative flight speed
float SPEED_SCALAR = 20.0f; // For clamping speed down to half of its max output (So 0.5 will be max speed on a system that goes up in 0.05'ths)
public void RegisterShortcutCommands()
{
// Abstract handling "value" to parse gamemodes 0-2 (survival; creative; spectator)
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("gm")
.then(CommandManager.argument("value", IntegerArgumentType.integer())
.executes(context -> GamemodeShortcut(IntegerArgumentType.getInteger(context, "value"), context))));
});
// Fly command
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("fly")
.executes(context -> { FlightShortcut(context);
return 0;
}));
});
// Fly command with speed
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("fly")
.then(CommandManager.argument("value", IntegerArgumentType.integer())
.executes(context -> FlightSpeedShortcut(IntegerArgumentType.getInteger(context, "value"), context))));
});
///TODO: Read TODO on function
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// dispatcher.register(CommandManager.literal("walk")
// .then(CommandManager.argument("value", IntegerArgumentType.integer())
// .executes(context -> WalkSpeedShortcut(IntegerArgumentType.getInteger(context, "value"), context))));
// });
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("heal")
.executes(context -> { HealShortcut(context);
return 0;
}));
});
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("day")
.executes(context -> { DayShortcut(context);
return 0;
}));
});
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("night")
.executes(context -> { NightShortcut(context);
return 0;
}));
});
}
/// Command handlers below
private int GamemodeShortcut(int value, CommandContext<ServerCommandSource> context)
{
int retValue = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
/// todo: change 4 to be OPERATOR level. 4 is DEFAULT operator level in Minecraft
if(player.hasPermissionLevel(4)) {
switch(value) {
case 0:
player.changeGameMode(net.minecraft.world.GameMode.SURVIVAL);
player.sendMessage(Text.literal("Changing your game mode to SURVIVAL"));
retValue = 0;
break;
case 1:
player.changeGameMode(net.minecraft.world.GameMode.CREATIVE);
player.sendMessage(Text.literal("Changing your game mode to CREATIVE"));
retValue = 0;
break;
case 2:
player.changeGameMode(net.minecraft.world.GameMode.SPECTATOR);
player.sendMessage(Text.literal("Changing your game mode to SPECTATOR"));
retValue = 0;
break;
default:
player.sendMessage(Text.literal("Valid entries are 0=SURVIVAL, 1=CREATIVE, 2=SPECTATOR"));
retValue = -1;
break;
}
}
else {
player.sendMessage(Text.literal("\033[31m You do not have permissions to run this command! \033[0m"));
}
}
else {
System.out.println(ChatUtil.ColoredString("This command cannot be executed by a non-player entity!", CONSOLE_COLOR.RED));
}
return retValue;
}
private int FlightShortcut(CommandContext<ServerCommandSource> context) {
int retValue = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
if (player.hasPermissionLevel(4)) {
PlayerAbilities abilities = player.getAbilities();
abilities.flying = !abilities.flying; // Toggle flying
abilities.setFlySpeed(DEFAULT_FLIGHT_SPEED); // It seems flight speed is on a 0-1 scale
player.sendAbilitiesUpdate();
ChatUtil.SendPlayerMsg(player, "You can now fly! Flight speed is " + abilities.getFlySpeed());
} else {
player.sendMessage(Text.literal("You do not have permission for this command"));
}
}
return retValue;
}
private int FlightSpeedShortcut(int value, CommandContext<ServerCommandSource> context) {
int retValue = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
if (player.hasPermissionLevel(4)) {
PlayerAbilities abilities = player.getAbilities();
if (value >= 1 && value <= 10) {
abilities.allowFlying = true;
abilities.setFlySpeed((float) (value / SPEED_SCALAR)); // Dividing by 20f yields max clamp value of 0.5 since MC does 0.0-> 1.0 flight. 0.1 flight is too fast!
player.sendAbilitiesUpdate();
ChatUtil.SendPlayerMsg(player, "Flight speed set to " + (value));
} else {
player.sendMessage(Text.literal("Only values from 1-10 are accepted"));
}
} else {
player.sendMessage(Text.literal("You do not have permission for this command"));
}
}
return retValue;
}
///TODO: There is a bug with walk speed that causes the players speed to behave weirdly despite the value. It's either a crawl or mach 10
///TODO: It's possible that the player may need to die first to reset speed; and reloading in the debugger does not count as resetting values. This
///TODO: code may actually work; but I just did not die first in between testing speed changes.
// private int WalkSpeedShortcut(int value, CommandContext<ServerCommandSource> context) {
// int retValue = -1;
// if (context.getSource().isExecutedByPlayer()) {
// ServerPlayerEntity player = context.getSource().getPlayer();
// if (player.hasPermissionLevel(4)) {
// PlayerAbilities abilities = player.getAbilities();
// if (value >= 1 && value <= 10) {
// abilities.setWalkSpeed((float) 1.0f);
// player.sendAbilitiesUpdate();
// ChatUtil.SendPlayerMsg(player, "Set walk speed to " + (value));
// } else {
// player.sendMessage(Text.literal("Only values from 1-10 are accepted"));
// }
// } else {
// player.sendMessage(Text.literal("You do not have permission for this command"));
// }
// }
// return retValue;
// }
// TODO: Add when we can find where in the API to fill players hunger level
// private int FeedShortcut(CommandContext<ServerCommandSource> context) {
// int retValue = -1;
// if (context.getSource().isExecutedByPlayer()) {
// ServerPlayerEntity player = context.getSource().getPlayer();
// if (player.hasPermissionLevel(4)) {
// } else {
// player.sendMessage(Text.literal("You do not have permission for this command"));
// }
// }
// return retValue;
// }
private int HealShortcut(CommandContext<ServerCommandSource> context) {
int retValue = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
if (player.hasPermissionLevel(4)) {
player.setHealth(player.getMaxHealth());
ChatUtil.SendPlayerMsg(player, "Healed!");
} else {
player.sendMessage(Text.literal("You do not have permission for this command"));
}
}
return retValue;
}
private int DayShortcut(CommandContext<ServerCommandSource> context) {
int retValue = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
if (player.hasPermissionLevel(4)) {
retValue=0;
TimeCommand.executeSet(context.getSource(), retValue);
} else {
player.sendMessage(Text.literal("You do not have permission for this command"));
}
}
return retValue;
}
private int NightShortcut(CommandContext<ServerCommandSource> context) {
int retValue = -1;
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
if (player.hasPermissionLevel(4)) {
retValue=13000;
TimeCommand.executeSet(context.getSource(), retValue);
} else {
player.sendMessage(Text.literal("You do not have permission for this command"));
}
}
return retValue;
}
}

View File

@ -0,0 +1,239 @@
/*
*
* ConfigManager
*
* This class is the central configuration file manager for all other classes and acts as a utility class for other classes to use.
* It is typical to define this class as a general object (not static instance) for each class in single-threaded actions. If you need
* a special function or action from this class; it is recommended to add it to this class and not make your own.
*
*
*/
package jesse.keeblarcraft.ConfigMgr;
import java.io.FileWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import java.util.List;
import org.apache.commons.io.FileUtils;
import java.util.ArrayList;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import jesse.keeblarcraft.Utils.CustomExceptions.*;
public class ConfigManager {
// Pedantic empty constructor
public ConfigManager() {}
// CreateFile
//
// Returns true if file is created, will return false if file cannot be created (returns true if already exists)
public Boolean CreateFile(String fileName) throws FILE_CREATE_EXCEPTION {
Boolean ret = false;
File file = new File(fileName);
// Check 1: Does the file already exist?
ret = file.exists();
System.out.println(ChatUtil.ColoredString("Does file exist? ", CONSOLE_COLOR.BLUE) + (ret ? ChatUtil.ColoredString("YES", CONSOLE_COLOR.YELLOW) : ChatUtil.ColoredString("NO", CONSOLE_COLOR.YELLOW)));
// Check 2: If the file does not exist, attempt to create it
if (ret == false) {
try {
ret = file.createNewFile();
} catch (IOException e) {
// The file could not be created
throw new FILE_CREATE_EXCEPTION();
}
} else {
ret = true; // This might be a hot fix, but technically the file already exists so would this be true or false? --?this statement is wild?
System.out.println(ChatUtil.ColoredString("File (name: ", CONSOLE_COLOR.BLUE) + ChatUtil.ColoredString(fileName, CONSOLE_COLOR.YELLOW) + ChatUtil.ColoredString(" was determined to already exist. Exiting out", CONSOLE_COLOR.BLUE));
}
return ret;
}
// DeleteFile
//
// Returns true if file is deleted, false if could not be deleted or does not exist
public Boolean DeleteFile(String fileName) throws FILE_DELETE_EXCEPTION {
Boolean ret = false;
File file = new File(fileName);
// Step 1: Does file exist?
ret = file.exists();
// Step 2: If file exists, attempt to delete
if (ret == true) {
try {
ret = file.delete();
} catch (SecurityException e) {
throw new FILE_DELETE_EXCEPTION();
}
} else {
System.out.println(ChatUtil.ColoredString("cannot delete file ", CONSOLE_COLOR.RED) + ChatUtil.ColoredString(fileName, CONSOLE_COLOR.YELLOW) + ChatUtil.ColoredString(" because file does not exist", CONSOLE_COLOR.BLUE));
}
return ret;
}
// WriteToFile
//
// Will write or append to file (valid modes: "w" or "a") if file is available. Returns false if not
public Boolean WriteToFile(String fileName, String data, String mode) {
Boolean ret = false;
FileWriter file;
try {
file = new FileWriter(fileName);
switch(mode) {
case "w":
file.write(data);
ret = true;
break;
case "a":
file.append(data);
ret = true;
break;
default:
System.out.println(ChatUtil.ColoredString("Invalid mode to WriteToFile!", CONSOLE_COLOR.RED));
break;
}
file.close();
} catch (IOException e) {
System.out.println(ChatUtil.ColoredString("Could not open file ", CONSOLE_COLOR.RED) + ChatUtil.ColoredString(fileName, CONSOLE_COLOR.YELLOW) + ChatUtil.ColoredString(" to write to it! Possibly permission issue?", CONSOLE_COLOR.RED));
}
return ret;
}
// WriteToJsonFile
//
// Will write to or append to a json file. It will search if the key exists first & update the field;
// or add a new entry. It should be noted that json objects *can* be buried inside each other. It is
// considered best (and only) practice to call the "GetJsonStringFromFile" function first from this
// class and simply iterate to what you would need and then update the entire entry alongside the
// top-level key.
//
// NOTE: THIS DOES NOT SAFE UPDATE THE KEY OBJECT. PRE-EXISTING DATA WILL BE DELETED FOREVER
public void WriteToJsonFile(String fileName, Object data) throws FILE_WRITE_EXCEPTION {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
try {
FileWriter writer = new FileWriter(fileName);
gson.toJson(data, writer);
writer.flush();
writer.close();
} catch (JsonIOException | IOException e) {
System.out.println(ChatUtil.ColoredString("Could not successfully write to json file", CONSOLE_COLOR.RED));
throw new FILE_WRITE_EXCEPTION();
}
}
// GetJsonStringFromFile
//
// Retrieves json file and converts to desired object. Returns JsonSyntaxException if file could not be fitted to class input
public <T> T GetJsonObjectFromFile(String fileName, Class<T> classToConvertTo) throws JsonSyntaxException {
Gson gson = new Gson();
String ret = "";
// hot fix: Not sure how to return "false" for invalid conversion when I'm forced to convert or just catch... Look into a better
// return value in the future - but for now throw JsonSyntaxException no matter what exception is caught
try {
File file = new File(fileName);
ret = FileUtils.readFileToString(file, "UTF-8");
} catch (NullPointerException e) {
System.out.println(ChatUtil.ColoredString("nullptr exception", CONSOLE_COLOR.RED));
throw new JsonSyntaxException("");
} catch (FileNotFoundException e) {
System.out.println(ChatUtil.ColoredString("file not found", CONSOLE_COLOR.RED));
throw new JsonSyntaxException("");
} catch (java.nio.charset.UnsupportedCharsetException e) {
System.out.println(ChatUtil.ColoredString("charset issue", CONSOLE_COLOR.RED));
throw new JsonSyntaxException("");
} catch (IOException e) {
System.out.println(ChatUtil.ColoredString("io exception", CONSOLE_COLOR.RED));
throw new JsonSyntaxException("");
}
return gson.fromJson(ret, classToConvertTo);
}
public Boolean DoesFileExist(String fileName) {
return new File(fileName).exists(); // untested
}
public Boolean DoesDirectoryExist(String dirName) {
return new File(dirName).isDirectory(); // untested
}
public Boolean CreateDirectory(String dirName) throws DIRECTORY_CREATE_EXCEPTION {
Boolean ret = false;
File dir = new File(dirName);
try {
if ( ! dir.exists()) {
ret = dir.mkdirs();
}
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Failed to make directory with name: ", CONSOLE_COLOR.RED) + ChatUtil.ColoredString(dirName, CONSOLE_COLOR.YELLOW));
ret = true; /// TODO: Hack to make Setup fn be fine with prev-created files! Make Setup more robust!
throw new DIRECTORY_CREATE_EXCEPTION();
}
return ret;
}
public Boolean DeleteDirectory(String dirName) throws DIRECTORY_DELETE_EXCEPTION {
Boolean ret = false;
File dir = new File(dirName);
try {
ret = dir.delete();
System.out.println(ChatUtil.ColoredString("Deleted directory ", CONSOLE_COLOR.GREEN) + ChatUtil.ColoredString(dirName, CONSOLE_COLOR.YELLOW));
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Failed to delete directory: ", CONSOLE_COLOR.RED) + ChatUtil.ColoredString(dirName, CONSOLE_COLOR.YELLOW));
throw new DIRECTORY_DELETE_EXCEPTION();
}
return ret;
}
// AddToKey
//
// Adds new text to a key if found inside the config
public String AddToKey(String key, String newInfo) {
String ret = "";
return ret;
}
// GetFile
//
// Returns a file as an arraylist of all the lines in the file. Generally only used for testing
//
// NOTE: Returns UTF-8 Encoding of file
public List<String> GetFile(String fileName) {
List<String> ret = new ArrayList<String>();
try {
return Files.readLines(new File(fileName), Charsets.UTF_8);
} catch (IOException e) {
ret.clear();
}
return ret;
}
}

View File

@ -0,0 +1,24 @@
package jesse.keeblarcraft.CustomBlocks;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.ExperienceDroppingBlock;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.util.math.intprovider.UniformIntProvider;
// Put all your blocks here
public class BlockList {
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterBlocks
///
/// @brief This function is called by the main thread to register all
/// block objects. Please register your blocks here
/////////////////////////////////////////////////////////////////////////////
public static void RegisterBlocks() {
BlockManager.RegisterBlock("example_block_ore", new Block(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).sounds(BlockSoundGroup.AMETHYST_BLOCK).requiresTool().breakInstantly()));
BlockManager.RegisterBlock("example_block", new ExperienceDroppingBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).sounds(BlockSoundGroup.ANCIENT_DEBRIS).requiresTool(), UniformIntProvider.create(4, 20)));
BlockManager.RegisterBlock("example_statue", new Block(FabricBlockSettings.copyOf(Blocks.BELL)));
}
}

View File

@ -0,0 +1,68 @@
/*
*
* BlockManager
*
* The mod block manager
*
*
*/
package jesse.keeblarcraft.CustomBlocks;
import java.util.HashMap;
import jesse.keeblarcraft.Keeblarcraft;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.minecraft.block.Block;
import net.minecraft.item.BlockItem;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
public class BlockManager {
/// The block list. DO NOT ADD TO THE LIST YOURSELF. USE
/// THE RegisterBlock(...) FUNCTION OTHERWISE YOUR BLOCK WILL
/// NOT BE REGISTERED PROPERLY AND MAY BREAK THE GAME
// public static final List<Block> blockList = new ArrayList<Block>();
public static final HashMap<String, Block> blockList = new HashMap<String, Block>();
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterBlock
///
/// @arg[in] name is the block name. IMPORTANT: Name must adhere to rules:
/// 1. The name provided here must match these names:
/// * This blocks models/block name
/// * This blocks textures/block name
/// 2 Name must be lowercase & no special characters besides '_'
///
/// @arg[in] block is the block to be added to the block list
///
/// @brief This is the call to register your block to the game! Please
/// do not forget to update the models/block json file, the
/// textures/block png name, and finally the lang/en_us.json file
/////////////////////////////////////////////////////////////////////////////
public static void RegisterBlock(String name, Block block) {
// This call registers the block as an item in inventories
Registry.register(Registries.ITEM, new Identifier(Keeblarcraft.MOD_ID, name), new BlockItem(block, new FabricItemSettings()));
// This call registers the block as placed
Block newBlock = Registry.register(Registries.BLOCK, new Identifier(Keeblarcraft.MOD_ID, name), block);
// Add the block to the block list
blockList.put(name, newBlock);
}
/////////////////////////////////////////////////////////////////////////////
/// @fn GetBlock
///
/// @brief Gets a block from the block list by name
///
/// @return Returns a block from the blockList object if name is found,
/// will return null if not found
/////////////////////////////////////////////////////////////////////////////
public static Block GetBlock(String name) {
return blockList.get(name);
}
}

View File

@ -0,0 +1,44 @@
package jesse.keeblarcraft.CustomItems;
import java.util.Map.Entry;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.CustomBlocks.BlockManager;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
import net.minecraft.block.Block;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
public class CustomItemGroups {
// This is the custom mod group for the mod
public static final ItemGroup MOD_GROUP = Registry.register(Registries.ITEM_GROUP,
new Identifier(Keeblarcraft.MOD_ID, "keeblarcraft"),
FabricItemGroup.builder().displayName(Text.translatable("itemgroup.keeblarcraft"))
.icon(() -> new ItemStack(Items.ANVIL)).entries((displayContext, entries) -> {
// We iterate through all the modded items and add them to the custom item group here
for (int i = 0; i < ItemManager.itemList.size(); i++) {
entries.add(ItemManager.itemList.get(i));
}
// We iterate through all the modded blocks and add them to the custom block group here
for(Entry<String, Block> blocks : BlockManager.blockList.entrySet()) {
entries.add(blocks.getValue());
}
}).build());
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterGroups
///
/// @brief Glorified print statement that the class has initialized
/// basically - meaning that all item groups have registered
/////////////////////////////////////////////////////////////////////////////
public static void RegisterGroups() {
Keeblarcraft.LOGGER.info("Registering item groups for " + Keeblarcraft.MOD_ID);
}
}

View File

@ -0,0 +1,69 @@
/*
*
* ItemManager
*
* This is the general purpose item manager for the mod. All items will be registered through
* this class in order to make custom items a lot cleaner to make!
*
*
*/
package jesse.keeblarcraft.CustomItems;
import java.util.ArrayList;
import java.util.List;
import jesse.keeblarcraft.Keeblarcraft;
import net.minecraft.item.Item;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
public class ItemManager {
/// The item list. DO NOT ADD TO THE LIST YOURSELF. USE
/// THE RegisterItem(...) FUNCTION OTHERWISE YOUR ITEM WILL
/// NOT BE REGISTERED PROPERLY AND MAY BREAK THE GAME
public static final List<Item> itemList = new ArrayList<Item>();
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterItem
///
/// @arg[in] name is the item name. IMPORTANT: Name must adhere to rules:
/// 1. The name provided here must match these names:
/// * This items models/item name
/// * This items textures/item name
/// 2 Name must be lowercase & no special characters besides '_'
///
/// @arg[in] item is the item to be added to the item list
///
/// @brief This is the call to register your item to the game! Please
/// do not forget to update the models/item json file, the
/// textures/item png name, and finally the lang/en_us.json file
/////////////////////////////////////////////////////////////////////////////
public static void RegisterItem(String name, Item item) {
Item newItem = Registry.register(Registries.ITEM, new Identifier(Keeblarcraft.MOD_ID, name), item);
itemList.add(newItem);
}
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterAllItems
///
/// @brief This function is only meant to be called by the main mod
/// execution path and should only be called once. This is a
/// glorified print statement - but also serves to add the
/// example item in the game
/////////////////////////////////////////////////////////////////////////////
public static void RegisterAllItems() {
Keeblarcraft.LOGGER.info("Registering mod items for " + Keeblarcraft.MOD_ID);
// Call the item group register function first
CustomItemGroups.RegisterGroups();
// The example item provides a demo of how you could make an item in your class
// Item exampleItem = new Item(new FabricItemSettings());
// RegisterItem("metaljacket_helmet", exampleItem);
}
}

View File

@ -0,0 +1,16 @@
/*
*
* EventManager
*
* This class is responsible for handling the setup and execution of events within the game for the mod. These events can be configured as plugins in this
* directory which are executed in an abstract fashion with handling. If you need a new event (for a story item, or handling a player event, etc) then it
* is preferred to add that as an extension class and called within this manager object
*
*/
package jesse.keeblarcraft.EventMgr;
public class EventManager {
}

View File

@ -0,0 +1,21 @@
package jesse.keeblarcraft.EventMgr;
import jesse.CommonServerUtils;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.server.MinecraftServer;
// This interface is responsible for the end tick of a server world event tick
public class ServerTickListener implements ServerTickEvents.EndTick {
CommonServerUtils config = new CommonServerUtils();
@Override
public void onEndTick(MinecraftServer server) {
if (server != null) {
config.SetServerInstance(server);
}
}
// Static method to register the server tick listener
public static void InitializeServerTicks() {
ServerTickEvents.END_SERVER_TICK.register(new ServerTickListener());
}
}

View File

@ -0,0 +1,15 @@
/*
*
* TeamManager
*
* Class is responsible for keeping track of teams/factions chosen by the players in the game and saves to the configuration
* file for persistent data storage. Class handles checks as well for eligibility purposes (making sure players can join, etc)
*
*/
package jesse.keeblarcraft.FactionMgr;
public class TeamManager {
// Class controls managing teams and pulling from configuration file and loading to configuration file
}

View File

@ -0,0 +1,86 @@
package jesse.keeblarcraft.GuiMgr;
import jesse.keeblarcraft.Keeblarcraft;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.SimpleInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.ScreenHandler;
// Server side screen handler for the attribute tree
public class TreeHandler extends ScreenHandler {
/////////////////////////////////////////////////////////////////////////////
/// @fn TreeHandler
///
/// @arg[in] syncId is SUPPOSED to be some sync identifier between the
/// server and client. This isn't being used yet, so there isn't
/// enough knowledge to make a good documentation note on it.
/// Coming soon!
///
/// @arg[in] inventory is the players inventory. Required by HandledScreen
/// object however is unused currently so we make the size 0. As
/// needs and use-cases change; this may be reworked in the future
///
/// @brief Class constructor
/////////////////////////////////////////////////////////////////////////////
public TreeHandler(int syncId, PlayerInventory inventory) {
// Call the constructor below
this(syncId, inventory, new SimpleInventory(0));
}
/////////////////////////////////////////////////////////////////////////////
/// @fn TreeHandler
///
/// @arg[in] syncId is SUPPOSED to be some sync identifier between the
/// server and client. This isn't being used yet, so there isn't
/// enough knowledge to make a good documentation note on it.
/// Coming soon!
///
/// @arg[in] inventory is the players inventory. Required by HandledScreen
/// object however is unused currently so we make the size 0. As
/// needs and use-cases change; this may be reworked in the future
///
/// @arg[in] inventory is the SERVER's inventory - which is what syncId
/// would likely be useful for. Once I (Jesse) get the screen stuff
/// more sorted out - a more useful doxygen comment will be applied
/// here in this class. Coming soon!
///
/// @brief Class constructor
/////////////////////////////////////////////////////////////////////////////
public TreeHandler(int syncId, PlayerInventory playerInventory, Inventory inventory) {
super(Keeblarcraft.TREE_HANDLER, syncId);
System.out.println("Created tree handler");
System.out.println("Is static var null? " + Keeblarcraft.TREE_HANDLER == null ? "YES":"NO");
}
/////////////////////////////////////////////////////////////////////////////
/// @fn quickMove
///
/// @arg[in] player is the player entity moving an item
///
/// @arg[in] slot is the inventory slot to quick move to (shift+click)
///
/// @brief Required by parent class; currently do nothing as we aren't
/// using inventories in the gui's!
/////////////////////////////////////////////////////////////////////////////
@Override
public ItemStack quickMove(PlayerEntity player, int slot) {
return ItemStack.EMPTY;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn canUse
///
/// @arg[in] player is the player entity moving an item
///
/// @brief Returns whether the screen handler can be used. Required by
/// parent class as its abstract; but for now players can always
/// use GUI's and need no confirmation!
/////////////////////////////////////////////////////////////////////////////
@Override
public boolean canUse(PlayerEntity player) {
return true;
}
}

View File

@ -0,0 +1,239 @@
/*
*
* Note.java
*
* Class defines what will be in a configuration file for a user note. All metadata is in this class when read from file
*/
package jesse.keeblarcraft.JsonClassObjects;
import java.util.HashMap;
import java.util.Map.Entry;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import jesse.keeblarcraft.Utils.CustomExceptions.FILE_WRITE_EXCEPTION;
public class PlayerNote {
/// Class variables
NoteFile thisNote = new NoteFile();
ConfigManager config = new ConfigManager();
/////////////////////////////////////////////////////////////////////////////
/// @class NoteMetadata
///
/// @brief The metadata that is attached to each entry in the notebook.
/// This includes not just the note itself, but other important
/// factors that can be characteristic to each note and useful
///
/// @note You can picture this as each page in the notebook if it's
/// an easier mental image
/////////////////////////////////////////////////////////////////////////////
private class NoteMetadata {
public NoteMetadata(String note, long id, /*long mcday,*/ long sysTime, long chapter, long part) {
this.note = note;
this.noteId = id;
// this.minecraftDay = mcday;
this.systemTime = sysTime;
this.storyChapter = chapter;
this.storyPart = part;
}
String note; // The note itself
long noteId; // Copied in from the file name, noteId
// long minecraftDay; // The minecraft day the note was taken on
long systemTime; // The current system time of the server
long storyChapter; // The chapter of the story the player is in
long storyPart; // Every event in a story is one part
}
/////////////////////////////////////////////////////////////////////////////
/// @class NoteFile
///
/// @brief This is the notebook
/////////////////////////////////////////////////////////////////////////////
public class NoteFile {
// the uuid is the name of the file written to disk
String uuid;
public HashMap<Integer, NoteMetadata> noteMap = new HashMap<Integer, NoteMetadata>();
}
/////////////////////////////////////////////////////////////////////////////
/// @fn PlayerNote
///
/// @brief This class's non-trivial constructor. Grabs a handle on the
/// server-side file that is the players notebook at object
/// creation. If one does not exist, one is created
///
/// @arg[in] uuid is the players uuid value
/////////////////////////////////////////////////////////////////////////////
public PlayerNote(String uuid) {
// DEVELOPER NOTE:
// If you are testing this part of the code, please be reminded that anonymous testing starts a new
// player instance everytime you launch. This means the UUID CAN CHANGE when you launch the
// game! This is not an issue with proper registered accounts in production
Boolean existingFile = false;
try {
thisNote = config.GetJsonObjectFromFile("notes/" + uuid + ".json", NoteFile.class);
existingFile = true;
} catch (Exception e) {
// Do nothing. This means the file does not exist
}
// In the event the above code failed out, this means a new file has to be created for the player's uuid
if (!existingFile)
{
System.out.println(ChatUtil.ColoredString("Trying to create new file", CONSOLE_COLOR.BLUE));
try {
thisNote.uuid = uuid;
FlashConfig();
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Could not write to file", CONSOLE_COLOR.RED));
}
} else {
System.out.println(ChatUtil.ColoredString("Moving on", CONSOLE_COLOR.BLUE));
}
// It's possible the above code will return a blank class if a file doesn't exist. This will make
// a new file with this players uuid
if ("".equals(thisNote.uuid)) {
System.out.println(ChatUtil.ColoredString("Assigning new config file for this uuid. No previous existing", CONSOLE_COLOR.BLUE));
thisNote.uuid = uuid;
}
}
/////////////////////////////////////////////////////////////////////////////
/// @fn AddNote
///
/// @brief Adds a new note to the notebook
///
/// @arg[in] newNote is the new string value of the note
///
/// @arg[in] minecraftDay is currently unsupported but will be the day of
/// the minecraft world when it is implemented
///
/// @arg[in] systemTime is the epoch time in milliseconds
///
/// @arg[in] storyChapter is the chapter in the story this note was taken
///
/// @arg[in] storyPart is the part in the chapter the note was taken
/////////////////////////////////////////////////////////////////////////////
public void AddNote(String newNote, long minecraftDay, long systemTime, long storyChapter, long storyPart) {
Integer noteKey = thisNote.noteMap.size() + 1;
thisNote.noteMap.put(noteKey, new NoteMetadata(newNote, noteKey, /*minecraftDay,*/ systemTime, storyChapter, storyPart));
FlashConfig(); ///TODO: This might be really unnecessary and may only be required on clean up as opposed to everytime the command is run
}
/////////////////////////////////////////////////////////////////////////////
/// @fn DeleteNote
///
/// @brief Deletes a note in the notebook
///
/// @arg[in] noteId is the id to delete
/////////////////////////////////////////////////////////////////////////////
public void DeleteNote(Integer noteId) {
thisNote.noteMap.remove(noteId);
FlashConfig();
}
/////////////////////////////////////////////////////////////////////////////
/// @fn ModifyNote
///
/// @brief Modifies a note at noteId entry OR creates new note at that
/// entry if the id didn't exist previously
///
/// @arg[in] noteId is the id we wish to modify the note of
///
/// @arg[in] newNote is the new string value of the note
///
/// @arg[in] minecraftDay is currently unsupported but will be the day of
/// the minecraft world when it is implemented
///
/// @arg[in] systemTime is the epoch time in milliseconds
///
/// @arg[in] storyChapter is the chapter in the story this note was taken
///
/// @arg[in] storyPart is the part in the chapter the note was taken
///
/// @return 0 if success, -1 if not
/////////////////////////////////////////////////////////////////////////////
public void ModifyNote(Integer noteId, String newNote, /*long minecraftDay,*/ long systemTime, long storyChapter, long storyPart) {
thisNote.noteMap.put(noteId, new NoteMetadata(newNote, noteId, /*minecraftDay,*/ systemTime, storyChapter, storyPart));
FlashConfig();
}
/////////////////////////////////////////////////////////////////////////////
/// @fn PurgeAllNotes
///
/// @brief Wipes the players notebook clean
/////////////////////////////////////////////////////////////////////////////
public void PurgeAllNotes() {
thisNote.noteMap.clear();
FlashConfig();
}
/////////////////////////////////////////////////////////////////////////////
/// @fn GetNoteString
///
/// @brief Gets the note string in the map by identifier. NOT metadata
///
/// @arg[in] key is the map key we wish to return
///
/// @return Empty string if that ID contains no note, or string with note
/////////////////////////////////////////////////////////////////////////////
public String GetNoteString(Integer key) {
if (thisNote.noteMap.containsKey(key)) {
return thisNote.noteMap.get(key).note;
} else {
return "";
}
}
/////////////////////////////////////////////////////////////////////////////
/// @fn GetNotebookSize
///
/// @brief Returns the size of the notebook object
///
/// @return Size of notebook
/////////////////////////////////////////////////////////////////////////////
public Integer GetNotebookSize() {
return thisNote.noteMap.size();
}
/////////////////////////////////////////////////////////////////////////////
/// @fn GetNoteKey
///
/// @brief Returns the key identifier of a note by the value in the map
///
/// @arg[in] currentNote is the string note that we wish to see if exists
///
/// @return 0 or positive number if key found, negative number if not
/////////////////////////////////////////////////////////////////////////////
public Integer GetNoteKey(String currentNote) {
Integer ret = -1;
for (Entry<Integer, NoteMetadata> entry : thisNote.noteMap.entrySet()) {
if (entry.getValue().note.equals(currentNote)) {
ret = entry.getKey();
}
}
FlashConfig();
return ret;
}
/////////////////////////////////////////////////////////////////////////////
/// @fn FlashConfig
///
/// @brief Writes to the configuration file
/////////////////////////////////////////////////////////////////////////////
public void FlashConfig() {
try {
config.WriteToJsonFile("notes/" + thisNote.uuid + ".json", thisNote);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
}
}
}

View File

@ -0,0 +1,111 @@
/*
*
* Keeblarcraft
*
* This is the primary server side "main" object that is referenced by Fabric. This is where everything is setup for the mod
* and a very important class. Please becareful as you add to it
*
*/
// color colour
package jesse.keeblarcraft;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.resource.featuretoggle.FeatureSet;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.util.Identifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jesse.keeblarcraft.AttributeMgr.AttributeMgr;
import jesse.keeblarcraft.AttributeMgr.AttributeTree;
import jesse.keeblarcraft.Commands.CustomCommandManager;
import jesse.keeblarcraft.CustomBlocks.BlockList;
import jesse.keeblarcraft.CustomItems.ItemManager;
import jesse.keeblarcraft.EventMgr.ServerTickListener;
import jesse.keeblarcraft.GuiMgr.TreeHandler;
import jesse.keeblarcraft.Utils.CustomExceptions.SETUP_FAILED_EXCEPTION;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.Setup;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
public class Keeblarcraft implements ModInitializer {
public static String MOD_ID = "keeblarcraft";
public static final Logger LOGGER = LoggerFactory.getLogger("keeblarcraft");
// May be moved in future; but is the universal TREE_HANDLER call for at least the tree gui. It's possible it can get a more
// generic name in the future after the tree gui is done; however until then it will remain TREE_HANDLER.
public static final ScreenHandlerType<TreeHandler> TREE_HANDLER = Registry.register(Registries.SCREEN_HANDLER,
Identifier.of(Keeblarcraft.MOD_ID, "tree_gui"),
new ScreenHandlerType<>(TreeHandler::new, FeatureSet.empty()));
CustomCommandManager cmdMgr = new CustomCommandManager();
Setup setup = Setup.GetInstance();
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
ChatUtil.LoggerColored("Hello Fabric world!", CONSOLE_COLOR.CYAN, LOGGER);
if (setup != null) {
try {
// Run setup. If setup fails; it throws SETUP_FAILED_EXCEPTION
LOGGER.info("\033[34m Running setup stage \033[0m");
setup.RunSetup();
// This is a very special case where this must be in this classes' initializer
// method
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
var player = handler.player;
Keeblarcraft.LOGGER.info("Player " + player.getName() + " has logged in. Creating tree...");
if (AttributeMgr.activeTrees.containsKey(player.getUuidAsString()) == false) {
AttributeMgr.activeTrees.put(player.getUuidAsString(), new AttributeTree(player.getUuidAsString()));
}
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
var player = handler.player;
Keeblarcraft.LOGGER.info("Player " + player.getName() + " has logged out. Deleting tree...");
if (AttributeMgr.activeTrees.containsKey(player.getUuidAsString()) == true) {
AttributeMgr.activeTrees.remove(player.getUuidAsString());
}
});
// Initialize our ticks!!
ServerTickListener.InitializeServerTicks();
// Run command registrations from the command manager
LOGGER.info("\033[34m Running command registration \033[0m");
cmdMgr.RegisterCustomCommands();
// Register attributes
AttributeMgr.RegisterAttributes();
/// THE BELOW ITEMS MUST BE DONE LAST IN THE STEPS
// Register items
ItemManager.RegisterAllItems();
// Register blocks
BlockList.RegisterBlocks();
} catch (SETUP_FAILED_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("ERROR. Setup failed to initialize environment. Mod likely does not have read/write permissions inside area. Mod will now close out.", CONSOLE_COLOR.RED));
e.printStackTrace();
}
} else {
// Program exit. Dual definition of setup somehow happened!
System.out.println(ChatUtil.ColoredString("Dual definition of singleton attempted! Out of order initialization? How did this even happen?", CONSOLE_COLOR.RED));
}
}
}

View File

@ -0,0 +1,17 @@
/*
*
* Story Manager
*
* Class keeps track of the story/note-taking done by players and saves it automagically to the configuration file for note taking / story tracking for
* the server.
*
* In larger detail, this class will set a timeline for each player and where they are on the story. Depending on where they are in the story, certain
* milestones will be tagged to their account so they may access certain things that are unlockable achievements within this class
*
*/
package jesse.keeblarcraft.StoryMgr;
public class StoryMgr {
}

View File

@ -0,0 +1,157 @@
/*
*
* ChatUtil
*
* Helpful utility for pretty printing in chat in the game with different supported functions and levels
*
*/
package jesse.keeblarcraft.Utils;
import org.slf4j.Logger;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map.Entry;
public class ChatUtil {
//This is a private class only used internally to get ANSI colors
private static class ConsoleColor {
public static String getColor(CONSOLE_COLOR color) {
String ret = "";
switch(color) {
case RED:
ret = "31";
break;
case GREEN:
ret = "32";
break;
case YELLOW:
ret = "33";
break;
case BLUE:
ret = "34";
break;
case MAGENTA:
ret = "35";
break;
case CYAN:
ret = "36";
break;
}
return ret;
}
}
public static enum CONSOLE_COLOR {
RED,
GREEN,
YELLOW,
BLUE,
MAGENTA,
CYAN;
}
static int CHATBOX_WIDTH_CHARS = 80; // Maximum length of the textbox in individual characters
// Helpful print wrapper function
static public void SendPlayerMsg(ServerPlayerEntity player, String text) {
player.sendMessage(Text.literal(text));
}
/// TODO: Add this back in later under a chat ticket
// Prints a table of data in chat
// static public void ChatBlock(ServerPlayerEntity player, HashMap<Text, List<Text>> table) {
// ///DEBUG
// for (Entry<Text, List<Text>> entry : table.entrySet()) {
// for (int debug = 0; debug < entry.getValue().size(); debug++) {
// System.out.println("KEY: " + entry.getKey().toString() + " VALUE: " + entry.getValue().get(debug).toString());
// }
// }
// // The user will likely pass in text strings longer than the character limit for num of columns; therefore
// // we are required to split these into this finalPrintList structure
// HashMap<Text, ArrayList<Text>> finalPrintList = new HashMap<Text, ArrayList<Text>>();
// int maxColumnWidth = CHATBOX_WIDTH_CHARS / table.size(); // Represents max char allowance per data column
// maxColumnWidth -= table.size(); // Represents a separating '|' between each column
// // This first behemoth of a loop is to take the given table hashmap and look at
// // the Text values & split them
// // should their size exceed the maxColumnWidth given for each entry key
// System.out.println("Entry data is size " + table.size());
// for (Entry<Text, List<Text>> entry : table.entrySet()) {
// // Each text line found cannot be longer than "maxColumnWidth" or else it must
// // wrap which splits it
// // into two texts; thus adding an additional row that is required for iteration.
// // Each split text must
// // maintain the same formatting as the root text it is split from
// finalPrintList.put(entry.getKey(), new ArrayList<Text>()); // Instantiate the key & array
// System.out.println("Map size is " + finalPrintList.size());
// System.out.println("Entry value size is " + entry.getValue().size());
// for (Text item : entry.getValue()) {
// int numItems = (int) Math.ceil((item.getString().length() / maxColumnWidth));
// int strOffset = numItems; // Represents number of items per string
// System.out.println("numItems: " + numItems);
// System.out.println("strOffset: " + strOffset);
// for (int offset = 0; offset <= numItems; offset++) { /// TODO: might need to be <=
// int start = strOffset * offset; // Multiple from start of string to needed point
// int end = start + strOffset; // The original start offset + the width spacer
// String substr = item.toString().substring(start, end); // Contains the string to be Textified
// MutableText newText = Text.literal(substr).setStyle(item.getStyle());
// finalPrintList.get(entry.getKey()).add(newText); // Add back into list
// System.out.println("SPLIT DEBUG: " + newText.toString());
// }
// }
// }
// // This loop does the printing of the table in chat
// int tempPreventInfiniteLoops = 10;
// while (finalPrintList.size() != 0) {
// // This is a one time print
// MutableText line = Text.literal("");
// for (Entry<Text, ArrayList<Text>> entry : finalPrintList.entrySet()) {
// if (entry.getValue().size() != 0) {
// line.append(entry.getValue().get(0));
// System.out.println("new line is now " + line.toString());
// line.append("|");
// } else {
// finalPrintList.remove(entry.getKey()); // Clear the key; as we are done with this column for
// // printing
// }
// player.sendMessage(line);
// String debugPrint = line.toString();
// System.out.println("Debug line to be printed: " + debugPrint);
// line = Text.literal("");
// break;
// }
// tempPreventInfiniteLoops--;
// if (tempPreventInfiniteLoops <= 0) {
// return;
// }
// }
// }
// Returns a string with the proper ANSI encoding for the specified CONSOLE_COLOR
static public String ColoredString(String msg, CONSOLE_COLOR color) {
return "\033[" + ConsoleColor.getColor(color) + "m" + msg + "\033[0m";
}
// Takes in a (already initialized) logger object and prints to console
static public void LoggerColored(String msg, CONSOLE_COLOR color, Logger logger) {
logger.info(ColoredString(msg, color));
}
}

View File

@ -0,0 +1,11 @@
package jesse.keeblarcraft.Utils.CommonStructures;
public class Position2d {
public Position2d(int x, int y) {
this.x = x;
this.y = y;
}
public int x;
public int y;
}

View File

@ -0,0 +1,13 @@
package jesse.keeblarcraft.Utils.CommonStructures;
public class Position3d {
public Position3d(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int x;
public int y;
public int z;
}

View File

@ -0,0 +1,18 @@
package jesse.keeblarcraft.Utils;
public class CustomExceptions {
// File exceptions
public static class FILE_CREATE_EXCEPTION extends Exception {}
public static class FILE_WRITE_EXCEPTION extends Exception {}
public static class FILE_DELETE_EXCEPTION extends Exception {}
// Directory exceptions
public static class DIRECTORY_DELETE_EXCEPTION extends Exception {}
public static class DIRECTORY_CREATE_EXCEPTION extends Exception {}
public static class DIRECTORY_MOVE_EXCEPTION extends Exception {}
// Setup exceptions
public static class SETUP_NO_WRITE_EXCEPTION extends Exception {}
public static class SETUP_NO_READ_EXCEPTION extends Exception {}
public static class SETUP_FAILED_EXCEPTION extends Exception {}
}

View File

@ -0,0 +1,162 @@
/*
*
* Setup.java
*
* Setup is a singleton object that runs on the initial mod setup to run critical checks to make sure the mod
* is allowed to do things on the users filesystem (reading conf files, writing conf files, etc). It's quite
* important for the mod to know this!
*
*/
package jesse.keeblarcraft.Utils;
import java.util.List;
import java.util.ArrayList;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import jesse.keeblarcraft.Utils.CustomExceptions.DIRECTORY_CREATE_EXCEPTION;
import jesse.keeblarcraft.Utils.CustomExceptions.SETUP_FAILED_EXCEPTION;
// Singleton class is designed to help the mod set itself up and create all the important things. It does two things:
//
// Thing 1: If on initial setup of mod for first time; creates all necessary directories and files.
// Doing this helps the mod know if it has permissions to do things, or if it shouldn't even bother!
// Some functionality is disabled if the mod can't access the hard drive in various ways.
//
// Thing 2: On any sequential startup; it checks to make sure all the directories exist & replaces missing ones.
// It will also do a state-check of the system to make sure we will have permissions to read & write.
// If we do not have these, or only partial - then functionality may be disabled for runtime performance
public final class Setup {
private static Setup static_inst;
public Setup() {
System.out.println(ChatUtil.ColoredString("Running system setup and checks...", CONSOLE_COLOR.BLUE));
}
// Returns the singleton object
public static Setup GetInstance() {
if (static_inst == null) {
static_inst = new Setup();
}
return static_inst;
}
/// Unit testing functions below
public static Boolean has_read = false;
public static Boolean has_write = false;
// First time setup variables
private static final List<String> DIRECTORY_LIST = new ArrayList<String>() {{
add("notes"); // Expect 1 file per player!
add("factions"); // Expect 1 file per faction!
add("story"); // Expect 1 file per story chapter!
add("commands"); // Expect 1 file per command that's configurable!
add("events"); // Expect 1 file per event that is configurable!
add("bank");
add("attributes");
}};
// These will be top-level config files above the directories this mod creates
private static final List<String> FILE_LIST = new ArrayList<String>() {{
add("story/general_story_config.json"); // Big config file, determines when players can do certain things at different story levels
add("factions/general_factions_config.json"); // General configuration file for factions stuff
add("events/general_event_config.json"); // General configuration file for story events!
add("general.json"); // The super general configuration file! (May be removed)
add("attributes/general_attribute_config.json");
}};
// RunChecks()
//
// Checks if we are able to create necessary directories and run reading over the file system for the current
// directory this mod is maintained in. This will return false if any checks fail; but individual checks can
// be accessed in class variables (above)
public Boolean RunChecks() {
ConfigManager conf = new ConfigManager();
// Create directory check
try {
has_write = conf.CreateDirectory("test_dir");
System.out.println(ChatUtil.ColoredString("test_dir created! has write: ", CONSOLE_COLOR.GREEN) + (has_write ? ChatUtil.ColoredString("YES", CONSOLE_COLOR.YELLOW) : ChatUtil.ColoredString("NO", CONSOLE_COLOR.YELLOW)));
} catch (DIRECTORY_CREATE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Failed to create test directory or it already exists", CONSOLE_COLOR.MAGENTA));
has_write = false;
}
// Write to disk then read that data back
if (has_write) {
try {
has_write = conf.CreateFile("test_dir/test_note.txt");
has_write = conf.WriteToFile("test_dir/test_note.txt", "test_write_read", "w");
List<String> lines = conf.GetFile("test_dir/test_note.txt");
if (lines.size() == 0) {
has_read = false;
} else {
has_read = true;
}
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Failed to create or write to test dir file ", CONSOLE_COLOR.RED));
has_read = false;
}
}
// Delete directory if created (it's a temporary dir)
if (has_write) {
try {
has_write = conf.DeleteFile("test_dir/test_note.txt");
has_write = conf.DeleteDirectory("test_dir");
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Lost access to writing mid-way", CONSOLE_COLOR.RED));
has_write = false;
}
}
//need to be able to take in raw booleans for coloredstrings functions
System.out.println(ChatUtil.ColoredString("CHECKS DEBUG: Value of has_write: ", CONSOLE_COLOR.BLUE) + ChatUtil.ColoredString(has_write.toString(), CONSOLE_COLOR.YELLOW) + ChatUtil.ColoredString(". Value of has_read: ", CONSOLE_COLOR.BLUE) + ChatUtil.ColoredString(has_read.toString(), CONSOLE_COLOR.YELLOW));
return has_write && has_read;
}
// RunSetup
//
// Primary function call that will execute when mod starts up
public Boolean RunSetup() throws SETUP_FAILED_EXCEPTION {
Boolean ret = false;
// Setup can only complete if it has guarenteed we have read & write permissions
if (ret = RunChecks()) {
try {
// Create necessary directories
ConfigManager conf = new ConfigManager();
for (Integer i = 0; i < DIRECTORY_LIST.size(); i++) {
if ( ! conf.DoesDirectoryExist(DIRECTORY_LIST.get(i))) {
conf.CreateDirectory(DIRECTORY_LIST.get(i));
System.out.println(ChatUtil.ColoredString("Creating directory ", CONSOLE_COLOR.GREEN) + ChatUtil.ColoredString(DIRECTORY_LIST.get(i), CONSOLE_COLOR.YELLOW) + ChatUtil.ColoredString("...", CONSOLE_COLOR.GREEN));
} else {
System.out.println(ChatUtil.ColoredString("Directory ", CONSOLE_COLOR.BLUE) + conf.DoesDirectoryExist(DIRECTORY_LIST.get(i)) + ChatUtil.ColoredString(" already exists. Skipping... ", CONSOLE_COLOR.BLUE));
}
}
// Create necessary files
for (Integer i = 0; i < FILE_LIST.size(); i++) {
if ( ! conf.DoesFileExist(FILE_LIST.get(i))) {
conf.CreateFile(FILE_LIST.get(i));
System.out.println(ChatUtil.ColoredString("Creating file ", CONSOLE_COLOR.GREEN) + ChatUtil.ColoredString(FILE_LIST.get(i), CONSOLE_COLOR.YELLOW) + ChatUtil.ColoredString("...", CONSOLE_COLOR.GREEN));
} else {
System.out.println(ChatUtil.ColoredString("File ", CONSOLE_COLOR.BLUE) + conf.DoesDirectoryExist(FILE_LIST.get(i)) + ChatUtil.ColoredString(" already exists. Skipping...", CONSOLE_COLOR.BLUE));
}
}
} catch (Exception e) {
throw new SETUP_FAILED_EXCEPTION();
}
} else {
System.out.println(ChatUtil.ColoredString("RunChecks() failed in its process. This mod has deemed it does not have read or write privileges in its hosted area and will now exit.", CONSOLE_COLOR.RED));
throw new SETUP_FAILED_EXCEPTION();
}
System.out.println(ChatUtil.ColoredString("DID SETUP COMPLETE SUCCESSFULLY? ", CONSOLE_COLOR.YELLOW) + (ret ? ChatUtil.ColoredString("YES", CONSOLE_COLOR.YELLOW) : ChatUtil.ColoredString("NO", CONSOLE_COLOR.YELLOW)));
return ret;
}
}

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "keeblarcraft:block/example_block"
}
}
}

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "keeblarcraft:block/example_block_ore"
}
}
}

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "keeblarcraft:block/example_statue"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,16 @@
{
"item.keeblarcraft.metaljacket_helmet": "MetalJacket Helmet",
"item.keeblarcraft.metaljacket_chestplate": "MetalJacket Chestplate",
"item.keeblarcraft.metaljacket_leggings": "MetalJacket Leggings",
"item.keeblarcraft.metaljacket_boots": "MetalJacket Booties",
"itemgroup.keeblarcraft": "Keeblarcraft Modded Items",
"block.keeblarcraft.example_block": "Keeblarcraft example block",
"block.keeblarcraft.example_block_ore": "Keeblarcraft example block ore",
"block.keeblarcraft.example_statue": "Keeblarcraft example statue",
"category.keeblarcraft.test": "Keeblarcraft bindings",
"key.keeblarcraft.treemap": "Tree GUI",
"key.keeblarcraft.globalconfig": "Config menu"
}

View File

@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "keeblarcraft:block/example_block"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "keeblarcraft:block/example_block_ore"
}
}

View File

@ -0,0 +1,481 @@
{
"credit": "Made with Blockbench",
"texture_size": [64, 64],
"textures": {
"0": "keeblarcraft:block/example_statue",
"particle": "keeblarcraft:block/example_statue"
},
"elements": [
{
"name": "platform",
"from": [0, 0, 0],
"to": [16, 1, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [5, 0, 3]},
"faces": {
"north": {"uv": [4, 2.5, 8, 2.75], "texture": "#0"},
"east": {"uv": [4, 2.75, 8, 3], "texture": "#0"},
"south": {"uv": [4, 3, 8, 3.25], "texture": "#0"},
"west": {"uv": [4, 3.25, 8, 3.5], "texture": "#0"},
"up": {"uv": [4, 4, 0, 0], "texture": "#0"},
"down": {"uv": [4, 4, 0, 8], "texture": "#0"}
}
},
{
"name": "left_leg",
"from": [7, 1, 3],
"to": [9, 5, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 0, 2]},
"faces": {
"north": {"uv": [5.5, 5.5, 6, 6.75], "texture": "#0"},
"east": {"uv": [6, 0, 6.5, 1.25], "texture": "#0"},
"south": {"uv": [6, 1.25, 6.5, 2.5], "texture": "#0"},
"west": {"uv": [6, 3.5, 6.5, 4.75], "texture": "#0"},
"up": {"uv": [6.75, 7.75, 6.25, 7.25], "texture": "#0"},
"down": {"uv": [7.75, 7, 7.25, 7.5], "texture": "#0"}
}
},
{
"name": "right_leg",
"from": [7, 1, 11],
"to": [9, 5, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 0, 11]},
"faces": {
"north": {"uv": [6, 4.75, 6.5, 6], "texture": "#0"},
"east": {"uv": [6, 6, 6.5, 7.25], "texture": "#0"},
"south": {"uv": [6.5, 0, 7, 1.25], "texture": "#0"},
"west": {"uv": [6.5, 1.25, 7, 2.5], "texture": "#0"},
"up": {"uv": [8, 0.5, 7.5, 0], "texture": "#0"},
"down": {"uv": [8, 0.5, 7.5, 1], "texture": "#0"}
}
},
{
"from": [7, 5, 5],
"to": [9, 10, 11],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 5, 7]},
"faces": {
"north": {"uv": [6.5, 3.5, 7, 4.75], "texture": "#0"},
"east": {"uv": [4, 0, 5.5, 1.25], "texture": "#0"},
"south": {"uv": [6.5, 4.75, 7, 6], "texture": "#0"},
"west": {"uv": [4, 1.25, 5.5, 2.5], "texture": "#0"},
"up": {"uv": [6, 1.5, 5.5, 0], "texture": "#0"},
"down": {"uv": [4.5, 5.5, 4, 7], "texture": "#0"}
}
},
{
"from": [7, 8, 10],
"to": [9, 10, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 8, 10]},
"faces": {
"north": {"uv": [7.5, 1, 8, 1.5], "texture": "#0"},
"east": {"uv": [7.5, 1.5, 8, 2], "texture": "#0"},
"south": {"uv": [7.5, 2, 8, 2.5], "texture": "#0"},
"west": {"uv": [7.5, 3.5, 8, 4], "texture": "#0"},
"up": {"uv": [8, 4.5, 7.5, 4], "texture": "#0"},
"down": {"uv": [8, 5, 7.5, 5.5], "texture": "#0"}
}
},
{
"from": [7, 10, 11],
"to": [9, 11, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 10, 11]},
"faces": {
"north": {"uv": [8, 2.75, 8.5, 3], "texture": "#0"},
"east": {"uv": [8, 3, 8.5, 3.25], "texture": "#0"},
"south": {"uv": [8, 3.25, 8.5, 3.5], "texture": "#0"},
"west": {"uv": [3.5, 8, 4, 8.25], "texture": "#0"},
"up": {"uv": [8, 6, 7.5, 5.5], "texture": "#0"},
"down": {"uv": [8, 6, 7.5, 6.5], "texture": "#0"}
}
},
{
"from": [7, 10, 13],
"to": [9, 11, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 10, 12]},
"faces": {
"north": {"uv": [8, 3.5, 8.5, 3.75], "texture": "#0"},
"east": {"uv": [7.5, 8.5, 7.75, 8.75], "texture": "#0"},
"south": {"uv": [8, 3.75, 8.5, 4], "texture": "#0"},
"west": {"uv": [8.75, 8.75, 9, 9], "texture": "#0"},
"up": {"uv": [8.5, 4.25, 8, 4], "texture": "#0"},
"down": {"uv": [8.5, 4.25, 8, 4.5], "texture": "#0"}
}
},
{
"from": [7, 11, 14],
"to": [9, 13, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 11, 13]},
"faces": {
"north": {"uv": [7.5, 6.5, 8, 7], "texture": "#0"},
"east": {"uv": [8, 5, 8.25, 5.5], "texture": "#0"},
"south": {"uv": [7.25, 7.5, 7.75, 8], "texture": "#0"},
"west": {"uv": [5.5, 8, 5.75, 8.5], "texture": "#0"},
"up": {"uv": [8.5, 5.75, 8, 5.5], "texture": "#0"},
"down": {"uv": [8.5, 5.75, 8, 6], "texture": "#0"}
}
},
{
"from": [7, 14, 12],
"to": [9, 15, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 14, 12]},
"faces": {
"north": {"uv": [8, 6, 8.5, 6.25], "texture": "#0"},
"east": {"uv": [8, 6.25, 8.5, 6.5], "texture": "#0"},
"south": {"uv": [8, 6.5, 8.5, 6.75], "texture": "#0"},
"west": {"uv": [8, 6.75, 8.5, 7], "texture": "#0"},
"up": {"uv": [4.5, 8.25, 4, 7.75], "texture": "#0"},
"down": {"uv": [5, 7.75, 4.5, 8.25], "texture": "#0"}
}
},
{
"from": [7, 14, 11],
"to": [9, 15, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 13, 10]},
"faces": {
"north": {"uv": [7.25, 8, 7.75, 8.25], "texture": "#0"},
"east": {"uv": [0, 9, 0.25, 9.25], "texture": "#0"},
"south": {"uv": [7.75, 8, 8.25, 8.25], "texture": "#0"},
"west": {"uv": [9, 0, 9.25, 0.25], "texture": "#0"},
"up": {"uv": [8.75, 0.75, 8.25, 0.5], "texture": "#0"},
"down": {"uv": [8.75, 0.75, 8.25, 1], "texture": "#0"}
}
},
{
"from": [7, 14, 10],
"to": [9, 15, 11],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 13, 9]},
"faces": {
"north": {"uv": [8.25, 1, 8.75, 1.25], "texture": "#0"},
"east": {"uv": [0.25, 9, 0.5, 9.25], "texture": "#0"},
"south": {"uv": [8.25, 1.25, 8.75, 1.5], "texture": "#0"},
"west": {"uv": [9, 0.25, 9.25, 0.5], "texture": "#0"},
"up": {"uv": [8.75, 1.75, 8.25, 1.5], "texture": "#0"},
"down": {"uv": [8.75, 1.75, 8.25, 2], "texture": "#0"}
}
},
{
"from": [7, 13, 9],
"to": [9, 14, 10],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 13, 9]},
"faces": {
"north": {"uv": [8.25, 2, 8.75, 2.25], "texture": "#0"},
"east": {"uv": [0.5, 9, 0.75, 9.25], "texture": "#0"},
"south": {"uv": [8.25, 2.25, 8.75, 2.5], "texture": "#0"},
"west": {"uv": [0.75, 9, 1, 9.25], "texture": "#0"},
"up": {"uv": [3.25, 8.5, 2.75, 8.25], "texture": "#0"},
"down": {"uv": [3.75, 8.25, 3.25, 8.5], "texture": "#0"}
}
},
{
"from": [7, 11, 8],
"to": [9, 13, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 11, 7]},
"faces": {
"north": {"uv": [5, 7.75, 5.5, 8.25], "texture": "#0"},
"east": {"uv": [5.75, 8, 6, 8.5], "texture": "#0"},
"south": {"uv": [6.25, 7.75, 6.75, 8.25], "texture": "#0"},
"west": {"uv": [3.75, 8.25, 4, 8.75], "texture": "#0"},
"up": {"uv": [4.5, 8.5, 4, 8.25], "texture": "#0"},
"down": {"uv": [5, 8.25, 4.5, 8.5], "texture": "#0"}
}
},
{
"from": [7, 10, 9],
"to": [9, 11, 11],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 9, 8]},
"faces": {
"north": {"uv": [5, 8.25, 5.5, 8.5], "texture": "#0"},
"east": {"uv": [8.25, 5, 8.75, 5.25], "texture": "#0"},
"south": {"uv": [8.25, 5.25, 8.75, 5.5], "texture": "#0"},
"west": {"uv": [6.25, 8.25, 6.75, 8.5], "texture": "#0"},
"up": {"uv": [8.25, 7.5, 7.75, 7], "texture": "#0"},
"down": {"uv": [8.25, 7.5, 7.75, 8], "texture": "#0"}
}
},
{
"from": [7, 13, 14],
"to": [9, 14, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 13, 14]},
"faces": {
"north": {"uv": [6.75, 8.25, 7.25, 8.5], "texture": "#0"},
"east": {"uv": [1, 9, 1.25, 9.25], "texture": "#0"},
"south": {"uv": [8.25, 7, 8.75, 7.25], "texture": "#0"},
"west": {"uv": [1.25, 9, 1.5, 9.25], "texture": "#0"},
"up": {"uv": [7.75, 8.5, 7.25, 8.25], "texture": "#0"},
"down": {"uv": [8.75, 7.25, 8.25, 7.5], "texture": "#0"}
}
},
{
"from": [7, 7, 4],
"to": [9, 9, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 7, 4]},
"faces": {
"north": {"uv": [0, 8, 0.5, 8.5], "texture": "#0"},
"east": {"uv": [8.25, 7.5, 8.5, 8], "texture": "#0"},
"south": {"uv": [8, 0, 8.5, 0.5], "texture": "#0"},
"west": {"uv": [7.75, 8.25, 8, 8.75], "texture": "#0"},
"up": {"uv": [8.5, 8.5, 8, 8.25], "texture": "#0"},
"down": {"uv": [8.75, 8, 8.25, 8.25], "texture": "#0"}
}
},
{
"from": [7, 8, 3],
"to": [9, 9, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 7, 2]},
"faces": {
"north": {"uv": [0, 8.5, 0.5, 8.75], "texture": "#0"},
"east": {"uv": [1.5, 9, 1.75, 9.25], "texture": "#0"},
"south": {"uv": [8.5, 0, 9, 0.25], "texture": "#0"},
"west": {"uv": [1.75, 9, 2, 9.25], "texture": "#0"},
"up": {"uv": [9, 0.5, 8.5, 0.25], "texture": "#0"},
"down": {"uv": [3.25, 8.5, 2.75, 8.75], "texture": "#0"}
}
},
{
"from": [7, 9, 2],
"to": [9, 12, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 9, 1]},
"faces": {
"north": {"uv": [4, 7, 4.5, 7.75], "texture": "#0"},
"east": {"uv": [2.25, 8, 2.5, 8.75], "texture": "#0"},
"south": {"uv": [4.5, 7, 5, 7.75], "texture": "#0"},
"west": {"uv": [2.5, 8, 2.75, 8.75], "texture": "#0"},
"up": {"uv": [9, 3, 8.5, 2.75], "texture": "#0"},
"down": {"uv": [9, 3, 8.5, 3.25], "texture": "#0"}
}
},
{
"from": [7, 12, 3],
"to": [9, 13, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 12, 3]},
"faces": {
"north": {"uv": [3.25, 8.5, 3.75, 8.75], "texture": "#0"},
"east": {"uv": [2, 9, 2.25, 9.25], "texture": "#0"},
"south": {"uv": [8.5, 3.25, 9, 3.5], "texture": "#0"},
"west": {"uv": [2.25, 9, 2.5, 9.25], "texture": "#0"},
"up": {"uv": [9, 3.75, 8.5, 3.5], "texture": "#0"},
"down": {"uv": [9, 3.75, 8.5, 4], "texture": "#0"}
}
},
{
"from": [7, 13, 4],
"to": [9, 14, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 13, 4]},
"faces": {
"north": {"uv": [4, 8.5, 4.5, 8.75], "texture": "#0"},
"east": {"uv": [2.5, 9, 2.75, 9.25], "texture": "#0"},
"south": {"uv": [8.5, 4, 9, 4.25], "texture": "#0"},
"west": {"uv": [2.75, 9, 3, 9.25], "texture": "#0"},
"up": {"uv": [9, 4.5, 8.5, 4.25], "texture": "#0"},
"down": {"uv": [5, 8.5, 4.5, 8.75], "texture": "#0"}
}
},
{
"from": [7, 10, 5],
"to": [9, 14, 6],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 10, 5]},
"faces": {
"north": {"uv": [5.5, 1.5, 6, 2.5], "texture": "#0"},
"east": {"uv": [6.75, 7.25, 7, 8.25], "texture": "#0"},
"south": {"uv": [7, 3.5, 7.5, 4.5], "texture": "#0"},
"west": {"uv": [0.5, 8, 0.75, 9], "texture": "#0"},
"up": {"uv": [9, 4.75, 8.5, 4.5], "texture": "#0"},
"down": {"uv": [9, 4.75, 8.5, 5], "texture": "#0"}
}
},
{
"from": [7, 10, 6],
"to": [9, 15, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 14, 6]},
"faces": {
"north": {"uv": [6.5, 6, 7, 7.25], "texture": "#0"},
"east": {"uv": [7, 5.75, 7.25, 7], "texture": "#0"},
"south": {"uv": [5.5, 6.75, 6, 8], "texture": "#0"},
"west": {"uv": [7, 7, 7.25, 8.25], "texture": "#0"},
"up": {"uv": [5.5, 8.75, 5, 8.5], "texture": "#0"},
"down": {"uv": [6, 8.5, 5.5, 8.75], "texture": "#0"}
}
},
{
"from": [7, 10, 7],
"to": [9, 15, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 14, 7]},
"faces": {
"north": {"uv": [7, 0, 7.5, 1.25], "texture": "#0"},
"east": {"uv": [7.25, 5.75, 7.5, 7], "texture": "#0"},
"south": {"uv": [7, 1.25, 7.5, 2.5], "texture": "#0"},
"west": {"uv": [6, 7.25, 6.25, 8.5], "texture": "#0"},
"up": {"uv": [9, 8, 8.5, 7.75], "texture": "#0"},
"down": {"uv": [8.5, 8.5, 8, 8.75], "texture": "#0"}
}
},
{
"from": [7, 15, 6],
"to": [9, 16, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 15, 6]},
"faces": {
"north": {"uv": [8.5, 5.5, 9, 5.75], "texture": "#0"},
"east": {"uv": [9, 2.75, 9.25, 3], "texture": "#0"},
"south": {"uv": [8.5, 5.75, 9, 6], "texture": "#0"},
"west": {"uv": [3, 9, 3.25, 9.25], "texture": "#0"},
"up": {"uv": [6.5, 8.75, 6, 8.5], "texture": "#0"},
"down": {"uv": [9, 6, 8.5, 6.25], "texture": "#0"}
}
},
{
"from": [7, 15, 13],
"to": [9, 16, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 15, 13]},
"faces": {
"north": {"uv": [8.5, 6.5, 9, 6.75], "texture": "#0"},
"east": {"uv": [9, 3, 9.25, 3.25], "texture": "#0"},
"south": {"uv": [8.5, 6.75, 9, 7], "texture": "#0"},
"west": {"uv": [3.25, 9, 3.5, 9.25], "texture": "#0"},
"up": {"uv": [7.5, 8.75, 7, 8.5], "texture": "#0"},
"down": {"uv": [9, 7.5, 8.5, 7.75], "texture": "#0"}
}
},
{
"from": [7, 10, 8],
"to": [9, 11, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 10, 8]},
"faces": {
"north": {"uv": [8.5, 8.25, 9, 8.5], "texture": "#0"},
"east": {"uv": [9, 3.25, 9.25, 3.5], "texture": "#0"},
"south": {"uv": [8.5, 8.5, 9, 8.75], "texture": "#0"},
"west": {"uv": [3.5, 9, 3.75, 9.25], "texture": "#0"},
"up": {"uv": [0.5, 9, 0, 8.75], "texture": "#0"},
"down": {"uv": [9.25, 0.5, 8.75, 0.75], "texture": "#0"}
}
},
{
"from": [7, 14, 8],
"to": [9, 15, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 14, 8]},
"faces": {
"north": {"uv": [8.75, 0.75, 9.25, 1], "texture": "#0"},
"east": {"uv": [9, 3.5, 9.25, 3.75], "texture": "#0"},
"south": {"uv": [8.75, 1, 9.25, 1.25], "texture": "#0"},
"west": {"uv": [3.75, 9, 4, 9.25], "texture": "#0"},
"up": {"uv": [9.25, 1.5, 8.75, 1.25], "texture": "#0"},
"down": {"uv": [9.25, 1.5, 8.75, 1.75], "texture": "#0"}
}
},
{
"from": [7, 13, 8],
"to": [9, 14, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 13, 8]},
"faces": {
"north": {"uv": [8.75, 1.75, 9.25, 2], "texture": "#0"},
"east": {"uv": [9, 3.75, 9.25, 4], "texture": "#0"},
"south": {"uv": [8.75, 2, 9.25, 2.25], "texture": "#0"},
"west": {"uv": [4, 9, 4.25, 9.25], "texture": "#0"},
"up": {"uv": [2.75, 9, 2.25, 8.75], "texture": "#0"},
"down": {"uv": [9.25, 2.25, 8.75, 2.5], "texture": "#0"}
}
},
{
"from": [7, 15, 8],
"to": [9, 16, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 15, 8]},
"faces": {
"north": {"uv": [8.75, 2.5, 9.25, 2.75], "texture": "#0"},
"east": {"uv": [9, 4, 9.25, 4.25], "texture": "#0"},
"south": {"uv": [2.75, 8.75, 3.25, 9], "texture": "#0"},
"west": {"uv": [4.25, 9, 4.5, 9.25], "texture": "#0"},
"up": {"uv": [3.75, 9, 3.25, 8.75], "texture": "#0"},
"down": {"uv": [4.25, 8.75, 3.75, 9], "texture": "#0"}
}
},
{
"from": [7, 15, 7],
"to": [9, 16, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 15, 7]},
"faces": {
"north": {"uv": [4.25, 8.75, 4.75, 9], "texture": "#0"},
"east": {"uv": [9, 4.25, 9.25, 4.5], "texture": "#0"},
"south": {"uv": [4.75, 8.75, 5.25, 9], "texture": "#0"},
"west": {"uv": [4.5, 9, 4.75, 9.25], "texture": "#0"},
"up": {"uv": [9.25, 5.25, 8.75, 5], "texture": "#0"},
"down": {"uv": [5.75, 8.75, 5.25, 9], "texture": "#0"}
}
},
{
"from": [7, 15, 9],
"to": [9, 16, 10],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 15, 9]},
"faces": {
"north": {"uv": [8.75, 5.25, 9.25, 5.5], "texture": "#0"},
"east": {"uv": [9, 4.5, 9.25, 4.75], "texture": "#0"},
"south": {"uv": [5.75, 8.75, 6.25, 9], "texture": "#0"},
"west": {"uv": [4.75, 9, 5, 9.25], "texture": "#0"},
"up": {"uv": [6.75, 9, 6.25, 8.75], "texture": "#0"},
"down": {"uv": [7.25, 8.75, 6.75, 9], "texture": "#0"}
}
},
{
"from": [7, 14, 9],
"to": [9, 15, 10],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 14, 9]},
"faces": {
"north": {"uv": [8.75, 7, 9.25, 7.25], "texture": "#0"},
"east": {"uv": [9, 4.75, 9.25, 5], "texture": "#0"},
"south": {"uv": [7.25, 8.75, 7.75, 9], "texture": "#0"},
"west": {"uv": [5, 9, 5.25, 9.25], "texture": "#0"},
"up": {"uv": [9.25, 7.5, 8.75, 7.25], "texture": "#0"},
"down": {"uv": [8.25, 8.75, 7.75, 9], "texture": "#0"}
}
},
{
"from": [7, 15, 10],
"to": [9, 16, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 15, 10]},
"faces": {
"north": {"uv": [8.75, 8, 9.25, 8.25], "texture": "#0"},
"east": {"uv": [8, 2.5, 8.75, 2.75], "texture": "#0"},
"south": {"uv": [8.25, 8.75, 8.75, 9], "texture": "#0"},
"west": {"uv": [2.75, 8, 3.5, 8.25], "texture": "#0"},
"up": {"uv": [5.5, 7.75, 5, 7], "texture": "#0"},
"down": {"uv": [7.5, 5, 7, 5.75], "texture": "#0"}
}
},
{
"from": [7, 16, 7],
"to": [9, 17, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 16, 7]},
"faces": {
"north": {"uv": [8.5, 6.25, 9, 6.5], "texture": "#0"},
"east": {"uv": [7, 4.5, 8.5, 4.75], "texture": "#0"},
"south": {"uv": [6.5, 8.5, 7, 8.75], "texture": "#0"},
"west": {"uv": [7, 4.75, 8.5, 5], "texture": "#0"},
"up": {"uv": [5, 7, 4.5, 5.5], "texture": "#0"},
"down": {"uv": [5.5, 5.5, 5, 7], "texture": "#0"}
}
},
{
"from": [6, 6, 6],
"to": [7, 10, 10],
"rotation": {"angle": 0, "axis": "y", "origin": [5, 6, 6]},
"faces": {
"north": {"uv": [8, 0.5, 8.25, 1.5], "texture": "#0"},
"east": {"uv": [4, 3.5, 5, 4.5], "texture": "#0"},
"south": {"uv": [0.75, 8, 1, 9], "texture": "#0"},
"west": {"uv": [4, 4.5, 5, 5.5], "texture": "#0"},
"up": {"uv": [1.25, 9, 1, 8], "texture": "#0"},
"down": {"uv": [1.5, 8, 1.25, 9], "texture": "#0"}
}
},
{
"from": [9, 6, 6],
"to": [10, 10, 10],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 6, 6]},
"faces": {
"north": {"uv": [1.5, 8, 1.75, 9], "texture": "#0"},
"east": {"uv": [5, 3.5, 6, 4.5], "texture": "#0"},
"south": {"uv": [8, 1.5, 8.25, 2.5], "texture": "#0"},
"west": {"uv": [5, 4.5, 6, 5.5], "texture": "#0"},
"up": {"uv": [2, 9, 1.75, 8], "texture": "#0"},
"down": {"uv": [2.25, 8, 2, 9], "texture": "#0"}
}
}
]
}

View File

@ -0,0 +1,3 @@
{
"parent": "keeblarcraft:block/example_block"
}

View File

@ -0,0 +1,3 @@
{
"parent": "keeblarcraft:block/example_block_ore"
}

View File

@ -0,0 +1,3 @@
{
"parent": "keeblarcraft:block/example_statue"
}

View File

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "keeblarcraft:item/metaljacket_boots"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "keeblarcraft:item/metaljacket_chestplate"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "keeblarcraft:item/metaljacket_helmet"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "keeblarcraft:item/metaljacket_leggings"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

View File

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"keeblarcraft:example_block_ore"
]
}

View File

@ -0,0 +1,20 @@
{
"type": "minecraft:block",
"pools": [
{
"bonus_rolls": 0.0,
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
],
"entries": [
{
"type": "minecraft:item",
"name": "keeblarcraft:example_block"
}
],
"rolls": 1.0
}
]
}

View File

@ -0,0 +1,20 @@
{
"type": "minecraft:block",
"pools": [
{
"bonus_rolls": 0.0,
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
],
"entries": [
{
"type": "minecraft:item",
"name": "keeblarcraft:example_block_ore"
}
],
"rolls": 1.0
}
]
}

View File

@ -0,0 +1,18 @@
{
"type": "minecraft:crafting_shaped",
"category": "misc",
"pattern": [
" # ",
"###",
" # "
],
"key": {
"#": {
"item": "keeblarcraft:example_block_ore"
}
},
"result": {
"item": "keeblarcraft:example_block",
"count": 8
}
}

View File

@ -0,0 +1,21 @@
{
"type": "minecraft:crafting_shaped",
"category": "misc",
"pattern": [
"#C#",
"###",
"#C#"
],
"key": {
"#": {
"item": "keeblarcraft:example_block"
},
"C": {
"item": "minecraft:coal"
}
},
"result": {
"item": "keeblarcraft:example_block",
"count": 8
}
}

View File

@ -0,0 +1,13 @@
{
"type": "minecraft:crafting_shapeless",
"category": "building",
"ingredients": [
{
"item": "keeblarcraft:example_block"
}
],
"result": {
"item": "keeblarcraft:example_block",
"count": 1
}
}

View File

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"keeblarcraft:example_block",
"keeblarcraft:example_block_ore"
]
}

View File

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"keeblarcraft:example_block"
]
}

View File

@ -0,0 +1,35 @@
{
"schemaVersion": 1,
"id": "keeblarcraft",
"version": "0.1",
"name": "keeblarcraft",
"description": "My everything mod",
"authors": [
"Jkibbels"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"icon": "assets/keeblarcraft/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"jesse.keeblarcraft.Keeblarcraft"
],
"client": [
"jesse.keeblarcraft.KeeblarcraftClient"
]
},
"depends": {
"fabricloader": ">=0.15.11",
"minecraft": "~1.20",
"java": ">=17",
"fabric-api": "*",
"fabric-key-binding-api-v1": "*"
},
"suggests": {
"another-mod": "*"
}
}