Initial commit.

master
kalle 2021-08-06 21:24:40 +02:00
commit 37fc63ac94
26 changed files with 1405 additions and 0 deletions

118
.gitignore vendored Normal file
View File

@ -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

34
build.gradle Normal file
View File

@ -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]
}
}

0
gradle.properties Normal file
View File

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

Binary file not shown.

View File

@ -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

188
gradlew vendored Executable file
View File

@ -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" "$@"

100
gradlew.bat vendored Normal file
View File

@ -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

View File

@ -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)

View File

@ -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)

1
settings.gradle Normal file
View File

@ -0,0 +1 @@
rootProject.name = 'dchat'

View File

@ -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";
}

View File

@ -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();
}
}

View File

@ -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 "";
}
}

View File

@ -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<String> 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;
}
}

View File

@ -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<String> 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;
}
}

View File

@ -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<String> 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;
}
}

View File

@ -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;
}
}

View File

@ -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<TextColor> {
@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());
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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())))
);
}
}

View File

@ -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<String, Emote> 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<Map.Entry<String, Emote>> 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<Emote> emotes = DChat.gson.fromJson(new FileReader(emoteDB), new TypeToken<List<Emote>>() {}.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();
}
}

View File

@ -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<UUID, PlayerSettings> 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;
}
}

View File

@ -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<String> description;
public HoverEvent<Component> getTooltip() {
return HoverEvent.showText(
Component.text(symbol)
.append(Component.text(":" + name + ":\n\n").decorate(TextDecoration.BOLD))
.append(Component.text(String.join("\n", description))));
}
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1,22 @@
name: DChat
version: @version@
main: nl.kallestruik.dchat.DChat
api-version: 1.16
commands:
emojitest:
usage: "/emojitest <start> <end>"
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