diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..37b249b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.gradle/ +/.idea/ +/build/ +/libs/ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d3dc6e8 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,188 @@ +# European Union Public Licence v. 1.2 +EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the *EUPL*) applies to the Work (as defined below) which is provided under the +terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). +The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following +notice immediately following the copyright notice for the Work: +Licensed under the EUPL +or has expressed by any other means his willingness to license under the EUPL. + +## 1. Definitions +In this Licence, the following terms have the following meaning: +* *The Licence*:this Licence. +* *The Original Work*:the work or software distributed or communicated by the Licensor under this Licence, available + as Source Code and also as Executable Code as the case may be. +* *Derivative Works*:the works or software that could be created by the Licensee, based upon the Original Work or + modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work + required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in + the country mentioned in Article 15. +* *The Work*:the Original Work or its Derivative Works. +* *The Source Code*:the human-readable form of the Work which is the most convenient for people to study and + modify. +* *The Executable Code*:any code which has generally been compiled and which is meant to be interpreted by + a computer as a program. +* *The Licensor*:the natural or legal person that distributes or communicates the Work under the Licence. +* *Contributor(s)*:any natural or legal person who modifies the Work under the Licence, or otherwise contributes to + the creation of a Derivative Work. +* *The Licensee* or *You*:any natural or legal person who makes any usage of the Work under the terms of the + Licence. +* *Distribution* or *Communication*:any act of selling, giving, lending, renting, distributing, communicating, + transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential + functionalities at the disposal of any other natural or legal person. + +## 2. Scope of the rights granted by the Licence +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for +the duration of copyright vested in the Original Work: +* use the Work in any circumstance and for all usage, +* reproduce the Work, +* modify the Work, and make Derivative Works based upon the Work, +* communicate to the public, including the right to make available or display the Work or copies thereof to the public + and perform publicly, as the case may be, the Work, +* distribute the Work or copies thereof, +* lend and rent the Work or copies thereof, +* sublicense rights in the Work or copies thereof. + Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the + applicable law permits so. + In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed + by law in order to make effective the licence of the economic rights here above listed. + The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the + extent necessary to make use of the rights granted on the Work under this Licence. + +## 3. Communication of the Source Code +The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as +Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with +each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to +the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to +distribute or communicate the Work. + +## 4. Limitations on copyright +Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the +exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +## 5. Obligations of the Licensee +The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those +obligations are the following: + +**Attribution right**: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to +the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the +Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work +to carry prominent notices stating that the Work has been modified and the date of modification. + +**Copyleft clause**: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this +Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless +the Original Work is expressly distributed only under this version of the Licence — for example by communicating +*EUPL v. 1.2 only*. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the +Work or Derivative Work that alter or restrict the terms of the Licence. + +**Compatibility clause**: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both +the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done +under the terms of this Compatible Licence. For the sake of this clause, *Compatible Licence* refers to the licences listed +in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. + +**Provision of Source Code**: When distributing or communicating copies of the Work, the Licensee will provide +a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available +for as long as the Licensee continues to distribute or communicate the Work. +Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names +of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +## 6. Chain of Authorship +The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions +to the Work, under the terms of this Licence. + +## 7. Disclaimer of Warranty +The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work +and may therefore contain defects or *bugs* inherent to this type of development. +For the above reason, the Work is provided under the Licence on an *as is* basis and without warranties of any kind +concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or +errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this +Licence. +This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work. + +## 8. Disclaimer of Liability +Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be +liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the +Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss +of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, +the Licensor will be liable under statutory product liability laws as far such laws apply to the Work. + +## 9. Additional agreements +While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services +consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +## 10. Acceptance of the Licence +The provisions of this Licence can be accepted by clicking on an icon *I agree* placed under the bottom of a window +displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms +and conditions. +Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You +by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution +or Communication by You of the Work or copies thereof. + +## 11. Information to the public +In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, +by offering to download the Work from a remote location) the distribution channel or media (for example, a website) +must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence +and the way it may be accessible, concluded, stored and reproduced by the Licensee. + +## 12. Termination of the Licence +The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms +of the Licence. +Such a termination will not terminate the licences of any person who has received the Work from the Licensee under +the Licence, provided such persons remain in full compliance with the Licence. + +## 13. Miscellaneous +Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the +Work. +If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or +enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid +and enforceable. +The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of +the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. +New versions of the Licence will be published with a unique version number. +All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take +advantage of the linguistic version of their choice. + +## 14. Jurisdiction +Without prejudice to specific agreement between parties, +* any litigation resulting from the interpretation of this License, arising between the European Union institutions, + bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice + of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union, +* any litigation arising between other parties and resulting from the interpretation of this License, will be subject to + the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. + +## 15. Applicable Law +Without prejudice to specific agreement between parties, +* this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, + resides or has his registered office, +* this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside + a European Union Member State. + +## Appendix + +*Compatible Licences* according to Article 5 EUPL are: +* GNU General Public License (GPL) v. 2, v. 3 +* GNU Affero General Public License (AGPL) v. 3 +* Open Software License (OSL) v. 2.1, v. 3.0 +* Eclipse Public License (EPL) v. 1.0 +* CeCILL v. 2.0, v. 2.1 +* Mozilla Public Licence (MPL) v. 2 +* GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +* Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software +* European Union Public Licence (EUPL) v. 1.1, v. 1.2 +* Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+). + The European Commission may update this Appendix to later versions of the above licences without producing + a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the + covered Source Code from exclusive appropriation. + All other changes or additions to this Appendix require the production of a new EUPL version. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3967406 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# VestriaDice + +![License: EUPL v1.2](https://img.shields.io/github/license/Loapu/VestriaDice?style=flat-square) ![Latest Release](https://img.shields.io/github/v/release/Loapu/VestriaDice?style=flat-square) + +## About + +A small Pen-and-Paper-like dice rolling plugin for Minecraft. + +## Dependencies +- [Paper](https://papermc.io/downloads#Paper-1.17) (or any of its forks) for Minecraft 1.17 or newer +- [CommandAPI](https://commandapi.jorel.dev/) version [6.3.0](https://github.com/JorelAli/CommandAPI/releases/tag/6.3.0) or newer + +## Installation + +Download the required release from the [Release-Section](https://github.com/Loapu/VestriaDice/releases) and drop it into your plugins folder. Then restart the server. + +| :warning: | Reloading the server or the plugin itself will NOT work. | +| ----------|:---------------------------------------------------------| + +## Usage + +``` +/vdice help +``` + +## Documentation + +For a full set of features, commands, permissions, etc. please visit the [wiki](https://github.com/Loapu/VestriaDice/wiki). + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. +Please follow these steps to set up your development environment: +1. Clone this repository to a folder of your liking. +2. Create a `libs` folder inside the cloned project root. +3. Download version 3.0.4 of [VentureChat](https://www.spigotmc.org/resources/venturechat.771/download?version=413013) and put the corresponding jar inside the newly created `libs` folder. +4. Now the project *should* be ready for development. If you find anything that's missing, just open an issue, and I will look into it. + +## License + +[EUPL v1.2](https://eupl.eu/1.2/en/) + +## Credits + +This plugin makes use of the following external libraries: +- [CommandAPI](https://github.com/JorelAli/CommandAPI) by [JorelAli](https://github.com/JorelAli) licensed under the [MIT License](https://opensource.org/licenses/MIT) +- [adventure-text-minimessage](https://github.com/KyoriPowered/adventure-text-minimessage) by [Kyori](https://github.com/KyoriPowered) licensed under the [MIT License](https://opensource.org/licenses/MIT) + +This plugin also shades the following external libraries inside its own code: +- [adventure-text-minimessage](https://github.com/KyoriPowered/adventure-text-minimessage) by [Kyori](https://github.com/KyoriPowered) licensed under the [MIT License](https://opensource.org/licenses/MIT) \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..5f75ae6 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,81 @@ +import org.apache.tools.ant.filters.ReplaceTokens + +plugins { + `java-library` + idea + id("com.github.johnrengelman.shadow") version "7.0.0" +} +group = project.property("pluginGroup") as String +version = project.property("pluginVersion") as String +repositories { + mavenCentral() + maven(uri("https://papermc.io/repo/repository/maven-public/")) + maven(uri("https://jitpack.io")) + maven(uri("https://raw.githubusercontent.com/JorelAli/CommandAPI/mvn-repo/")) + maven(uri("https://repo.codemc.org/repository/maven-public/")) + maven(uri("https://oss.sonatype.org/content/repositories/snapshots/")) + flatDir { + dirs("libs") + } +} +dependencies { + implementation("io.papermc.paper:paper-api:" + project.property("versionPaper") as String) + implementation("dev.jorel.CommandAPI:commandapi-core:" + project.property("versionCommandApi") as String) + implementation("dev.jorel.CommandAPI:commandapi-annotations:" + project.property("versionCommandApi") as String) + implementation("org.jetbrains:annotations:19.0.0") + annotationProcessor("dev.jorel.CommandAPI:commandapi-annotations:" + project.property("versionCommandApi") as String) + implementation("net.kyori:adventure-text-minimessage:" + project.property("versionMiniMessage") as String) + implementation(name, "VentureChat-3.0.4") +} +sourceSets { + main { + java.srcDirs("src/main/java") + resources.srcDirs("src/main/resources") + } +} +tasks { + register("bumpBuild") { + doLast { + val pluginBuildNumber = project.property("pluginBuildNumber") as String + val build = Integer.parseInt(pluginBuildNumber) + 1 + val token = "pluginBuildNumber=$pluginBuildNumber" + val value = "pluginBuildNumber=$build" + ant.withGroovyBuilder { + "replace"("file" to "gradle.properties", "token" to token, "value" to value) + } + } + } + compileJava { + options.encoding = project.property("projectEncoding") as String + options.release.set(Integer.parseInt(project.property("versionJava") as String)) + } + shadowJar { + dependencies { + include(dependency("net.kyori:adventure-text-minimessage:" + project.property("versionMiniMessage") as String)) + } + archiveBaseName.set(project.property("pluginName") as String) + if (project.property("pluginVersion").toString().contains('-')) + { + archiveVersion.set("${project.version}+B" + project.property("pluginBuildNumber") as String) + } + archiveClassifier.set("") + } + processResources { + outputs.upToDateWhen { false } + duplicatesStrategy = DuplicatesStrategy.INCLUDE + val tokens = mapOf( + "group" to project.property("pluginGroup") as String, + "name" to project.property("pluginName") as String, + "prefix" to project.property("pluginPrefix") as String, + "ver" to project.property("pluginVersion") as String, + "apiVer" to project.property("pluginApiVersion") as String, + "author" to project.property("pluginAuthor") as String, + "description" to project.property("pluginDescription") as String, + "homepage" to project.property("pluginHomepage") as String + ) + inputs.properties(tokens) + from(sourceSets.main.get().resources.srcDirs) { + filter("tokens" to tokens) + } + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..c35333b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,14 @@ +pluginGroup=dev.loapu +pluginName=VestriaDice +pluginPrefix=VD +pluginVersion=2.2.0 +pluginBuildNumber=177 +pluginApiVersion=1.17 +pluginAuthor=Loapu +pluginDescription=A Pen-and-Paper-like dice rolling system inside Minecraft. +pluginHomepage=https://github.com/Loapu/VestriaDice +projectEncoding=UTF-8 +versionPaper=1.17-R0.1-SNAPSHOT +versionCommandApi=6.3.0 +versionMiniMessage=4.1.0-SNAPSHOT +versionJava=16 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /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-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/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=`expr $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" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@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 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%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..4be1280 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "VestriaDice" \ No newline at end of file diff --git a/src/main/java/dev/loapu/vestriadice/VestriaDice.java b/src/main/java/dev/loapu/vestriadice/VestriaDice.java new file mode 100644 index 0000000..b1b1a4f --- /dev/null +++ b/src/main/java/dev/loapu/vestriadice/VestriaDice.java @@ -0,0 +1,103 @@ +package dev.loapu.vestriadice; + +import dev.jorel.commandapi.CommandAPI; +import dev.loapu.vestriadice.commands.BaseCommand; +import dev.loapu.vestriadice.commands.RollCommand; +import dev.loapu.vestriadice.utils.LangManager; +import org.bukkit.Bukkit; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.UserDefinedFileAttributeView; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; + +public class VestriaDice extends VestriaDicePlugin +{ + private final List enabledSoftdepends = new ArrayList<>(); + private final int CONFIG_VERSION = 3; + + private static VestriaDice instance; + public VestriaDice() + { + instance = this; + } + public static VestriaDice getInstance() + { + return instance; + } + + @Override + public void onEnableInner() + { + + log("Loading config file v" + CONFIG_VERSION + "..."); + try + { + loadConfig(); + } + catch (Exception e) + { + error(e); + } + log("Success!"); + log("Loading language files..."); + new LangManager(); + log("Success!"); + log("Loading commands..."); + CommandAPI.registerCommand(BaseCommand.class); + CommandAPI.registerCommand(RollCommand.class); + log("Success!"); + log("Loading available hooks..."); + for (String softDepend : getDescription().getSoftDepend()) + { + if (Bukkit.getPluginManager().isPluginEnabled(softDepend)) + { + log("- " + softDepend); + enabledSoftdepends.add(softDepend); + } + } + log("Success!"); + } + + private void loadConfig() throws Exception + { + File cFile = new File(getDataFolder(), "config.yml"); + Path cFileAbsolutePath = Paths.get(getDataFolder().getAbsolutePath(), "config.yml"); + String attrName = "dev.loapu.version"; + String attrValue = CONFIG_VERSION + ""; + byte[] bytes = attrValue.getBytes(StandardCharsets.UTF_8); + int configVersionOld = CONFIG_VERSION; + UserDefinedFileAttributeView view = Files.getFileAttributeView(cFileAbsolutePath, UserDefinedFileAttributeView.class); + if (cFile.exists()) + { + ByteBuffer readBuffer = ByteBuffer.allocate(view.size(attrName)); + view.read(attrName, readBuffer); + readBuffer.flip(); + String valueFromAttributes = new String(readBuffer.array(), StandardCharsets.UTF_8); + configVersionOld = Integer.parseInt(valueFromAttributes); + } + if (configVersionOld < CONFIG_VERSION) + { + File bFile = new File(getDataFolder(), "old_config.yml"); + Files.move(cFile.toPath(), bFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + log(Level.WARNING, "New config version detected. Please update your settings!"); + } + saveDefaultConfig(); + ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length); + writeBuffer.put(bytes); + writeBuffer.flip(); + view.write(attrName, writeBuffer); + } + + public List getEnabledSoftdepends() + { + return enabledSoftdepends; + } +} diff --git a/src/main/java/dev/loapu/vestriadice/VestriaDicePlugin.java b/src/main/java/dev/loapu/vestriadice/VestriaDicePlugin.java new file mode 100644 index 0000000..711fec1 --- /dev/null +++ b/src/main/java/dev/loapu/vestriadice/VestriaDicePlugin.java @@ -0,0 +1,124 @@ +package dev.loapu.vestriadice; + +import dev.loapu.vestriadice.utils.Setting; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import org.bukkit.Bukkit; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.logging.Level; + +public class VestriaDicePlugin extends JavaPlugin +{ + private TextComponent logPrefixColored; + private String logPrefixPlain; + private final TextColor primary = NamedTextColor.GOLD; + private final TextColor dark = NamedTextColor.DARK_GRAY; + private long enableTime; + + @Override + public void onLoad() + { + onLoadPre(); + onLoadInner(); + onLoadPost(); + } + + private void onLoadPre() + { + logPrefixColored = (Component.text("[").color(dark).append(Component.text("VD").color(primary)).append(Component.text("]").color(dark))); + logPrefixPlain = "[" + getDescription().getPrefix() + "]"; + } + + private void onLoadInner() + { + } + + private void onLoadPost() + { + } + + public long getEnableTime() + { + return enableTime; + } + + @Override + public void onEnable() + { + if (!onEnablePre()) return; + onEnableInner(); + onEnablePost(); + } + + private boolean onEnablePre() + { + enableTime = System.currentTimeMillis(); + + log("// ========== STARTING PLUGIN ========== //"); + + return true; + } + + public void onEnableInner() + { + } + + private void onEnablePost() + { + long ms = System.currentTimeMillis() - enableTime; + log("// ========== DONE (Time: " + ms + "ms) ========== //"); + } + + @Override + public void onDisable() + { + } + + public void suicide() + { + log(Level.WARNING, "Plugin will be killed!"); + Bukkit.getPluginManager().disablePlugin(this); + } + + public void debug(String debugString) + { + if (Setting.ENABLE_DEBUG_MESSAGES.asBoolean()) + { + log("DEBUG", debugString); + } + } + + public void error(Exception e) + { + log(Level.SEVERE, "Looks like we have a " + e.toString() + " here."); + log(Level.SEVERE, "Here is a bit more detail:"); + e.printStackTrace(); + } + + public void log(String prefixString, String messageString) + { + log(Level.INFO, "[" + prefixString + "] " + messageString); + } + + public void log(String messageString) + { + log(Level.INFO, messageString); + } + + public void log(Level level, String messageString) + { + ConsoleCommandSender console = Bukkit.getConsoleSender(); + if (level == Level.INFO) + { + console.sendMessage(logPrefixColored.append(Component.text(" ")).append(Component.text(messageString).color(primary))); + } + else + { + Bukkit.getLogger().log(level, logPrefixPlain + " " + messageString); + } + } +} diff --git a/src/main/java/dev/loapu/vestriadice/commands/BaseCommand.java b/src/main/java/dev/loapu/vestriadice/commands/BaseCommand.java new file mode 100644 index 0000000..ad7c837 --- /dev/null +++ b/src/main/java/dev/loapu/vestriadice/commands/BaseCommand.java @@ -0,0 +1,59 @@ +package dev.loapu.vestriadice.commands; + +import dev.jorel.commandapi.annotations.Alias; +import dev.jorel.commandapi.annotations.Command; +import dev.jorel.commandapi.annotations.Default; +import dev.jorel.commandapi.annotations.Permission; +import dev.jorel.commandapi.annotations.Subcommand; +import dev.loapu.vestriadice.VestriaDice; +import dev.loapu.vestriadice.utils.LangManager; +import dev.loapu.vestriadice.utils.Language; +import dev.loapu.vestriadice.utils.Message; +import org.bukkit.command.CommandSender; + +import java.util.Map; + +@Command("vestriadice") +@Alias({"vd", "vestriadice:vd", "dice", "vestriadice:dice", "vdice", "vestriadice:vdice"}) +@Permission("vestriadice.command.base") +public class BaseCommand +{ + private static final VestriaDice plugin = VestriaDice.getInstance(); + private static final LangManager lm = LangManager.getInstance(); + private static final Map commandMap = Map.of( + "command.base", "/vestriadice", + "command.base.help", "/vestriadice help", + "command.base.lang", "/vestriadice lang", + "command.base.reload", "/vestriadice reload", + "command.roll", "/roll" + ); + + @Default + public static void vestriadice(CommandSender sender) + { + sender.sendMessage(lm.getComponent(Message.COMMAND_BASE_TEXT, sender, commandMap)); + } + @Subcommand("help") + public static void vestriadiceHelp(CommandSender sender) + { + sender.sendMessage(lm.getComponent(Message.COMMAND_BASE_HELP_TEXT, sender, commandMap)); + } + @Subcommand("lang") + public static void vestriadiceLang(CommandSender sender) + { + StringBuilder stringBuilder = new StringBuilder(); + for (Language lang : Language.values()) + { + stringBuilder.append("\n- ").append(lang.getLanguage()).append(""); + } + Map languageMap = Map.of("languages", stringBuilder.toString()); + sender.sendMessage(lm.getComponent(Message.COMMAND_BASE_LANG_TEXT, sender, languageMap)); + } + @Subcommand("reload") + public static void vestriadiceReload(CommandSender sender) + { + plugin.reloadConfig(); + lm.reload(); + sender.sendMessage(lm.getComponent(Message.COMMAND_BASE_RELOAD_SUCCESS, sender)); + } +} diff --git a/src/main/java/dev/loapu/vestriadice/commands/RollCommand.java b/src/main/java/dev/loapu/vestriadice/commands/RollCommand.java new file mode 100644 index 0000000..5860c70 --- /dev/null +++ b/src/main/java/dev/loapu/vestriadice/commands/RollCommand.java @@ -0,0 +1,188 @@ +package dev.loapu.vestriadice.commands; + +import dev.jorel.commandapi.annotations.Alias; +import dev.jorel.commandapi.annotations.Command; +import dev.jorel.commandapi.annotations.Default; +import dev.jorel.commandapi.annotations.Permission; +import dev.jorel.commandapi.annotations.arguments.AIntegerArgument; +import dev.jorel.commandapi.annotations.arguments.AStringArgument; +import dev.loapu.vestriadice.VestriaDice; +import dev.loapu.vestriadice.utils.LangManager; +import dev.loapu.vestriadice.utils.Message; +import dev.loapu.vestriadice.utils.Setting; +import mineverse.Aust1n46.chat.api.MineverseChatAPI; +import mineverse.Aust1n46.chat.api.MineverseChatPlayer; +import mineverse.Aust1n46.chat.channel.ChatChannel; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.sound.Sound.Emitter; +import net.kyori.adventure.sound.Sound.Source; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +@Command("roll") +@Alias({"vestriadice:roll"}) +@Permission("vestriadice.command.roll") +public class RollCommand +{ + private static final VestriaDice plugin = VestriaDice.getInstance(); + private static final LangManager lm = LangManager.getInstance(); + private static final Map commandMap = Map.of( + "command.roll", "/roll" + ); + @Default + public static void roll(CommandSender sender) + { + sender.sendMessage(lm.getComponent(Message.COMMAND_ROLL_TEXT, sender, commandMap)); + } + @Default + public static void roll(Player player, @AStringArgument String dice) + { + rollDice(player, dice); + } + @Default + public static void roll(Player player, @AStringArgument String dice, @AIntegerArgument int checkValue) + { + rollDice(player, dice, checkValue); + } + private static void rollDice(Player player, String dice) + { + rollDice(player, dice, 0); + } + private static void rollDice(Player sender, String input, int checkValue) + { + if (!input.matches("(0?[1-9]|[1-9][0-9])[wWdD](0?[1-9]|[1-9][0-9]{1,2})(\\.(0?[1-9]|[1-9][0-9])[wWdD](0?[1-9]|[1-9][0-9]{1,2}))*([+-]\\d{1,3})?")) + { + sender.sendMessage(lm.getComponent(Message.COMMAND_ROLL_WRONG_SYNTAX, sender, commandMap)); + return; + } + + String diceString = input.toLowerCase(); + String diceStringWithoutModifier = diceString.split("\\+")[0].split("-")[0]; + String[] diceArray; + int modifier = 0; + if (diceStringWithoutModifier.contains(".")) diceArray = diceStringWithoutModifier.split("\\."); // Split all different dice into one array + else diceArray = new String[] { diceStringWithoutModifier }; + if (diceString.contains("+")) modifier += Integer.parseInt(diceString.split("\\+")[1]); + if (diceString.contains("-")) modifier -= Integer.parseInt(diceString.split("-")[1]); + int result = 0; + int successCounter = 0; + int missCounter = 0; + // Now we have the following parts: + // diceArray containing all dice + // modifier being the modifier of the end result + List diceRolls = new ArrayList<>(); + for (String dice : diceArray) + { + StringBuilder stringBuilder = new StringBuilder(); + String[] tempArray = dice.contains("w") ? dice.split("w") : dice.split("d"); + int numberOfRolls = Integer.parseInt(tempArray[0]); + int sides = Integer.parseInt(tempArray[1]); + List rolls = new ArrayList<>(); + boolean useLowestNumberForCriticalSuccess = Setting.USE_LOWEST_NUMBER_FOR_CRITICAL_SUCCESS.asBoolean(); + for (int i = 0; i < numberOfRolls; i++) + { + int roll = ThreadLocalRandom.current().nextInt(1, sides + 1); + result += roll; + int green = 255; + int red = 255; + float successPercentage = 0; + if (roll == 1) successPercentage = (useLowestNumberForCriticalSuccess ? 1 : 0); + else if (roll == sides) successPercentage = (useLowestNumberForCriticalSuccess ? 0 : 1); + else successPercentage = (float) roll / (sides + 1); + float finalColorFloat = successPercentage * 510; + int finalColor = Math.round(finalColorFloat); + if (finalColor > 255) red = 510 - finalColor; + else green = finalColor; + String finalColorString = String.format("#%02X%02X%02X", red, green, 0); + String rollString = roll + ""; + if (sides <= 6) + { + rollString = switch(roll) { + case 6 -> "⚅"; + case 5 -> "⚄"; + case 4 -> "⚃"; + case 3 -> "⚂"; + case 2 -> "⚁"; + default -> "⚀"; + }; + } + if (finalColorString.equalsIgnoreCase("#FF0000") || finalColorString.equalsIgnoreCase("#00FF00")) rollString += "!"; + if (successPercentage == 1) successCounter++; + if (successPercentage == 0) missCounter++; + rolls.add("" + rollString + ""); + } + stringBuilder.append(", ", rolls)).append("'>").append(dice).append(""); + diceRolls.add(stringBuilder.toString()); + } + diceString = String.join(", ", diceRolls); + String modifierString = ((modifier > 0) ? "+" : "") + modifier + ""; + String resultString = result + modifier + ""; + String deserializedDisplayname = MiniMessage.builder().build().serialize(sender.displayName()); + + sendComponent(lm.getComponent(Message.COMMAND_ROLL_RESULT, sender, Map.of( + "player", deserializedDisplayname, + "dice", diceString, + "modifier", modifierString, + "result", resultString + )), sender); + + if (checkValue == 0) return; + String checkValueString = checkValue + ""; + String checkResultString = (result >= checkValue) ? "" : ""; + String criticalSuccessesString = "" + successCounter + ""; + String criticalMissesString = "" + missCounter + ""; + sendComponent(lm.getComponent(Message.COMMAND_ROLL_CHECK, sender, Map.of( + "checkValue", checkValueString, + "checkResult", checkResultString, + "criticalSuccesses", criticalSuccessesString, + "criticalMisses", criticalMissesString + )), sender); + } + + private static void sendComponent(Component component, Player originalReceiver) + { + int range = Setting.RANGE_FOR_RESULT_ANNOUNCEMENT.asInt(); + String channelPermission = null; + if (Setting.USE_VENTURE_CHAT_CHANNEL_RANGE.asBoolean() && plugin.getEnabledSoftdepends().contains("VentureChat")) + { + MineverseChatPlayer mcp = MineverseChatAPI.getMineverseChatPlayer(originalReceiver); + ChatChannel chatChannel = mcp.getCurrentChannel(); + range = chatChannel.getDistance().intValue(); + if (chatChannel.hasPermission()) + { + channelPermission = chatChannel.getPermission(); + } + } + plugin.debug("Range: " + range); + Audience audience; + if (range <= 0 && channelPermission == null) audience = Audience.audience(Bukkit.getOnlinePlayers()); + else + { + List receiverList = new ArrayList<>(); + for (Player potentialReceiver : Bukkit.getOnlinePlayers()) + { + boolean permissionBoolean = channelPermission == null || potentialReceiver.hasPermission(channelPermission); + boolean rangeBoolean = true; + if (range >= 1) rangeBoolean = originalReceiver.getWorld() == potentialReceiver.getWorld() && originalReceiver.getLocation().distance(potentialReceiver.getLocation()) <= range; + if (permissionBoolean && rangeBoolean) + { + receiverList.add(potentialReceiver); + } + } + audience = Audience.audience(receiverList); + } + audience.sendMessage(component); + Sound sound = Sound.sound(Key.key("block.stem.step"), Source.MASTER, 1F, .1F); + audience.playSound(sound, Emitter.self()); + } +} diff --git a/src/main/java/dev/loapu/vestriadice/utils/LangManager.java b/src/main/java/dev/loapu/vestriadice/utils/LangManager.java new file mode 100644 index 0000000..f72eda0 --- /dev/null +++ b/src/main/java/dev/loapu/vestriadice/utils/LangManager.java @@ -0,0 +1,145 @@ +package dev.loapu.vestriadice.utils; + +import dev.loapu.vestriadice.VestriaDice; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +public class LangManager +{ + private final VestriaDice plugin = VestriaDice.getInstance(); + private final Locale DEFAULT_LOCALE = Locale.ENGLISH; + private ClassLoader languageLoader; + private final Map globalPlaceholders = new HashMap<>(); + private final MiniMessage serializer = MiniMessage.get(); + + private static LangManager instance; + + public LangManager() + { + instance = this; + initialize(); + } + + private void initialize() + { + File langFolder = new File(plugin.getDataFolder(), "lang"); + if (!langFolder.exists()) + { + plugin.debug("+ language folder"); + langFolder.mkdirs(); + } + for (Language language : Language.values()) + { + plugin.debug(language.getLanguage()); + String fileName = "lang" + (language == Language.DEFAULT ? "" : "_" + language) + ".properties"; + File langFile = new File(langFolder, fileName); + InputStream langResource = plugin.getResource("lang" + File.separator + fileName); + if (langResource == null) continue; + try + { + Files.copy(langResource, langFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + catch (IOException e) + { + plugin.error(e); + } + } + try + { + URL[] urls = { langFolder.getAbsoluteFile().toURI().toURL() }; + languageLoader = new URLClassLoader(urls); + } + catch (MalformedURLException e) + { + plugin.error(e); + } + globalPlaceholders.clear(); + globalPlaceholders.put("author", plugin.getDescription().getAuthors().get(0)); + globalPlaceholders.put("prefix", "[]"); + globalPlaceholders.put("plugin", plugin.getName()); + globalPlaceholders.put("version", plugin.getDescription().getVersion()); + globalPlaceholders.put("docs", "" + plugin.getDescription().getWebsite() + ""); + globalPlaceholders.put("colorPrimary", ""); + globalPlaceholders.put("colorDark", ""); + globalPlaceholders.put("colorLight", ""); + } + + public static LangManager getInstance() + { + return instance; + } + + public void reload() + { + initialize(); + } + + public String getString(Message message) + { + return getString(message, Bukkit.getConsoleSender()); + } + public String getString(Message message, CommandSender sender) + { + String key = message.toString(); + Locale locale = DEFAULT_LOCALE; + if (Setting.USE_PLAYER_LOCALE.asBoolean() && sender instanceof Player player) + { + Locale playerLocale = player.locale(); + locale = new Locale(playerLocale.getLanguage(), playerLocale.getCountry(), "custom"); + } + else + { + String languageString = Setting.LANGUAGE.asString(); + plugin.debug(languageString); + if (!languageString.isEmpty()) + { + String[] langArr = languageString.split("_"); + locale = switch (langArr.length) + { + case 3 -> new Locale(langArr[0], langArr[1], langArr[2]); + case 2 -> new Locale(langArr[0], langArr[1]); + default -> new Locale(langArr[0]); + }; + } + } + ResourceBundle rb = ResourceBundle.getBundle("lang", locale, languageLoader); + return rb.containsKey(key) ? rb.getString(key) : key; + } + public Component getComponent(Message message) + { + return serializer.parse(getString(message), globalPlaceholders); + } + public Component getComponent(Message message, CommandSender sender) + { + return serializer.parse(getString(message, sender), globalPlaceholders); + } + public Component getComponent(Message message, Map placeholders) + { + Map placeholdersMutable = new HashMap<>(placeholders); + placeholdersMutable.putAll(globalPlaceholders); + return serializer.parse(getString(message), placeholdersMutable); + } + public Component getComponent(Message message, CommandSender sender, Map placeholders) + { + Map placeholdersMutable = new HashMap<>(placeholders); + placeholdersMutable.putAll(globalPlaceholders); + return serializer.parse(getString(message, sender), placeholdersMutable); + } +} diff --git a/src/main/java/dev/loapu/vestriadice/utils/Language.java b/src/main/java/dev/loapu/vestriadice/utils/Language.java new file mode 100644 index 0000000..6344ad2 --- /dev/null +++ b/src/main/java/dev/loapu/vestriadice/utils/Language.java @@ -0,0 +1,26 @@ +package dev.loapu.vestriadice.utils; + +public enum Language +{ + DEFAULT("en_UK", "English (United Kingdom)"), + DE_DE("de_DE", "Deutsch (Deutschland)"); + + private final String langCode; + private final String language; + Language(String langCode, String language) + { + this.langCode = langCode; + this.language = language; + } + + @Override + public String toString() + { + return langCode; + } + + public String getLanguage() + { + return language; + } +} diff --git a/src/main/java/dev/loapu/vestriadice/utils/Message.java b/src/main/java/dev/loapu/vestriadice/utils/Message.java new file mode 100644 index 0000000..3b0823b --- /dev/null +++ b/src/main/java/dev/loapu/vestriadice/utils/Message.java @@ -0,0 +1,21 @@ +package dev.loapu.vestriadice.utils; + +public enum Message +{ + COMMAND_BASE_TEXT, + COMMAND_BASE_HELP_TEXT, + COMMAND_BASE_LANG_TEXT, + COMMAND_BASE_RELOAD_SUCCESS, + + COMMAND_ROLL_TEXT, + COMMAND_ROLL_WRONG_SYNTAX, + COMMAND_ROLL_RESULT, + COMMAND_ROLL_CHECK; + + + @Override + public String toString() + { + return super.toString().replace('_', '.').toLowerCase(); + } +} diff --git a/src/main/java/dev/loapu/vestriadice/utils/Setting.java b/src/main/java/dev/loapu/vestriadice/utils/Setting.java new file mode 100644 index 0000000..2518964 --- /dev/null +++ b/src/main/java/dev/loapu/vestriadice/utils/Setting.java @@ -0,0 +1,37 @@ +package dev.loapu.vestriadice.utils; + +import dev.loapu.vestriadice.VestriaDice; + +public enum Setting +{ + LANGUAGE("language"), + USE_PLAYER_LOCALE("usePlayerLocale"), + USE_LOWEST_NUMBER_FOR_CRITICAL_SUCCESS("useLowestNumberForCriticalSuccess"), + RANGE_FOR_RESULT_ANNOUNCEMENT("rangeForResultAnnouncement"), + USE_VENTURE_CHAT_CHANNEL_RANGE("useVentureChatChannelRange"), + + + ENABLE_DEBUG_MESSAGES("enableDebugMessages"); + + private final String path; + private final VestriaDice plugin = VestriaDice.getInstance(); + Setting(String path) + { + this.path = path; + } + + public boolean asBoolean() + { + return plugin.getConfig().getBoolean(path, false); + } + + public String asString() + { + return plugin.getConfig().getString(path, ""); + } + + public int asInt() + { + return plugin.getConfig().getInt(path, 0); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..f7a1c84 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,27 @@ +# You will find all available translations inside the lang folder. +# +# To use custom translations just make a new file called "lang___custom.properties", replace with your +# language code (i.e. en) and with your country code (i.e. US) and then replace the below code (i.e. en_US_custom). +# The plugin will automatically use the default language files for every string that isn't customized by you. +# Expected type: string, default: en_UK +language: en_UK + +# If you want the messages to be shown depending on the player selected locale. Custom translations will be used, if done correctly. +# Expected type: boolean, default: false +usePlayerLocale: false + +# Per default the plugin handles the highest roll-able number as a critical success. If you want to change this behaviour, you may do this here. +# Expected type: boolean, default: false +useLowestNumberForCriticalSuccess: false + +# The range where other players around the one who rolls the dice will be getting his results. If changed to 0 the announcement ist global. +# Expected type: int, default: 15 +rangeForResultAnnouncement: 15 + +# Uses the chat range of the VentureChat channel instead of the rangeForResultAnnouncement value. +# Expected type: boolean, default: false +useVentureChatChannelRange: false + +# Not meant for production use. Enable if you need help finding configuration errors or in correspondence with the developer. +# Expected type: boolean, default: false +enableDebugMessages: false \ No newline at end of file diff --git a/src/main/resources/lang/lang.properties b/src/main/resources/lang/lang.properties new file mode 100644 index 0000000..7ab0224 --- /dev/null +++ b/src/main/resources/lang/lang.properties @@ -0,0 +1,34 @@ +command.base.text= v\n\ + Author: , Docs: \n\ + Use for help. +command.base.help.text= // ===== Help ===== //\n\ + - Show basic info.\n\ + - Show this page.\n\ + - Show list with inbuilt translations.\n\ + - Reload the plugin.\n\ + - Show help for rolling. +command.base.lang.text= Here is a list with all inbuilt translations: +command.base.reload.success= successfully reloaded! +command.roll.text= // ===== Guide ===== //\n\ + - Show this page.\n\ + - Roll the mentioned .\n\ + - Roll the mentioned and compare the result against .\n\n\ + Explanation:\n\ + A dice is made up of:\n\ + (1) the number of dice that you want to be rolled,\n\ + (2) the letter w or d,\n\ + (3) the number of faces the dice should have and optionally\n\ + (4) a + or - followed by the amount of points the result should be modified with.\n\n\ + Examples:\n\ + 2d20 - Rolls 2 dice with 20 faces each.\n\ + 1d10+3 - Rolls 1 dice with 10 faces and adds 3 to the end-result.\n\ + 3d4.1d6+2 - Rolls 3 dice with 4 faces each, 1 dice with 6 faces and adds 2 to the end-result.\n\ + 2d6-3 - Rolls 2 dice with 6 faces each an subtracts 3 from the end-result.\n\ + 1d20 10 - Rolls 1 dice with 20 faces and checks if the end-result is greater or equal than 10.\n\n\ + Advice:\n\ + 1. Critical misses or successes are marked by a ! after the rolled number.\n\ + 2. Hover over dice to view their individual results or over the end-result to see the modifier. +command.roll.result= rolls with an end-result of: Modified by '> +command.roll.check= Roll against a test value of passed: \n\ + Critical successes: , Critical failures: +command.roll.wrong.syntax=Please provide the correct syntax. Use for help. \ No newline at end of file diff --git a/src/main/resources/lang/lang_de_DE.properties b/src/main/resources/lang/lang_de_DE.properties new file mode 100644 index 0000000..6126662 --- /dev/null +++ b/src/main/resources/lang/lang_de_DE.properties @@ -0,0 +1,34 @@ +command.base.text= v\n\ + Autor: , Dokumentation: \n\ + Nutze fr Hilfe. +command.base.help.text= // ===== Hilfe ===== //\n\ + - Zeigt Infos ber das Plugin.\n\ + - Zeigt diese Seite.\n\ + - Zeigt eine Liste eingebauter bersetzungen.\n\ + - Ldt das Plugin neu.\n\ + - Zeigt Hilfe zum Wrfeln. +command.base.lang.text= Hier ist eine Liste mit allen eingebauten Sprachen: +command.base.reload.success= erfolgreich neu geladen! +command.roll.text= // ===== Handhabung ===== //\n\ + - Zeigt diese Seite.\n\ + - Werfe die angegebenen .\n\ + - Werfe die angegebenen und vergleiche das Ergebnis gegen den .\n\n\ + Erklrung:\n\ + Ein Wrfel besteht aus 4 Teilen:\n\ + (1) der Anzahl an Wrfel die gerollt werden sollen,\n\ + (2) dem Buchstaben w oder d,\n\ + (3) die Anzahl an Seiten, die ein Wrfel haben soll und optional noch\n\ + (4) ein + oder - gefolgt von der Zahl an Punkten, die mit dem Wrfelergebnis verrechnet werden soll.\n\n\ + Beispiele:\n\ + 2w20 - Wirft zwei Wrfel mit jeweils 20 Seiten.\n\ + 1w10+3 - Wirft einen Wrfel mit 10 Seiten und addiert 3 Punkte auf das Ergebnis.\n\ + 3w4.1w6+2 - Wirft drei Wrfel mit jeweils 4 Seiten und einen Wrfel mit 6 Seiten und addiert 2 Punkte auf das Ergebnis.\n\ + 2w6-3 - Wirft zwei Wrfel mit jeweils 6 Seiten und subtrahiert 3 Punkte vom Ergebnis.\n\ + 1w20 10 - Wirft einen Wrfel mit 20 Seiten und prft, ob das Ergebnis hher oder gleich 10 ist.\n\n\ + Hinweise:\n\ + 1. Kritische Miss- und Erfolge erkennst du an einem ! hinter der gewrfelten Zahl.\n\ + 2. Hovere ber Wrfel um deren Einzelergebnisse und ber das Endergebnis um die Modifizierung zu sehen. +command.roll.result= wrfelt mit ein Ergebnis von: Modifiziert um '> +command.roll.check= Wurf gegen einen Prfwert von bestanden: \n\ + Kritische Erfolge: , Kritische Misserfolge: +command.roll.wrong.syntax=Bitte gib den Wrfel in der richtigen Syntax ein. Nutze fr Hilfe. \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..6fb3e10 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,12 @@ +name: @name@ +version: @ver@ +api-version: @apiVer@ +main: @group@.vestriadice.@name@ +prefix: @prefix@ +authors: [ @author@ ] +description: @description@ +website: @homepage@ +depend: + - CommandAPI +softdepend: + - VentureChat \ No newline at end of file