commit 37fc63ac94ed0b275dffd64049806ded966666fa Author: Kalle Struik Date: Fri Aug 6 21:24:40 2021 +0200 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c37caf --- /dev/null +++ b/.gitignore @@ -0,0 +1,118 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..3331795 --- /dev/null +++ b/build.gradle @@ -0,0 +1,34 @@ +import org.apache.tools.ant.filters.ReplaceTokens + +plugins { + id 'java' + id "io.freefair.lombok" version "6.0.0-m2" +} + +group = 'nl.kallestruik' +version = '1.0' + +sourceCompatibility = '1.8' +targetCompatibility = '1.8' + +repositories { + mavenCentral() + maven { + name = 'papermc-repo' + url = 'https://papermc.io/repo/repository/maven-public/' + } + maven { + name = 'sonatype' + url = 'https://oss.sonatype.org/content/groups/public/' + } +} + +dependencies { + compileOnly 'com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT' +} + +processResources { + from(sourceSets.main.resources.srcDirs) { + filter ReplaceTokens, tokens: [version: version] + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e69de29 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..5c2d1cf Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..59b5f89 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..83f2acf --- /dev/null +++ b/gradlew @@ -0,0 +1,188 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9618d8d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/resource pack/generators/combine.py b/resource pack/generators/combine.py new file mode 100644 index 0000000..631aca9 --- /dev/null +++ b/resource pack/generators/combine.py @@ -0,0 +1,70 @@ +import json +import sys +import os +import pathlib +import shutil + +# A list of folders to check for icon_info.json files and sheets/ folders. +FOLDERS = ["mc_items"] + +def to_symbol_string(number): + return "\\ue" + str(hex(number))[2:].zfill(3) + +# The emotedb to store later. +emotedb = [] +current_index = 0 + +# A map containing information about all texture sheets and where they belong. +# Format: {folder_name: [{icons: [[],[],[]]}]} +sheets = {} + + +for folder in FOLDERS: + with open(f"{folder}/icon_info.json", "r") as icon_info_file: + icon_info = json.load(icon_info_file) + sheet_array = [] + for sheet in os.listdir(f"{folder}/sheets/"): + sheet_array.append({"icons": [['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000'], ['\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000', '\\u0000']]}) + + sheets[folder] = sheet_array + + for icon in icon_info: + symbol = to_symbol_string(current_index) + sheets[folder][icon["sheet"]]["icons"][icon["pos"] // 16][icon["pos"] % 16] = symbol + emotedb.append({ + "name": icon["name"], + "symbol": symbol, + "description": icon["description"] + }) + + current_index += 1 + +with open("emotedb.json", "w") as emotedb_file: + data = json.dumps(emotedb).encode().decode("unicode-escape") + emotedb_file.write(data) + +providers = [] + +os.mkdir + +pathlib.Path("pack/assets/dchat/textures/font/").mkdir(parents=True, exist_ok=True) +pathlib.Path("pack/assets/minecraft/font/").mkdir(parents=True, exist_ok=True) + +for sheet_group in sheets: + sheet_array = sheets[sheet_group] + for index in range(len(sheet_array)): + sheet = sheet_array[index] + shutil.copy(f"{sheet_group}/sheets/sheet_{index}.png", f"pack/assets/dchat/textures/font/{sheet_group}_{index}.png") + chars = [] + for row in sheet["icons"]: + chars.append("".join(row)) + providers.append({ + "type": "bitmap", + "file": f"dchat:font/{sheet_group}_{index}.png", + "ascent": 7, + "chars": chars + }) + +with open("pack/assets/minecraft/font/default.json", "w") as font_file: + data = json.dumps({"providers": providers}).encode().decode("unicode-escape") + font_file.write(data) diff --git a/resource pack/generators/mc_items/generate.py b/resource pack/generators/mc_items/generate.py new file mode 100644 index 0000000..f88a37b --- /dev/null +++ b/resource pack/generators/mc_items/generate.py @@ -0,0 +1,81 @@ +from PIL import Image +import os +import json + +# The size of each image in pixels. +IMAGE_SIZE = 16 +# The directory containing the item image files. +ITEM_DIR = "item/" +# The directory that willcontain the texture sheet files. +SHEET_DIR = "sheets/" + +# The size that the texture sheet will be. Should be 16 times the IMAGE_SIZE. +SHEET_SIZE = IMAGE_SIZE * 16 + +# Get a list of files in the item directory. +item_files = os.listdir(ITEM_DIR) + +# Sort the list of items. +item_files = sorted(item_files) + +# Create a new image of size SHEET_SIZE x SHEET_SIZE. +current_sheet = Image.new("RGBA", (SHEET_SIZE, SHEET_SIZE), (0, 0, 0, 0)) +# Set the current position on the sheet to 0. +current_pos = 0 +# Create an array to store finished sheets. +sheets = [] + +# Create an array to hold icon info. +icon_info = [] + +# Create the SHEET_DIR directory. +os.makedirs(SHEET_DIR) + +# Loop over every item and add it to the texture sheet we are currently working on. +for item_file in item_files: + # Get the name of the item. + name = item_file.replace(".png", "") + + # Load the image from disk. + image = Image.open(f"{ITEM_DIR}{item_file}") + + # Calculate our position on the sheet. + x = current_pos % 16 + y = current_pos // 16 + + # Paste the current item on the sheet. + current_sheet.paste(im=image, box=(x*16, y * 16)) + + icon_info.append({ + "name": name, + "sheet": len(sheets), + "pos": current_pos, + "description": ["A default minecraft item."] + }) + + # Move one texture over on the sheet. + current_pos += 1 + + # Check if the current sheet is full. + if current_pos == 256: + # If it is add it to the array of finished sheets. + sheets.append(current_sheet) + # And create a new one. + current_sheet = Image.new("RGBA", (SHEET_SIZE, SHEET_SIZE), (0, 0, 0, 0)) + # Move us back to the start of the current sheet. + current_pos = 0 + +# Check if the current sheet is partially filled. +if not current_pos == 0: + # If it is add it to the finished sheets. + sheets.append(current_sheet) + +# Loop over all finished sheets. +for i in range(len(sheets)): + sheet = sheets[i] + # Save the sheet to disk. + sheet.save(f"{SHEET_DIR}sheet_{i}.png") + +# Store icon_info into a json file. +with open("icon_info.json", "w") as icon_info_file: + json.dump(icon_info, icon_info_file, indent=4) diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..8404c90 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'dchat' diff --git a/src/main/java/nl/kallestruik/dchat/Config.java b/src/main/java/nl/kallestruik/dchat/Config.java new file mode 100644 index 0000000..b1052c1 --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/Config.java @@ -0,0 +1,10 @@ +package nl.kallestruik.dchat; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; + +public class Config { + public static final Component CHAT_FORMAT = GsonComponentSerializer.gson().deserialize("{\"text\": \"%USERNAME%: %MESSAGE%\"}"); + public static final String RESOURCE_PACK_URL_BASE = "http://dchat.kallestruik.nl/downloads/pack/"; + public static final String EMOTE_DB_URL = "http://dchat.kallestruik.nl/downloads/emotedb/emotedb.json"; +} diff --git a/src/main/java/nl/kallestruik/dchat/DChat.java b/src/main/java/nl/kallestruik/dchat/DChat.java new file mode 100644 index 0000000..8ccb61d --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/DChat.java @@ -0,0 +1,77 @@ +package nl.kallestruik.dchat; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.kyori.adventure.text.format.TextColor; +import nl.kallestruik.dchat.commands.CommandCAdmin; +import nl.kallestruik.dchat.commands.CommandChat; +import nl.kallestruik.dchat.commands.CommandChatsettings; +import nl.kallestruik.dchat.commands.CommandEmojitest; +import nl.kallestruik.dchat.gson.TextColorTypeAdapter; +import nl.kallestruik.dchat.listeners.ChatListener; +import nl.kallestruik.dchat.listeners.JoinListener; +import nl.kallestruik.dchat.managers.EmojiManager; +import nl.kallestruik.dchat.managers.PlayerDataManager; +import org.bukkit.plugin.java.JavaPlugin; +import org.objectweb.asm.Type; + +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.UUID; +import java.util.logging.Logger; + +public final class DChat extends JavaPlugin { + public static DChat instance; + public static Logger logger; + public static Gson gson = new GsonBuilder() + .registerTypeAdapter(TextColor.class, new TextColorTypeAdapter()) + .create(); + public static String packHash = ""; + + @Override + public void onEnable() { + instance = this; + logger = this.getLogger(); + + createDataFolderIfNotExists(); + + updateResourcePackHash(); + + EmojiManager.loadEmotesFromDisk(); + + // Load console player data. + PlayerDataManager.loadPlayerData(new UUID(0, 0)); + + getServer().getPluginManager().registerEvents(new ChatListener(),this); + getServer().getPluginManager().registerEvents(new JoinListener(), this); + + getCommand("emojitest").setExecutor(new CommandEmojitest()); + + getCommand("chat").setExecutor(new CommandChat()); + getCommand("chat").setTabCompleter(new CommandChat()); + + getCommand("chatsettings").setExecutor(new CommandChatsettings()); + getCommand("chatsettings").setTabCompleter(new CommandChatsettings()); + + getCommand("cadmin").setExecutor(new CommandCAdmin()); + getCommand("cadmin").setTabCompleter(new CommandCAdmin()); + } + + @Override + public void onDisable() { + // Plugin shutdown logic + } + + private void updateResourcePackHash() { + packHash = Util.httpGET(Config.RESOURCE_PACK_URL_BASE + "get_hash.php"); + } + + private void createDataFolderIfNotExists() { + File dataFolder = new File(this.getDataFolder(), "data"); + dataFolder.mkdirs(); + } +} diff --git a/src/main/java/nl/kallestruik/dchat/Util.java b/src/main/java/nl/kallestruik/dchat/Util.java new file mode 100644 index 0000000..93d62a1 --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/Util.java @@ -0,0 +1,58 @@ +package nl.kallestruik.dchat; + + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import javax.net.ssl.HttpsURLConnection; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.UUID; +import java.util.stream.Collectors; + +public class Util { + + public static UUID getUUID(CommandSender sender) { + // A UUID of all zero's is used as console. + return sender instanceof Player ? ((Player) sender).getUniqueId() : new UUID(0, 0); + } + + /** + * Method to export a file from the jar to an external folder. + * + * @param resourceName the name of the resource + * @param output file that will be outputted to + */ + public static void exportResource(String resourceName, File output) { + try (InputStream stream = Util.class.getResourceAsStream(resourceName); OutputStream resStreamOut = new FileOutputStream(output)) { + if (stream == null) { + throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file."); + } + + int readBytes; + byte[] buffer = new byte[4096]; + while ((readBytes = stream.read(buffer)) > 0) { + resStreamOut.write(buffer, 0, readBytes); + } + } catch (Exception ex) { + //Fail silently + } + } + + public static String httpGET(String url) { + try { + HttpURLConnection connection = ((HttpURLConnection) new URL(url).openConnection()); + connection.setRequestMethod("GET"); + connection.setRequestProperty("User-Agent", "DChat"); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + return reader.lines().collect(Collectors.joining()); + } + } catch (IOException e) { + e.printStackTrace(); + } + + return ""; + } + +} diff --git a/src/main/java/nl/kallestruik/dchat/commands/CommandCAdmin.java b/src/main/java/nl/kallestruik/dchat/commands/CommandCAdmin.java new file mode 100644 index 0000000..090b778 --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/commands/CommandCAdmin.java @@ -0,0 +1,83 @@ +package nl.kallestruik.dchat.commands; + +import nl.kallestruik.dchat.managers.EmojiManager; +import nl.kallestruik.dchat.managers.PlayerDataManager; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class CommandCAdmin implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (args.length == 1 && args[0].equalsIgnoreCase("update-emotes")) { + EmojiManager.updateEmotes(); + return true; + } else if (args.length == 4 && args[0].equalsIgnoreCase("user")) { + UUID uuid = Bukkit.getPlayer(args[2]).getUniqueId(); + switch (args[2]) { + case "pingSound": + if (PlayerDataManager.changePingSoundSettingForUUID(uuid, args[3])) + sender.sendMessage("Changed ping sound to: " + args[3]); + else + sender.sendMessage("That is not a valid value!"); + break; + case "nameColor": + if (PlayerDataManager.changeNameColorSettingForUUID(uuid, args[3])) + sender.sendMessage("Changed name color to: " + args[3]); + else + sender.sendMessage("That is not a valid value!"); + break; + case "prefixColor": + if (PlayerDataManager.changePrefixColorSettingForUUID(uuid, args[3])) + sender.sendMessage("Changed prefix color to: " + args[3]); + else + sender.sendMessage("That is not a valid value!"); + break; + case "prefix": + String prefix = String.join(" ", Arrays.copyOfRange(args, 3, args.length)); + if (PlayerDataManager.changePrefixSettingForUUID(uuid, prefix)) + sender.sendMessage("Changed prefix to: " + prefix); + else + sender.sendMessage("That is not a valid value!"); + break; + } + } + + + return false; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + if (args.length == 1) + return List.of("update-emotes", "user").stream().filter((option) -> option.startsWith(args[0])).collect(Collectors.toList()); + + if (args.length == 3) { + switch (args[0]) { + case "user": { + return List.of("pingSound", "nameColor", "prefixColor", "prefix").stream().filter((option) -> option.startsWith(args[1])).collect(Collectors.toList()); + } + } + } + + if (args.length == 4) + switch (args[2]) { + case "pingSound": + return List.of("Yes", "No"); + case "nameColor": + case "prefixColor": + return List.of("#"); + } + + return null; + } +} diff --git a/src/main/java/nl/kallestruik/dchat/commands/CommandChat.java b/src/main/java/nl/kallestruik/dchat/commands/CommandChat.java new file mode 100644 index 0000000..801002f --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/commands/CommandChat.java @@ -0,0 +1,47 @@ +package nl.kallestruik.dchat.commands; + +import net.kyori.adventure.text.Component; +import nl.kallestruik.dchat.managers.ChatManager; +import nl.kallestruik.dchat.managers.EmojiManager; +import nl.kallestruik.dchat.types.Emote; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class CommandChat implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (args.length < 1) + //TODO: Add warning that you need a chat message + return true; + + ChatManager.sendMessage(sender, Component.text(String.join(" ", args))); + + return false; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + if (args.length == 0) + return null; + + String last = args[args.length - 1]; + + // Tab completion for @ mentions. + if (last.startsWith("@")) + return Bukkit.getOnlinePlayers().stream().map((player) -> "@" + player.getName()).filter((name) -> name.startsWith(last)).collect(Collectors.toList()); + + if (last.startsWith(":")) + return EmojiManager.findMatches(last.substring(1)).stream().map(Map.Entry::getValue).map(Emote::getSymbol).collect(Collectors.toList()); + + return null; + } +} diff --git a/src/main/java/nl/kallestruik/dchat/commands/CommandChatsettings.java b/src/main/java/nl/kallestruik/dchat/commands/CommandChatsettings.java new file mode 100644 index 0000000..fb3c028 --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/commands/CommandChatsettings.java @@ -0,0 +1,71 @@ +package nl.kallestruik.dchat.commands; + +import nl.kallestruik.dchat.Util; +import nl.kallestruik.dchat.managers.PlayerDataManager; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class CommandChatsettings implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (args.length < 2) + return false; + + UUID uuid = Util.getUUID(sender); + + switch (args[0]) { + case "pingSound": + if (PlayerDataManager.changePingSoundSettingForUUID(uuid, args[1])) + sender.sendMessage("Changed ping sound to: " + args[1]); + else + sender.sendMessage("That is not a valid value!"); + break; + case "nameColor": + if (PlayerDataManager.changeNameColorSettingForUUID(uuid, args[1])) + sender.sendMessage("Changed name color to: " + args[1]); + else + sender.sendMessage("That is not a valid value!"); + break; + case "prefixColor": + if (PlayerDataManager.changePrefixColorSettingForUUID(uuid, args[1])) + sender.sendMessage("Changed prefix color to: " + args[1]); + else + sender.sendMessage("That is not a valid value!"); + break; + case "prefix": + if (PlayerDataManager.changePrefixSettingForUUID(uuid, String.join(" ", Arrays.copyOfRange(args, 1, args.length)))) + sender.sendMessage("Changed prefix to: " + args[1]); + else + sender.sendMessage("That is not a valid value!"); + break; + } + + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + if (args.length == 1) + return List.of("pingSound", "nameColor", "prefixColor", "prefix").stream().filter((option) -> option.startsWith(args[0])).collect(Collectors.toList()); + + if (args.length == 2) + switch (args[0]) { + case "pingSound": + return List.of("Yes", "No"); + case "nameColor": + case "prefixColor": + return List.of("#"); + } + + return null; + } +} diff --git a/src/main/java/nl/kallestruik/dchat/commands/CommandEmojitest.java b/src/main/java/nl/kallestruik/dchat/commands/CommandEmojitest.java new file mode 100644 index 0000000..130c810 --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/commands/CommandEmojitest.java @@ -0,0 +1,39 @@ +package nl.kallestruik.dchat.commands; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +public class CommandEmojitest implements CommandExecutor { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (args.length < 2) + return false; + + try { + for (int i = Integer.parseInt(args[0]); i < Integer.parseInt(args[1]); i++) { + for (int j = 0; j < 16; j++) { + StringBuilder line = new StringBuilder("0xE" + Integer.toString(i, 16) + Integer.toString(j, 16) + "0 "); + for (int k = 0; k < 16; k++) { + byte[] bytes = new byte[2]; + bytes[0] = (byte) (0xE0 + i); + bytes[1] = (byte) (j * 16 + k); + try { + line.append(new String(bytes, StandardCharsets.UTF_16)); + } catch (Exception e) { + e.printStackTrace(); + } + } + sender.sendMessage(line.toString()); + } + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + return true; + } +} diff --git a/src/main/java/nl/kallestruik/dchat/gson/TextColorTypeAdapter.java b/src/main/java/nl/kallestruik/dchat/gson/TextColorTypeAdapter.java new file mode 100644 index 0000000..42093ca --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/gson/TextColorTypeAdapter.java @@ -0,0 +1,20 @@ +package nl.kallestruik.dchat.gson; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import net.kyori.adventure.text.format.TextColor; + +import java.io.IOException; + +public class TextColorTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, TextColor value) throws IOException { + out.value(value.value()); + } + + @Override + public TextColor read(JsonReader in) throws IOException { + return TextColor.color(in.nextInt()); + } +} diff --git a/src/main/java/nl/kallestruik/dchat/listeners/ChatListener.java b/src/main/java/nl/kallestruik/dchat/listeners/ChatListener.java new file mode 100644 index 0000000..a3c0fe1 --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/listeners/ChatListener.java @@ -0,0 +1,14 @@ +package nl.kallestruik.dchat.listeners; + +import io.papermc.paper.event.player.AsyncChatEvent; +import nl.kallestruik.dchat.managers.ChatManager; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class ChatListener implements Listener { + @EventHandler + public void onChat(AsyncChatEvent event) { + ChatManager.sendMessage(event.getPlayer(), event.message()); + event.setCancelled(true); + } +} diff --git a/src/main/java/nl/kallestruik/dchat/listeners/JoinListener.java b/src/main/java/nl/kallestruik/dchat/listeners/JoinListener.java new file mode 100644 index 0000000..adaf49a --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/listeners/JoinListener.java @@ -0,0 +1,21 @@ +package nl.kallestruik.dchat.listeners; + +import nl.kallestruik.dchat.Config; +import nl.kallestruik.dchat.DChat; +import nl.kallestruik.dchat.managers.PlayerDataManager; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class JoinListener implements Listener { + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + + player.setResourcePack(Config.RESOURCE_PACK_URL_BASE + "pack.zip", DChat.packHash); + + PlayerDataManager.loadPlayerData(player.getUniqueId()); + } +} diff --git a/src/main/java/nl/kallestruik/dchat/managers/ChatManager.java b/src/main/java/nl/kallestruik/dchat/managers/ChatManager.java new file mode 100644 index 0000000..ab616af --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/managers/ChatManager.java @@ -0,0 +1,106 @@ +package nl.kallestruik.dchat.managers; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.format.TextColor; +import nl.kallestruik.dchat.Config; +import nl.kallestruik.dchat.types.Emote; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.UUID; + +public class ChatManager { + + public static void sendMessage(CommandSender sender, Component message) { + message = mentions(message); + message = item(message, sender); + message = emoji(message); + + message = populateTemplate(message, sender); + Bukkit.broadcast(message, "dchat.receive"); + Bukkit.getConsoleSender().sendMessage(message); + } + + private static Component populateTemplate(Component message, CommandSender sender) { + UUID uuid = sender instanceof Player ? ((Player) sender).getUniqueId() : new UUID(0,0); // Console get UUID 0 + + return Config.CHAT_FORMAT + .replaceText((builder) -> builder.match("%PREFIX%") + .replacement(PlayerDataManager.getPrefixForUUID(uuid)) + .times(1)) + .replaceText((builder) -> builder.match("%USERNAME%") + .replacement(Component.text(sender.getName()).color(PlayerDataManager.getColorForUUID(uuid))) + .times(1)) + .replaceText((builder) -> builder.match("%MESSAGE%") + .replacement(message) + .times(1)); + } + + private static Component mentions(Component message) { + for (Player player : Bukkit.getOnlinePlayers()) { + if (!((TextComponent)message).content().matches(".*@" + player.getName() + "\\b.*")) + continue; + + if (PlayerDataManager.getSettingsForUUID(player.getUniqueId()).isPingSound()) + player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1); + + message = message.replaceText((builder) -> builder + .match("\\@" + player.getName() + "\\b") + .replacement( + Component.text("@" + player.getName()) + .hoverEvent(player.asHoverEvent()) + .clickEvent(ClickEvent.suggestCommand("/msg " + player.getName() + " ")) + .color(PlayerDataManager.getColorForUUID(player.getUniqueId())) + ).times(1)); + } + return message; + } + + private static Component emoji(Component message) { + String messageText = ((TextComponent)message).content(); + + if (!messageText.contains(":")) + return message; + + String[] parts = messageText.split(":"); + for (int i = 1; i < parts.length; i++) { + String emojiName = parts[i].split(":")[0]; + Emote emote = EmojiManager.getExact(emojiName); + if (emote != null) + message = message.replaceText((builder) -> builder + .match(":" + emojiName + ":") + .replacement(Component.text(emote.getSymbol()))); + } + + return message; + + } + + private static Component item(Component message, CommandSender sender) { + if (!(sender instanceof Player)) + return message; + + Player player = (Player) sender; + ItemStack item = player.getInventory().getItemInMainHand(); + + Component itemName = Component.text(item.getI18NDisplayName()); + if (item.hasItemMeta() && item.getItemMeta().hasDisplayName()) + itemName = item.getItemMeta().displayName(); + + if (itemName == null) + return message; + + //TODO: Better item color? + Component toShow = Component.text("[").append(itemName).append(Component.text("]")).color(TextColor.color(0xC3C3)); + + return message.replaceText(( + builder -> builder.match("\\[item\\]") + .replacement(toShow.hoverEvent(item.asHoverEvent()))) + ); + } +} diff --git a/src/main/java/nl/kallestruik/dchat/managers/EmojiManager.java b/src/main/java/nl/kallestruik/dchat/managers/EmojiManager.java new file mode 100644 index 0000000..96aed15 --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/managers/EmojiManager.java @@ -0,0 +1,63 @@ +package nl.kallestruik.dchat.managers; + +import com.google.common.reflect.TypeToken; +import nl.kallestruik.dchat.Config; +import nl.kallestruik.dchat.DChat; +import nl.kallestruik.dchat.Util; +import nl.kallestruik.dchat.types.Emote; + +import java.io.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class EmojiManager { + private static final HashMap emojiMap = new HashMap<>(); +// static { +// emojiMap.put("ban_dream", new Emoji("ban_dream", "\ue000", "Just ban him already!")); +// emojiMap.put("wood_pickaxe", new Emoji("wood_pickaxe", "\ue001", "The most basic pickaxe.")); +// emojiMap.put("wood_shovel", new Emoji("wood_shovel", "\ue002", "The most basic shovel.")); +// emojiMap.put("wood_axe", new Emoji("wood_axe", "\ue003", "The most basic axe.")); +// emojiMap.put("wood_hoe", new Emoji("wood_hoe", "\ue004", "The most basic hoe.")); +// } + + public static Set> findMatches(String search) { + return emojiMap.entrySet().stream().filter((entry) -> entry.getKey().contains(search)).collect(Collectors.toSet()); + } + + public static Emote getExact(String emojiName) { + return emojiMap.getOrDefault(emojiName, null); + } + + public static void loadEmotesFromDisk() { + File emoteDB = new File(DChat.instance.getDataFolder(), "emotedb.json"); + if (!emoteDB.exists()) { + DChat.logger.warning("Could not load emotes from disk. You should run /cadmin update-emotes"); + return; + } + + try { + List emotes = DChat.gson.fromJson(new FileReader(emoteDB), new TypeToken>() {}.getType()); + emotes.forEach((emote) -> { + emojiMap.put(emote.getName(), emote); + }); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + + public static void updateEmotes() { + String emoteDBJson = Util.httpGET(Config.EMOTE_DB_URL); + File emoteDB = new File(DChat.instance.getDataFolder(), "emotedb.json"); + + try (FileWriter writer = new FileWriter(emoteDB)) { + writer.write(emoteDBJson); + } catch (IOException e) { + e.printStackTrace(); + } + + loadEmotesFromDisk(); + } +} diff --git a/src/main/java/nl/kallestruik/dchat/managers/PlayerDataManager.java b/src/main/java/nl/kallestruik/dchat/managers/PlayerDataManager.java new file mode 100644 index 0000000..6ce4d0b --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/managers/PlayerDataManager.java @@ -0,0 +1,105 @@ +package nl.kallestruik.dchat.managers; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; +import nl.kallestruik.dchat.DChat; +import nl.kallestruik.dchat.types.PlayerSettings; + +import java.io.File; +import java.util.HashMap; +import java.util.Locale; +import java.util.UUID; + +public class PlayerDataManager { + private static final HashMap playerSettings = new HashMap<>(); + + public static Component getPrefixForUUID(UUID uuid) { + PlayerSettings settings = getSettingsForUUID(uuid); + + return Component.text(settings.getPrefix()).color(TextColor.color(settings.getPrefixColor())); + } + + public static TextColor getColorForUUID(UUID uuid) { + PlayerSettings settings = getSettingsForUUID(uuid); + + return settings.getNameColor(); + } + + public static void loadPlayerData(UUID uuid) { + File dataFile = new File(DChat.instance.getDataFolder(), "/data/" + uuid.toString() + ".json"); + PlayerSettings settings = PlayerSettings.loadFromFile(dataFile); + if (settings == null) + return; + + playerSettings.put(uuid, settings); + } + + public static PlayerSettings getSettingsForUUID(UUID uuid) { + return playerSettings.getOrDefault(uuid, new PlayerSettings(uuid)); + } + + public static boolean changePingSoundSettingForUUID(UUID uuid, String value) { + boolean enablePingSound; + switch (value.toLowerCase(Locale.ROOT)) { + case "true": + case "yes": + case "enable": + enablePingSound = true; + break; + case "false": + case "no": + case "disable": + enablePingSound = false; + break; + default: + return false; + } + + PlayerSettings settings = playerSettings.getOrDefault(uuid, new PlayerSettings(uuid)); + settings.setPingSound(enablePingSound); + playerSettings.put(uuid, settings); + settings.save(); + return true; + } + + public static boolean changeNameColorSettingForUUID(UUID uuid, String value) { + if (!value.startsWith("#")) + value = "#" + value; + + TextColor nameColor = TextColor.fromCSSHexString(value); + if (nameColor == null) + return false; + + PlayerSettings settings = playerSettings.getOrDefault(uuid, new PlayerSettings(uuid)); + settings.setNameColor(nameColor); + playerSettings.put(uuid, settings); + settings.save(); + return true; + } + + public static boolean changePrefixColorSettingForUUID(UUID uuid, String value) { + if (!value.startsWith("#")) + value = "#" + value; + + TextColor prefixColor = TextColor.fromCSSHexString(value); + if (prefixColor == null) + return false; + + PlayerSettings settings = playerSettings.getOrDefault(uuid, new PlayerSettings(uuid)); + settings.setPrefixColor(prefixColor); + playerSettings.put(uuid, settings); + settings.save(); + return true; + } + + public static boolean changePrefixSettingForUUID(UUID uuid, String value) { + if (value == null || value.isEmpty()) + return false; + + PlayerSettings settings = playerSettings.getOrDefault(uuid, new PlayerSettings(uuid)); + settings.setPrefix(value); + playerSettings.put(uuid, settings); + settings.save(); + return true; + } +} diff --git a/src/main/java/nl/kallestruik/dchat/types/Emote.java b/src/main/java/nl/kallestruik/dchat/types/Emote.java new file mode 100644 index 0000000..5be36ad --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/types/Emote.java @@ -0,0 +1,24 @@ +package nl.kallestruik.dchat.types; + +import lombok.AllArgsConstructor; +import lombok.Data; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.TextDecoration; + +import java.util.List; + +@Data +@AllArgsConstructor +public class Emote { + private String name; + private String symbol; + private List description; + + public HoverEvent getTooltip() { + return HoverEvent.showText( + Component.text(symbol) + .append(Component.text(":" + name + ":\n\n").decorate(TextDecoration.BOLD)) + .append(Component.text(String.join("\n", description)))); + } +} diff --git a/src/main/java/nl/kallestruik/dchat/types/PlayerSettings.java b/src/main/java/nl/kallestruik/dchat/types/PlayerSettings.java new file mode 100644 index 0000000..4755d8d --- /dev/null +++ b/src/main/java/nl/kallestruik/dchat/types/PlayerSettings.java @@ -0,0 +1,48 @@ +package nl.kallestruik.dchat.types; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import net.kyori.adventure.text.format.TextColor; +import nl.kallestruik.dchat.DChat; + +import java.io.*; +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PlayerSettings { + private UUID owner; + private boolean pingSound = true; + private TextColor nameColor = TextColor.color(0xFFFFFF); + private TextColor prefixColor = TextColor.color(0xFFFFFF); + private String prefix = "default"; + + public PlayerSettings(UUID owner) { + this.owner = owner; + } + + public static PlayerSettings loadFromFile(File dataFile) { + if (!dataFile.exists()) { + return null; + } + + try { + return DChat.gson.fromJson(new FileReader(dataFile), PlayerSettings.class); + } catch (FileNotFoundException e) { + DChat.logger.warning("Could not load player data file: " + dataFile.getName()); + } + + return null; + } + + public void save() { + File dataFile = new File(DChat.instance.getDataFolder(), "/data/" + owner.toString() + ".json"); + try (FileWriter writer = new FileWriter(dataFile)) { + writer.write(DChat.gson.toJson(this)); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..6c6dd50 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,22 @@ +name: DChat +version: @version@ +main: nl.kallestruik.dchat.DChat +api-version: 1.16 +commands: + emojitest: + usage: "/emojitest " + description: "Debugging command to see all the emojis within the specified space." + chat: + description: "Send a chat message with tab completion." + aliases: [ "c" ] + chatsettings: + description: "Change your chat settings" + aliases: [ "csettings" ] + cadmin: + description: "Admin commands for DChat" + permission: "dchat.admin" +permissions: + dchat.admin: + default: op + dchat.receive: + default: true