Compare commits

...
34 Commits
Author SHA1 Message Date
Jason Penilla fa67cd3538 release: Version 2.0.3 2021-06-24 16:09:19 -07:00
Jason Penilla 615fce3cd6 Update MiniMessage to 4.2.0-SNAPSHOT and Adventure to 4.8.1 2021-06-24 05:01:41 -07:00
Jason Penilla faccf19535 build: Update Run Paper to 1.0.3 2021-06-24 05:00:04 -07:00
Jason Penilla e50953c9fb Update Adventure to 4.8.0 2021-06-11 07:23:27 -07:00
Jason Penilla 5f2df1debd fabric: Update to 1.17 2021-06-08 13:30:56 -07:00
Jason Penilla 0c90ef0886 Fix typo in ConnectionMixin 2021-06-08 02:46:34 -07:00
Jason Penilla 9bfd73e862 Update Velocity API to 1.1.7 2021-06-08 02:46:21 -07:00
Jason Penilla ec74878a1b Appease checkstyle 2021-06-07 15:23:46 -07:00
Jason Penilla 502a775a41 velocity: Register listeners on ProxyInitializeEvent and make PingListener public 2021-06-07 15:22:21 -07:00
Jason Penilla 245596426e build: Update run-paper to 1.0.2 2021-06-06 17:09:54 -07:00
Jason Penilla 996fd0f7d2 bukkit: Specify command permission in plugin.yml 2021-06-04 21:26:56 -07:00
Jason Penilla 4713809a27 build: Tag all subproject versions with git hash when snapshot 2021-06-04 21:26:53 -07:00
Jason Penilla 259a1e784e Minor codestyle fixes 2021-06-04 21:18:16 -07:00
Jason Penilla 793d3d7736 Update README 2021-06-04 21:16:29 -07:00
Jason Penilla 49f86cc4ef build: Improve FileCopyTask up to date checking 2021-06-04 21:14:13 -07:00
Jason Penilla 6d70e82fbe build: Update blossom to 1.3.0 2021-06-04 21:13:11 -07:00
Jason Penilla ffe1f389e2 build: centralize dependency declarations and remove unnecessary explicit task dependencies 2021-06-02 17:28:35 -07:00
Jason Penilla 2c709d3dc7 Rename Bukkit module from 'spigot' to 'bukkit'
Update build scripts to use convention plugins

Clean up command handling

Refactor ping handlers to use a response object
2021-05-29 22:27:27 -07:00
Jason Penilla 5fe30bf8f9 Update checkstyle config 2021-05-29 21:26:59 -07:00
Jason Penilla 196e6d8f00 Fix indent on checkstyle.xml 2021-05-29 21:26:58 -07:00
jmp 1465423927 Fix #62 2021-05-18 23:39:45 -07:00
jmp 4b61e91220 build: Update Gradle to 7.0.2, Indra to 2.0.4, polyglot-version-catalogs to 5.0.0 2021-05-14 18:52:38 -07:00
jmp 09c590c873 build: Update Indra to 2.0.3 and Gradle to 7.0.1 2021-05-11 01:30:24 -07:00
jmp 57f3de39a2 Update Indra and switch from buildSrc to build-logic included build 2021-05-09 19:37:14 -07:00
jmp f472736b32 Update GH Actions workflow 2021-05-09 02:45:26 -07:00
jmp 27fbefdd9c Add virtual host testing mode
Will print virtual host debug info to console on each ping when enabled. Intended to aid in configuration of the virtual host configs feature.

Also bump a couple of dependencies
2021-05-09 02:39:25 -07:00
jmp 454439a10c sponge8: Update for plugin-spi changes 2021-05-06 22:17:04 -07:00
jmp eebe13ae06 Fix MiniMessage shading 2021-04-20 18:59:19 -07:00
jmp 8b36863244 Build script improvements
- Use version catalog for plugin versions (except shadow in buildSrc)
- Don't shade annotations that we don't need at runtime
- Organize relocations
2021-04-20 18:56:19 -07:00
jmp 0b89b1fbf3 Use VERSION_CATALOGS and TYPESAFE_PROJECT_ACCESSORS Gradle feature previews and polyglot-version-catalogs 2021-04-14 19:27:50 -07:00
jmp 34ece94cbb Update GitHub Actions setup-java to v2 2021-04-12 14:38:48 -07:00
Renovate Bot 880757cd09 chore(deps): update dependency org.spongepowered.gradle.plugin:org.spongepowered.gradle.plugin.gradle.plugin to v1.0.3 2021-04-10 21:28:24 -07:00
jmp dbcbb9e48e Update Gradle to 7.0 2021-04-09 17:41:28 -07:00
jmp 9f31ba4406 Bump version to 2.0.3-SNAPSHOT 2021-04-08 23:31:27 -07:00
65 changed files with 1645 additions and 1153 deletions

No files matched your search

+272 -230
View File
@@ -1,245 +1,287 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "https://checkstyle.org/dtds/configuration_1_3.dtd">
<!-- This file is modified from KyoriPowered/adventure's checkstyle.xml -->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="fileExtensions" value="java, properties, xml"/>
<property name="severity" value="error"/>
<property name="charset" value="UTF-8"/>
<property name="fileExtensions" value="java, properties, xml"/>
<property name="severity" value="error"/>
<!-- https://checkstyle.org/config_filefilters.html#BeforeExecutionExclusionFileFilter -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
<!-- https://checkstyle.org/config_filefilters.html#BeforeExecutionExclusionFileFilter -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#FileTabCharacter -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<!-- https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
<module name="SuppressionFilter">
<property name="file" value="${configDirectory}/suppressions.xml"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressWarningsFilter -->
<module name="SuppressWarningsFilter"/>
<!-- https://checkstyle.org/config_filters.html#SuppressWithPlainTextCommentFilter -->
<module name="SuppressWithPlainTextCommentFilter"/>
<module name="TreeWalker">
<!-- https://checkstyle.org/config_misc.html#ArrayTypeStyle -->
<module name="ArrayTypeStyle"/>
<!-- https://checkstyle.org/config_javadoc.html#AtclauseOrder -->
<module name="AtclauseOrder">
<property name="violateExecutionOnNonTightHtml" value="true"/>
<property name="tagOrder" value="@author, @deprecated, @exception, @param, @return, @serial, @serialData, @serialField, @throws, @see, @since, @sinceMinecraft, @version"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#FileTabCharacter -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
<!-- https://checkstyle.org/config_imports.html#AvoidStarImport -->
<module name="AvoidStarImport"/>
<!-- https://checkstyle.org/config_misc.html#AvoidEscapedUnicodeCharacters -->
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowByTailComment" value="true"/>
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<!-- https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- https://checkstyle.org/config_misc.html#CommentsIndentation -->
<module name="CommentsIndentation"/>
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
<module name="SuppressionFilter">
<property name="file" value="${configDirectory}/suppressions.xml"/>
<!-- https://checkstyle.org/config_imports.html#CustomImportOrder -->
<module name="CustomImportOrder">
<property name="customImportOrderRules" value="THIRD_PARTY_PACKAGE###STATIC"/>
<property name="standardPackageRegExp" value="^$"/>
<property name="sortImportsInGroupAlphabetically" value="true"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressWarningsFilter -->
<module name="SuppressWarningsFilter"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyForInitializerPad -->
<module name="EmptyForInitializerPad"/>
<!-- https://checkstyle.org/config_filters.html#SuppressWithPlainTextCommentFilter -->
<module name="SuppressWithPlainTextCommentFilter"/>
<module name="TreeWalker">
<!-- https://checkstyle.org/config_misc.html#ArrayTypeStyle -->
<module name="ArrayTypeStyle"/>
<!-- https://checkstyle.org/config_javadoc.html#AtclauseOrder -->
<module name="AtclauseOrder">
<property name="violateExecutionOnNonTightHtml" value="true"/>
<property name="tagOrder" value="@author, @deprecated, @exception, @param, @return, @serial, @serialData, @serialField, @throws, @see, @since, @sinceMinecraft, @version"/>
</module>
<!-- https://checkstyle.org/config_imports.html#AvoidStarImport -->
<module name="AvoidStarImport"/>
<!-- https://checkstyle.org/config_misc.html#AvoidEscapedUnicodeCharacters -->
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowByTailComment" value="true"/>
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<!-- https://checkstyle.org/config_misc.html#CommentsIndentation -->
<module name="CommentsIndentation"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyForInitializerPad -->
<module name="EmptyForInitializerPad"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyForIteratorPad -->
<module name="EmptyForIteratorPad"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyLineSeparator -->
<module name="EmptyLineSeparator">
<property name="allowMultipleEmptyLines" value="false"/>
<property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
<property name="allowNoEmptyLineBetweenFields" value="true"/>
<property name="tokens" value="CLASS_DEF, CTOR_DEF, ENUM_DEF, IMPORT, INSTANCE_INIT, INTERFACE_DEF, METHOD_DEF, STATIC_IMPORT, STATIC_INIT, VARIABLE_DEF"/> <!-- remove PACKAGE_DEF, temporarily remove COMPACT_CTOR_DEF, RECORD_DEF -->
</module>
<!-- https://checkstyle.org/config_coding.html#FallThrough -->
<module name="FallThrough">
<property name="checkLastCaseGroup" value="true"/>
</module>
<!-- https://checkstyle.org/config_design.html#FinalClass -->
<module name="FinalClass"/>
<!-- https://checkstyle.org/config_coding.html#FinalLocalVariable -->
<module name="FinalLocalVariable">
<property name="tokens" value="PARAMETER_DEF, VARIABLE_DEF"/> <!-- add PARAMETER_DEF -->
<property name="validateEnhancedForLoopVariable" value="true"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#GenericWhitespace -->
<module name="GenericWhitespace"/>
<!-- https://checkstyle.org/config_design.html#HideUtilityClassConstructor -->
<module name="HideUtilityClassConstructor"/>
<!-- https://checkstyle.org/config_imports.html#IllegalImport -->
<module name="IllegalImport">
<property name="illegalPkgs" value="sun.misc, jdk"/>
</module>
<!-- https://checkstyle.org/config_coding.html#IllegalTokenText -->
<module name="IllegalTokenText">
<property name="format" value="($|[^\\])\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message" value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
<property name="tokens" value="CHAR_LITERAL, STRING_LITERAL"/>
</module>
<!-- https://checkstyle.org/config_misc.html#Indentation -->
<module name="Indentation">
<property name="arrayInitIndent" value="2"/>
<property name="basicOffset" value="2"/>
<property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="2"/>
<property name="lineWrappingIndentation" value="0"/>
<property name="throwsIndent" value="2"/>
</module>
<!-- https://checkstyle.org/config_javadoc.html#InvalidJavadocPosition -->
<module name="InvalidJavadocPosition"/>
<!-- https://checkstyle.org/config_javadoc.html#JavadocContentLocation -->
<module name="JavadocContentLocation"/>
<!-- https://checkstyle.org/config_javadoc.html#JavadocMissingWhitespaceAfterAsterisk -->
<module name="JavadocMissingWhitespaceAfterAsterisk"/>
<!-- https://checkstyle.org/config_javadoc.html#JavadocParagraph -->
<module name="JavadocParagraph"/>
<!-- https://checkstyle.org/config_javadoc.html#JavadocMissingWhitespaceAfterAsterisk -->
<module name="JavadocTagContinuationIndentation"/>
<!-- https://checkstyle.org/config_blocks.html#LeftCurly -->
<module name="LeftCurly"/>
<!-- https://checkstyle.org/config_naming.html#MethodName -->
<module name="MethodName">
<property name="format" value="^(?:(?:.{1,3})|(?:[gs]et[^A-Z].*)|(?:(?:[^gsA-Z]..|.[^e].|..[^t]).+))$"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#MethodParamPad -->
<module name="MethodParamPad"/>
<!-- https://checkstyle.org/config_coding.html#MultipleVariableDeclarations -->
<module name="MultipleVariableDeclarations"/>
<!-- https://checkstyle.org/config_coding.html#NoFinalizer -->
<module name="NoFinalizer"/>
<!-- https://checkstyle.org/config_whitespace.html#NoLineWrap -->
<module name="NoLineWrap"/>
<!-- https://checkstyle.org/config_javadoc.html#NonEmptyAtclauseDescription -->
<module name="NonEmptyAtclauseDescription"/>
<!-- https://checkstyle.org/config_whitespace.html#NoWhitespaceAfter -->
<module name="NoWhitespaceAfter">
<property name="allowLineBreaks" value="false"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#NoWhitespaceBefore -->
<module name="NoWhitespaceBefore">
<property name="allowLineBreaks" value="true"/>
<property name="tokens" value="COMMA, DOT, LABELED_STAT, METHOD_REF, POST_DEC, POST_INC, SEMI"/> <!-- remove ELLIPSIS -->
</module>
<!-- https://checkstyle.org/config_coding.html#OneStatementPerLine -->
<module name="OneStatementPerLine"/>
<!-- https://checkstyle.org/config_misc.html#OuterTypeFilename -->
<module name="OuterTypeFilename"/>
<!-- https://checkstyle.org/config_imports.html#RedundantImport -->
<module name="RedundantImport"/>
<!-- https://checkstyle.org/config_modifier.html#RedundantModifier -->
<module name="RedundantModifier">
<property name="tokens" value="ANNOTATION_FIELD_DEF, CLASS_DEF, CTOR_DEF, ENUM_DEF, INTERFACE_DEF, VARIABLE_DEF"/> <!-- remove METHOD_DEF and RESOURCE -->
</module>
<!-- https://checkstyle.org/config_javadoc.html#RequireEmptyLineBeforeBlockTagGroup -->
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
<!-- https://checkstyle.org/config_coding.html#RequireThis -->
<module name="RequireThis">
<property name="validateOnlyOverlapping" value="false"/>
</module>
<!-- https://checkstyle.org/config_blocks.html#RightCurly -->
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens" value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_DEF, INSTANCE_INIT, LITERAL_FOR, LITERAL_WHILE, METHOD_DEF, STATIC_INIT"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="option" value="same"/>
<property name="tokens" value="LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_IF, LITERAL_TRY"/> <!-- add LITERAL_DO -->
</module>
<!-- https://checkstyle.org/config_whitespace.html#SeparatorWrap -->
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapEol"/>
<property name="option" value="eol"/>
<property name="tokens" value="COMMA, SEMI, ELLIPSIS, RBRACK, ARRAY_DECLARATOR, METHOD_REF"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapNl"/>
<property name="option" value="nl"/>
<property name="tokens" value="DOT, AT"/>
</module>
<!-- https://checkstyle.org/config_coding.html#SimplifyBooleanExpression -->
<module name="SimplifyBooleanExpression"/>
<!-- https://checkstyle.org/config_coding.html#SimplifyBooleanReturn -->
<module name="SimplifyBooleanReturn"/>
<!-- https://checkstyle.org/config_whitespace.html#SingleSpaceSeparator -->
<module name="SingleSpaceSeparator">
<property name="validateComments" value="true"/>
</module>
<!-- https://checkstyle.org/config_javadoc.html#SummaryJavadoc -->
<module name="SummaryJavadoc"/>
<!-- https://checkstyle.org/config_annotation.html#SuppressWarningsHolder -->
<module name="SuppressWarningsHolder"/>
<!-- https://checkstyle.org/config_whitespace.html#TypecastParenPad -->
<module name="TypecastParenPad"/>
<!-- https://checkstyle.org/config_coding.html#UnnecessaryParentheses -->
<module name="UnnecessaryParentheses"/>
<!-- https://checkstyle.org/config_imports.html#UnusedImports -->
<module name="UnusedImports"/>
<!-- https://checkstyle.org/config_whitespace.html#WhitespaceAfter -->
<module name="WhitespaceAfter">
<property name="tokens" value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, DO_WHILE"/>
</module>
<!--
#####################
#### third-party ####
#####################
-->
<!-- https://checkstyle.org/config_whitespace.html#EmptyForIteratorPad -->
<module name="EmptyForIteratorPad"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyLineSeparator -->
<module name="EmptyLineSeparator">
<property name="allowMultipleEmptyLines" value="false"/>
<property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
<property name="allowNoEmptyLineBetweenFields" value="true"/>
<property name="tokens" value="CLASS_DEF, CTOR_DEF, ENUM_DEF, IMPORT, INSTANCE_INIT, INTERFACE_DEF, METHOD_DEF, STATIC_IMPORT, STATIC_INIT, VARIABLE_DEF"/> <!-- remove PACKAGE_DEF, temporarily remove COMPACT_CTOR_DEF, RECORD_DEF -->
</module>
<!-- https://checkstyle.org/config_coding.html#FallThrough -->
<module name="FallThrough">
<property name="checkLastCaseGroup" value="true"/>
</module>
<!-- https://checkstyle.org/config_design.html#FinalClass -->
<module name="FinalClass"/>
<!-- https://checkstyle.org/config_coding.html#FinalLocalVariable -->
<module name="FinalLocalVariable">
<property name="tokens" value="PARAMETER_DEF, VARIABLE_DEF"/> <!-- add PARAMETER_DEF -->
<property name="validateEnhancedForLoopVariable" value="true"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#GenericWhitespace -->
<module name="GenericWhitespace"/>
<!-- https://checkstyle.org/config_design.html#HideUtilityClassConstructor -->
<module name="HideUtilityClassConstructor"/>
<!-- https://checkstyle.org/config_imports.html#IllegalImport -->
<module name="IllegalImport">
<property name="illegalPkgs" value="sun, jdk, com.sun"/>
</module>
<!-- https://checkstyle.org/config_coding.html#IllegalTokenText -->
<module name="IllegalTokenText">
<property name="format" value="($|[^\\])\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message" value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
<property name="tokens" value="CHAR_LITERAL, STRING_LITERAL"/>
</module>
<!-- https://checkstyle.org/config_misc.html#Indentation -->
<module name="Indentation">
<property name="arrayInitIndent" value="2"/>
<property name="basicOffset" value="2"/>
<property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="2"/>
<property name="lineWrappingIndentation" value="0"/>
<property name="throwsIndent" value="2"/>
</module>
<!-- https://checkstyle.org/config_javadoc.html#InvalidJavadocPosition -->
<module name="InvalidJavadocPosition"/>
<!-- https://checkstyle.org/config_javadoc.html#JavadocContentLocation -->
<module name="JavadocContentLocation"/>
<!-- https://checkstyle.org/config_javadoc.html#JavadocMissingWhitespaceAfterAsterisk -->
<module name="JavadocMissingWhitespaceAfterAsterisk"/>
<!-- https://checkstyle.org/config_javadoc.html#JavadocParagraph -->
<module name="JavadocParagraph"/>
<!-- https://checkstyle.org/config_javadoc.html#JavadocMissingWhitespaceAfterAsterisk -->
<module name="JavadocTagContinuationIndentation"/>
<!-- https://checkstyle.org/config_blocks.html#LeftCurly -->
<module name="LeftCurly"/>
<!-- https://checkstyle.org/config_naming.html#MethodName -->
<module name="MethodName">
<property name="format" value="^(?:(?:.{1,3})|(?:[gs]et[^A-Z].*)|(?:(?:[^gsA-Z]..|.[^e].|..[^t]).+))$"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#MethodParamPad -->
<module name="MethodParamPad"/>
<!-- https://checkstyle.org/config_javadoc.html#MissingJavadocMethod -->
<!-- <module name="MissingJavadocMethod"/> -->
<!-- https://checkstyle.org/config_javadoc.html#MissingJavadocPackage -->
<!-- <module name="MissingJavadocPackage"/> -->
<!-- https://checkstyle.org/config_javadoc.html#MissingJavadocType -->
<!-- <module name="MissingJavadocType"/> -->
<!-- https://checkstyle.org/config_coding.html#MultipleVariableDeclarations -->
<module name="MultipleVariableDeclarations"/>
<!-- https://checkstyle.org/config_coding.html#NoFinalizer -->
<module name="NoFinalizer"/>
<!-- https://checkstyle.org/config_whitespace.html#NoLineWrap -->
<module name="NoLineWrap"/>
<!-- https://checkstyle.org/config_javadoc.html#NonEmptyAtclauseDescription -->
<module name="NonEmptyAtclauseDescription"/>
<!-- https://checkstyle.org/config_whitespace.html#NoWhitespaceAfter -->
<module name="NoWhitespaceAfter">
<property name="allowLineBreaks" value="false"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#NoWhitespaceBefore -->
<module name="NoWhitespaceBefore">
<property name="allowLineBreaks" value="true"/>
<property name="tokens" value="COMMA, DOT, LABELED_STAT, METHOD_REF, POST_DEC, POST_INC, SEMI"/> <!-- remove ELLIPSIS -->
</module>
<!-- https://checkstyle.org/config_coding.html#OneStatementPerLine -->
<module name="OneStatementPerLine"/>
<!-- https://checkstyle.org/config_misc.html#OuterTypeFilename -->
<module name="OuterTypeFilename"/>
<!-- https://checkstyle.org/config_imports.html#RedundantImport -->
<module name="RedundantImport"/>
<!-- https://checkstyle.org/config_modifier.html#RedundantModifier -->
<module name="RedundantModifier">
<property name="tokens" value="ANNOTATION_FIELD_DEF, CLASS_DEF, CTOR_DEF, ENUM_DEF, INTERFACE_DEF, VARIABLE_DEF"/> <!-- remove METHOD_DEF and RESOURCE -->
</module>
<!-- https://checkstyle.org/config_javadoc.html#RequireEmptyLineBeforeBlockTagGroup -->
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
<!-- https://checkstyle.org/config_coding.html#RequireThis -->
<module name="RequireThis">
<property name="validateOnlyOverlapping" value="false"/>
</module>
<!-- https://checkstyle.org/config_blocks.html#RightCurly -->
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens" value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_DEF, INSTANCE_INIT, LITERAL_FOR, LITERAL_WHILE, METHOD_DEF, STATIC_INIT"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="option" value="same"/>
<property name="tokens" value="LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_IF, LITERAL_TRY"/> <!-- add LITERAL_DO -->
</module>
<!-- https://checkstyle.org/config_whitespace.html#SeparatorWrap -->
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapEol"/>
<property name="option" value="eol"/>
<property name="tokens" value="COMMA, SEMI, ELLIPSIS, RBRACK, ARRAY_DECLARATOR, METHOD_REF"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapNl"/>
<property name="option" value="nl"/>
<property name="tokens" value="DOT, AT"/>
</module>
<!-- https://checkstyle.org/config_coding.html#SimplifyBooleanExpression -->
<module name="SimplifyBooleanExpression"/>
<!-- https://checkstyle.org/config_coding.html#SimplifyBooleanReturn -->
<module name="SimplifyBooleanReturn"/>
<!-- https://checkstyle.org/config_whitespace.html#SingleSpaceSeparator -->
<module name="SingleSpaceSeparator">
<property name="validateComments" value="true"/>
</module>
<!-- https://checkstyle.org/config_javadoc.html#SummaryJavadoc -->
<module name="SummaryJavadoc"/>
<!-- https://checkstyle.org/config_annotation.html#SuppressWarningsHolder -->
<module name="SuppressWarningsHolder"/>
<!-- https://checkstyle.org/config_whitespace.html#TypecastParenPad -->
<module name="TypecastParenPad"/>
<!-- https://checkstyle.org/config_coding.html#UnnecessaryParentheses -->
<module name="UnnecessaryParentheses"/>
<!-- https://checkstyle.org/config_imports.html#UnusedImports -->
<module name="UnusedImports"/>
<!-- https://checkstyle.org/config_whitespace.html#WhitespaceAfter -->
<module name="WhitespaceAfter">
<property name="tokens" value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, DO_WHILE"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#WhitespaceAround -->
<module name="WhitespaceAround">
<property name="ignoreEnhancedForColon" value="false"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLambdas" value="true"/>
<property name="tokens" value="ASSIGN, COLON, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, EQUAL, GE, GT, LAND, LCURLY, LE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
</module>
<!--
#####################
#### third-party ####
#####################
-->
<!-- https://checkstyle.org/config_javadoc.html#WriteTag -->
<!-- https://gitlab.com/stellardrift/stylecheck/-/blob/dev/src/main/java/ca/stellardrift/stylecheck/FilteringWriteTag.java -->
<!--
<module name="FilteringWriteTag">
<property name="tag" value="@since\s"/>
<property name="tagFormat" value="\d\.\d+\.\d+"/>
<property name="tagSeverity" value="ignore"/>
<property name="minimumScope" value="public"/>
<property name="tokens" value="INTERFACE_DEF, CLASS_DEF, ENUM_DEF, ANNOTATION_DEF, RECORD_DEF, METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, RECORD_DEF, COMPACT_CTOR_DEF"/>
</module>
-->
<!-- https://gitlab.com/stellardrift/stylecheck/-/blob/dev/src/main/java/ca/stellardrift/stylecheck/StatementNoWhitespaceAfter.java -->
<!--
<module name="StatementNoWhitespaceAfter">
<property name="tokens" value="LITERAL_CATCH, LITERAL_FOR, LITERAL_IF, LITERAL_TRY, LITERAL_WHILE"/>
</module>
-->
</module>
</module>
+10 -14
View File
@@ -1,24 +1,20 @@
name: MiniMOTD
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
name: Build MiniMOTD
on: [ push, pull_request ]
jobs:
build:
# Only run on PRs if the source branch is on someone else's repo
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
- uses: gradle/wrapper-validation-action@v1
- name: Set up JDK
uses: actions/setup-java@v2
with:
java-version: 1.8
distribution: 'adopt'
java-version: '16'
- name: Build with Gradle
run: ./gradlew clean build
run: ./gradlew build
- run: mkdir staging && cp build/libs/*.jar staging
- uses: actions/upload-artifact@v2
with:
+1 -1
View File
@@ -116,4 +116,4 @@ run/
.gradle
# Ignore Gradle build output directory
build
build/
+1
View File
@@ -1,5 +1,6 @@
![MiniMOTD logo](https://i.imgur.com/CXWwjOJ.png)
[![build](https://img.shields.io/github/checks-status/jpenilla/MiniMOTD/master?label=build)](https://github.com/jpenilla/MiniMOTD/actions) [![latest release](https://img.shields.io/github/v/release/jpenilla/MiniMOTD)](https://github.com/jpenilla/MiniMOTD/releases)
### MiniMOTD is a basic server list MOTD plugin/mod for Minecraft servers and proxies
+16
View File
@@ -0,0 +1,16 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
dependencies {
implementation(libs.indraCommon)
implementation(libs.shadow)
implementation(libs.testLoggerPlugin)
// https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
}
+12
View File
@@ -0,0 +1,12 @@
import ca.stellardrift.build.configurate.ConfigFormats
import ca.stellardrift.build.configurate.catalog.PolyglotVersionCatalogExtension
enableFeaturePreview("VERSION_CATALOGS")
plugins {
id("ca.stellardrift.polyglot-version-catalogs") version "5.0.0"
}
extensions.configure<PolyglotVersionCatalogExtension> {
from(ConfigFormats.YAML, file("../gradle/libs.versions.yml"))
}
+6
View File
@@ -0,0 +1,6 @@
object Constants {
const val GITHUB_USER = "jpenilla"
const val GITHUB_REPO = "MiniMOTD"
const val GITHUB_URL = "https://github.com/$GITHUB_USER/$GITHUB_REPO"
const val RELOCATION_BASE_PACKAGE = "xyz.jpenilla.minimotd.lib"
}
+10
View File
@@ -0,0 +1,10 @@
import net.kyori.indra.git.IndraGitExtension
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.Project
import org.gradle.kotlin.dsl.the
fun Project.lastCommitHash(): String =
the<IndraGitExtension>().commit()?.name?.substring(0, 7) ?: "unknown"
val Project.libs: LibrariesForLibs
get() = the()
@@ -0,0 +1,18 @@
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
abstract class FileCopyTask : DefaultTask() {
@InputFile
val fileToCopy = project.objects.fileProperty()
@OutputFile
val destination = project.objects.fileProperty()
@TaskAction
private fun copyFile() {
destination.get().asFile.parentFile.mkdirs()
fileToCopy.get().asFile.copyTo(destination.get().asFile, overwrite = true)
}
}
@@ -0,0 +1,8 @@
import org.gradle.api.Project
import org.gradle.api.provider.Property
import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.kotlin.dsl.property
open class MiniMOTDPlatformExtension(private val project: Project) {
val jarTask: Property<AbstractArchiveTask> = project.objects.property()
}
@@ -0,0 +1,33 @@
import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
fun ShadowJar.commonRelocation(pkg: String) {
relocate(pkg, "${Constants.RELOCATION_BASE_PACKAGE}.$pkg")
}
fun ShadowJar.platformRelocation(platform: String, pkg: String) {
relocate(pkg, "${Constants.RELOCATION_BASE_PACKAGE}.platform_$platform.$pkg")
}
fun ShadowJar.configureForNativeAdventurePlatform() {
platformRelocation("kyori", "net.kyori.adventure.text.minimessage")
dependencies {
excludeAdventureExceptMiniMessage()
}
}
fun ShadowJar.commonConfiguration() {
commonRelocation("org.spongepowered.configurate")
commonRelocation("com.typesafe.config")
dependencies {
exclude(dependency("org.checkerframework:checker-qual"))
exclude(dependency("org.jetbrains:annotations")) // transitive dep from adventure
}
}
private fun DependencyFilter.excludeAdventureExceptMiniMessage() {
exclude { dependency ->
dependency.moduleGroup == "net.kyori"
&& dependency.moduleName != "adventure-text-minimessage"
}
}
@@ -0,0 +1,43 @@
import com.adarshr.gradle.testlogger.theme.ThemeType
plugins {
id("java-library")
id("net.kyori.indra")
id("net.kyori.indra.publishing")
id("net.kyori.indra.checkstyle")
id("net.kyori.indra.license-header")
id("com.adarshr.test-logger")
}
version = (version as String)
.run { if (this.endsWith("-SNAPSHOT")) "$this+${lastCommitHash()}" else this }
indra {
javaVersions {
target(8)
}
github(Constants.GITHUB_USER, Constants.GITHUB_REPO)
mitLicense()
}
testlogger {
theme = ThemeType.MOCHA_PARALLEL
showPassed = false
}
dependencies {
testImplementation(libs.jupiterEngine)
}
tasks {
tasks {
withType<JavaCompile> {
options.compilerArgs.add("-Xlint:-processing")
}
sequenceOf(javadocJar, javadoc).forEach {
it.configure {
onlyIf { false }
}
}
}
}
@@ -0,0 +1,3 @@
plugins {
id("base")
}
@@ -0,0 +1,18 @@
plugins {
id("minimotd.base-conventions")
id("net.kyori.indra.git")
}
val platformExtension = extensions.create<MiniMOTDPlatformExtension>("miniMOTDPlatform", project)
tasks {
val copyJar = register<FileCopyTask>("copyJar") {
fileToCopy.set(platformExtension.jarTask.flatMap { it.archiveFile })
destination.set(rootProject.layout.buildDirectory.dir("libs").flatMap {
it.file(fileToCopy.map { file -> file.asFile.name })
})
}
build {
dependsOn(copyJar)
}
}
@@ -0,0 +1,18 @@
plugins {
id("minimotd.platform-conventions")
id("com.github.johnrengelman.shadow")
}
tasks {
jar {
archiveClassifier.set("unshaded")
}
shadowJar {
archiveClassifier.set(null as String?)
commonConfiguration()
}
}
extensions.configure<MiniMOTDPlatformExtension> {
jarTask.set(tasks.named<AbstractArchiveTask>("shadowJar"))
}
+4 -99
View File
@@ -1,102 +1,7 @@
import com.adarshr.gradle.testlogger.TestLoggerPlugin
import com.adarshr.gradle.testlogger.theme.ThemeType
import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
import net.kyori.indra.IndraCheckstylePlugin
import net.kyori.indra.IndraLicenseHeaderPlugin
import net.kyori.indra.IndraPlugin
import net.kyori.indra.sonatypeSnapshots
import java.io.ByteArrayOutputStream
plugins {
`java-library`
id("net.kyori.indra") version "1.3.1"
id("com.github.johnrengelman.shadow") version "6.1.0"
id("com.adarshr.test-logger") version "3.0.0"
id("net.kyori.blossom") version "1.2.0" apply false
id("minimotd.build-logic")
}
allprojects {
group = "xyz.jpenilla"
version = "2.0.2+${lastCommitHash()}-SNAPSHOT"
description = "Use MiniMessage text formatting in the server list MOTD."
}
ext["url"] = "https://github.com/jpenilla/MiniMOTD"
subprojects {
apply<JavaLibraryPlugin>()
apply<ShadowPlugin>()
apply<IndraPlugin>()
apply<IndraCheckstylePlugin>()
apply<IndraLicenseHeaderPlugin>()
apply<TestLoggerPlugin>()
repositories {
//mavenLocal()
mavenCentral()
sonatypeSnapshots()
jcenter()
maven("https://papermc.io/repo/repository/maven-public/")
maven("https://nexus.velocitypowered.com/repository/maven-public/")
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
maven("https://oss.sonatype.org/content/groups/public/")
maven("https://repo.spongepowered.org/repository/maven-public/")
maven("https://repo.jpenilla.xyz/snapshots/")
maven("https://jitpack.io")
maven("https://repo.codemc.org/repository/maven-public")
}
dependencies {
testImplementation("org.junit.jupiter", "junit-jupiter-engine", "5.7.0")
}
indra {
javaVersions {
testWith(8, 11, 15)
target.set(8)
}
github("jpenilla", "MiniMOTD") {
issues = true
}
mitLicense()
}
testlogger {
theme = ThemeType.MOCHA_PARALLEL
showPassed = false
}
tasks {
shadowJar {
minimize()
exclude("META-INF/versions/**")
if (!project.name.contains("fabric")) {
archiveClassifier.set("")
doLast {
val output = outputs.files.singleFile
output.copyTo(rootProject.buildDir.resolve("libs").resolve(output.name), overwrite = true)
}
}
}
withType<JavaCompile> {
options.compilerArgs.add("-Xlint:-processing")
}
withType<Jar> {
onlyIf { archiveClassifier.get() != "javadoc" }
}
withType<Javadoc> {
onlyIf { false }
}
}
}
tasks.withType<Jar> {
onlyIf { false }
}
fun lastCommitHash(): String = ByteArrayOutputStream().apply {
exec {
commandLine = listOf("git", "rev-parse", "--short", "HEAD")
standardOutput = this@apply
}
}.toString(Charsets.UTF_8.name()).trim()
group = "xyz.jpenilla"
version = "2.0.3"
description = "Use MiniMessage text formatting in the server list MOTD."
+44
View File
@@ -0,0 +1,44 @@
plugins {
id("minimotd.shadow-platform")
id("net.minecrell.plugin-yml.bukkit")
id("xyz.jpenilla.run-paper")
}
dependencies {
implementation(projects.minimotdCommon)
implementation(libs.slf4jJdk14)
implementation(libs.adventurePlatformBukkit)
implementation(libs.bstatsBukkit)
compileOnly(libs.paperApi)
}
tasks {
runServer {
minecraftVersion("1.17")
}
generateBukkitPluginDescription {
mustRunAfter(clean)
}
shadowJar {
commonRelocation("org.slf4j")
commonRelocation("io.leangen.geantyref")
commonRelocation("net.kyori")
commonRelocation("org.bstats")
}
}
bukkit {
name = rootProject.name
main = "xyz.jpenilla.minimotd.bukkit.MiniMOTDPlugin"
apiVersion = "1.13"
website = Constants.GITHUB_URL
authors = listOf("jmp")
softDepend = listOf("ViaVersion")
commands {
create("minimotd") {
description = "MiniMOTD Command"
usage = "/minimotd help"
permission = "minimotd.admin"
}
}
}
@@ -21,37 +21,37 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.spigot;
package xyz.jpenilla.minimotd.bukkit;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.checkerframework.checker.nullness.qual.NonNull;
import xyz.jpenilla.minimotd.common.CommandHandlerFactory;
import xyz.jpenilla.minimotd.common.CommandHandler;
import java.util.Collections;
import java.util.List;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.event.ClickEvent.runCommand;
import static net.kyori.adventure.text.format.NamedTextColor.RED;
public class SpigotCommand implements CommandExecutor, TabCompleter {
final class BukkitCommand implements CommandExecutor, TabCompleter {
private final MiniMOTDPlugin plugin;
private final CommandHandlerFactory handlerFactory;
private final CommandHandler handler;
public SpigotCommand(final @NonNull MiniMOTDPlugin plugin) {
BukkitCommand(final @NonNull MiniMOTDPlugin plugin) {
this.plugin = plugin;
this.handlerFactory = new CommandHandlerFactory(plugin.miniMOTD());
this.handler = new CommandHandler(plugin.miniMOTD());
}
@Override
public boolean onCommand(final @NonNull CommandSender sender, final @NonNull Command command, final @NonNull String label, final @NonNull String[] args) {
final Audience audience = this.plugin.audiences().sender(sender);
if (!sender.hasPermission("minimotd.admin")) {
audience.sendMessage(Component.text("No permission.", NamedTextColor.RED));
audience.sendMessage(text("No permission.", RED));
return true;
}
@@ -62,13 +62,13 @@ public class SpigotCommand implements CommandExecutor, TabCompleter {
switch (args[0]) {
case "about":
this.handlerFactory.about().execute(audience);
this.handler.about(audience);
return true;
case "help":
this.handlerFactory.help().execute(audience);
this.handler.help(audience);
return true;
case "reload":
this.handlerFactory.reload().execute(audience);
this.handler.reload(audience);
return true;
}
@@ -77,11 +77,9 @@ public class SpigotCommand implements CommandExecutor, TabCompleter {
}
private void onInvalidUse(final @NonNull Audience audience) {
audience.sendMessage(
Component.text("Invalid command usage. Use '/minimotd help' for a list of command provided by MiniMOTD.", NamedTextColor.RED)
.hoverEvent(Component.text("Click to execute '/minimotd help'"))
.clickEvent(ClickEvent.runCommand("/minimotd help"))
);
audience.sendMessage(text("Invalid command usage. Use '/minimotd help' for a list of command provided by MiniMOTD.", RED)
.hoverEvent(text("Click to execute '/minimotd help'"))
.clickEvent(runCommand("/minimotd help")));
}
private static final List<String> COMMANDS = ImmutableList.of("about", "reload", "help");
@@ -21,11 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.spigot;
package xyz.jpenilla.minimotd.bukkit;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.CachedServerIcon;
@@ -35,10 +36,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.MiniMOTDPlatform;
import xyz.jpenilla.minimotd.common.UpdateChecker;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import xyz.jpenilla.minimotd.common.util.UpdateChecker;
public final class MiniMOTDPlugin extends JavaPlugin implements MiniMOTDPlatform<CachedServerIcon> {
private static final boolean PAPER_PING_EVENT_EXISTS = findClass("com.destroystokyo.paper.event.server.PaperServerListPingEvent") != null;
@@ -60,9 +58,9 @@ public final class MiniMOTDPlugin extends JavaPlugin implements MiniMOTDPlatform
this.majorMinecraftVersion = Integer.parseInt(this.serverApiVersion.split("_")[1]);
if (PAPER_PING_EVENT_EXISTS) {
Bukkit.getPluginManager().registerEvents(new PaperPingListener(this, this.miniMOTD), this);
this.getServer().getPluginManager().registerEvents(new PaperPingListener(this, this.miniMOTD), this);
} else {
Bukkit.getPluginManager().registerEvents(new PingListener(this, this.miniMOTD), this);
this.getServer().getPluginManager().registerEvents(new PingListener(this, this.miniMOTD), this);
if (this.majorMinecraftVersion >= 12) { // PaperServerListPingEvent was added in 1.12
this.suggestPaper();
}
@@ -70,15 +68,15 @@ public final class MiniMOTDPlugin extends JavaPlugin implements MiniMOTDPlatform
final PluginCommand command = this.getCommand("minimotd");
if (command != null) {
final SpigotCommand spigotCommand = new SpigotCommand(this);
command.setExecutor(spigotCommand);
command.setTabCompleter(spigotCommand);
final BukkitCommand bukkitCommand = new BukkitCommand(this);
command.setExecutor(bukkitCommand);
command.setTabCompleter(bukkitCommand);
}
final Metrics metrics = new Metrics(this, 8132);
if (this.miniMOTD.configManager().pluginSettings().updateChecker()) {
Bukkit.getScheduler().runTaskAsynchronously(this, () ->
this.getServer().getScheduler().runTaskAsynchronously(this, () ->
new UpdateChecker().checkVersion().forEach(this.logger::info));
}
}
@@ -99,7 +97,7 @@ public final class MiniMOTDPlugin extends JavaPlugin implements MiniMOTDPlatform
@Override
public @NonNull CachedServerIcon loadIcon(final @NonNull BufferedImage image) throws Exception {
return Bukkit.loadServerIcon(image);
return this.getServer().loadServerIcon(image);
}
public int majorMinecraftVersion() {
@@ -21,63 +21,50 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.spigot;
package xyz.jpenilla.minimotd.bukkit;
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.util.CachedServerIcon;
import org.checkerframework.checker.nullness.qual.NonNull;
import xyz.jpenilla.minimotd.common.MOTDIconPair;
import xyz.jpenilla.minimotd.common.Constants;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.PingResponse;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig.PlayerCount;
public class PaperPingListener implements Listener {
final class PaperPingListener implements Listener {
private final MiniMOTDPlugin plugin;
private final LegacyComponentSerializer serializer = LegacyComponentSerializer.builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build();
private final LegacyComponentSerializer legacySerializer = LegacyComponentSerializer.builder().build();
private final LegacyComponentSerializer unusualHexSerializer = LegacyComponentSerializer.builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build();
private final MiniMOTD<CachedServerIcon> miniMOTD;
public PaperPingListener(final @NonNull MiniMOTDPlugin plugin, final @NonNull MiniMOTD<CachedServerIcon> miniMOTD) {
PaperPingListener(final @NonNull MiniMOTDPlugin plugin, final @NonNull MiniMOTD<CachedServerIcon> miniMOTD) {
this.miniMOTD = miniMOTD;
this.plugin = plugin;
}
@EventHandler
public void onPing(final @NonNull PaperServerListPingEvent e) {
public void handlePing(final @NonNull PaperServerListPingEvent event) {
final MiniMOTDConfig cfg = this.miniMOTD.configManager().mainConfig();
final PlayerCount count = cfg.modifyPlayerCount(e.getNumPlayers(), e.getMaxPlayers());
final int onlinePlayers = count.onlinePlayers();
final int maxPlayers = count.maxPlayers();
final PingResponse<CachedServerIcon> response = this.miniMOTD.createMOTD(cfg, event.getNumPlayers(), event.getMaxPlayers());
e.setNumPlayers(onlinePlayers);
e.setMaxPlayers(maxPlayers);
final MOTDIconPair<CachedServerIcon> pair = this.miniMOTD.createMOTD(cfg, onlinePlayers, maxPlayers);
final Component motdComponent = pair.motd();
if (motdComponent != null) {
if (e.getClient().getProtocolVersion() < 735 || this.plugin.majorMinecraftVersion() < 16) {
e.setMotd(this.legacySerializer.serialize(motdComponent));
response.playerCount().applyCount(event::setNumPlayers, event::setMaxPlayers);
response.motd(motd -> {
if (event.getClient().getProtocolVersion() < Constants.MINECRAFT_1_16_PROTOCOL_VERSION || this.plugin.majorMinecraftVersion() < 16) {
event.setMotd(LegacyComponentSerializer.legacySection().serialize(motd));
} else {
e.setMotd(this.serializer.serialize(motdComponent));
event.setMotd(this.unusualHexSerializer.serialize(motd));
}
}
});
response.icon(event::setServerIcon);
final CachedServerIcon favicon = pair.icon();
if (favicon != null) {
e.setServerIcon(favicon);
if (response.disablePlayerListHover()) {
event.getPlayerSample().clear();
}
if (cfg.disablePlayerListHover()) {
e.getPlayerSample().clear();
}
if (cfg.hidePlayerCount()) {
e.setHidePlayers(true);
if (response.hidePlayerCount()) {
event.setHidePlayers(true);
}
}
}
@@ -21,54 +21,42 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.spigot;
package xyz.jpenilla.minimotd.bukkit;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.ServerListPingEvent;
import org.bukkit.util.CachedServerIcon;
import org.checkerframework.checker.nullness.qual.NonNull;
import xyz.jpenilla.minimotd.common.MOTDIconPair;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.PingResponse;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig.PlayerCount;
public class PingListener implements Listener {
final class PingListener implements Listener {
private final MiniMOTD<CachedServerIcon> miniMOTD;
private final LegacyComponentSerializer serializer = LegacyComponentSerializer.builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build();
private final LegacyComponentSerializer legacySerializer = LegacyComponentSerializer.builder().build();
private final LegacyComponentSerializer unusualHexSerializer = LegacyComponentSerializer.builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build();
private final MiniMOTDPlugin plugin;
public PingListener(final @NonNull MiniMOTDPlugin plugin, final @NonNull MiniMOTD<CachedServerIcon> miniMOTD) {
PingListener(final @NonNull MiniMOTDPlugin plugin, final @NonNull MiniMOTD<CachedServerIcon> miniMOTD) {
this.plugin = plugin;
this.miniMOTD = miniMOTD;
}
@EventHandler
public void onPing(final @NonNull ServerListPingEvent e) {
public void handlePing(final @NonNull ServerListPingEvent event) {
final MiniMOTDConfig cfg = this.miniMOTD.configManager().mainConfig();
final int onlinePlayers = e.getNumPlayers();
final PlayerCount count = cfg.modifyPlayerCount(onlinePlayers, e.getMaxPlayers());
final int maxPlayers = count.maxPlayers();
e.setMaxPlayers(maxPlayers);
final PingResponse<CachedServerIcon> response = this.miniMOTD.createMOTD(cfg, event.getNumPlayers(), event.getMaxPlayers());
final MOTDIconPair<CachedServerIcon> pair = this.miniMOTD.createMOTD(cfg, onlinePlayers, maxPlayers);
final Component motdComponent = pair.motd();
if (motdComponent != null) {
event.setMaxPlayers(response.playerCount().maxPlayers());
response.motd(motd -> {
if (this.plugin.majorMinecraftVersion() > 15) {
e.setMotd(this.serializer.serialize(motdComponent));
event.setMotd(this.unusualHexSerializer.serialize(motd));
} else {
e.setMotd(this.legacySerializer.serialize(motdComponent));
event.setMotd(LegacyComponentSerializer.legacySection().serialize(motd));
}
}
final CachedServerIcon favicon = pair.icon();
if (favicon != null) {
e.setServerIcon(favicon);
}
});
response.icon(event::setServerIcon);
}
}
-32
View File
@@ -1,32 +0,0 @@
plugins {
id("net.minecrell.plugin-yml.bungee") version "0.3.0"
}
dependencies {
implementation(project(":minimotd-common"))
implementation("org.slf4j", "slf4j-jdk14","1.7.30")
implementation("net.kyori", "adventure-platform-bungeecord", "4.0.0-SNAPSHOT")
implementation("org.bstats", "bstats-bungeecord", "2.2.1")
compileOnly("io.github.waterfallmc", "waterfall-api", "1.16-R0.4-SNAPSHOT")
}
tasks {
shadowJar {
relocate("org.slf4j", "xyz.jpenilla.minimotd.lib.slf4j")
relocate("io.leangen.geantyref", "xyz.jpenilla.minimotd.lib.io.leangen.geantyref")
relocate("org.spongepowered.configurate", "xyz.jpenilla.minimotd.lib.spongepowered.configurate")
relocate("com.typesafe.config", "xyz.jpenilla.minimotd.lib.typesafe.config")
relocate("net.kyori", "xyz.jpenilla.minimotd.lib.kyori")
relocate("org.checkerframework", "xyz.jpenilla.minimotd.lib.checkerframework")
relocate("org.bstats", "xyz.jpenilla.minimotd.lib.bstats")
}
build {
dependsOn(shadowJar)
}
}
bungee {
name = rootProject.name
main = "xyz.jpenilla.minimotd.bungee.MiniMOTDPlugin"
author = "jmp"
}
+30
View File
@@ -0,0 +1,30 @@
plugins {
id("minimotd.shadow-platform")
id("net.minecrell.plugin-yml.bungee")
}
dependencies {
implementation(projects.minimotdCommon)
implementation(libs.slf4jJdk14)
implementation(libs.adventurePlatformBungeecord)
implementation(libs.bstatsBungeecord)
compileOnly(libs.waterfallApi)
}
tasks {
generateBungeePluginDescription {
mustRunAfter(clean)
}
shadowJar {
commonRelocation("org.slf4j")
commonRelocation("io.leangen.geantyref")
commonRelocation("net.kyori")
commonRelocation("org.bstats")
}
}
bungee {
name = rootProject.name
main = "xyz.jpenilla.minimotd.bungee.MiniMOTDPlugin"
author = "jmp"
}
@@ -24,29 +24,30 @@
package xyz.jpenilla.minimotd.bungee;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;
import org.checkerframework.checker.nullness.qual.NonNull;
import xyz.jpenilla.minimotd.common.CommandHandlerFactory;
import xyz.jpenilla.minimotd.common.CommandHandler;
public class BungeeCommand extends Command {
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.event.ClickEvent.runCommand;
import static net.kyori.adventure.text.format.NamedTextColor.RED;
final class BungeeCommand extends Command {
private final MiniMOTDPlugin plugin;
private final CommandHandlerFactory handlerFactory;
private final CommandHandler handler;
public BungeeCommand(final @NonNull MiniMOTDPlugin plugin) {
BungeeCommand(final @NonNull MiniMOTDPlugin plugin) {
super("minimotd");
this.plugin = plugin;
this.handlerFactory = new CommandHandlerFactory(plugin.miniMOTD());
this.handler = new CommandHandler(plugin.miniMOTD());
}
@Override
public void execute(final @NonNull CommandSender sender, final @NonNull String @NonNull [] args) {
final Audience audience = this.plugin.audiences().sender(sender);
if (!sender.hasPermission("minimotd.admin")) {
audience.sendMessage(Component.text("No permission.", NamedTextColor.RED));
audience.sendMessage(text("No permission.", RED));
return;
}
@@ -57,13 +58,13 @@ public class BungeeCommand extends Command {
switch (args[0]) {
case "about":
this.handlerFactory.about().execute(audience);
this.handler.about(audience);
return;
case "help":
this.handlerFactory.help().execute(audience);
this.handler.help(audience);
return;
case "reload":
this.handlerFactory.reload().execute(audience);
this.handler.reload(audience);
return;
}
@@ -71,10 +72,8 @@ public class BungeeCommand extends Command {
}
private void onInvalidUse(final @NonNull Audience audience) {
audience.sendMessage(
Component.text("Invalid command usage. Use '/minimotd help' for a list of command provided by MiniMOTD.", NamedTextColor.RED)
.hoverEvent(Component.text("Click to execute '/minimotd help'"))
.clickEvent(ClickEvent.runCommand("/minimotd help"))
);
audience.sendMessage(text("Invalid command usage. Use '/minimotd help' for a list of command provided by MiniMOTD.", RED)
.hoverEvent(text("Click to execute '/minimotd help'"))
.clickEvent(runCommand("/minimotd help")));
}
}
@@ -24,6 +24,9 @@
package xyz.jpenilla.minimotd.bungee;
import com.google.gson.Gson;
import java.awt.image.BufferedImage;
import java.lang.reflect.Field;
import java.nio.file.Path;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.Favicon;
@@ -36,13 +39,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.MiniMOTDPlatform;
import xyz.jpenilla.minimotd.common.UpdateChecker;
import xyz.jpenilla.minimotd.common.util.UpdateChecker;
import java.awt.image.BufferedImage;
import java.lang.reflect.Field;
import java.nio.file.Path;
public class MiniMOTDPlugin extends Plugin implements MiniMOTDPlatform<Favicon> {
public final class MiniMOTDPlugin extends Plugin implements MiniMOTDPlatform<Favicon> {
private Logger logger;
private BungeeAudiences audiences;
private MiniMOTD<Favicon> miniMOTD;
@@ -23,7 +23,6 @@
*/
package xyz.jpenilla.minimotd.bungee;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.Favicon;
import net.md_5.bungee.api.ServerPing;
@@ -34,17 +33,14 @@ import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import org.checkerframework.checker.nullness.qual.NonNull;
import xyz.jpenilla.minimotd.common.Constants;
import xyz.jpenilla.minimotd.common.MOTDIconPair;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.PingResponse;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig.PlayerCount;
import java.util.Optional;
public class PingListener implements Listener {
public final class PingListener implements Listener {
private final MiniMOTD<Favicon> miniMOTD;
public PingListener(final @NonNull MiniMOTD<Favicon> miniMOTD) {
PingListener(final @NonNull MiniMOTD<Favicon> miniMOTD) {
this.miniMOTD = miniMOTD;
}
@@ -55,48 +51,33 @@ public class PingListener implements Listener {
return;
}
final String configString = this.miniMOTD.configManager().pluginSettings().configStringForHost(
Optional.ofNullable(e.getConnection().getVirtualHost())
.map(inetSocketAddress -> String.format("%s:%s", inetSocketAddress.getHostName(), inetSocketAddress.getPort()))
.orElse("default")
).orElse("default");
final MiniMOTDConfig cfg = this.miniMOTD.configManager().resolveConfig(configString);
final ServerPing.Players players = response.getPlayers();
final PlayerCount count = cfg.modifyPlayerCount(players.getOnline(), players.getMax());
final int onlinePlayers = count.onlinePlayers();
final int maxPlayers = count.maxPlayers();
final MiniMOTDConfig cfg = this.miniMOTD.configManager().resolveConfig(e.getConnection().getVirtualHost());
final PingResponse<Favicon> mini = this.miniMOTD.createMOTD(cfg, players.getOnline(), players.getMax());
if (cfg.hidePlayerCount()) {
if (mini.hidePlayerCount()) {
response.setPlayers(null);
} else {
players.setOnline(onlinePlayers);
players.setMax(maxPlayers);
if (cfg.disablePlayerListHover()) {
mini.playerCount().applyCount(players::setOnline, players::setMax);
if (mini.disablePlayerListHover()) {
players.setSample(new ServerPing.PlayerInfo[]{});
}
}
final MOTDIconPair<Favicon> pair = this.miniMOTD.createMOTD(cfg, onlinePlayers, maxPlayers);
final Component motdComponent = pair.motd();
if (motdComponent != null) {
mini.motd(motd -> {
final BaseComponent[] bungee;
if (e.getConnection().getVersion() < Constants.MINECRAFT_1_16_PROTOCOL_VERSION) {
bungee = BungeeComponentSerializer.legacy().serialize(motdComponent);
bungee = BungeeComponentSerializer.legacy().serialize(motd);
} else {
bungee = BungeeComponentSerializer.get().serialize(motdComponent);
bungee = BungeeComponentSerializer.get().serialize(motd);
}
if (BungeeComponentSerializer.isNative()) {
response.setDescriptionComponent(bungee[0]);
} else {
response.setDescriptionComponent(new TextComponent(bungee));
}
}
final Favicon favicon = pair.icon();
if (favicon != null) {
response.setFavicon(favicon);
}
});
mini.icon(response::setFavicon);
e.setResponse(response);
}
+20 -15
View File
@@ -1,5 +1,6 @@
plugins {
id("net.kyori.blossom")
id("minimotd.base-conventions")
}
tasks.jar {
@@ -9,26 +10,30 @@ tasks.jar {
}
dependencies {
api("org.slf4j", "slf4j-api", "1.7.30")
api("org.spongepowered", "configurate-hocon", "4.1.0-SNAPSHOT")
platform(api("net.kyori", "adventure-bom", "4.7.0"))
api("net.kyori", "adventure-text-serializer-gson") {
api(libs.slf4jApi)
api(libs.configurateHocon)
api(platform(libs.adventureBom))
api(libs.adventureApi)
api(libs.adventureTextSerializerPlain)
api(libs.adventureTextSerializerGson) {
exclude("com.google.code.gson", "gson")
}
api("net.kyori", "adventure-text-minimessage", "4.1.0-SNAPSHOT")
compileOnlyApi("com.google.code.gson", "gson", "2.8.0")
testImplementation("com.google.code.gson", "gson", "2.8.0")
compileOnlyApi("com.google.guava", "guava", "21.0")
api(libs.minimessage)
compileOnlyApi(libs.checkerQual)
compileOnlyApi(libs.gson)
testImplementation(libs.gson)
compileOnlyApi(libs.guava)
}
blossom {
fun replaceTokens(file: String, vararg tokens: Pair<String, String>) = tokens.forEach { (k, v) ->
replaceToken("\${$k}", v, file)
}
replaceTokens(
"src/main/java/xyz/jpenilla/minimotd/common/Constants.java",
val file = "src/main/java/xyz/jpenilla/minimotd/common/Constants.java"
mapOf(
"PLUGIN_NAME" to rootProject.name,
"PLUGIN_VERSION" to project.version.toString(),
"PLUGIN_WEBSITE" to rootProject.ext["url"].toString()
)
"PLUGIN_WEBSITE" to Constants.GITHUB_URL,
"GITHUB_USER" to Constants.GITHUB_USER,
"GITHUB_REPO" to Constants.GITHUB_REPO
).forEach { (k, v) ->
replaceToken("\${$k}", v, file)
}
}
@@ -23,13 +23,13 @@
*/
package xyz.jpenilla.minimotd.common;
import java.util.stream.Stream;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.stream.Stream;
import org.checkerframework.framework.qual.DefaultQualifier;
import static net.kyori.adventure.text.Component.space;
import static net.kyori.adventure.text.Component.text;
@@ -37,88 +37,83 @@ import static net.kyori.adventure.text.LinearComponents.linear;
import static net.kyori.adventure.text.event.ClickEvent.openUrl;
import static net.kyori.adventure.text.event.ClickEvent.runCommand;
import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
import static net.kyori.adventure.text.format.TextColor.color;
import static net.kyori.adventure.text.format.TextDecoration.STRIKETHROUGH;
public final class CommandHandlerFactory {
@DefaultQualifier(NonNull.class)
public final class CommandHandler {
private final MiniMOTD<?> miniMOTD;
public CommandHandlerFactory(final @NonNull MiniMOTD<?> miniMOTD) {
public CommandHandler(final MiniMOTD<?> miniMOTD) {
this.miniMOTD = miniMOTD;
}
public @NonNull CommandHandler about() {
return audience -> {
final Component header = miniMessage("<gradient:white:#007FFF:white> ").decorate(STRIKETHROUGH);
Stream.of(
header,
text()
.hoverEvent(miniMessage("<rainbow>click me!"))
.clickEvent(openUrl(Constants.PluginMetadata.WEBSITE))
.content(Constants.PluginMetadata.NAME)
.color(WHITE)
.append(space())
.append(miniMessage("<gradient:#0047AB:#007FFF>" + Constants.PluginMetadata.VERSION))
.build(),
text()
.content("By ")
.color(GRAY)
.append(text("jmp", WHITE))
.build(),
header
).forEach(audience::sendMessage);
};
}
public @NonNull CommandHandler reload() {
return audience -> {
this.miniMOTD.reload();
audience.sendMessage(linear(Constants.COMMAND_PREFIX, space(), text("Done reloading configuration.", NamedTextColor.GREEN)));
};
}
public @NonNull CommandHandler help() {
return audience -> Stream.of(
linear(Constants.COMMAND_PREFIX, space(), text(Constants.PluginMetadata.NAME + " command help", WHITE)),
commandInfo("/minimotd about", "Show information about MiniMOTD"),
commandInfo("/minimotd reload", "Reload MiniMOTD configuration files"),
commandInfo("/minimotd help", "Show this help menu")
public void about(final Audience audience) {
final Component header = miniMessage("<gradient:white:#007FFF:white> ").decorate(STRIKETHROUGH);
Stream.of(
header,
text()
.hoverEvent(miniMessage("<rainbow>click me!"))
.clickEvent(openUrl(Constants.PluginMetadata.WEBSITE))
.content(Constants.PluginMetadata.NAME)
.color(WHITE)
.append(space())
.append(miniMessage("<gradient:#0047AB:#007FFF>" + Constants.PluginMetadata.VERSION))
.build(),
text()
.content("By ")
.color(GRAY)
.append(text("jmp", WHITE))
.build(),
header
).forEach(audience::sendMessage);
}
private static @NonNull Component commandInfo(final @NonNull String command, final @NonNull String description) {
public void reload(final Audience audience) {
this.miniMOTD.reload();
audience.sendMessage(TextComponent.ofChildren(
Constants.COMMAND_PREFIX,
space(),
text("Done reloading configuration.", GREEN)
));
}
public void help(final Audience audience) {
Stream.of(
linear(Constants.COMMAND_PREFIX, space(), text(Constants.PluginMetadata.NAME + " command help", WHITE)),
commandInfo("minimotd about", "Show information about MiniMOTD"),
commandInfo("minimotd reload", "Reload MiniMOTD configuration files"),
commandInfo("minimotd help", "Show this help menu")
).forEach(audience::sendMessage);
}
private static Component commandInfo(final String command, final String description) {
return text()
.content(" - ")
.color(GRAY)
.append(
text(command, color(0x007FFF))
.replaceText(config -> {
config.matchLiteral("/");
config.replacement(builder -> builder.color(WHITE));
})
)
.append(text(':', GRAY))
.append(text('/', WHITE))
.append(text(command, color(0x007FFF)))
.append(text(':'))
.append(space())
.append(text(description, WHITE))
.hoverEvent(
text()
.content("Click to execute '")
.color(GRAY)
.append(text(command, WHITE))
.append(text("'"))
.build()
)
.clickEvent(runCommand(command))
.hoverEvent(text()
.content("Click to execute '")
.color(GRAY)
.append(text("/" + command, WHITE))
.append(text("'"))
.build())
.clickEvent(runCommand("/" + command))
.build();
}
private static @NonNull Component miniMessage(final @NonNull String message) {
private static Component miniMessage(final String message) {
return MiniMessage.get().parse(message);
}
@FunctionalInterface
public interface CommandHandler {
void execute(@NonNull Audience audience);
public interface Executor {
void execute(Audience audience);
}
}
@@ -37,6 +37,9 @@ public final class Constants {
public static final String NAME = "${PLUGIN_NAME}";
public static final String VERSION = "${PLUGIN_VERSION}";
public static final String WEBSITE = "${PLUGIN_WEBSITE}";
public static final String GITHUB_USER = "${GITHUB_USER}";
public static final String GITHUB_REPO = "${GITHUB_REPO}";
public static final String GITHUB_REPO_URL = "https://github.com/" + GITHUB_USER + "/" + GITHUB_REPO;
}
public static final int MINECRAFT_1_16_PROTOCOL_VERSION = 735;
@@ -23,22 +23,21 @@
*/
package xyz.jpenilla.minimotd.common;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Stream;
import javax.imageio.ImageIO;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class IconManager<I> {
private final Map<String, I> icons = new HashMap<>();
private final MiniMOTD<I> miniMOTD;
private final Path iconsDirectory;
@@ -55,27 +54,27 @@ public final class IconManager<I> {
if (!Files.exists(this.iconsDirectory)) {
Files.createDirectories(this.iconsDirectory);
}
Files.list(this.iconsDirectory)
.map(Path::toFile)
.filter(File::isFile)
.filter(file -> file.getName().endsWith(".png"))
.forEach(this::loadIcon);
try (final Stream<Path> stream = Files.list(this.iconsDirectory)) {
stream.filter(Files::isRegularFile)
.filter(file -> file.getFileName().toString().endsWith(".png"))
.forEach(this::loadIcon);
}
} catch (final IOException ex) {
throw new RuntimeException("Exception loading server icons", ex);
}
}
private void loadIcon(final @NonNull File iconFile) {
try {
final BufferedImage bufferedImage = ImageIO.read(iconFile);
private void loadIcon(final @NonNull Path iconFile) {
try (final InputStream inputStream = Files.newInputStream(iconFile)) {
final BufferedImage bufferedImage = ImageIO.read(inputStream);
if (bufferedImage.getHeight() == 64 && bufferedImage.getWidth() == 64) {
final I newIcon = this.miniMOTD.platform().loadIcon(bufferedImage);
this.icons.put(iconFile.getName().split("\\.")[0], newIcon);
this.icons.put(iconFile.getFileName().toString().split("\\.")[0], newIcon);
} else {
this.miniMOTD.logger().warn("Could not load " + iconFile.getName() + ": image must be 64x64px");
this.miniMOTD.logger().warn("Could not load {}: image must be 64x64px", iconFile.getFileName());
}
} catch (final Exception ex) {
this.miniMOTD.logger().warn("Could not load " + iconFile.getName() + ": invalid image file", ex);
this.miniMOTD.logger().warn("Could not load {}: invalid image file", iconFile.getFileName(), ex);
}
}
@@ -95,5 +94,4 @@ public final class IconManager<I> {
return this.icons.get(iconString);
}
}
@@ -1,47 +0,0 @@
/*
* This file is part of MiniMOTD, licensed under the MIT License.
*
* Copyright (c) 2021 Jason Penilla
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.common;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class MOTDIconPair<I> {
private final I icon;
private final Component motd;
public MOTDIconPair(final @Nullable I icon, final @Nullable Component motd) {
this.icon = icon;
this.motd = motd;
}
public @Nullable I icon() {
return this.icon;
}
public @Nullable Component motd() {
return this.motd;
}
}
@@ -23,75 +23,95 @@
*/
package xyz.jpenilla.minimotd.common;
import java.nio.file.Path;
import java.util.concurrent.ThreadLocalRandom;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.slf4j.Logger;
import xyz.jpenilla.minimotd.common.config.ConfigManager;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig;
import java.nio.file.Path;
import java.util.concurrent.ThreadLocalRandom;
import static net.kyori.adventure.text.Component.newline;
@DefaultQualifier(NonNull.class)
public final class MiniMOTD<I> {
private final ConfigManager configManager;
private final IconManager<I> iconManager;
private final MiniMOTDPlatform<I> platform;
public MiniMOTD(final @NonNull MiniMOTDPlatform<I> platform) {
public MiniMOTD(final MiniMOTDPlatform<I> platform) {
this.platform = platform;
this.iconManager = new IconManager<>(this);
this.configManager = new ConfigManager(this);
this.configManager.loadConfigs();
}
public @NonNull MiniMOTDPlatform<I> platform() {
public MiniMOTDPlatform<I> platform() {
return this.platform;
}
public final @NonNull Path dataDirectory() {
public final Path dataDirectory() {
return this.platform.dataDirectory();
}
public final @NonNull IconManager<I> iconManager() {
public final IconManager<I> iconManager() {
return this.iconManager;
}
public final @NonNull Logger logger() {
public final Logger logger() {
return this.platform.logger();
}
public final @NonNull ConfigManager configManager() {
public final ConfigManager configManager() {
return this.configManager;
}
public final @NonNull MOTDIconPair<I> createMOTD(final @NonNull MiniMOTDConfig config, final int onlinePlayers, final int maxPlayers) {
I icon = null;
Component motd = null;
String iconString = null;
public final PingResponse<I> createMOTD(final MiniMOTDConfig config, final int onlinePlayers, final int maxPlayers) {
final PingResponse.PlayerCount count = config.modifyPlayerCount(onlinePlayers, maxPlayers);
final PingResponse.Builder<I> response = PingResponse.<I>builder()
.playerCount(count)
.disablePlayerListHover(config.disablePlayerListHover())
.hidePlayerCount(config.hidePlayerCount());
@Nullable String iconString = null;
if (config.motdEnabled()) {
if (config.motds().size() == 0) {
if (config.motds().isEmpty()) {
throw new IllegalStateException("MOTD is enabled, but there are no MOTDs in the config file?");
}
final int index = config.motds().size() == 1 ? 0 : ThreadLocalRandom.current().nextInt(config.motds().size());
final MiniMOTDConfig.MOTD motdConfig = config.motds().get(index);
motd = TextComponent.ofChildren(
MiniMessage.get().parse(replacePlayerCount(motdConfig.line1(), onlinePlayers, maxPlayers)),
Component.newline(),
MiniMessage.get().parse(replacePlayerCount(motdConfig.line2(), onlinePlayers, maxPlayers))
final Component motd = TextComponent.ofChildren(
parse(motdConfig.line1(), count),
newline(),
parse(motdConfig.line2(), count)
);
response.motd(motd);
iconString = motdConfig.icon();
}
if (config.iconEnabled()) {
icon = this.iconManager().icon(iconString);
response.icon(this.iconManager().icon(iconString));
}
return new MOTDIconPair<>(icon, motd);
return response.build();
}
private static @NonNull String replacePlayerCount(final @NonNull String input, final int onlinePlayers, final int maxPlayers) {
return input.replace("{onlinePlayers}", String.valueOf(onlinePlayers))
.replace("{maxPlayers}", String.valueOf(maxPlayers));
private static Component parse(final String input, final PingResponse.PlayerCount count) {
final String online = Integer.toString(count.onlinePlayers());
final String max = Integer.toString(count.maxPlayers());
return MiniMessage.get().parse(
replacePlayerCount(input, online, max),
"online_players", online,
"max_players", max
);
}
private static String replacePlayerCount(final String input, final String online, final String max) {
return input.replace("{onlinePlayers}", online).replace("{maxPlayers}", max);
}
public void reload() {
@@ -23,11 +23,10 @@
*/
package xyz.jpenilla.minimotd.common;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
public interface MiniMOTDPlatform<I> {
@NonNull Path dataDirectory();
@@ -0,0 +1,222 @@
/*
* This file is part of MiniMOTD, licensed under the MIT License.
*
* Copyright (c) 2021 Jason Penilla
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.common;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;
import static java.util.Objects.requireNonNull;
@DefaultQualifier(NonNull.class)
public final class PingResponse<I> {
private final @Nullable I icon;
private final @Nullable Component motd;
private final PlayerCount playerCount;
private final boolean hidePlayerCount;
private final boolean disablePlayerListHover;
private PingResponse(
final @Nullable I icon,
final @Nullable Component motd,
final PlayerCount playerCount,
final boolean hidePlayerCount,
final boolean disablePlayerListHover
) {
this.icon = icon;
this.motd = motd;
this.playerCount = playerCount;
this.hidePlayerCount = hidePlayerCount;
this.disablePlayerListHover = disablePlayerListHover;
}
public @Nullable I icon() {
return this.icon;
}
/**
* If {@link #icon()} is non-null, the provided {@link Consumer} will be called using
* it's return value. If {@link #icon()} is {@code null}, do nothing.
*
* @param iconConsumer icon consumer
*/
public void icon(final Consumer<I> iconConsumer) {
if (this.icon != null) {
iconConsumer.accept(this.icon);
}
}
public @Nullable Component motd() {
return this.motd;
}
/**
* If {@link #motd()} is non-null, the provided {@link Consumer} will be called using
* it's return value. If {@link #motd()} is {@code null}, do nothing.
*
* @param motdConsumer motd consumer
*/
public void motd(final Consumer<Component> motdConsumer) {
if (this.motd != null) {
motdConsumer.accept(this.motd);
}
}
public PlayerCount playerCount() {
return this.playerCount;
}
public boolean hidePlayerCount() {
return this.hidePlayerCount;
}
public boolean disablePlayerListHover() {
return this.disablePlayerListHover;
}
public Builder<I> toBuilder() {
return new BuilderImpl<>(
this.motd,
this.icon,
this.playerCount,
this.hidePlayerCount,
this.disablePlayerListHover
);
}
public static <I> Builder<I> builder() {
return new BuilderImpl<>();
}
public static final class PlayerCount {
private final int onlinePlayers;
private final int maxPlayers;
private PlayerCount(final int onlinePlayers, final int maxPlayers) {
this.onlinePlayers = onlinePlayers;
this.maxPlayers = maxPlayers;
}
public int onlinePlayers() {
return this.onlinePlayers;
}
public int maxPlayers() {
return this.maxPlayers;
}
public void applyCount(final IntConsumer onlinePlayersSetter, final IntConsumer maxPlayersSetter) {
onlinePlayersSetter.accept(this.onlinePlayers);
maxPlayersSetter.accept(this.maxPlayers);
}
public static PlayerCount playerCount(final int onlinePlayers, final int maxPlayers) {
return new PlayerCount(onlinePlayers, maxPlayers);
}
}
public interface Builder<I> {
Builder<I> motd(@Nullable Component motd);
Builder<I> icon(@Nullable I icon);
Builder<I> playerCount(PlayerCount playerCount);
Builder<I> hidePlayerCount(boolean hidePlayerCount);
Builder<I> disablePlayerListHover(boolean disablePlayerListHover);
PingResponse<I> build();
}
private static final class BuilderImpl<I> implements Builder<I> {
private @Nullable Component motd;
private @Nullable I icon;
private @Nullable PlayerCount playerCount;
private boolean hidePlayerCount = false;
private boolean disablePlayerListHover = false;
private BuilderImpl() {
}
private BuilderImpl(
final @Nullable Component motd,
final @Nullable I icon,
final @Nullable PlayerCount playerCount,
final boolean hidePlayerCount,
final boolean disablePlayerListHover
) {
this.motd = motd;
this.icon = icon;
this.playerCount = playerCount;
this.hidePlayerCount = hidePlayerCount;
this.disablePlayerListHover = disablePlayerListHover;
}
@Override
public Builder<I> motd(final @Nullable Component motd) {
this.motd = motd;
return this;
}
@Override
public Builder<I> icon(final @Nullable I icon) {
this.icon = icon;
return this;
}
@Override
public Builder<I> playerCount(final PlayerCount playerCount) {
this.playerCount = playerCount;
return this;
}
@Override
public Builder<I> hidePlayerCount(final boolean hidePlayerCount) {
this.hidePlayerCount = hidePlayerCount;
return this;
}
@Override
public Builder<I> disablePlayerListHover(final boolean disablePlayerListHover) {
this.disablePlayerListHover = disablePlayerListHover;
return this;
}
@Override
public PingResponse<I> build() {
requireNonNull(this.playerCount, "playerCount");
return new PingResponse<>(
this.icon,
this.motd,
this.playerCount,
this.hidePlayerCount,
this.disablePlayerListHover
);
}
}
}
@@ -23,6 +23,8 @@
*/
package xyz.jpenilla.minimotd.common.config;
import java.nio.file.Path;
import java.util.function.UnaryOperator;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.ConfigurateException;
@@ -32,9 +34,6 @@ import org.spongepowered.configurate.objectmapping.ObjectMapper;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializerCollection;
import java.nio.file.Path;
import java.util.function.UnaryOperator;
public final class ConfigLoader<C> {
private static final TypeSerializerCollection SERIALIZERS;
@@ -24,18 +24,22 @@
package xyz.jpenilla.minimotd.common.config;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.configurate.ConfigurateException;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.util.Pair;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurateException;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.util.Pair;
import static xyz.jpenilla.minimotd.common.util.Pair.pair;
public final class ConfigManager {
@@ -83,8 +87,11 @@ public final class ConfigManager {
Files.createDirectories(extraConfigsDir);
this.createDefaultExtraConfigs(extraConfigsDir);
}
for (final Path path : Files.list(extraConfigsDir).collect(Collectors.toList())) {
if (path.toString().endsWith(".conf")) {
try (final Stream<Path> stream = Files.list(extraConfigsDir)) {
for (final Path path : stream.collect(Collectors.toList())) {
if (!path.toString().endsWith(".conf")) {
continue;
}
final String name = path.getFileName().toString().replace(".conf", "");
final ConfigLoader<MiniMOTDConfig> loader = new ConfigLoader<>(
MiniMOTDConfig.class,
@@ -103,8 +110,8 @@ public final class ConfigManager {
private void createDefaultExtraConfigs(final @NonNull Path extraConfigsDir) throws ConfigurateException {
final List<Pair<Path, MiniMOTDConfig.MOTD>> defaults = ImmutableList.of(
Pair.of(extraConfigsDir.resolve("skyblock.conf"), new MiniMOTDConfig.MOTD("<green><italic>Skyblock</green>", "<bold><rainbow>MiniMOTD Skyblock Server")),
Pair.of(extraConfigsDir.resolve("survival.conf"), new MiniMOTDConfig.MOTD("<gradient:blue:red>Survival Mode Hardcore", "<green><bold>MiniMOTD Survival Server"))
pair(extraConfigsDir.resolve("skyblock.conf"), new MiniMOTDConfig.MOTD("<green><italic>Skyblock</green>", "<bold><rainbow>MiniMOTD Skyblock Server")),
pair(extraConfigsDir.resolve("survival.conf"), new MiniMOTDConfig.MOTD("<gradient:blue:red>Survival Mode Hardcore", "<green><bold>MiniMOTD Survival Server"))
);
for (final Pair<Path, MiniMOTDConfig.MOTD> pair : defaults) {
final ConfigLoader<MiniMOTDConfig> loader = new ConfigLoader<>(
@@ -129,6 +136,23 @@ public final class ConfigManager {
return this.pluginSettings;
}
public @NonNull MiniMOTDConfig resolveConfig(final @Nullable InetSocketAddress address) {
if (address == null) {
return this.mainConfig();
}
final String hostString = address.getHostString() + ":" + address.getPort();
final String configString = this.pluginSettings().proxySettings().findConfigStringForHost(hostString);
if (this.pluginSettings().proxySettings().virtualHostTestMode()) {
this.miniMOTD.platform().logger().info("[virtual-host-debug] Virtual Host: '{}', Selected Config: '{}'", hostString, configString == null ? "default" : configString);
}
if (configString == null) {
return this.mainConfig();
}
return this.resolveConfig(configString);
}
public @NonNull MiniMOTDConfig resolveConfig(final @NonNull String name) {
if ("default".equals(name)) {
return this.mainConfig();
@@ -137,7 +161,7 @@ public final class ConfigManager {
if (cfg != null) {
return cfg;
}
this.miniMOTD.logger().warn(String.format("Invalid extra-config name: '%s', falling back to main.conf", name));
this.miniMOTD.logger().warn("Invalid extra-config name: '{}', falling back to main.conf", name);
return this.mainConfig();
}
@@ -23,13 +23,15 @@
*/
package xyz.jpenilla.minimotd.common.config;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import xyz.jpenilla.minimotd.common.PingResponse;
import static xyz.jpenilla.minimotd.common.PingResponse.PlayerCount.playerCount;
@ConfigSerializable
public final class MiniMOTDConfig {
@@ -196,30 +198,12 @@ public final class MiniMOTDConfig {
return maxPlayers;
}
public @NonNull PlayerCount modifyPlayerCount(final int onlinePlayers, final int maxPlayers) {
public PingResponse.@NonNull PlayerCount modifyPlayerCount(final int onlinePlayers, final int maxPlayers) {
final int online = this.calculateOnlinePlayers(onlinePlayers);
final int max = this.calculateMaxPlayers(online, maxPlayers);
if (!this.playerCountSettings.allowExceedingMaximum) {
return new PlayerCount(Math.min(online, max), max);
}
return new PlayerCount(online, max);
}
public static final class PlayerCount {
private final int onlinePlayers;
private final int maxPlayers;
private PlayerCount(final int onlinePlayers, final int maxPlayers) {
this.onlinePlayers = onlinePlayers;
this.maxPlayers = maxPlayers;
}
public int onlinePlayers() {
return this.onlinePlayers;
}
public int maxPlayers() {
return this.maxPlayers;
return playerCount(Math.min(online, max), max);
}
return playerCount(online, max);
}
}
@@ -23,16 +23,15 @@
*/
package xyz.jpenilla.minimotd.common.config;
import java.lang.reflect.Type;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Predicate;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.serialize.ScalarSerializer;
import org.spongepowered.configurate.serialize.SerializationException;
import xyz.jpenilla.minimotd.common.util.Int2IntFunction;
import java.lang.reflect.Type;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Predicate;
public final class PlayerCountModifier implements Int2IntFunction {
private final String input;
private final Int2IntFunction function;
@@ -23,13 +23,12 @@
*/
package xyz.jpenilla.minimotd.common.config;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
@ConfigSerializable
public final class PluginSettings {
@@ -56,14 +55,25 @@ public final class PluginSettings {
+ "Format is \"hostname:port\"=\"configName|default\"")
private final Map<String, String> virtualHostConfigs = new HashMap<>();
@Comment("Set whether to enable virtual host testing mode.\n"
+ "When enabled, MiniMOTD will print virtual host debug info to the console on each server ping.")
private final boolean virtualHostTestMode = false;
public boolean virtualHostTestMode() {
return this.virtualHostTestMode;
}
public @Nullable String findConfigStringForHost(final @NonNull String host) {
return this.virtualHostConfigs.get(host);
}
}
public @NonNull ProxySettings proxySettings() {
return this.proxySettings;
}
public boolean updateChecker() {
return this.updateChecker;
}
public @NonNull Optional<String> configStringForHost(final @NonNull String host) {
return Optional.ofNullable(this.proxySettings.virtualHostConfigs.get(host));
}
}
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.common;
package xyz.jpenilla.minimotd.common.util;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
@@ -23,11 +23,10 @@
*/
package xyz.jpenilla.minimotd.common.util;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
public final class Pair<L, R> {
private final L left;
private final R right;
@@ -63,7 +62,7 @@ public final class Pair<L, R> {
return Objects.hash(this.left, this.right);
}
public static <L, R> @NonNull Pair<L, R> of(final @NonNull L left, final @NonNull R right) {
public static <L, R> @NonNull Pair<L, R> pair(final @NonNull L left, final @NonNull R right) {
return new Pair<>(left, right);
}
}
@@ -21,15 +21,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.common;
package xyz.jpenilla.minimotd.common.util;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Collections;
@@ -37,16 +36,19 @@ import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.NonNull;
import xyz.jpenilla.minimotd.common.Constants;
public final class UpdateChecker {
private final JsonParser parser = new JsonParser();
public @NonNull List<String> checkVersion() {
final JsonArray result;
try {
result = this.parser.parse(new InputStreamReader(new URL("https://api.github.com/repos/jpenilla/MiniMOTD/releases").openStream(), Charsets.UTF_8)).getAsJsonArray();
} catch (final IOException exception) {
return Collections.singletonList("Cannot look for updates: " + exception.getMessage());
final String url = String.format("https://api.github.com/repos/%s/%s/releases", Constants.PluginMetadata.GITHUB_USER, Constants.PluginMetadata.GITHUB_REPO);
try (final InputStream is = new URL(url).openStream(); InputStreamReader reader = new InputStreamReader(is, Charsets.UTF_8)) {
result = this.parser.parse(reader).getAsJsonArray();
} catch (final IOException ex) {
return Collections.singletonList("Cannot look for updates: " + ex.getMessage());
}
final Map<String, String> versionMap = new LinkedHashMap<>();
result.forEach(element -> versionMap.put(element.getAsJsonObject().get("tag_name").getAsString(), element.getAsJsonObject().get("html_url").getAsString()));
@@ -57,7 +59,7 @@ public final class UpdateChecker {
}
if (currentVersion.contains("SNAPSHOT")) {
return ImmutableList.of(
"This server is running a development build of MiniMOTD! (" + currentVersion + ")",
"This server is running a development build of " + Constants.PluginMetadata.NAME + "! (" + currentVersion + ")",
"The latest official release is " + versionList.get(0)
);
}
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.common;
package xyz.jpenilla.minimotd.common.util;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
@@ -30,14 +30,15 @@ import org.junit.jupiter.api.Test;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.format.NamedTextColor.nearestTo;
import static net.kyori.adventure.text.format.TextColor.color;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ComponentColorDownsamplerTest {
private static final TextColor GREEN = TextColor.color(0x04E762);
private static final TextColor ORANGE_YELLOW = TextColor.color(0xF5B700);
private static final TextColor PINK = TextColor.color(0xDC0073);
private static final TextColor BLUE = TextColor.color(0x008BF8);
private static final TextColor LIME_GREEN = TextColor.color(0x89FC00);
private static final TextColor GREEN = color(0x04E762);
private static final TextColor ORANGE_YELLOW = color(0xF5B700);
private static final TextColor PINK = color(0xDC0073);
private static final TextColor BLUE = color(0x008BF8);
private static final TextColor LIME_GREEN = color(0x89FC00);
@Test
void testColorDownsampling() {
@@ -57,9 +58,6 @@ class ComponentColorDownsamplerTest {
);
final Component actualDownsample = ComponentColorDownsampler.downsampler().downsample(fullColor);
assertEquals(
expectedDownsample,
actualDownsample
);
assertEquals(expectedDownsample, actualDownsample);
}
}
+37 -27
View File
@@ -1,55 +1,65 @@
plugins {
id("fabric-loom") version "0.6-SNAPSHOT"
id("minimotd.platform-conventions")
id("com.github.johnrengelman.shadow")
id("fabric-loom")
}
val shade: Configuration by configurations.creating
val minecraftVersion = "1.16.5"
val minecraftVersion = libs.versions.fabricMinecraft.get()
dependencies {
minecraft("com.mojang", "minecraft", minecraftVersion)
minecraft(libs.fabricMinecraft)
mappings(minecraft.officialMojangMappings())
modImplementation("net.fabricmc", "fabric-loader", "0.11.1")
modImplementation("net.fabricmc.fabric-api", "fabric-api", "0.31.0+1.16")
modImplementation(libs.fabricLoader)
modImplementation(libs.fabricApi)
shade(implementation(project(":minimotd-common")) {
shade(implementation(projects.minimotdCommon) {
exclude("net.kyori")
exclude("org.slf4j")
})
modImplementation(include("net.kyori", "adventure-platform-fabric", "4.0.0-SNAPSHOT"))
implementation(include("net.kyori", "adventure-text-minimessage", "4.1.0-SNAPSHOT"))
modImplementation(libs.adventurePlatformFabric)
include(libs.adventurePlatformFabric)
implementation(libs.minimessage)
include(libs.minimessage)
}
implementation(include("org.slf4j", "slf4j-api", "1.7.30"))
implementation(include("org.apache.logging.log4j", "log4j-slf4j-impl", "2.8.1"))
miniMOTDPlatform {
jarTask.set(tasks.remapJar)
}
indra {
javaVersions {
target(16)
}
}
tasks {
runServer {
standardInput = System.`in`
}
shadowJar {
configurations = listOf(shade)
relocate("io.leangen.geantyref", "xyz.jpenilla.minimotd.lib.io.leangen.geantyref")
relocate("org.spongepowered.configurate", "xyz.jpenilla.minimotd.lib.spongepowered.configurate")
relocate("com.typesafe.config", "xyz.jpenilla.minimotd.lib.typesafe.config")
relocate("org.checkerframework", "xyz.jpenilla.minimotd.lib.checkerframework")
relocate("xyz.jpenilla.minimotd.common", "xyz.jpenilla.minimotd.lib.fabric.minimotd.common")
commonConfiguration()
commonRelocation("io.leangen.geantyref")
platformRelocation("fabric", "xyz.jpenilla.minimotd.common")
}
remapJar {
dependsOn(shadowJar)
input.set(shadowJar.get().outputs.files.singleFile)
input.set(shadowJar.flatMap { it.archiveFile })
archiveFileName.set("${project.name}-mc$minecraftVersion-${project.version}.jar")
destinationDirectory.set(rootProject.buildDir.resolve("libs"))
}
processResources {
val replacements = mapOf(
"modid" to project.name,
"name" to rootProject.name,
"version" to project.version.toString(),
"description" to project.description.toString(),
"github_url" to Constants.GITHUB_URL
)
inputs.properties(replacements)
filesMatching("fabric.mod.json") {
mapOf(
"{project.name}" to project.name,
"{rootProject.name}" to rootProject.name,
"{project.version}" to project.version.toString(),
"{project.description}" to project.description.toString(),
"{project.github_url}" to rootProject.ext["url"].toString()
).forEach { (k, v) ->
filter { it.replace(k, v) }
}
expand(replacements)
}
}
}
@@ -28,6 +28,13 @@ import com.mojang.brigadier.context.CommandContext;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Base64;
import java.util.concurrent.CompletableFuture;
import javax.imageio.ImageIO;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
@@ -37,18 +44,10 @@ import net.minecraft.commands.CommandSourceStack;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.jpenilla.minimotd.common.CommandHandlerFactory;
import xyz.jpenilla.minimotd.common.CommandHandler;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.MiniMOTDPlatform;
import xyz.jpenilla.minimotd.common.UpdateChecker;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Base64;
import java.util.concurrent.CompletableFuture;
import xyz.jpenilla.minimotd.common.util.UpdateChecker;
import static net.minecraft.commands.Commands.literal;
@@ -86,9 +85,9 @@ public final class MiniMOTDFabric implements ModInitializer, MiniMOTDPlatform<St
private void registerCommand() {
final class WrappingExecutor implements Command<CommandSourceStack> {
private final CommandHandlerFactory.CommandHandler handler;
private final CommandHandler.Executor handler;
WrappingExecutor(final CommandHandlerFactory.@NonNull CommandHandler handler) {
WrappingExecutor(final CommandHandler.@NonNull Executor handler) {
this.handler = handler;
}
@@ -99,13 +98,13 @@ public final class MiniMOTDFabric implements ModInitializer, MiniMOTDPlatform<St
}
}
final CommandHandlerFactory handlerFactory = new CommandHandlerFactory(this.miniMOTD);
final CommandHandler handler = new CommandHandler(this.miniMOTD);
CommandRegistrationCallback.EVENT.register((dispatcher, $) -> dispatcher.register(
literal("minimotd")
.requires(source -> source.hasPermission(4))
.then(literal("reload").executes(new WrappingExecutor(handlerFactory.reload())))
.then(literal("about").executes(new WrappingExecutor(handlerFactory.about())))
.then(literal("help").executes(new WrappingExecutor(handlerFactory.help())))
.then(literal("reload").executes(new WrappingExecutor(handler::reload)))
.then(literal("about").executes(new WrappingExecutor(handler::about)))
.then(literal("help").executes(new WrappingExecutor(handler::help)))
));
}
@@ -30,18 +30,17 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import xyz.jpenilla.minimotd.fabric.access.ConnectionAccess;
@SuppressWarnings("checkstyle:FinalLocalVariable")
@Unique
@Mixin(Connection.class)
@Implements({@Interface(iface = ConnectionAccess.class, prefix = "tabtps$")})
@Implements({@Interface(iface = ConnectionAccess.class, prefix = "miniMOTD$")})
abstract class ConnectionMixin {
private int protocolVersion = -1;
private int miniMOTD$protocolVersion = -1;
public void tabtps$protocolVersion(int protocolVersion) {
this.protocolVersion = protocolVersion;
public void miniMOTD$protocolVersion(final int protocolVersion) {
this.miniMOTD$protocolVersion = protocolVersion;
}
public int tabtps$protocolVersion() {
return this.protocolVersion;
public int miniMOTD$protocolVersion() {
return this.miniMOTD$protocolVersion;
}
}
@@ -29,13 +29,11 @@ import net.minecraft.server.network.ServerHandshakePacketListenerImpl;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import xyz.jpenilla.minimotd.fabric.access.ConnectionAccess;
@Unique
@Mixin(ServerHandshakePacketListenerImpl.class)
abstract class ServerHandshakePacketListenerImplMixin {
@Shadow @Final private Connection connection;
@@ -24,7 +24,6 @@
package xyz.jpenilla.minimotd.fabric.mixin;
import com.mojang.authlib.GameProfile;
import net.kyori.adventure.text.Component;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.status.ServerStatus;
import net.minecraft.server.MinecraftServer;
@@ -32,19 +31,16 @@ import net.minecraft.server.network.ServerStatusPacketListenerImpl;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import xyz.jpenilla.minimotd.common.ComponentColorDownsampler;
import xyz.jpenilla.minimotd.common.Constants;
import xyz.jpenilla.minimotd.common.MOTDIconPair;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.PingResponse;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig.PlayerCount;
import xyz.jpenilla.minimotd.common.util.ComponentColorDownsampler;
import xyz.jpenilla.minimotd.fabric.MiniMOTDFabric;
import xyz.jpenilla.minimotd.fabric.access.ConnectionAccess;
@Unique
@Mixin(ServerStatusPacketListenerImpl.class)
abstract class ServerStatusPacketListenerImplMixin {
@Shadow @Final private Connection connection;
@@ -62,32 +58,21 @@ abstract class ServerStatusPacketListenerImplMixin {
final MiniMOTD<String> miniMOTD = miniMOTDFabric.miniMOTD();
final MiniMOTDConfig config = miniMOTD.configManager().mainConfig();
final PlayerCount count = config.modifyPlayerCount(minecraftServer.getPlayerCount(), vanillaStatus.getPlayers().getMaxPlayers());
final int onlinePlayers = count.onlinePlayers();
final int maxPlayers = count.maxPlayers();
final PingResponse<String> response = miniMOTD.createMOTD(config, minecraftServer.getPlayerCount(), vanillaStatus.getPlayers().getMaxPlayers());
final MOTDIconPair<String> pair = miniMOTD.createMOTD(config, onlinePlayers, maxPlayers);
final Component motdComponent = pair.motd();
if (motdComponent != null) {
response.motd(motd -> {
if (((ConnectionAccess) this.connection).protocolVersion() >= Constants.MINECRAFT_1_16_PROTOCOL_VERSION) {
modifiedStatus.setDescription(miniMOTDFabric.audiences().toNative(motdComponent));
modifiedStatus.setDescription(miniMOTDFabric.audiences().toNative(motd));
} else {
modifiedStatus.setDescription(miniMOTDFabric.audiences().toNative(
ComponentColorDownsampler.downsampler().downsample(motdComponent)
));
modifiedStatus.setDescription(miniMOTDFabric.audiences().toNative(ComponentColorDownsampler.downsampler().downsample(motd)));
}
}
});
response.icon(modifiedStatus::setFavicon);
final String favicon = pair.icon();
if (favicon != null) {
modifiedStatus.setFavicon(favicon);
}
if (!config.hidePlayerCount()) {
if (!response.hidePlayerCount()) {
final GameProfile[] oldSample = vanillaStatus.getPlayers().getSample();
final ServerStatus.Players newPlayers = new ServerStatus.Players(maxPlayers, onlinePlayers);
if (config.disablePlayerListHover()) {
final ServerStatus.Players newPlayers = new ServerStatus.Players(response.playerCount().maxPlayers(), response.playerCount().onlinePlayers());
if (response.disablePlayerListHover()) {
newPlayers.setSample(new GameProfile[]{});
} else {
newPlayers.setSample(oldSample);
+8 -8
View File
@@ -1,16 +1,16 @@
{
"schemaVersion": 1,
"id": "{project.name}",
"version": "{project.version}",
"name": "{rootProject.name}",
"description": "{project.description}",
"id": "${modid}",
"version": "${version}",
"name": "${name}",
"description": "${description}",
"authors": [
"jmp"
],
"contact": {
"homepage": "{project.github_url}",
"sources": "{project.github_url}",
"issues": "{project.github_url}/issues"
"homepage": "${github_url}",
"sources": "${github_url}",
"issues": "${github_url}/issues"
},
"license": "MIT",
"environment": "server",
@@ -25,6 +25,6 @@
"depends": {
"fabricloader": ">=0.11.1",
"fabric": ">=0.31.0",
"minecraft": "1.16.x"
"minecraft": "1.17.x"
}
}
+3 -2
View File
@@ -1,2 +1,3 @@
org.gradle.jvmargs=-Xmx1G
#org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx2G
org.gradle.parallel=true
+173
View File
@@ -0,0 +1,173 @@
metadata:
format: { version: 1.0 }
polyglot-extensions:
- plugins
plugins:
net.kyori.blossom: 1.3.0
org.spongepowered.gradle.plugin: 1.1.1
net.minecrell.plugin-yml.bungee: &plugin-yml 0.4.0
net.minecrell.plugin-yml.bukkit: *plugin-yml
xyz.jpenilla.run-paper: 1.0.3
versions:
indra: 2.0.5
shadow: 7.0.0
testLoggerPlugin: 3.0.0
adventure: 4.8.1
adventurePlatform: 4.0.0-SNAPSHOT
adventurePlatformFabric: 4.1.0-SNAPSHOT
minimessage: 4.2.0-SNAPSHOT
configurate: 4.1.1
checkerQual: 3.14.0
slf4j: 1.7.30
log4jSlf4jImpl: 2.8.1
gson: 2.8.0
guava: 21.0
bstats: 2.2.1
paperApi: 1.16.5-R0.1-SNAPSHOT
waterfallApi: 1.16-R0.4-SNAPSHOT
fabricApi: 0.34.9+1.17
fabricLoader: 0.11.3
fabricMinecraft: 1.17
spongeApi7: 7.2.0
velocityApi: 1.1.7
jupiterEngine: 5.7.0
dependencies:
adventureBom:
group: net.kyori
name: adventure-bom
version: { ref: adventure }
adventureApi:
group: net.kyori
name: adventure-api
adventureTextSerializerPlain:
group: net.kyori
name: adventure-text-serializer-plain
adventureTextSerializerGson:
group: net.kyori
name: adventure-text-serializer-gson
adventurePlatformBungeecord:
group: net.kyori
name: adventure-platform-bungeecord
version: { ref: adventurePlatform }
adventurePlatformBukkit:
group: net.kyori
name: adventure-platform-bukkit
version: { ref: adventurePlatform }
adventurePlatformFabric:
group: net.kyori
name: adventure-platform-fabric
version: { ref: adventurePlatformFabric }
adventurePlatformSpongeApi:
group: net.kyori
name: adventure-platform-spongeapi
version: { ref: adventurePlatform }
minimessage:
group: net.kyori
name: adventure-text-minimessage
version: { ref: minimessage }
configurateHocon:
group: org.spongepowered
name: configurate-hocon
version: { ref: configurate }
checkerQual:
group: org.checkerframework
name: checker-qual
version: { ref: checkerQual }
slf4jApi:
group: org.slf4j
name: slf4j-api
version: { ref: slf4j }
slf4jJdk14:
group: org.slf4j
name: slf4j-jdk14
version: { ref: slf4j }
log4jSlf4jImpl:
group: org.apache.logging.log4j
name: log4j-slf4j-impl
version: { ref: log4jSlf4jImpl }
gson:
group: com.google.code.gson
name: gson
version: { ref: gson }
guava:
group: com.google.guava
name: guava
version: { ref: guava }
bstatsBukkit:
group: org.bstats
name: bstats-bukkit
version: { ref: bstats }
bstatsBungeecord:
group: org.bstats
name: bstats-bungeecord
version: { ref: bstats }
bstatsVelocity:
group: org.bstats
name: bstats-velocity
version: { ref: bstats }
bstatsSponge:
group: org.bstats
name: bstats-sponge
version: { ref: bstats }
paperApi:
group: com.destroystokyo.paper
name: paper-api
version: { ref: paperApi }
waterfallApi:
group: io.github.waterfallmc
name: waterfall-api
version: { ref: waterfallApi }
fabricApi:
group: net.fabricmc.fabric-api
name: fabric-api
version: { ref: fabricApi }
fabricLoader:
group: net.fabricmc
name: fabric-loader
version: { ref: fabricLoader }
fabricMinecraft:
group: com.mojang
name: minecraft
version: { ref: fabricMinecraft }
spongeApi7:
group: org.spongepowered
name: spongeapi
version: { ref: spongeApi7 }
velocityApi:
group: com.velocitypowered
name: velocity-api
version: { ref: velocityApi }
jupiterEngine:
group: org.junit.jupiter
name: junit-jupiter-engine
version: { ref: jupiterEngine }
# build-logic
indraCommon:
group: net.kyori
name: indra-common
version: { ref: indra }
shadow:
group: gradle.plugin.com.github.jengelman.gradle.plugins
name: shadow
version: { ref: shadow }
testLoggerPlugin:
group: com.adarshr
name: gradle-test-logger-plugin
version: { ref: testLoggerPlugin }
bundles:
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+34 -29
View File
@@ -1,39 +1,44 @@
rootProject.name = "MiniMOTD"
enableFeaturePreview("VERSION_CATALOGS")
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
dependencyResolutionManagement {
repositories {
//mavenLocal()
mavenCentral()
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://papermc.io/repo/repository/maven-public/")
maven("https://nexus.velocitypowered.com/repository/maven-public/")
maven("https://repo.spongepowered.org/repository/maven-public/")
maven("https://repo.jpenilla.xyz/snapshots/")
}
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
}
pluginManagement {
repositories {
jcenter()
maven("https://maven.fabricmc.net/")
gradlePluginPortal()
maven("https://maven.fabricmc.net/")
}
includeBuild("build-logic")
}
setupSubproject("minimotd-common") {
projectDir = file("common")
}
setupSubproject("minimotd-spigot") {
projectDir = file("spigot")
}
setupSubproject("minimotd-sponge8") {
projectDir = file("sponge8")
}
setupSubproject("minimotd-sponge7") {
projectDir = file("sponge7")
}
setupSubproject("minimotd-bungeecord") {
projectDir = file("bungee")
}
setupSubproject("minimotd-velocity") {
projectDir = file("velocity")
}
setupSubproject("minimotd-fabric") {
projectDir = file("fabric")
}
setupSubproject("minimotd-universal") {
projectDir = file("universal")
plugins {
id("ca.stellardrift.polyglot-version-catalogs") version "5.0.0"
id("fabric-loom") version "0.8-SNAPSHOT"
}
inline fun setupSubproject(name: String, block: ProjectDescriptor.() -> Unit) {
include(name)
project(":$name").apply(block)
rootProject.name = "MiniMOTD"
sequenceOf(
"common",
"bukkit",
"sponge8",
"sponge7",
"bungeecord",
"velocity",
"fabric",
"universal"
).forEach {
include("minimotd-$it")
project(":minimotd-$it").projectDir = file(it)
}
-41
View File
@@ -1,41 +0,0 @@
plugins {
id("net.minecrell.plugin-yml.bukkit") version "0.3.0"
}
dependencies {
implementation(project(":minimotd-common"))
implementation("org.slf4j", "slf4j-jdk14","1.7.30")
implementation("net.kyori", "adventure-platform-bukkit", "4.0.0-SNAPSHOT")
implementation("org.bstats", "bstats-bukkit", "2.2.1")
compileOnly("com.destroystokyo.paper", "paper-api", "1.16.5-R0.1-SNAPSHOT")
}
tasks {
shadowJar {
relocate("org.slf4j", "xyz.jpenilla.minimotd.lib.slf4j")
relocate("io.leangen.geantyref", "xyz.jpenilla.minimotd.lib.io.leangen.geantyref")
relocate("org.spongepowered.configurate", "xyz.jpenilla.minimotd.lib.spongepowered.configurate")
relocate("com.typesafe.config", "xyz.jpenilla.minimotd.lib.typesafe.config")
relocate("net.kyori", "xyz.jpenilla.minimotd.lib.kyori")
relocate("org.checkerframework", "xyz.jpenilla.minimotd.lib.checkerframework")
relocate("org.bstats", "xyz.jpenilla.minimotd.lib.bstats")
}
build {
dependsOn(shadowJar)
}
}
bukkit {
name = rootProject.name
main = "xyz.jpenilla.minimotd.spigot.MiniMOTDPlugin"
apiVersion = "1.13"
website = rootProject.ext["url"].toString()
authors = listOf("jmp")
softDepend = listOf("ViaVersion")
commands {
create("minimotd") {
description = "MiniMOTD Command"
usage = "/minimotd help"
}
}
}
+20 -22
View File
@@ -1,35 +1,33 @@
plugins {
id("minimotd.shadow-platform")
}
dependencies {
implementation(project(":minimotd-common")) {
implementation(projects.minimotdCommon) {
exclude("org.slf4j", "slf4j-api")
}
implementation("net.kyori", "adventure-platform-spongeapi", "4.0.0-SNAPSHOT")
compileOnly("org.spongepowered", "spongeapi", "7.2.0")
implementation("org.bstats", "bstats-sponge", "2.2.1")
implementation(libs.adventurePlatformSpongeApi)
compileOnly(libs.spongeApi7)
implementation(libs.bstatsSponge)
}
tasks {
processResources {
val replacements = mapOf(
"modid" to project.name,
"name" to rootProject.name,
"version" to project.version.toString(),
"description" to project.description.toString(),
"url" to Constants.GITHUB_URL
)
inputs.properties(replacements)
filesMatching("mcmod.info") {
mapOf(
"{project.name}" to project.name,
"{rootProject.name}" to rootProject.name,
"{project.version}" to project.version.toString(),
"{project.description}" to project.description.toString(),
"{url}" to rootProject.ext["url"].toString()
).forEach { (k, v) ->
filter { resource -> resource.replace(k, v) }
}
expand(replacements)
}
}
shadowJar {
relocate("io.leangen.geantyref", "xyz.jpenilla.minimotd.lib.io.leangen.geantyref")
relocate("org.spongepowered.configurate", "xyz.jpenilla.minimotd.lib.spongepowered.configurate")
relocate("com.typesafe.config", "xyz.jpenilla.minimotd.lib.typesafe.config")
relocate("net.kyori", "xyz.jpenilla.minimotd.lib.kyori")
relocate("org.checkerframework", "xyz.jpenilla.minimotd.lib.checkerframework")
relocate("org.bstats", "xyz.jpenilla.minimotd.lib.bstats")
}
build {
dependsOn(shadowJar)
commonRelocation("io.leangen.geantyref")
commonRelocation("net.kyori")
commonRelocation("org.bstats")
}
}
@@ -23,21 +23,21 @@
*/
package xyz.jpenilla.minimotd.sponge7;
import net.kyori.adventure.text.Component;
import com.google.inject.Inject;
import net.kyori.adventure.text.serializer.spongeapi.SpongeComponentSerializer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.api.event.EventListener;
import org.spongepowered.api.event.server.ClientPingServerEvent;
import org.spongepowered.api.network.status.Favicon;
import xyz.jpenilla.minimotd.common.MOTDIconPair;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.PingResponse;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig.PlayerCount;
final class ClientPingServerEventListener implements EventListener<ClientPingServerEvent> {
private final MiniMOTD<Favicon> miniMOTD;
ClientPingServerEventListener(final @NonNull MiniMOTD<Favicon> miniMOTD) {
@Inject
private ClientPingServerEventListener(final @NonNull MiniMOTD<Favicon> miniMOTD) {
this.miniMOTD = miniMOTD;
}
@@ -60,28 +60,15 @@ final class ClientPingServerEventListener implements EventListener<ClientPingSer
final MiniMOTDConfig config = this.miniMOTD.configManager().mainConfig();
final PlayerCount count = config.modifyPlayerCount(players.getOnline(), players.getMax());
final int onlinePlayers = count.onlinePlayers();
final int maxPlayers = count.maxPlayers();
players.setOnline(onlinePlayers);
players.setMax(maxPlayers);
final PingResponse<Favicon> mini = this.miniMOTD.createMOTD(config, players.getOnline(), players.getMax());
mini.playerCount().applyCount(players::setOnline, players::setMax);
mini.motd(motd -> response.setDescription(SpongeComponentSerializer.get().serialize(motd)));
mini.icon(response::setFavicon);
final MOTDIconPair<Favicon> pair = this.miniMOTD.createMOTD(config, onlinePlayers, maxPlayers);
final Component motd = pair.motd();
if (motd != null) {
response.setDescription(SpongeComponentSerializer.get().serialize(motd));
}
final Favicon favicon = pair.icon();
if (favicon != null) {
response.setFavicon(favicon);
}
if (config.disablePlayerListHover()) {
if (mini.disablePlayerListHover()) {
players.getProfiles().clear();
}
if (config.hidePlayerCount()) {
if (mini.hidePlayerCount()) {
response.setHidePlayers(true);
}
}
@@ -23,11 +23,18 @@
*/
package xyz.jpenilla.minimotd.sponge7;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Path;
import net.kyori.adventure.platform.spongeapi.SpongeAudiences;
import org.bstats.sponge.Metrics;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.spongepowered.api.Game;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
@@ -42,14 +49,10 @@ import org.spongepowered.api.event.server.ClientPingServerEvent;
import org.spongepowered.api.network.status.Favicon;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.scheduler.Task;
import xyz.jpenilla.minimotd.common.CommandHandlerFactory;
import xyz.jpenilla.minimotd.common.CommandHandler;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.MiniMOTDPlatform;
import xyz.jpenilla.minimotd.common.UpdateChecker;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Path;
import xyz.jpenilla.minimotd.common.util.UpdateChecker;
@Plugin(id = "minimotd-sponge7")
public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
@@ -57,24 +60,36 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
private final Path dataDirectory;
private final MiniMOTD<Favicon> miniMOTD;
private final SpongeAudiences audiences;
private final Game game;
private final Injector injector;
@Inject
public MiniMOTDPlugin(
final @NonNull Logger logger,
@ConfigDir(sharedRoot = false) final @NonNull Path dataDirectory,
final @NonNull SpongeAudiences audiences,
final Metrics.@NonNull Factory metricsFactory
final Metrics.@NonNull Factory metricsFactory,
final @NonNull Injector injector,
final @NonNull Game game
) {
this.logger = logger;
this.dataDirectory = dataDirectory;
this.audiences = audiences;
this.game = game;
this.miniMOTD = new MiniMOTD<>(this);
this.injector = injector.createChildInjector(new AbstractModule() {
@Override
protected void configure() {
this.bind(new TypeLiteral<MiniMOTD<Favicon>>() {
}).toInstance(MiniMOTDPlugin.this.miniMOTD);
}
});
metricsFactory.make(10768);
}
@Listener
public void gameStarted(final @NonNull GameStartedServerEvent event) {
Sponge.getEventManager().registerListener(this, ClientPingServerEvent.class, new ClientPingServerEventListener(this.miniMOTD));
Sponge.getEventManager().registerListener(this, ClientPingServerEvent.class, this.injector.getInstance(ClientPingServerEventListener.class));
this.registerCommands();
if (this.miniMOTD.configManager().pluginSettings().updateChecker()) {
Task.builder()
@@ -91,28 +106,28 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
private void registerCommands() {
final class WrappingExecutor implements CommandExecutor {
private final CommandHandlerFactory.CommandHandler handler;
private final CommandHandler.Executor handler;
WrappingExecutor(final CommandHandlerFactory.@NonNull CommandHandler handler) {
WrappingExecutor(final CommandHandler.@NonNull Executor handler) {
this.handler = handler;
}
@Override
public CommandResult execute(final @NonNull CommandSource src, final @NonNull CommandContext args) {
public @NonNull CommandResult execute(final @NonNull CommandSource src, final @NonNull CommandContext args) {
this.handler.execute(MiniMOTDPlugin.this.audiences.receiver(src));
return CommandResult.success();
}
}
final CommandHandlerFactory factory = new CommandHandlerFactory(this.miniMOTD);
final CommandHandler handler = new CommandHandler(this.miniMOTD);
final CommandSpec help = CommandSpec.builder()
.executor(new WrappingExecutor(factory.help()))
.executor(new WrappingExecutor(handler::help))
.build();
final CommandSpec about = CommandSpec.builder()
.executor(new WrappingExecutor(factory.about()))
.executor(new WrappingExecutor(handler::about))
.build();
final CommandSpec reload = CommandSpec.builder()
.executor(new WrappingExecutor(factory.reload()))
.executor(new WrappingExecutor(handler::reload))
.build();
Sponge.getCommandManager().register(
this,
@@ -138,6 +153,6 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
@Override
public @NonNull Favicon loadIcon(final @NonNull BufferedImage image) throws IOException {
return Sponge.getRegistry().loadFavicon(image);
return this.game.getRegistry().loadFavicon(image);
}
}
+5 -5
View File
@@ -1,10 +1,10 @@
[
{
"modid": "{project.name}",
"name": "{rootProject.name}",
"version": "{project.version}",
"description": "{project.description}",
"url": "{url}",
"modid": "${modid}",
"name": "${name}",
"version": "${version}",
"description": "${description}",
"url": "${url}",
"authorList": [
"jmp"
],
+17 -26
View File
@@ -1,35 +1,36 @@
import org.spongepowered.gradle.plugin.config.PluginLoaders.JAVA_PLAIN
import org.spongepowered.plugin.metadata.PluginDependency.LoadOrder.AFTER
import org.spongepowered.gradle.plugin.config.PluginLoaders
import org.spongepowered.plugin.metadata.PluginDependency
plugins {
id("org.spongepowered.gradle.plugin") version "1.0.2"
id("minimotd.shadow-platform")
id("org.spongepowered.gradle.plugin")
}
dependencies {
implementation(project(":minimotd-common"))
implementation("org.apache.logging.log4j", "log4j-slf4j-impl", "2.8.1") {
implementation(projects.minimotdCommon)
implementation(libs.log4jSlf4jImpl) {
isTransitive = false
}
}
sponge {
injectRepositories(false)
apiVersion("8.0.0")
plugin(project.name) {
loader(JAVA_PLAIN)
loader(PluginLoaders.JAVA_PLAIN)
displayName(rootProject.name)
mainClass("xyz.jpenilla.minimotd.sponge8.MiniMOTDPlugin")
description(project.description)
links {
val url = rootProject.ext["url"].toString()
homepage(url)
source(url)
issues("${url}/issues")
homepage(Constants.GITHUB_URL)
source(Constants.GITHUB_URL)
issues("${Constants.GITHUB_URL}/issues")
}
contributor("jmp") {
description("Lead Developer")
}
dependency("spongeapi") {
loadOrder(AFTER)
loadOrder(PluginDependency.LoadOrder.AFTER)
optional(false)
}
}
@@ -37,22 +38,12 @@ sponge {
tasks {
shadowJar {
relocate("org.apache.logging.slf4j", "xyz.jpenilla.minimotd.lib.apache.logging.slf4j")
relocate("org.slf4j", "xyz.jpenilla.minimotd.lib.slf4j_log4j")
relocate("org.spongepowered.configurate", "xyz.jpenilla.minimotd.lib.spongepowered.configurate")
relocate("io.leangen.geantyref", "xyz.jpenilla.minimotd.lib.io.leangen.geantyref")
relocate("com.typesafe.config", "xyz.jpenilla.minimotd.lib.typesafe.config")
relocate("net.kyori.adventure.text.minimessage", "xyz.jpenilla.minimotd.lib.kyori_native.minimessage")
relocate("org.checkerframework", "xyz.jpenilla.minimotd.lib.checkerframework")
relocate("xyz.jpenilla.minimotd.common", "xyz.jpenilla.minimotd.lib.sponge8.minimotd.common")
configureForNativeAdventurePlatform()
commonRelocation("org.apache.logging.slf4j")
platformRelocation("log4j", "org.slf4j")
platformRelocation("sponge8", "xyz.jpenilla.minimotd.common")
dependencies {
exclude { dep -> dep.moduleGroup == "net.kyori" && !dep.name.contains("minimessage") }
exclude(dependency("io.leangen.geantyref:geantyref"))
}
}
runServer {
classpath(shadowJar)
}
build {
dependsOn(shadowJar)
}
}
@@ -23,27 +23,26 @@
*/
package xyz.jpenilla.minimotd.sponge8;
import net.kyori.adventure.text.Component;
import com.google.inject.Inject;
import java.lang.reflect.Method;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.api.MinecraftVersion;
import org.spongepowered.api.event.EventListener;
import org.spongepowered.api.event.server.ClientPingServerEvent;
import org.spongepowered.api.network.status.Favicon;
import xyz.jpenilla.minimotd.common.ComponentColorDownsampler;
import xyz.jpenilla.minimotd.common.Constants;
import xyz.jpenilla.minimotd.common.MOTDIconPair;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.PingResponse;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig.PlayerCount;
import java.lang.reflect.Method;
import xyz.jpenilla.minimotd.common.util.ComponentColorDownsampler;
final class ClientPingServerEventListener implements EventListener<ClientPingServerEvent> {
private final MiniMOTD<Favicon> miniMOTD;
private @MonotonicNonNull Method SpongeMinecraftVersion_getProtocol;
ClientPingServerEventListener(final @NonNull MiniMOTD<Favicon> miniMOTD) {
@Inject
private ClientPingServerEventListener(final @NonNull MiniMOTD<Favicon> miniMOTD) {
this.miniMOTD = miniMOTD;
}
@@ -66,33 +65,21 @@ final class ClientPingServerEventListener implements EventListener<ClientPingSer
final MiniMOTDConfig config = this.miniMOTD.configManager().mainConfig();
final PlayerCount count = config.modifyPlayerCount(players.online(), players.max());
final int onlinePlayers = count.onlinePlayers();
final int maxPlayers = count.maxPlayers();
players.setOnline(onlinePlayers);
players.setMax(maxPlayers);
final MOTDIconPair<Favicon> pair = this.miniMOTD.createMOTD(config, onlinePlayers, maxPlayers);
final Component motdComponent = pair.motd();
if (motdComponent != null) {
final MinecraftVersion version = event.client().version();
if (this.legacy(version)) {
response.setDescription(ComponentColorDownsampler.downsampler().downsample(motdComponent));
final PingResponse<Favicon> mini = this.miniMOTD.createMOTD(config, players.online(), players.max());
mini.playerCount().applyCount(players::setOnline, players::setMax);
mini.motd(motd -> {
if (this.legacy(event.client().version())) {
response.setDescription(ComponentColorDownsampler.downsampler().downsample(motd));
} else {
response.setDescription(motdComponent);
response.setDescription(motd);
}
}
});
mini.icon(response::setFavicon);
final Favicon favicon = pair.icon();
if (favicon != null) {
response.setFavicon(favicon);
}
if (config.disablePlayerListHover()) {
if (mini.disablePlayerListHover()) {
players.profiles().clear();
}
if (config.hidePlayerCount()) {
if (mini.hidePlayerCount()) {
response.setHidePlayers(true);
}
}
@@ -23,7 +23,12 @@
*/
package xyz.jpenilla.minimotd.sponge8;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,13 +48,10 @@ import org.spongepowered.api.scheduler.Task;
import org.spongepowered.plugin.PluginContainer;
import org.spongepowered.plugin.jvm.Plugin;
import org.spongepowered.plugin.metadata.PluginMetadata;
import xyz.jpenilla.minimotd.common.CommandHandlerFactory;
import xyz.jpenilla.minimotd.common.CommandHandler;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.MiniMOTDPlatform;
import xyz.jpenilla.minimotd.common.UpdateChecker;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import xyz.jpenilla.minimotd.common.util.UpdateChecker;
@Plugin("minimotd-sponge8")
public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
@@ -58,21 +60,30 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
private final Logger logger;
private final PluginContainer pluginContainer;
private final MiniMOTD<Favicon> miniMOTD;
private final Injector injector;
@Inject
public MiniMOTDPlugin(
@ConfigDir(sharedRoot = false) final @NonNull Path dataDirectory,
final @NonNull PluginContainer pluginContainer
final @NonNull PluginContainer pluginContainer,
final @NonNull Injector injector
) {
this.dataDirectory = dataDirectory;
this.pluginContainer = pluginContainer;
this.pluginMetadata = pluginContainer.getMetadata();
this.logger = LoggerFactory.getLogger(this.pluginMetadata.getId());
this.pluginMetadata = pluginContainer.metadata();
this.logger = LoggerFactory.getLogger(this.pluginMetadata.id());
this.miniMOTD = new MiniMOTD<>(this);
this.injector = injector.createChildInjector(new AbstractModule() {
@Override
protected void configure() {
this.bind(new TypeLiteral<MiniMOTD<Favicon>>() {
}).toInstance(MiniMOTDPlugin.this.miniMOTD);
}
});
Sponge.eventManager().registerListener(
pluginContainer,
ClientPingServerEvent.class,
new ClientPingServerEventListener(this.miniMOTD)
this.injector.getInstance(ClientPingServerEventListener.class)
);
}
@@ -89,9 +100,9 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
@Listener
public void registerCommands(final @NonNull RegisterCommandEvent<Command.Parameterized> event) {
final class WrappingExecutor implements CommandExecutor {
private final CommandHandlerFactory.CommandHandler handler;
private final CommandHandler.Executor handler;
WrappingExecutor(final CommandHandlerFactory.@NonNull CommandHandler handler) {
WrappingExecutor(final CommandHandler.@NonNull Executor handler) {
this.handler = handler;
}
@@ -102,15 +113,15 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
}
}
final CommandHandlerFactory handlerFactory = new CommandHandlerFactory(this.miniMOTD);
final CommandHandler handler = new CommandHandler(this.miniMOTD);
final Command.Parameterized about = Command.builder()
.executor(new WrappingExecutor(handlerFactory.about()))
.executor(new WrappingExecutor(handler::about))
.build();
final Command.Parameterized help = Command.builder()
.executor(new WrappingExecutor(handlerFactory.help()))
.executor(new WrappingExecutor(handler::help))
.build();
final Command.Parameterized reload = Command.builder()
.executor(new WrappingExecutor(handlerFactory.reload()))
.executor(new WrappingExecutor(handler::reload))
.build();
event.register(
this.pluginContainer,
+30 -17
View File
@@ -1,19 +1,32 @@
tasks {
val universal = register<Jar>("universal") {
artifacts.add("archives", this)
archiveClassifier.set("")
destinationDirectory.set(rootProject.buildDir.resolve("libs"))
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
arrayOf("spigot", "bungeecord", "velocity", "sponge7", "sponge8").forEach {
val shadowJar = rootProject.project(":minimotd-$it").tasks.getByName("shadowJar")
from(zipTree(shadowJar.outputs.files.singleFile))
dependsOn(shadowJar)
}
val fabricRemapJarTask = rootProject.project(":minimotd-fabric").tasks.getByName("remapJar")
dependsOn(fabricRemapJarTask)
from(zipTree(fabricRemapJarTask.outputs.files.singleFile))
}
build {
dependsOn(universal)
plugins {
id("minimotd.platform-conventions")
}
tasks.jar {
archiveClassifier.set("empty")
}
val platforms = setOf(
projects.minimotdBukkit,
projects.minimotdBungeecord,
projects.minimotdVelocity,
projects.minimotdFabric,
projects.minimotdSponge7,
//projects.minimotdSponge8
).map { it.dependencyProject }
val universal = tasks.register<Jar>("universal") {
artifacts.add("archives", this)
archiveClassifier.set(null as String?)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
for (platform in platforms) {
val jarTask = platform.miniMOTDPlatform.jarTask
from(zipTree(jarTask.flatMap { it.archiveFile }))
dependsOn(jarTask) // todo: remove when updating Gradle to 7.1 (https://github.com/gradle/gradle/issues/15569)
}
}
miniMOTDPlatform {
jarTask.set(universal)
}
+15 -24
View File
@@ -1,42 +1,33 @@
plugins {
id("minimotd.shadow-platform")
id("net.kyori.blossom")
}
dependencies {
implementation(project(":minimotd-common")) {
implementation(projects.minimotdCommon) {
exclude("org.slf4j", "slf4j-api")
}
implementation("org.bstats", "bstats-velocity", "2.2.1")
compileOnly("com.velocitypowered", "velocity-api", "1.1.4")
annotationProcessor("com.velocitypowered", "velocity-api", "1.1.4")
implementation(libs.bstatsVelocity)
compileOnly(libs.velocityApi)
annotationProcessor(libs.velocityApi)
}
tasks {
shadowJar {
relocate("org.spongepowered.configurate", "xyz.jpenilla.minimotd.lib.spongepowered.configurate")
relocate("io.leangen.geantyref", "xyz.jpenilla.minimotd.lib.io.leangen.geantyref")
relocate("com.typesafe.config", "xyz.jpenilla.minimotd.lib.typesafe.config")
relocate("net.kyori.adventure.text.minimessage", "xyz.jpenilla.minimotd.lib.kyori_native.minimessage")
relocate("xyz.jpenilla.minimotd.common", "xyz.jpenilla.minimotd.lib.velocity.minimotd.common")
relocate("org.checkerframework", "xyz.jpenilla.minimotd.lib.checkerframework")
relocate("org.bstats", "xyz.jpenilla.minimotd.lib.bstats")
dependencies {
exclude { dep -> dep.moduleGroup == "net.kyori" && !dep.name.contains("minimessage") }
}
}
build {
dependsOn(shadowJar)
configureForNativeAdventurePlatform()
commonRelocation("io.leangen.geantyref")
platformRelocation("velocity", "xyz.jpenilla.minimotd.common")
commonRelocation("org.bstats")
}
}
blossom {
fun replaceTokens(file: String, vararg tokens: Pair<String, String>) = tokens.forEach { (k, v) ->
val file = "src/main/java/xyz/jpenilla/minimotd/velocity/MiniMOTDPlugin.java"
mapOf(
"project.name" to project.name,
"description" to description as String,
"url" to Constants.GITHUB_URL
).forEach { (k, v) ->
replaceToken("\${$k}", v, file)
}
replaceTokens(
"src/main/java/xyz/jpenilla/minimotd/velocity/MiniMOTDPlugin.java",
"project.name" to project.name,
"description" to description!!,
"url" to rootProject.ext["url"].toString()
)
}
@@ -23,7 +23,11 @@
*/
package xyz.jpenilla.minimotd.velocity;
import com.google.common.collect.ImmutableSet;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
@@ -32,27 +36,21 @@ import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.Favicon;
import net.kyori.adventure.text.Component;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import java.util.Set;
import org.bstats.velocity.Metrics;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import xyz.jpenilla.minimotd.common.CommandHandlerFactory;
import xyz.jpenilla.minimotd.common.CommandHandler;
import xyz.jpenilla.minimotd.common.Constants;
import xyz.jpenilla.minimotd.common.MOTDIconPair;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.MiniMOTDPlatform;
import xyz.jpenilla.minimotd.common.UpdateChecker;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig.PlayerCount;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import xyz.jpenilla.minimotd.common.util.UpdateChecker;
@Plugin(
id = "${project.name}",
@@ -63,12 +61,17 @@ import java.nio.file.Path;
authors = {"jmp"}
)
public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
private static final Set<Class<?>> LISTENER_CLASSES = ImmutableSet.of(
PingListener.class
);
private final MiniMOTD<Favicon> miniMOTD;
private final ProxyServer server;
private final Logger logger;
private final CommandManager commandManager;
private final Path dataDirectory;
private final Metrics.Factory metricsFactory;
private final Injector injector;
@Inject
public MiniMOTDPlugin(
@@ -76,7 +79,8 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
final @NonNull Logger logger,
final @NonNull CommandManager commandManager,
@DataDirectory final @NonNull Path dataDirectory,
final Metrics.@NonNull Factory metricsFactory
final Metrics.@NonNull Factory metricsFactory,
final @NonNull Injector injector
) {
this.server = server;
this.logger = logger;
@@ -85,10 +89,20 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
this.metricsFactory = metricsFactory;
this.miniMOTD = new MiniMOTD<>(this);
this.miniMOTD.configManager().loadExtraConfigs();
this.injector = injector.createChildInjector(new AbstractModule() {
@Override
protected void configure() {
this.bind(new TypeLiteral<MiniMOTD<Favicon>>() {
}).toInstance(MiniMOTDPlugin.this.miniMOTD);
}
});
}
@Subscribe
public void onProxyInitialization(final @NonNull ProxyInitializeEvent event) {
for (final Class<?> clazz : LISTENER_CLASSES) {
this.server.getEventManager().register(this, this.injector.getInstance(clazz));
}
this.registerCommand();
this.metricsFactory.make(this, 10257);
if (this.miniMOTD.configManager().pluginSettings().updateChecker()) {
@@ -101,9 +115,9 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
private void registerCommand() {
final class WrappingExecutor implements Command<CommandSource> {
private final CommandHandlerFactory.CommandHandler handler;
private final CommandHandler.Executor handler;
WrappingExecutor(final CommandHandlerFactory.@NonNull CommandHandler handler) {
WrappingExecutor(final CommandHandler.@NonNull Executor handler) {
this.handler = handler;
}
@@ -114,54 +128,16 @@ public final class MiniMOTDPlugin implements MiniMOTDPlatform<Favicon> {
}
}
final CommandHandlerFactory handlerFactory = new CommandHandlerFactory(this.miniMOTD);
final CommandHandler handler = new CommandHandler(this.miniMOTD);
this.commandManager.register(this.commandManager.metaBuilder("minimotd").build(), new BrigadierCommand(
LiteralArgumentBuilder.<CommandSource>literal("minimotd")
.requires(source -> source.hasPermission("minimotd.admin"))
.then(LiteralArgumentBuilder.<CommandSource>literal("help").executes(new WrappingExecutor(handlerFactory.help())))
.then(LiteralArgumentBuilder.<CommandSource>literal("about").executes(new WrappingExecutor(handlerFactory.about())))
.then(LiteralArgumentBuilder.<CommandSource>literal("reload").executes(new WrappingExecutor(handlerFactory.reload())))
.then(LiteralArgumentBuilder.<CommandSource>literal("help").executes(new WrappingExecutor(handler::help)))
.then(LiteralArgumentBuilder.<CommandSource>literal("about").executes(new WrappingExecutor(handler::about)))
.then(LiteralArgumentBuilder.<CommandSource>literal("reload").executes(new WrappingExecutor(handler::reload)))
));
}
@Subscribe
public void onServerListPing(final @NonNull ProxyPingEvent event) {
final String configString = this.miniMOTD.configManager().pluginSettings().configStringForHost(
event.getConnection().getVirtualHost()
.map(inetSocketAddress -> String.format("%s:%s", inetSocketAddress.getHostName(), inetSocketAddress.getPort()))
.orElse("default")
).orElse("default");
final MiniMOTDConfig config = this.miniMOTD.configManager().resolveConfig(configString);
final ServerPing.Builder pong = event.getPing().asBuilder();
final PlayerCount count = config.modifyPlayerCount(pong.getOnlinePlayers(), pong.getMaximumPlayers());
final int onlinePlayers = count.onlinePlayers();
final int maxPlayers = count.maxPlayers();
pong.onlinePlayers(onlinePlayers);
pong.maximumPlayers(maxPlayers);
final MOTDIconPair<Favicon> pair = this.miniMOTD.createMOTD(config, onlinePlayers, maxPlayers);
final Favicon favicon = pair.icon();
if (favicon != null) {
pong.favicon(favicon);
}
final Component motdComponent = pair.motd();
if (motdComponent != null) {
pong.description(motdComponent);
}
if (config.disablePlayerListHover()) {
pong.clearSamplePlayers();
}
if (config.hidePlayerCount()) {
pong.nullPlayers();
}
event.setPing(pong.build());
}
@Override
public @NonNull Path dataDirectory() {
return this.dataDirectory;
@@ -0,0 +1,63 @@
/*
* This file is part of MiniMOTD, licensed under the MIT License.
*
* Copyright (c) 2021 Jason Penilla
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package xyz.jpenilla.minimotd.velocity;
import com.google.inject.Inject;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.Favicon;
import org.checkerframework.checker.nullness.qual.NonNull;
import xyz.jpenilla.minimotd.common.MiniMOTD;
import xyz.jpenilla.minimotd.common.PingResponse;
import xyz.jpenilla.minimotd.common.config.MiniMOTDConfig;
public final class PingListener {
private final MiniMOTD<Favicon> miniMOTD;
@Inject
private PingListener(final @NonNull MiniMOTD<Favicon> miniMOTD) {
this.miniMOTD = miniMOTD;
}
@Subscribe
public void handlePing(final @NonNull ProxyPingEvent event) {
final MiniMOTDConfig config = this.miniMOTD.configManager().resolveConfig(event.getConnection().getVirtualHost().orElse(null));
final ServerPing.Builder pong = event.getPing().asBuilder();
final PingResponse<Favicon> response = this.miniMOTD.createMOTD(config, pong.getOnlinePlayers(), pong.getMaximumPlayers());
response.icon(pong::favicon);
response.motd(pong::description);
response.playerCount().applyCount(pong::onlinePlayers, pong::maximumPlayers);
if (response.disablePlayerListHover()) {
pong.clearSamplePlayers();
}
if (response.hidePlayerCount()) {
pong.nullPlayers();
}
event.setPing(pong.build());
}
}