Compare commits

..

No commits in common. "master" and "v1.0-pre2" have entirely different histories.

167 changed files with 4273 additions and 4357 deletions

View file

@ -13,5 +13,3 @@ tab_width=4
indent_style=space indent_style=space
indent_size=2 indent_size=2
[*.xml]
indent_style = tab

View file

@ -1,75 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '0 10 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: [ 'java' ]
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Setup Java JDK
uses: actions/setup-java@v1.3.0
with:
java-version: 11
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

6
.gitignore vendored
View file

@ -218,10 +218,10 @@ pip-log.txt
.mr.developer.cfg .mr.developer.cfg
.metadata/* .metadata/*
TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar
*.iml #*.iml
*.name *.name
.idea .idea/compiler.xml
dependency-reduced-pom.xml *.xml
TBMC/ TBMC/
/.apt_generated/ /.apt_generated/

13
.idea/ButtonCore.iml Executable file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:3.8.1" level="project" />
</component>
</module>

View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

28
.idea/compiler.xml Executable file
View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="BuildConfigUpdater" />
<module name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel>
<module name="BuildConfigUpdater" target="1.8" />
<module name="ButtonCore" target="1.5" />
<module name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" target="1.8" />
<module name="ButtonProcessor" target="1.8" />
<module name="ChunkArchive" target="1.8" />
<module name="RandomTeleport" target="1.8" />
</bytecodeTargetLevel>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="ButtonProcessor" options="-proc:none" />
</option>
</component>
</project>

7
.idea/encodings.xml Executable file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/ButtonCore" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/ButtonProcessor" charset="UTF-8" />
</component>
</project>

View file

@ -0,0 +1,11 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="WeakerAccess" enabled="false" level="WARNING" enabled_by_default="false">
<option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="false" />
<option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="false" />
<option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" />
<disabledExtension id="moduleInfo" />
</inspection_tool>
</profile>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: com.google.code.findbugs:annotations:2.0.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/findbugs/annotations/2.0.1/annotations-2.0.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/findbugs/annotations/2.0.1/annotations-2.0.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/findbugs/annotations/2.0.1/annotations-2.0.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: com.google.code.gson:gson:2.8.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.8.0/gson-2.8.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.8.0/gson-2.8.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.8.0/gson-2.8.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: com.google.guava:guava:15.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/15.0/guava-15.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/15.0/guava-15.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/15.0/guava-15.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: com.google.guava:guava:21.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/21.0/guava-21.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/21.0/guava-21.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/21.0/guava-21.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: com.googlecode.json-simple:json-simple:1.1.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: commons-io:commons-io:1.3.2">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/1.3.2/commons-io-1.3.2.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/1.3.2/commons-io-1.3.2-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/1.3.2/commons-io-1.3.2-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: commons-io:commons-io:2.6">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: commons-lang:commons-lang:2.6">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/commons-lang/commons-lang/2.6/commons-lang-2.6.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/commons-lang/commons-lang/2.6/commons-lang-2.6-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/commons-lang/commons-lang/2.6/commons-lang-2.6-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: junit:junit:3.8.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/3.8.1/junit-3.8.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/3.8.1/junit-3.8.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/3.8.1/junit-3.8.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: net.bytebuddy:byte-buddy:1.6.11">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.6.11/byte-buddy-1.6.11.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.6.11/byte-buddy-1.6.11-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.6.11/byte-buddy-1.6.11-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: net.bytebuddy:byte-buddy-agent:1.6.11">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.6.11/byte-buddy-agent-1.6.11.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.6.11/byte-buddy-agent-1.6.11-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.6.11/byte-buddy-agent-1.6.11-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: net.ess3:Essentials:2.13.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/net/ess3/Essentials/2.13.1/Essentials-2.13.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/net/ess3/Essentials/2.13.1/Essentials-2.13.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/net/ess3/Essentials/2.13.1/Essentials-2.13.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: net.md-5:bungeecord-chat:1.12-SNAPSHOT">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/net/md-5/bungeecord-chat/1.12-SNAPSHOT/bungeecord-chat-1.12-20180712.114550-97.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/net/md-5/bungeecord-chat/1.12-SNAPSHOT/bungeecord-chat-1.12-20180712.114550-97-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/net/md-5/bungeecord-chat/1.12-SNAPSHOT/bungeecord-chat-1.12-20180712.114550-97-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.bukkit:bukkit:1.13.1-R0.1-SNAPSHOT">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/bukkit/bukkit/1.13.1-R0.1-SNAPSHOT/bukkit-1.13.1-R0.1-20181022.190036-99.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/bukkit/bukkit/1.13.1-R0.1-SNAPSHOT/bukkit-1.13.1-R0.1-20181022.190036-99-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/bukkit/bukkit/1.13.1-R0.1-SNAPSHOT/bukkit-1.13.1-R0.1-20181022.190036-99-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.javassist:javassist:3.20.0-GA">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.mockito:mockito-core:2.7.20">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/mockito/mockito-core/2.7.20/mockito-core-2.7.20.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/mockito/mockito-core/2.7.20/mockito-core-2.7.20-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/mockito/mockito-core/2.7.20/mockito-core-2.7.20-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.objenesis:objenesis:2.5">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/2.5/objenesis-2.5.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/2.5/objenesis-2.5-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/2.5/objenesis-2.5-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,14 @@
<component name="libraryTable">
<library name="Maven: org.projectlombok:lombok:1.16.16">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.16.16/lombok-1.16.16.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.16.16/lombok-1.16.16-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.16.16/lombok-1.16.16-sources.jar!/" />
<root url="jar://$USER_HOME$/.ideaLibSources/lombok-1.16.16-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.reflections:reflections:0.9.10">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/reflections/reflections/0.9.10/reflections-0.9.10.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/reflections/reflections/0.9.10/reflections-0.9.10-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/reflections/reflections/0.9.10/reflections-0.9.10-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.12.2-R0.1-SNAPSHOT/spigot-api-1.12.2-R0.1-SNAPSHOT.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.12.2-R0.1-SNAPSHOT/spigot-api-1.12.2-R0.1-SNAPSHOT-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/spigotmc/spigot-api/1.12.2-R0.1-SNAPSHOT/spigot-api-1.12.2-R0.1-SNAPSHOT-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.yaml:snakeyaml:1.19">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/yaml/snakeyaml/1.19/snakeyaml-1.19.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/yaml/snakeyaml/1.19/snakeyaml-1.19-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/yaml/snakeyaml/1.19/snakeyaml-1.19-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownExportedFiles">
<htmlFiles />
<imageFiles />
<otherFiles />
</component>
</project>

View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings" wasCopied="false">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="ADMONITION_EXT" value="false" />
<option name="ATTRIBUTES_EXT" value="false" />
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="GITLAB_EXT" value="false" />
<option name="GITLAB_MATH_EXT" value="false" />
<option name="GITLAB_MERMAID_EXT" value="false" />
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="MACROS_EXT" value="false" />
<option name="NO_TEXT_ATTRIBUTES" value="false" />
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false" addDocTypeHtml="true">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="false" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>

View file

@ -0,0 +1,3 @@
<component name="MarkdownNavigator.ProfileManager">
<settings default="" pdf-export="" />
</component>

18
.idea/misc.xml Executable file
View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
<option name="ignoredFiles">
<set>
<option value="$PROJECT_DIR$/pom.xml" />
</set>
</option>
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

10
.idea/modules.xml Executable file
View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/BuildConfigUpdater/BuildConfigUpdater.iml" filepath="$PROJECT_DIR$/BuildConfigUpdater/BuildConfigUpdater.iml" />
<module fileurl="file://$PROJECT_DIR$/ButtonCore/ButtonCore (1) (com.github.TBMCPlugins.ButtonCore).iml" filepath="$PROJECT_DIR$/ButtonCore/ButtonCore (1) (com.github.TBMCPlugins.ButtonCore).iml" />
<module fileurl="file://$PROJECT_DIR$/ButtonProcessor/ButtonProcessor.iml" filepath="$PROJECT_DIR$/ButtonProcessor/ButtonProcessor.iml" />
</modules>
</component>
</project>

124
.idea/uiDesigner.xml Normal file
View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

6
.idea/vcs.xml Executable file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -9,13 +9,18 @@ before_install: | # Wget BuildTools and run if cached folder not found
fi fi
language: java language: java
jdk: jdk:
- oraclejdk11 - oraclejdk8
sudo: true sudo: true
deploy: deploy:
- provider: releases # deploy develop to the staging environment
api_key: - provider: script
secure: "T/ITeRn4JSx4HS4n44P7vy03HWmJGVpYeR6tABu8qOLrwz2MUCFu8xvYqoq4MiqIKqFIKtZHgCSWiB1WsTIJPHm9S2xYs0BFocoQDthE+eCtT2wgCHj/xdo0wBm8uktXr2hxRF5Nd2R/mfSLkoVxAM6otOoSa1SidRGKgDCJxRnsgssk5igQR+tpahZYPvuzM5xJ4G6J07cC+HHM7RLYXkkKrsK3LFugTsnNtctwmxQkm6SqzDcc8YPwoJGhj8PLbXc5S8/KaWT0//LNdypnzeIaRv+aY+ky/zLntXlw3EFOk4erTthAEMSu2x6PgX5B/paMawdwnqKul/L4ynMGojrLH8ha9KM7p/lze2bxCaf08RFVFvTQt6yzmvM1suL4KH6gAF4mKhyaU0kBeOsnTTskoxyGxvbZgdo0Y1V+Yd7w58eu8zfxnauaX7f2DEz7gH4qkSl+hC7HAFoY7m7IXkgVN6Vuv3lGdFO5S15lbGoihOPqt9T79lBxi/efiXEE4BmA1g8MeLsyN8N/EO+LqVr6xXAKJet6YtOAe/iIAqxpNDSuME1szusc6+4q3jOy6cPmB1TKsfnIKQmbT98SHc9wE4/76VLd2PlNPppa0AIG/g+qnjhjpFQxB7LnWdP+D6io9N1FYrvoOX8UVAp4XYhwUMNLUInDT/6P6j8lL7A=" script: chmod +x deploy.sh && sh deploy.sh staging
file: 'Chroma-Core/target/Chroma-Core.jar'
on: on:
tags: true branch: dev
skip_cleanup: true
# deploy master to production
- provider: script
script: chmod +x deploy.sh && sh deploy.sh production
on:
branch: master
skip_cleanup: true skip_cleanup: true

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" />
<orderEntry type="library" name="Maven: org.mockito:mockito-core:2.7.20" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: net.bytebuddy:byte-buddy:1.6.11" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: net.bytebuddy:byte-buddy-agent:1.6.11" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.objenesis:objenesis:2.5" level="project" />
<orderEntry type="module" module-name="ButtonProcessor" />
<orderEntry type="library" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:21.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.19" level="project" />
<orderEntry type="library" name="Maven: net.md-5:bungeecord-chat:1.12-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:3.8.1" level="project" />
</component>
</module>

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ButtonCore</artifactId>
<groupId>com.github.TBMCPlugins</groupId>
<version>master-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>BuildConfigUpdater</artifactId>
<repositories>
<repository>
<id>Jitpack</id>
<url>https://jitpack.io/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.TBMCPlugins.ButtonCore</groupId>
<artifactId>ButtonCore</artifactId>
<version>master-SNAPSHOT</version>
</dependency>
<dependency> <!-- Needed for TBMCCoreAPI -->
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.12.2-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,16 @@
import buttondevteam.core.component.updater.PluginUpdater;
import java.util.List;
import java.util.stream.Collectors;
public class BCUMain {
public static void main(String[] args) {
System.out.println("Getting list of repositories...");
List<String> plugins = PluginUpdater.GetPluginNames();
System.out.println("Removing non-Maven projects...");
plugins.removeIf(plugin -> PluginUpdater.isNotMaven(plugin, "master"));
System.out.println(plugins.stream().collect(Collectors.joining("\n")));
for (String plugin : plugins) { //TODO: We don't want to apply it all at once, especially to unused/unowned repos
} //TODO: Add it to ButtonCore - or actually as a plugin or ButtonProcessor
}
}

2
ButtonCore/.factorypath Executable file
View file

@ -0,0 +1,2 @@
<factorypath>
</factorypath>

23
ButtonCore/.project Executable file
View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ButtonCore</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,6 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/main/resources=UTF-8
encoding//src/test/java=UTF-8
encoding/<project>=UTF-8
encoding/src=UTF-8

View file

@ -0,0 +1,4 @@
eclipse.preferences.version=1
org.eclipse.jdt.apt.aptEnabled=true
org.eclipse.jdt.apt.genSrcDir=.apt_generated
org.eclipse.jdt.apt.reconcileEnabled=true

View file

@ -0,0 +1,14 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.processAnnotations=enabled
org.eclipse.jdt.core.compiler.source=1.8

View file

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:15.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.gson:gson:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.19" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: net.md-5:bungeecord-chat:1.12-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-io:commons-io:1.3.2" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.TBMCPlugins.ButtonCore:Towny:master-b9ce3dc0e5-1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.milkbowl:VaultAPI:master-68f14eca20-1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.bukkit:bukkit:1.13.1-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" />
<orderEntry type="library" name="Maven: org.mockito:mockito-core:2.7.20" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: net.bytebuddy:byte-buddy:1.6.11" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: net.bytebuddy:byte-buddy-agent:1.6.11" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.objenesis:objenesis:2.5" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.16.16" level="project" />
<orderEntry type="module" module-name="ButtonProcessor" />
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:Essentials:2.13.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.vexsoftware:nuvotifier-universal:2.3.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:3.8.1" level="project" />
</component>
</module>

2
ButtonCore/README.md Executable file
View file

@ -0,0 +1,2 @@
# ButtonCore
A plugin that serves as a container for things common to many of our plugins

View file

@ -2,15 +2,14 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.github.TBMCPlugins.ChromaCore</groupId> <groupId>com.github.TBMCPlugins</groupId>
<artifactId>CorePOM</artifactId> <artifactId>ButtonCore</artifactId>
<version>master-SNAPSHOT</version> <version>master-SNAPSHOT</version>
<relativePath>../CorePOM</relativePath>
</parent> </parent>
<artifactId>Chroma-Core</artifactId> <groupId>com.github.TBMCPlugins.ButtonCore</groupId>
<name>Chroma-Core</name> <artifactId>ButtonCore</artifactId>
<description>Chroma-Core</description> <name>ButtonCore</name>
<version>v${noprefix.version}-SNAPSHOT</version> <description>ButtonCore</description>
<build> <build>
<sourceDirectory>src/main/java</sourceDirectory> <sourceDirectory>src/main/java</sourceDirectory>
<resources> <resources>
@ -25,12 +24,19 @@
<filtering>true</filtering> <filtering>true</filtering>
</resource> </resource>
</resources> </resources>
<finalName>Chroma-Core</finalName> <finalName>ButtonCore</finalName>
<plugins> <plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version> <version>2.4.2</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
@ -39,18 +45,7 @@
</goals> </goals>
<configuration> <configuration>
<artifactSet> <artifactSet>
<includes>
<include>me.lucko:commodore</include>
<include>org.javatuples:javatuples</include>
</includes>
</artifactSet> </artifactSet>
<relocations>
<relocation>
<pattern>me.lucko.commodore</pattern>
<!-- vvv Replace with the package of your plugin vvv -->
<shadedPattern>buttondevteam.core.commodore</shadedPattern>
</relocation>
</relocations>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -73,7 +68,6 @@
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
</resource> </resource>
</resources> </resources>
<encoding>UTF-8</encoding>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -117,22 +111,18 @@
</repository> --> </repository> -->
<repository> <repository>
<id>ess-repo</id> <id>ess-repo</id>
<url>https://ci.ender.zone/plugin/repository/everything/</url> <url>http://repo.ess3.net/content/repositories/essrel/</url>
</repository> </repository>
<repository> <repository>
<id>Multiverse-Core</id> <id>Votifier</id>
<url>https://repo.onarandombox.com/content/groups/public/</url> <url>https://dl.bintray.com/nuvotifier/maven/</url>
</repository>
<repository>
<id>minecraft-repo</id>
<url>https://libraries.minecraft.net/</url>
</repository> </repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.reflections</groupId> <groupId>org.reflections</groupId>
<artifactId>reflections</artifactId> <artifactId>reflections</artifactId>
<version>0.10.2</version> <version>0.9.10</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -141,10 +131,18 @@
<version>1.12.2-R0.1-SNAPSHOT</version> <version>1.12.2-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-io -->
<dependency> <dependency>
<groupId>com.github.TownyAdvanced</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
<scope>provided</scope>
</dependency>
<!-- Change jitpack.yml to set location of Towny JAR -->
<dependency>
<groupId>com.github.TBMCPlugins.ButtonCore</groupId>
<artifactId>Towny</artifactId> <artifactId>Towny</artifactId>
<version>0.94.0.9</version> <version>master-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -157,49 +155,38 @@
<dependency> <dependency>
<groupId>org.javassist</groupId> <groupId>org.javassist</groupId>
<artifactId>javassist</artifactId> <artifactId>javassist</artifactId>
<version>3.28.0-GA</version> <version>3.20.0-GA</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>4.2.0</version> <version>2.7.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.TBMCPlugins.ChromaCore</groupId> <groupId>com.github.TBMCPlugins.ButtonCore</groupId>
<artifactId>ButtonProcessor</artifactId> <artifactId>ButtonProcessor</artifactId>
<version>master-SNAPSHOT</version> <version>master-SNAPSHOT</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.ess3</groupId> <groupId>net.ess3</groupId>
<artifactId>EssentialsX</artifactId> <artifactId>Essentials</artifactId>
<version>2.17.1</version> <version>2.13.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.NuVotifier.NuVotifier/nuvotifier-bukkit -->
<dependency>
<groupId>com.github.NuVotifier.NuVotifier</groupId>
<artifactId>nuvotifier-bukkit</artifactId>
<version>v2.7.1</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.onarandombox.multiversecore</groupId> <groupId>com.vexsoftware</groupId>
<artifactId>Multiverse-Core</artifactId> <artifactId>nuvotifier-universal</artifactId>
<version>4.3.1</version> <version>2.3.4</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>me.lucko</groupId>
<artifactId>commodore</artifactId>
<version>1.11</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.javatuples</groupId>
<artifactId>javatuples</artifactId>
<version>1.2</version>
</dependency>
</dependencies> </dependencies>
<organization> <organization>
<name>TBMCPlugins</name> <name>TBMCPlugins</name>
@ -216,7 +203,6 @@
<!-- github server corresponds to entry in ~/.m2/settings.xml --> <!-- github server corresponds to entry in ~/.m2/settings.xml -->
<github.global.server>github</github.global.server> <github.global.server>github</github.global.server>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<noprefix.version>1.0.1</noprefix.version>
</properties> </properties>
<scm> <scm>
<url>https://github.com/TBMCPlugins/mvn-repo</url> <url>https://github.com/TBMCPlugins/mvn-repo</url>

View file

@ -0,0 +1,107 @@
package buttondevteam.core;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.chat.TBMCCommandBase;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Arrays;
public class CommandCaller implements CommandExecutor {
private CommandCaller() {
}
private static CommandCaller instance;
public static void RegisterCommand(TBMCCommandBase cmd) throws Exception {
if (instance == null)
instance = new CommandCaller();
String[] topcmd = new String[1]; //Holds out param
PluginCommand pc = getPluginCommand(cmd, topcmd);
pc.setExecutor(instance);
String[] helptext = cmd.GetHelpText(topcmd[0]);
if (helptext == null || helptext.length == 0)
throw new Exception("Command " + cmd.GetCommandPath() + " has no help text!");
pc.setUsage(helptext.length > 1 ? helptext[1] : helptext[0]);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) {
StringBuilder path = new StringBuilder(command.getName().toLowerCase());
for (String arg : args)
path.append(" ").append(arg);
TBMCCommandBase cmd = TBMCChatAPI.GetCommands().get(path.toString());
int argc = 0;
String[] subcmds = null;
while (cmd == null && (subcmds = TBMCChatAPI.GetSubCommands(path.toString(), sender)).length == 0 && path.toString().contains(" ")) {
path = new StringBuilder(path.substring(0, path.toString().lastIndexOf(' ')));
argc++;
cmd = TBMCChatAPI.GetCommands().get(path.toString());
}
if (cmd == null) {
if (subcmds.length > 0) //Subcmds will always have value here (see assignment above)
sender.sendMessage(subcmds);
else {
final String errormsg = "§cYou don't have access to any of this command's subcommands or it doesn't have any.";
sender.sendMessage(errormsg);
}
return true;
}
if (cmd.isModOnly() && (MainPlugin.permission != null ? !MainPlugin.permission.has(sender, "tbmc.admin") : !sender.isOp())) {
sender.sendMessage("§cYou need to be a mod to use this command.");
return true;
}
final String[] cmdargs = args.length > 0 ? Arrays.copyOfRange(args, args.length - argc, args.length) : args;
try {
if (!cmd.OnCommand(sender, alias, cmdargs)) {
if (cmd.GetHelpText(alias) == null) {
sender.sendMessage("Wrong usage, but there's no help text! Error is being reported to devs.");
throw new NullPointerException("GetHelpText is null for comand /" + cmd.GetCommandPath());
} else
sender.sendMessage(cmd.GetHelpText(alias));
}
} catch (Exception e) {
TBMCCoreAPI.SendException("Failed to execute command /" + cmd.GetCommandPath() + " with arguments "
+ Arrays.toString(cmdargs), e);
}
return true;
}
public static void UnregisterCommand(TBMCCommandBase cmd) throws Exception {
PluginCommand pc = getPluginCommand(cmd, null);
pc.setExecutor(null); //Sets the executor to this plugin
}
/**
* Gets the plugin command from the TBMC command.
*
* @param cmd The TBMC command
* @param out_topcmd An array with at least 1 elements or null
* @return The Bukkit plugin command - an exception is generated if null
* @throws Exception If the command isn't set up properly (or a different error)
*/
public static PluginCommand getPluginCommand(TBMCCommandBase cmd, String[] out_topcmd) throws Exception {
String topcmd = cmd.GetCommandPath();
if (topcmd == null)
throw new Exception("Command " + cmd.getClass().getSimpleName() + " has no command path!");
if (cmd.getPlugin() == null)
throw new Exception("Command " + cmd.GetCommandPath() + " has no plugin!");
int i;
if ((i = topcmd.indexOf(' ')) != -1) // Get top-level command
topcmd = topcmd.substring(0, i);
if (out_topcmd != null && out_topcmd.length > 0)
out_topcmd[0] = topcmd;
{
PluginCommand pc = ((JavaPlugin) cmd.getPlugin()).getCommand(topcmd);
if (pc == null)
throw new Exception("Top level command " + topcmd + " not registered in plugin.yml for plugin: "
+ cmd.getPlugin().getName());
return pc;
}
}
}

View file

@ -6,17 +6,13 @@ import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.Command2.Subcommand; import buttondevteam.lib.chat.Command2.Subcommand;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.CustomTabCompleteMethod;
import buttondevteam.lib.chat.ICommand2MC; import buttondevteam.lib.chat.ICommand2MC;
import lombok.val; import lombok.val;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream;
@CommandClass(modOnly = true, helpText = { @CommandClass(modOnly = true, helpText = {
"Component command", "Component command",
@ -24,37 +20,24 @@ import java.util.stream.Stream;
}) })
public class ComponentCommand extends ICommand2MC { public class ComponentCommand extends ICommand2MC {
public ComponentCommand() { public ComponentCommand() {
getManager().addParamConverter(Plugin.class, arg -> Bukkit.getPluginManager().getPlugin(arg), "Plugin not found!", getManager().addParamConverter(Plugin.class, arg -> Bukkit.getPluginManager().getPlugin(arg), "Plugin not found!");
() -> Arrays.stream(Bukkit.getPluginManager().getPlugins()).map(Plugin::getName)::iterator);
} }
@Subcommand(helpText = { @Subcommand
"Enable component", public boolean enable(CommandSender sender, Plugin plugin, String component) {
"Temporarily or permanently enables a component." if (plugin instanceof ButtonPlugin)
}) ((ButtonPlugin) plugin).justReload();
public boolean enable(CommandSender sender, Plugin plugin, String component, @Command2.OptionalArg boolean permanent) { else
if (plugin instanceof ButtonPlugin) {
if (!((ButtonPlugin) plugin).justReload()) {
sender.sendMessage("§cCouldn't reload config, check console.");
return true;
}
} else
plugin.reloadConfig(); //Reload config so the new config values are read - All changes are saved to disk on disable plugin.reloadConfig(); //Reload config so the new config values are read - All changes are saved to disk on disable
return enable_disable(sender, plugin, component, true, permanent); return enable_disable(sender, plugin, component, true);
} }
@Subcommand(helpText = { @Subcommand
"Disable component", public boolean disable(CommandSender sender, Plugin plugin, String component) {
"Temporarily or permanently disables a component." return enable_disable(sender, plugin, component, false);
})
public boolean disable(CommandSender sender, Plugin plugin, String component, @Command2.OptionalArg boolean permanent) {
return enable_disable(sender, plugin, component, false, permanent);
} }
@Subcommand(helpText = { @Subcommand
"List components",
"Lists all of the registered Chroma components"
})
public boolean list(CommandSender sender, @Command2.OptionalArg String plugin) { public boolean list(CommandSender sender, @Command2.OptionalArg String plugin) {
sender.sendMessage("§6List of components:"); sender.sendMessage("§6List of components:");
Component.getComponents().values().stream().filter(c -> plugin == null || c.getPlugin().getName().equalsIgnoreCase(plugin)) //If plugin is null, don't check Component.getComponents().values().stream().filter(c -> plugin == null || c.getPlugin().getName().equalsIgnoreCase(plugin)) //If plugin is null, don't check
@ -62,41 +45,25 @@ public class ComponentCommand extends ICommand2MC {
return true; return true;
} }
@CustomTabCompleteMethod(param = "component", subcommand = {"enable", "disable"}) private boolean enable_disable(CommandSender sender, Plugin plugin, String component, boolean enable) {
public Iterable<String> componentTabcomplete(Plugin plugin) {
return getPluginComponents(plugin).map(c -> c.getClass().getSimpleName())::iterator;
}
@CustomTabCompleteMethod(param = "plugin", subcommand = {"list", "enable", "disable"}, ignoreTypeCompletion = true)
public Iterable<String> pluginTabcomplete() {
return Component.getComponents().values().stream().map(Component::getPlugin)
.distinct().map(Plugin::getName)::iterator;
}
private boolean enable_disable(CommandSender sender, Plugin plugin, String component, boolean enable, boolean permanent) {
try { try {
val oc = getComponentOrError(plugin, component, sender); val oc = getComponentOrError(plugin, component, sender);
if (!oc.isPresent()) if (!oc.isPresent())
return true; return true;
Component.setComponentEnabled(oc.get(), enable); Component.setComponentEnabled(oc.get(), enable);
if (permanent) sender.sendMessage(oc.get().getClass().getSimpleName() + " " + (enable ? "en" : "dis") + "abled.");
oc.get().shouldBeEnabled.set(enable);
sender.sendMessage(oc.get().getClass().getSimpleName() + " " + (enable ? "en" : "dis") + "abled " + (permanent ? "permanently" : "temporarily") + ".");
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Couldn't " + (enable ? "en" : "dis") + "able component " + component + "!", e, (JavaPlugin) plugin); TBMCCoreAPI.SendException("Couldn't " + (enable ? "en" : "dis") + "able component " + component + "!", e);
} }
return true; return true;
} }
private Stream<Component<? extends JavaPlugin>> getPluginComponents(Plugin plugin) {
return Component.getComponents().values().stream()
.filter(c -> plugin.getName().equals(c.getPlugin().getName()));
}
private Optional<Component<?>> getComponentOrError(Plugin plugin, String arg, CommandSender sender) { private Optional<Component<?>> getComponentOrError(Plugin plugin, String arg, CommandSender sender) {
val oc = getPluginComponents(plugin).filter(c -> c.getClass().getSimpleName().equalsIgnoreCase(arg)).findAny(); val oc = Component.getComponents().values().stream()
.filter(c -> plugin.getName().equals(c.getPlugin().getName()))
.filter(c -> c.getClass().getSimpleName().equalsIgnoreCase(arg)).findAny();
if (!oc.isPresent()) if (!oc.isPresent())
sender.sendMessage("§cComponent not found!"); //^ Much simpler to solve in the new command system sender.sendMessage("§cComponent not found!"); //^ Much simpler to solve in the new command system
return oc; return oc;
} } //TODO: Tabcompletion for the new command system
} }

View file

@ -21,11 +21,11 @@ public final class ComponentManager {
*/ */
public static void enableComponents() { public static void enableComponents() {
//Component.getComponents().values().stream().filter(c->cs.getConfigurationSection(c.getClass().getSimpleName()).getBoolean("enabled")).forEach(c-> { //Component.getComponents().values().stream().filter(c->cs.getConfigurationSection(c.getClass().getSimpleName()).getBoolean("enabled")).forEach(c-> {
Component.getComponents().values().stream().filter(c -> c.shouldBeEnabled.get()).forEach(c -> { Component.getComponents().values().stream().filter(c -> c.shouldBeEnabled().get()).forEach(c -> {
try { try {
Component.setComponentEnabled(c, true); Component.setComponentEnabled(c, true);
} catch (Exception | NoClassDefFoundError e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Failed to enable one of the components: " + c.getClass().getSimpleName(), e, c); TBMCCoreAPI.SendException("Failed to enable one of the components: " + c.getClass().getSimpleName(), e);
} }
}); });
componentsEnabled = true; componentsEnabled = true;
@ -38,7 +38,7 @@ public final class ComponentManager {
public static <T extends ButtonPlugin> void unregComponents(T plugin) { public static <T extends ButtonPlugin> void unregComponents(T plugin) {
while (!plugin.getComponentStack().empty()) //Unregister in reverse order while (!plugin.getComponentStack().empty()) //Unregister in reverse order
Component.unregisterComponent(plugin, (Component<T>) plugin.getComponentStack().pop()); //Components are pushed on register Component.unregisterComponent(plugin, (Component<T>) plugin.getComponentStack().pop()); //Components are pushed on register
//componentsEnabled = false; - continue enabling new components after a plugin gets disabled componentsEnabled = false;
} }
/** /**

View file

@ -6,8 +6,10 @@ import buttondevteam.core.component.channel.ChatRoom;
import buttondevteam.core.component.members.MemberComponent; import buttondevteam.core.component.members.MemberComponent;
import buttondevteam.core.component.randomtp.RandomTPComponent; import buttondevteam.core.component.randomtp.RandomTPComponent;
import buttondevteam.core.component.restart.RestartComponent; import buttondevteam.core.component.restart.RestartComponent;
import buttondevteam.core.component.spawn.SpawnComponent;
import buttondevteam.core.component.towny.TownyComponent; import buttondevteam.core.component.towny.TownyComponent;
import buttondevteam.core.component.updater.PluginUpdater;
import buttondevteam.core.component.updater.PluginUpdaterComponent;
import buttondevteam.core.component.votifier.VotifierComponent;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.ButtonPlugin; import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
@ -23,12 +25,10 @@ import lombok.Setter;
import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.permission.Permission; import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.BlockCommandSender; import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.RegisteredServiceProvider;
@ -37,16 +37,16 @@ import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays; import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.function.Supplier;
import java.util.logging.Logger; import java.util.logging.Logger;
public class MainPlugin extends ButtonPlugin { public class MainPlugin extends ButtonPlugin {
public static MainPlugin Instance; public static MainPlugin Instance;
public static Permission permission; public static Permission permission;
@Nullable public static boolean Test;
public static Essentials ess; public static Essentials ess;
private Logger logger; private Logger logger;
@ -60,58 +60,46 @@ public class MainPlugin extends ButtonPlugin {
@Setter @Setter
private boolean chatHandlerEnabled = true; private boolean chatHandlerEnabled = true;
/** private ConfigData<Boolean> writePluginList() {
* Sets whether the plugin should write a list of installed plugins in a txt file. return getIConfig().getData("writePluginList", false);
* It can be useful if some other software needs to know the plugins. }
*/
private final ConfigData<Boolean> writePluginList = getIConfig().getData("writePluginList", false);
/** ConfigData<String> chatFormat() {
* The chat format to use for messages from other platforms if Chroma-Chat is not installed. return getIConfig().getData("chatFormat", "[{origin}|" +
*/
ConfigData<String> chatFormat = getIConfig().getData("chatFormat", "[{origin}|" +
"{channel}] <{name}> {message}"); "{channel}] <{name}> {message}");
}
/**
* Print some debug information.
*/
public final ConfigData<Boolean> test = getIConfig().getData("test", false);
/**
* If a Chroma command clashes with another plugin's command, this setting determines whether the Chroma command should be executed or the other plugin's.
*/
public final ConfigData<Boolean> prioritizeCustomCommands = getIConfig().getData("prioritizeCustomCommands", false);
@Override @Override
public void pluginEnable() { public void pluginEnable() {
// Logs "Plugin Enabled", registers commands
Instance = this; Instance = this;
PluginDescriptionFile pdf = getDescription(); PluginDescriptionFile pdf = getDescription();
logger = getLogger(); logger = getLogger();
if (!setupPermissions()) if (!setupPermissions())
throw new NullPointerException("No permission plugin found!"); throw new NullPointerException("No permission plugin found!");
if (!setupEconomy()) //Though Essentials always provides economy, but we don't require Essentials if (!setupEconomy()) //Though Essentials always provides economy so this shouldn't happen
getLogger().warning("No economy plugin found! Components using economy will not be registered."); getLogger().warning("No economy plugin found! Components using economy will not be registered.");
Test = getConfig().getBoolean("test", true);
saveConfig(); saveConfig();
Component.registerComponent(this, new PluginUpdaterComponent());
Component.registerComponent(this, new RestartComponent()); Component.registerComponent(this, new RestartComponent());
//noinspection unchecked - needed for testing
Component.registerComponent(this, new ChannelComponent()); Component.registerComponent(this, new ChannelComponent());
Component.registerComponent(this, new RandomTPComponent()); Component.registerComponent(this, new RandomTPComponent());
Component.registerComponent(this, new MemberComponent()); Component.registerComponent(this, new MemberComponent());
if (Bukkit.getPluginManager().isPluginEnabled("Multiverse-Core"))
Component.registerComponent(this, new SpawnComponent());
if (Bukkit.getPluginManager().isPluginEnabled("Towny")) //It fails to load the component class otherwise if (Bukkit.getPluginManager().isPluginEnabled("Towny")) //It fails to load the component class otherwise
Component.registerComponent(this, new TownyComponent()); Component.registerComponent(this, new TownyComponent());
/*if (Bukkit.getPluginManager().isPluginEnabled("Votifier") && economy != null) if (Bukkit.getPluginManager().isPluginEnabled("Votifier") && economy != null)
Component.registerComponent(this, new VotifierComponent(economy));*/ Component.registerComponent(this, new VotifierComponent(economy));
ComponentManager.enableComponents(); ComponentManager.enableComponents();
registerCommand(new ComponentCommand()); getCommand2MC().registerCommand(new ComponentCommand());
registerCommand(new ChromaCommand()); getCommand2MC().registerCommand(new ThorpeCommand());
TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this); TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this);
TBMCCoreAPI.RegisterEventsForExceptions(getCommand2MC(), this);
ChromaGamerBase.addConverter(commandSender -> Optional.ofNullable(commandSender instanceof ConsoleCommandSender || commandSender instanceof BlockCommandSender ChromaGamerBase.addConverter(commandSender -> Optional.ofNullable(commandSender instanceof ConsoleCommandSender || commandSender instanceof BlockCommandSender
? TBMCPlayer.getPlayer(new UUID(0, 0), TBMCPlayer.class) : null)); //Console & cmdblocks ? TBMCPlayer.getPlayer(new UUID(0, 0), TBMCPlayer.class) : null)); //Console & cmdblocks
ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof Player ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof Player
? TBMCPlayer.getPlayer(((Player) sender).getUniqueId(), TBMCPlayer.class) : null)); //Players, has higher priority ? TBMCPlayer.getPlayer(((Player) sender).getUniqueId(), TBMCPlayer.class) : null)); //Players, has higher priority
TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase.class, TBMCPlayer::new); TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase.class);
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null)); //The /ooc ID has moved to the config TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null)); //The /ooc ID has moved to the config
TBMCChatAPI.RegisterChatChannel( TBMCChatAPI.RegisterChatChannel(
Channel.AdminChat = new Channel("§cADMIN§f", Color.Red, "a", Channel.inGroupFilter(null))); Channel.AdminChat = new Channel("§cADMIN§f", Color.Red, "a", Channel.inGroupFilter(null)));
@ -124,26 +112,37 @@ public class MainPlugin extends ButtonPlugin {
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§aGREEN§f", Color.Green, "green")); TBMCChatAPI.RegisterChatChannel(new ChatRoom("§aGREEN§f", Color.Green, "green"));
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§bBLUE§f", Color.Blue, "blue")); TBMCChatAPI.RegisterChatChannel(new ChatRoom("§bBLUE§f", Color.Blue, "blue"));
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§5PURPLE§f", Color.DarkPurple, "purple")); TBMCChatAPI.RegisterChatChannel(new ChatRoom("§5PURPLE§f", Color.DarkPurple, "purple"));
Supplier<Iterable<String>> playerSupplier = () -> Bukkit.getOnlinePlayers().stream().map(HumanEntity::getName)::iterator; if (writePluginList().get()) {
getCommand2MC().addParamConverter(OfflinePlayer.class, Bukkit::getOfflinePlayer, "Player not found!", playerSupplier);
getCommand2MC().addParamConverter(Player.class, Bukkit::getPlayer, "Online player not found!", playerSupplier);
if (writePluginList.get()) {
try { try {
Files.write(new File("plugins", "plugins.txt").toPath(), Arrays.stream(Bukkit.getPluginManager().getPlugins()).map(p -> (CharSequence) p.getDataFolder().getName())::iterator); Files.write(new File("plugins", "plugins.txt").toPath(), Arrays.stream(Bukkit.getPluginManager().getPlugins()).map(p -> (CharSequence) p.getDataFolder().getName())::iterator);
} catch (IOException e) { } catch (IOException e) {
TBMCCoreAPI.SendException("Failed to write plugin list!", e, this); TBMCCoreAPI.SendException("Failed to write plugin list!", e);
} }
} }
if (getServer().getPluginManager().isPluginEnabled("Essentials"))
ess = Essentials.getPlugin(Essentials.class); ess = Essentials.getPlugin(Essentials.class);
logger.info(pdf.getName() + " has been Enabled (V." + pdf.getVersion() + ") Test: " + test.get() + "."); logger.info(pdf.getName() + " has been Enabled (V." + pdf.getVersion() + ") Test: " + Test + ".");
} }
@Override @Override
public void pluginDisable() { public void pluginDisable() {
logger.info("Saving player data..."); logger.info("Saving player data...");
ChromaGamerBase.saveUsers(); TBMCPlayerBase.savePlayers();
logger.info("Player data saved."); logger.info("Player data saved.");
new Thread(() -> {
File[] files = PluginUpdater.updatedir.listFiles();
if (files == null)
return;
logger.info("Updating " + files.length + " plugins...");
for (File file : files) {
try {
Files.move(file.toPath(), new File("plugins", file.getName()).toPath(), StandardCopyOption.REPLACE_EXISTING);
logger.info("Updated " + file.getName());
} catch (IOException e) {
e.printStackTrace();
}
}
logger.info("Update complete!");
}).start();
} }
private boolean setupPermissions() { private boolean setupPermissions() {

View file

@ -5,7 +5,6 @@ import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.chat.ChatMessage; import buttondevteam.lib.chat.ChatMessage;
import buttondevteam.lib.chat.Command2MCSender; import buttondevteam.lib.chat.Command2MCSender;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer; import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerBase; import buttondevteam.lib.player.TBMCPlayerBase;
import lombok.val; import lombok.val;
@ -28,21 +27,12 @@ public class PlayerListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL) @EventHandler(priority = EventPriority.NORMAL)
public void OnPlayerJoin(PlayerJoinEvent event) { public void OnPlayerJoin(PlayerJoinEvent event) {
var p = event.getPlayer(); TBMCPlayerBase.joinPlayer(event.getPlayer());
TBMCPlayer player = TBMCPlayerBase.getPlayer(p.getUniqueId(), TBMCPlayer.class);
String pname = player.PlayerName.get();
if (pname.length() == 0) {
player.PlayerName.set(p.getName());
MainPlugin.Instance.getLogger().info("Player name saved: " + player.PlayerName.get());
} else if (!p.getName().equals(pname)) {
MainPlugin.Instance.getLogger().info(pname + " renamed to " + p.getName());
player.PlayerName.set(p.getName());
}
} }
@EventHandler(priority = EventPriority.NORMAL) @EventHandler(priority = EventPriority.NORMAL)
public void OnPlayerLeave(PlayerQuitEvent event) { public void OnPlayerLeave(PlayerQuitEvent event) {
TBMCPlayerBase.getPlayer(event.getPlayer().getUniqueId(), TBMCPlayer.class).uncache(); TBMCPlayerBase.quitPlayer(event.getPlayer());
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
@ -52,7 +42,7 @@ public class PlayerListener implements Listener {
if (Arrays.stream(event.getExceptions()).anyMatch("Minecraft"::equalsIgnoreCase)) if (Arrays.stream(event.getExceptions()).anyMatch("Minecraft"::equalsIgnoreCase))
return; return;
Bukkit.getOnlinePlayers().stream().filter(event::shouldSendTo) Bukkit.getOnlinePlayers().stream().filter(event::shouldSendTo)
.forEach(p -> p.sendMessage(event.getChannel().DisplayName.get().substring(0, 2) + event.getMessage())); .forEach(p -> p.sendMessage(event.getChannel().DisplayName().get().substring(0, 2) + event.getMessage()));
} }
@EventHandler @EventHandler
@ -68,9 +58,7 @@ public class PlayerListener implements Listener {
private void handlePreprocess(CommandSender sender, String message, Cancellable event) { private void handlePreprocess(CommandSender sender, String message, Cancellable event) {
if (event.isCancelled()) return; if (event.isCancelled()) return;
val cg = ChromaGamerBase.getFromSender(sender); val ev = new TBMCCommandPreprocessEvent(sender, message);
if (cg == null) throw new RuntimeException("Couldn't get user from sender for " + sender.getName() + "!");
val ev = new TBMCCommandPreprocessEvent(sender, cg.channel.get(), message, sender);
Bukkit.getPluginManager().callEvent(ev); Bukkit.getPluginManager().callEvent(ev);
if (ev.isCancelled()) if (ev.isCancelled())
event.setCancelled(true); //Cancel the original event event.setCancelled(true); //Cancel the original event
@ -80,9 +68,9 @@ public class PlayerListener implements Listener {
public void onTBMCPreprocess(TBMCCommandPreprocessEvent event) { public void onTBMCPreprocess(TBMCCommandPreprocessEvent event) {
if (event.isCancelled()) return; if (event.isCancelled()) return;
try { try {
event.setCancelled(ButtonPlugin.getCommand2MC().handleCommand(new Command2MCSender(event.getSender(), event.getChannel(), event.getPermCheck()), event.getMessage())); event.setCancelled(ButtonPlugin.getCommand2MC().handleCommand(new Command2MCSender(event.getSender()), event.getMessage()));
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Command processing failed for sender '" + event.getSender() + "' and message '" + event.getMessage() + "'", e, MainPlugin.Instance); TBMCCoreAPI.SendException("Command processing failed for sender '" + event.getSender() + "' and message '" + event.getMessage() + "'", e);
} }
} }
@ -102,12 +90,11 @@ public class PlayerListener implements Listener {
return; return;
if (!MainPlugin.Instance.isChatHandlerEnabled()) return; if (!MainPlugin.Instance.isChatHandlerEnabled()) return;
if (event.getOrigin().equals("Minecraft")) return; //Let other plugins handle MC messages if (event.getOrigin().equals("Minecraft")) return; //Let other plugins handle MC messages
var channel = event.getChannel(); String msg = MainPlugin.Instance.chatFormat().get()
String msg = MainPlugin.Instance.chatFormat.get() .replace("{channel}", event.getChannel().DisplayName().get())
.replace("{channel}", channel.DisplayName.get())
.replace("{origin}", event.getOrigin().substring(0, 1)) .replace("{origin}", event.getOrigin().substring(0, 1))
.replace("{name}", ChromaUtils.getDisplayName(event.getSender())) .replace("{name}", ThorpeUtils.getDisplayName(event.getSender()))
.replace("{message}", String.format("§%x%s", channel.Color.get().ordinal(), event.getMessage())); .replace("{message}", event.getMessage());
for (Player player : Bukkit.getOnlinePlayers()) for (Player player : Bukkit.getOnlinePlayers())
if (event.shouldSendTo(player)) if (event.shouldSendTo(player))
player.sendMessage(msg); player.sendMessage(msg);

View file

@ -2,7 +2,6 @@ package buttondevteam.core;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.core.component.channel.ChannelComponent; import buttondevteam.core.component.channel.ChannelComponent;
import buttondevteam.lib.ChromaUtils;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.chat.Color; import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
@ -20,9 +19,7 @@ import java.util.Collections;
import java.util.logging.Logger; import java.util.logging.Logger;
public class TestPrepare { public class TestPrepare {
public static void PrepareServer() { public static void PrepareServer() {
ChromaUtils.setTest(); //Needs to be in a separate class because of the potential lack of Mockito
Bukkit.setServer(Mockito.mock(Server.class, new Answer<Object>() { Bukkit.setServer(Mockito.mock(Server.class, new Answer<Object>() {
@Override @Override
@ -44,6 +41,7 @@ public class TestPrepare {
return cl.isAssignableFrom(invocation.getMethod().getReturnType()); return cl.isAssignableFrom(invocation.getMethod().getReturnType());
} }
})); }));
//noinspection unchecked
Component.registerComponent(Mockito.mock(JavaPlugin.class), new ChannelComponent()); Component.registerComponent(Mockito.mock(JavaPlugin.class), new ChannelComponent());
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null)); TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null));
} }

View file

@ -0,0 +1,17 @@
package buttondevteam.core;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import org.bukkit.command.CommandSender;
@CommandClass
public class ThorpeCommand extends ICommand2MC {
@Command2.Subcommand //TODO: Main permissions (groups) like 'mod'
public void reload(CommandSender sender) {
if (MainPlugin.Instance.tryReloadConfig())
sender.sendMessage("§bConfig reloaded.");
else
sender.sendMessage("§cFailed to reload config. Check console.");
}
}

View file

@ -0,0 +1,231 @@
package buttondevteam.core.component.channel;
import buttondevteam.core.ComponentManager;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.chat.Color;
import com.google.common.collect.Lists;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Channel {
/**
* Specifies a score that means it's OK to send - but it does not define any groups, only send or not send. See {@link #GROUP_EVERYONE}
*/
public static final int SCORE_SEND_OK = 0;
/**
* Specifies a score that means the user doesn't have permission to see or send the message. Any negative value has the same effect.
*/
public static final int SCORE_SEND_NOPE = -1;
/**
* Send the message to everyone <i>who has access to the channel</i> - this does not necessarily mean all players
*/
public static final String GROUP_EVERYONE = "everyone";
private static ChannelComponent component;
private String defDisplayName;
private Color defColor;
private void throwGame() {
if (component == null) throw new RuntimeException("Cannot access channel properties until registered!");
}
public final ConfigData<Boolean> Enabled() {
throwGame();
return component.getConfig().getData(ID + ".enabled", true);
}
/**
* Must start with a color code
*/
public final ConfigData<String> DisplayName() {
throwGame();
return component.getConfig().getData(ID + ".displayName", defDisplayName);
}
public final ConfigData<Color> Color() {
throwGame();
return component.getConfig().getData(ID + ".color", defColor, c -> Color.valueOf((String) c), Enum::toString);
}
public final String ID;
@SuppressWarnings("unchecked")
public ConfigData<String[]> IDs() {
throwGame();
return component.getConfig().getData(ID + ".IDs", new String[0], l -> ((List<String>) l).toArray(new String[0]), Lists::newArrayList);
}
/**
* Filters both the sender and the targets
*/
private final Function<CommandSender, RecipientTestResult> filteranderrormsg;
private static final List<Channel> channels = new ArrayList<>();
/**
* Creates a channel.
*
* @param displayname The name that should appear at the start of the message. <b>A chat color is expected at the beginning (§9).</b>
* @param color The default color of the messages sent in the channel
* @param command The command to be used for the channel <i>without /</i>. For example "mod". It's also used for scoreboard objective names.
* @param filteranderrormsg Checks all senders against the criteria provided here and sends the message if the index matches the sender's - if no score at all, displays the error.<br>
* May be null to send to everyone.
*/
public Channel(String displayname, Color color, String command,
Function<CommandSender, RecipientTestResult> filteranderrormsg) {
defDisplayName = displayname;
defColor = color;
ID = command;
this.filteranderrormsg = filteranderrormsg;
}
/**
* Must be only called from a subclass - otherwise it'll throw an exception.
*
* @see Channel#Channel(String, Color, String, Function)
*/
@SuppressWarnings("unchecked")
protected <T extends Channel> Channel(String displayname, Color color, String command,
BiFunction<T, CommandSender, RecipientTestResult> filteranderrormsg) {
defDisplayName = displayname;
defColor = color;
ID = command;
this.filteranderrormsg = s -> filteranderrormsg.apply((T) this, s);
}
public boolean isGlobal() {
return filteranderrormsg == null;
}
/**
* Note: Errors are sent to the sender automatically
*
* @param sender The user we're sending to
* @param score The (source) score to compare with the user's
*/
public boolean shouldSendTo(CommandSender sender, int score) {
return score == getMCScore(sender); //If there's any error, the score won't be equal
}
/**
* Note: Errors are sent to the sender automatically
*/
public int getMCScore(CommandSender sender) {
return getRTR(sender).score; //No need to check if there was an error
}
/**
* Note: Errors are sent to the sender automatically<br>
* <p>
* Null means don't send
*/
@Nullable
public String getGroupID(CommandSender sender) {
return getRTR(sender).groupID; //No need to check if there was an error
}
public RecipientTestResult getRTR(CommandSender sender) {
if (filteranderrormsg == null)
return new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE);
return filteranderrormsg.apply(sender);
}
/**
* Get a stream of the enabled channels
*
* @return Only the enabled channels
*/
public static Stream<Channel> getChannels() {
return channels.stream().filter(ch -> ch.Enabled().get());
}
/**
* Return all channels whether they're enabled or not
*
* @return A list of all channels
*/
public static List<Channel> getChannelList() {
return Collections.unmodifiableList(channels);
}
/**
* Convenience method for the function parameter of {@link #Channel(String, Color, String, Function)}. It checks if the sender is OP or optionally has the specified group. The error message is
* generated automatically.
*
* @param permgroup The group that can access the channel or <b>null</b> to only allow OPs.
* @return If has access
*/
public static Function<CommandSender, RecipientTestResult> inGroupFilter(String permgroup) {
return noScoreResult(
s -> s.isOp() || (permgroup != null && (s instanceof Player && MainPlugin.permission != null && MainPlugin.permission.playerInGroup((Player) s, permgroup))),
"You need to be a(n) " + (permgroup != null ? permgroup : "OP") + " to use this channel.");
}
public static Function<CommandSender, RecipientTestResult> noScoreResult(Predicate<CommandSender> filter,
String errormsg) {
return s -> filter.test(s) ? new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE) : new RecipientTestResult(errormsg);
}
public static <T extends Channel> BiFunction<T, CommandSender, RecipientTestResult> noScoreResult(
BiPredicate<T, CommandSender> filter, String errormsg) {
return (this_, s) -> filter.test(this_, s) ? new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE) : new RecipientTestResult(errormsg);
}
public static Channel GlobalChat;
public static Channel AdminChat;
public static Channel ModChat;
public static void RegisterChannel(Channel channel) {
if (!channel.isGlobal() && !ComponentManager.isEnabled(ChannelComponent.class))
return; //Allow registering the global chat (and I guess other chats like the RP chat)
if (component == null)
component = (ChannelComponent) Component.getComponents().get(ChannelComponent.class);
if (component == null)
throw new RuntimeException("Attempting to register a channel before the component is registered!");
channels.add(channel);
Bukkit.getScheduler().runTask(MainPlugin.Instance, () -> Bukkit.getPluginManager().callEvent(new ChatChannelRegisterEvent(channel))); // Wait for server start
}
public static class RecipientTestResult {
public final String errormessage;
public final int score; // Anything below 0 is "never send"
public final String groupID;
public static final RecipientTestResult ALL = new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE);
/**
* Creates a result that indicates an <b>error</b>
*
* @param errormessage The error message to show the sender if they don't meet the criteria.
*/
public RecipientTestResult(String errormessage) {
this.errormessage = errormessage;
this.score = SCORE_SEND_NOPE;
this.groupID = null;
}
/**
* Creates a result that indicates a <b>success</b>
*
* @param score The score that identifies the target group. <b>Must be non-negative.</b> For example, the index of the town or nation to send to.
* @param groupID The ID of the target group.
*/
public RecipientTestResult(int score, String groupID) {
if (score < 0) throw new IllegalArgumentException("Score must be non-negative!");
this.score = score;
this.groupID = groupID;
this.errormessage = null;
}
}
}

View file

@ -0,0 +1,34 @@
package buttondevteam.core.component.channel;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.architecture.Component;
import org.bukkit.plugin.java.JavaPlugin;
/**
* Manages chat channels. If disabled, only global channels will be registered.
*/
public class ChannelComponent extends Component {
static TBMCSystemChatEvent.BroadcastTarget roomJoinLeave;
@Override
protected void register(JavaPlugin plugin) {
super.register(plugin);
roomJoinLeave = TBMCSystemChatEvent.BroadcastTarget.add("roomJoinLeave"); //Even if it's disabled, global channels continue to work
}
@Override
protected void unregister(JavaPlugin plugin) {
super.unregister(plugin);
TBMCSystemChatEvent.BroadcastTarget.remove(roomJoinLeave);
roomJoinLeave = null;
}
@Override
protected void enable() {
}
@Override
protected void disable() {
}
}

View file

@ -7,11 +7,8 @@ import buttondevteam.lib.chat.ICommand2MC;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.concurrent.TimeUnit; @CommandClass(modOnly = true, path = "member", helpText = { //
@CommandClass(path = "member", helpText = { //
"Member command", // "Member command", //
"Add or remove server members.", // "Add or remove server members.", //
}) })
@ -19,15 +16,16 @@ public class MemberCommand extends ICommand2MC {
private final MemberComponent component; private final MemberComponent component;
public MemberCommand(MemberComponent component) { public MemberCommand(MemberComponent component) {
getManager().addParamConverter(OfflinePlayer.class, Bukkit::getOfflinePlayer, "Player not found!");
this.component = component; this.component = component;
} }
@Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP) @Command2.Subcommand
public boolean add(CommandSender sender, OfflinePlayer player) { public boolean add(CommandSender sender, OfflinePlayer player) {
return addRemove(sender, player, true); return addRemove(sender, player, true);
} }
@Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP) @Command2.Subcommand
public boolean remove(CommandSender sender, OfflinePlayer player) { public boolean remove(CommandSender sender, OfflinePlayer player) {
return addRemove(sender, player, false); return addRemove(sender, player, false);
} }
@ -38,35 +36,12 @@ public class MemberCommand extends ICommand2MC {
sender.sendMessage("§cCannot find player or haven't played before."); sender.sendMessage("§cCannot find player or haven't played before.");
return; return;
} }
if (add ? MainPlugin.permission.playerAddGroup(null, op, component.memberGroup.get()) if (add ? MainPlugin.permission.playerAddGroup(null, op, component.memberGroup().get())
: MainPlugin.permission.playerRemoveGroup(null, op, component.memberGroup.get())) : MainPlugin.permission.playerRemoveGroup(null, op, component.memberGroup().get()))
sender.sendMessage("§b" + op.getName() + " " + (add ? "added" : "removed") + " as a member!"); sender.sendMessage("§b" + op.getName() + " " + (add ? "added" : "removed") + " as a member!");
else else
sender.sendMessage("§cFailed to " + (add ? "add" : "remove") + " " + op.getName() + " as a member!"); sender.sendMessage("§cFailed to " + (add ? "add" : "remove") + " " + op.getName() + " as a member!");
}); });
return true; return true;
} }
@Command2.Subcommand
public void def(Player player) {
String msg;
if (!component.checkNotMember(player))
msg = "You are a member.";
else {
double pt = component.getPlayTime(player);
long rt = component.getRegTime(player);
if (pt == -1 || rt == -1) {
Boolean result = component.addPlayerAsMember(player);
if (result == null)
msg = "Can't assign member group because groups are not supported by the permissions plugin.";
else if (result)
msg = "You meet all the requirements.";
else
msg = "You should be a member but failed to add you to the group.";
} else
msg = String.format("You need to play for %.2f hours total or play for %d more days to become a member.",
pt, TimeUnit.MILLISECONDS.toDays(rt));
}
player.sendMessage(msg);
}
} }

View file

@ -0,0 +1,62 @@
package buttondevteam.core.component.members;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import org.bukkit.Statistic;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import static buttondevteam.core.MainPlugin.permission;
/**
* Allows giving a 'member' group over some time elapsed OR played.
*/
public class MemberComponent extends Component<MainPlugin> implements Listener {
/**
* The permission group to give to the player
*/
ConfigData<String> memberGroup() {
return getConfig().getData("memberGroup", "member");
}
/**
* The amount of hours needed to play before promotion
*/
private ConfigData<Integer> playedHours() {
return getConfig().getData("playedHours", 12);
}
/**
* The amount of days passed since first login
*/
private ConfigData<Integer> registeredForDays() {
return getConfig().getData("registeredForDays", 7);
}
@Override
protected void enable() {
registerListener(this);
registerCommand(new MemberCommand(this));
}
@Override
protected void disable() {
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
if (permission != null && !permission.playerInGroup(event.getPlayer(), memberGroup().get())
&& (new Date(event.getPlayer().getFirstPlayed()).toInstant().plus(registeredForDays().get(), ChronoUnit.DAYS).isBefore(Instant.now())
|| event.getPlayer().getStatistic(Statistic.PLAY_ONE_TICK) > 20 * 3600 * playedHours().get())) {
permission.playerAddGroup(null, event.getPlayer(), memberGroup().get());
event.getPlayer().sendMessage("§bYou are a member now. YEEHAW");
MainPlugin.Instance.getLogger().info("Added " + event.getPlayer().getName() + " as a member.");
}
}
}

View file

@ -1,20 +1,16 @@
package buttondevteam.core.component.randomtp; package buttondevteam.core.component.randomtp;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC; import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.chat.TBMCCommandBase;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.logging.Logger;
// @formatter:off // @formatter:off
@SuppressWarnings("FieldCanBeLocal")@CommandClass(helpText = { @SuppressWarnings("FieldCanBeLocal")@CommandClass
"§6---- Random Teleport ----", public class RandomTP extends TBMCCommandBase
"Teleport player to random location within world border. Every five players teleport to the same general area, and then a new general area is randomly selected for the next five players."
})
public class RandomTP extends ICommand2MC
{ {
private final int radius = 70; //set how far apart the five teleport positions are private final int radius = 70; //set how far apart the five teleport positions are
@ -55,22 +51,33 @@ public class RandomTP extends ICommand2MC
/*================================================================================================*/ /*================================================================================================*/
public void onEnable(RandomTPComponent component) public void onEnable(Component component)
{ {
TBMCChatAPI.AddCommand(component, this);
world = Bukkit.getWorld("World"); world = Bukkit.getWorld("World");
border = world.getWorldBorder(); border = world.getWorldBorder();
component.log("Getting new location"); newLocation();
if(border.getSize() > 100000)
component.logWarn("World border is wide, it may take a minute...");
component.log("Success: "+newLocation());
} }
/*================================================================================================*/ /*================================================================================================*/
@Command2.Subcommand public String[] GetHelpText(String alias)
public boolean def(CommandSender sender, Player player)
{ {
if (sender.isOp()) return rtp(player); return new String[]
{
"§6---- Random Teleport ----",
"Teleport player to random location within world border. Every five players teleport to the same general area, and then a new general area is randomly selected for the next five players."
};
}
/*================================================================================================*/
public boolean OnCommand(CommandSender sender, String command, String[] args)
{
if (args.length == 0) return false;
if (sender.isOp()) return rtp(Bukkit.getPlayer(args[0]));
else sender.sendMessage("§7 hmm, " + sender.getName() + "... " + sender.getName() + "... nope, no operator permissions."); else sender.sendMessage("§7 hmm, " + sender.getName() + "... " + sender.getName() + "... nope, no operator permissions.");

View file

@ -2,20 +2,15 @@ package buttondevteam.core.component.randomtp;
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ComponentMetadata;
/** /**
* Teleport player to random location within world border. * Teleport player to random location within world border.
* Every five players teleport to the same general area, and then a new general area is randomly selected for the next five players. * Every five players teleport to the same general area, and then a new general area is randomly selected for the next five players.
* Author: github.com/iiegit
*/ */
@ComponentMetadata(enabledByDefault = false)
public class RandomTPComponent extends Component<MainPlugin> { public class RandomTPComponent extends Component<MainPlugin> {
@Override @Override
protected void enable() { protected void enable() {
var rtp = new RandomTP(); new RandomTP().onEnable(this); //It registers it's command
registerCommand(rtp);
rtp.onEnable(this);
} }
@Override @Override

View file

@ -0,0 +1,48 @@
package buttondevteam.core.component.restart;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.chat.TBMCCommandBase;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
@CommandClass(path = "primerestart", modOnly = true)
@RequiredArgsConstructor
public class PrimeRestartCommand extends TBMCCommandBase {
private final RestartComponent component;
@Override
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
loud = args.length > 0;
if (Bukkit.getOnlinePlayers().size() > 0) {
sender.sendMessage("§bPlayers online, restart delayed.");
if (loud)
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", component.getRestartBroadcast());
plsrestart = true;
} else {
sender.sendMessage("§bNobody is online. Restarting now.");
if (loud)
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online. Restarting server.", component.getRestartBroadcast());
Bukkit.spigot().restart();
}
return true;
}
@Getter
private static boolean plsrestart = false;
@Getter
private static boolean loud = false;
@Override
public String[] GetHelpText(String alias) {
return new String[]{ //
"§6---- Prime restart ----", //
"Restarts the server as soon as nobody is online.", //
"To be loud, type something after, like /primerestart lol (it doesn't matter what you write)", //
"To be silent, don't type anything" //
};
}
}

View file

@ -4,8 +4,6 @@ import buttondevteam.core.MainPlugin;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.chat.IFakePlayer; import buttondevteam.lib.chat.IFakePlayer;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
import lombok.Getter; import lombok.Getter;
@ -15,29 +13,17 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
/** /**
* Provides commands such as /schrestart (restart after a countdown) and /primerestart (restart when nobody is online). * Provides commands such as /schrestart (restart after a countdown) and /primerestart (restart when nobody is online)
* Also can automatically restart at a given time.
*/ */
@ComponentMetadata(enabledByDefault = false)
public class RestartComponent extends Component<MainPlugin> implements Listener { public class RestartComponent extends Component<MainPlugin> implements Listener {
@Override @Override
public void enable() { public void enable() {
var scheduledRestartCommand = new ScheduledRestartCommand(this); //TODO: Permissions for the commands
registerCommand(scheduledRestartCommand); registerCommand(new ScheduledRestartCommand(this));
registerCommand(new PrimeRestartCommand(this)); TBMCChatAPI.AddCommand(this, new PrimeRestartCommand(this));
registerListener(this); registerListener(this);
restartBroadcast = TBMCSystemChatEvent.BroadcastTarget.add("restartCountdown"); restartBroadcast = TBMCSystemChatEvent.BroadcastTarget.add("restartCountdown");
int restartAt = this.restartAt.get();
if (restartAt < 0) return;
int restart = syncStart(restartAt);
log("Scheduled restart " + (restart / 3600. / 20.) + " hours from now");
Bukkit.getScheduler().runTaskLater(getPlugin(), () -> scheduledRestartCommand.def(Bukkit.getConsoleSender(), 0), restart);
} }
@Override @Override
@ -45,28 +31,10 @@ public class RestartComponent extends Component<MainPlugin> implements Listener
TBMCSystemChatEvent.BroadcastTarget.remove(restartBroadcast); TBMCSystemChatEvent.BroadcastTarget.remove(restartBroadcast);
} }
/**
* Specifies the hour of day when the server should be restarted. Set to -1 to disable.
*/
private final ConfigData<Integer> restartAt = getConfig().getData("restartAt", 12);
private long lasttime = 0; private long lasttime = 0;
@Getter @Getter
private TBMCSystemChatEvent.BroadcastTarget restartBroadcast; private TBMCSystemChatEvent.BroadcastTarget restartBroadcast;
private int syncStart(int hour) {
var now = ZonedDateTime.now(ZoneId.ofOffset("", ZoneOffset.UTC));
int secs = now.getHour() * 3600 + now.getMinute() * 60 + now.getSecond();
int diff = secs - hour * 3600;
if (diff < 0) {
diff += 24 * 3600;
}
int count = diff / (24 * 3600);
int intervalPart = diff - count * 24 * 3600;
int remaining = 24 * 3600 - intervalPart;
return remaining * 20;
}
@EventHandler @EventHandler
public void onPlayerLeave(PlayerQuitEvent event) { public void onPlayerLeave(PlayerQuitEvent event) {
if (PrimeRestartCommand.isPlsrestart() if (PrimeRestartCommand.isPlsrestart()
@ -76,7 +44,7 @@ public class RestartComponent extends Component<MainPlugin> implements Listener
if (PrimeRestartCommand.isLoud()) if (PrimeRestartCommand.isLoud())
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online anymore. Restarting.", restartBroadcast); TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§cNobody is online anymore. Restarting.", restartBroadcast);
Bukkit.spigot().restart(); Bukkit.spigot().restart();
} else if (!(event.getPlayer() instanceof IFakePlayer) && System.nanoTime() - 10 * 60 * 1000000000L - lasttime > 0) { //10 minutes passed since last reminder } else if (!(event.getPlayer() instanceof IFakePlayer) && System.nanoTime() - 10 * 1000000000L - lasttime > 0) { //Ten seconds passed since last reminder
lasttime = System.nanoTime(); lasttime = System.nanoTime();
if (PrimeRestartCommand.isLoud()) if (PrimeRestartCommand.isLoud())
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", restartBroadcast); TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, ChatColor.DARK_RED + "The server will restart as soon as nobody is online.", restartBroadcast);

View file

@ -18,8 +18,6 @@ import org.bukkit.boss.BossBar;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import javax.annotation.Nonnull;
@CommandClass(modOnly = true, path = "schrestart", helpText = { @CommandClass(modOnly = true, path = "schrestart", helpText = {
"Scheduled restart", // "Scheduled restart", //
"This command restarts the server 1 minute after it's executed, warning players every 10 seconds.", // "This command restarts the server 1 minute after it's executed, warning players every 10 seconds.", //
@ -33,7 +31,6 @@ public class ScheduledRestartCommand extends ICommand2MC {
private BukkitTask restarttask; private BukkitTask restarttask;
private volatile BossBar restartbar; private volatile BossBar restartbar;
@Getter @Getter
@Nonnull
private final RestartComponent component; private final RestartComponent component;
@Command2.Subcommand @Command2.Subcommand
@ -57,8 +54,8 @@ public class ScheduledRestartCommand extends ICommand2MC {
restartbar.getPlayers().forEach(p -> restartbar.removePlayer(p)); restartbar.getPlayers().forEach(p -> restartbar.removePlayer(p));
Bukkit.spigot().restart(); Bukkit.spigot().restart();
} }
if (restartCounter % 200 == 0 && Bukkit.getOnlinePlayers().size() > 0) if (restartCounter % 200 == 0)
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§c-- The server is restarting in " + restartCounter / 20 + " seconds!", component.getRestartBroadcast()); TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§c-- The server is restarting in " + restartCounter / 20 + " seconds! (/press)", component.getRestartBroadcast());
restartbar.setProgress(restartCounter / (double) restarttime); restartbar.setProgress(restartCounter / (double) restarttime);
restartbar.setTitle(String.format("Server restart in %.2f", restartCounter / 20f)); restartbar.setTitle(String.format("Server restart in %.2f", restartCounter / 20f));
restartCounter--; restartCounter--;

View file

@ -0,0 +1,51 @@
package buttondevteam.core.component.towny;
import buttondevteam.core.ComponentManager;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import com.palmergames.bukkit.towny.Towny;
import com.palmergames.bukkit.towny.exceptions.AlreadyRegisteredException;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.TownyUniverse;
import org.bukkit.Bukkit;
/**
* Automatically renames Towny players if they changed their Minecraft name
*/
public class TownyComponent extends Component<MainPlugin> {
@Override
protected void enable() {
}
@Override
protected void disable() {
}
/**
* Only renames the resident if this component is enabled. Used to handle name changes.
*
* @param oldName The player's old name as known by us
* @param newName The player's new name
*/
public static void renameInTowny(String oldName, String newName) {
if (!ComponentManager.isEnabled(TownyComponent.class))
return; TownyUniverse tu = Towny.getPlugin(Towny.class).getTownyUniverse();
Resident resident = tu.getResidentMap().get(oldName.toLowerCase()); //The map keys are lowercase
if (resident == null) {
Bukkit.getLogger().warning("Resident not found - couldn't rename in Towny.");
TBMCCoreAPI.sendDebugMessage("Resident not found - couldn't rename in Towny.");
} else if (tu.getResidentMap().contains(newName.toLowerCase())) {
Bukkit.getLogger().warning("Target resident name is already in use."); // TODO: Handle
TBMCCoreAPI.sendDebugMessage("Target resident name is already in use.");
} else
try {
TownyUniverse.getDataSource().renamePlayer(resident, newName); //Fixed in Towny 0.91.1.2
} catch (AlreadyRegisteredException e) {
TBMCCoreAPI.SendException("Failed to rename resident, there's already one with this name.", e);
} catch (NotRegisteredException e) {
TBMCCoreAPI.SendException("Failed to rename resident, the resident isn't registered.", e);
}
}
}

View file

@ -0,0 +1,195 @@
package buttondevteam.core.component.updater;
import buttondevteam.lib.TBMCCoreAPI;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.io.FileUtils;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class PluginUpdater {
private PluginUpdater() {
}
public static final File updatedir = new File("TBMC", "pluginupdates");
/**
* See {@link TBMCCoreAPI#UpdatePlugin(String, CommandSender, String)}
*/
public static boolean UpdatePlugin(String name, CommandSender sender, String branch) {
if (!updatedir.exists() && !updatedir.mkdirs()) {
error(sender, "Failed to create update directory!");
return false;
}
info(sender, "Checking plugin name...");
List<String> plugins = GetPluginNames();
String correctname = null;
for (String plugin : plugins) {
if (plugin.equalsIgnoreCase(name)) {
correctname = plugin; // Fixes capitalization
break;
}
}
if (correctname == null) {
error(sender, "Can't find plugin: " + name);
return false;
}
info(sender, "Checking branch name...");
if (!TBMCCoreAPI.IsTestServer() && !branch.equalsIgnoreCase("master")) {
error(sender, "The server is in production mode, updating only allowed from master!");
return false;
}
Optional<String> correctbranch = GetPluginBranches(correctname).stream().filter(b -> b.equalsIgnoreCase(branch))
.findAny();
if (!correctbranch.isPresent()) {
error(sender, "Can't find branch \"" + branch + "\" for plugin \"" + correctname + "\"");
return false;
}
if (isNotMaven(correctname, correctbranch.get())) {
error(sender, "The plugin doesn't appear to have a pom.xml. Make sure it's a Maven project.");
return false;
}
info(sender, "Updating TBMC plugin: " + correctname + " from " + correctbranch.get());
return updatePluginJitPack(sender, correctname, correctbranch.get());
}
private static boolean updatePluginJitPack(CommandSender sender, String correctname,
String correctbranch) {
URL url;
File result = new File(updatedir, correctname + ".jar");
try {
url = new URL("https://jitpack.io/com/github/TBMCPlugins/"
+ (correctname.equals("ButtonCore") ? "ButtonCore/ButtonCore" : correctname) + "/"
+ correctbranch + "-SNAPSHOT/" + correctname + "-" + correctbranch + "-SNAPSHOT.jar"); // ButtonCore exception required since it hosts Towny as well
FileUtils.copyURLToFile(url, result);
if (!result.exists() || result.length() < 25) {
result.delete();
error(sender, "The downloaded JAR for " + correctname + " from " + correctbranch
+ " is too small (smnaller than 25 bytes). Am I downloading from the right place?");
return false;
} else {
info(sender, "Updating plugin " + correctname + " from " + correctbranch + " done!");
return true;
}
} catch (FileNotFoundException e) {
error(sender,
"Can't find JAR for " + correctname + " from " + correctbranch
+ ", the build probably failed. Build log (scroll to bottom):" + "\n"
+ "https://jitpack.io/com/github/TBMCPlugins/" + correctname + "/" + correctbranch
+ "-SNAPSHOT/build.log\nIf you'd like to rebuild the same commit, go to:\nhttps://jitpack.io/#TBMCPlugins/"
+ correctname + "\nAnd delete the newest build.");
} catch (IOException e) {
error(sender, "IO error while updating " + correctname + "\n" + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
error(sender, "Unknown error while updating " + correctname + ": " + e);
}
return false;
}
/**
* Checks if pom.xml is not present for the project.
*
* @param pluginname
* Does not have to match case
* @param branch
* Does not have to match case
*/
public static boolean isNotMaven(String pluginname, String branch) {
try {
return TBMCCoreAPI
.DownloadString(
"https://raw.githubusercontent.com/TBMCPlugins/" + pluginname + "/" + branch + "/pom.xml")
.equals("404: Not Found\n");
} catch (IOException e1) {
return true;
}
}
private static void error(CommandSender sender, String message) {
if (!sender.equals(Bukkit.getConsoleSender()))
Bukkit.getLogger().warning(message);
sender.sendMessage("§c" + message);
}
private static void info(CommandSender sender, String message) {
if (!sender.equals(Bukkit.getConsoleSender()))
Bukkit.getLogger().info(message);
sender.sendMessage("§b" + message);
}
/**
* Retrieves all the repository names from the GitHub organization.
*
* @return A list of names
*/
public static List<String> GetPluginNames() {
List<String> ret = new ArrayList<>();
try {
String resp = TBMCCoreAPI.DownloadString("https://api.github.com/orgs/" + "TBMCPlugins" + "/repos"); //TODO: PluginUpdater
JsonArray arr = new JsonParser().parse(resp).getAsJsonArray();
for (JsonElement obj : arr) {
JsonObject jobj = obj.getAsJsonObject();
ret.add(jobj.get("name").getAsString());
}
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
/**
* Retrieves all the branches from the plugin repository.
*
* @return A list of names
*/
public static List<String> GetPluginBranches(String plugin) {
List<String> ret = new ArrayList<>();
try {
String resp = TBMCCoreAPI
.DownloadString("https://api.github.com/repos/TBMCPlugins/" + plugin + "/branches");
JsonArray arr = new JsonParser().parse(resp).getAsJsonArray();
for (JsonElement obj : arr) {
JsonObject jobj = obj.getAsJsonObject();
ret.add(jobj.get("name").getAsString());
}
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
public static class UpdatedEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final JsonObject data;
public UpdatedEvent(JsonObject data) {
this.data = data;
}
public JsonObject getData() {
return data;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}
}

View file

@ -0,0 +1,20 @@
package buttondevteam.core.component.updater;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.chat.TBMCChatAPI;
/**
* Downloads plugin updates built from their source using JitPack - older code
*/
public class PluginUpdaterComponent extends Component<MainPlugin> { //TODO: Config
@Override
public void enable() {
TBMCChatAPI.AddCommand(this, new UpdatePluginCommand());
}
@Override
public void disable() { //Commands are automatically unregistered
}
}

View file

@ -0,0 +1,41 @@
package buttondevteam.core.component.updater;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.TBMCCommandBase;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
@CommandClass(modOnly = true)
public class UpdatePluginCommand extends TBMCCommandBase {
@Override
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> {
if (args.length == 0) {
sender.sendMessage("Downloading plugin names...");
boolean first = true;
for (String plugin : PluginUpdater.GetPluginNames()) {
if (first) {
sender.sendMessage("§6---- Plugin names ----");
first = false;
}
sender.sendMessage("- " + plugin);
}
} else {
TBMCCoreAPI.UpdatePlugin(args[0], sender, args.length == 1 ? "master" : args[1]);
}
});
return true;
}
@Override
public String[] GetHelpText(String alias) {
return new String[] { //
"§6---- Update plugin ----", //
"This command downloads the latest version of a TBMC plugin from GitHub", //
"To update a plugin: /" + alias + " <plugin>", //
"To list the plugin names: /" + alias //
};
}
}

View file

@ -0,0 +1,49 @@
package buttondevteam.core.component.votifier;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import com.vexsoftware.votifier.model.Vote;
import com.vexsoftware.votifier.model.VotifierEvent;
import lombok.RequiredArgsConstructor;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
/**
* Do not use (EULA)
*/
@RequiredArgsConstructor
public class VotifierComponent extends Component<MainPlugin> {
private final Economy economy;
private ConfigData<Double> rewardAmount() {
return getConfig().getData("rewardAmount", 0.0);
}
@Override
protected void enable() {
}
@Override
protected void disable() {
}
@EventHandler
@SuppressWarnings("deprecation")
public void onVotifierEvent(VotifierEvent event) {
Vote vote = event.getVote();
getPlugin().getLogger().info("Vote: " + vote);
org.bukkit.OfflinePlayer op = Bukkit.getOfflinePlayer(vote.getUsername());
Player p = Bukkit.getPlayer(vote.getUsername());
if (op != null) {
economy.depositPlayer(op, rewardAmount().get());
}
if (p != null) {
p.sendMessage("§bThanks for voting! $50 was added to your account.");
}
}
}

View file

@ -1,13 +1,12 @@
package buttondevteam.lib; package buttondevteam.lib;
import org.bukkit.Bukkit;
import org.bukkit.event.Event; import org.bukkit.event.Event;
class EventExceptionCoreHandler extends EventExceptionHandler { class EventExceptionCoreHandler extends EventExceptionHandler {
@Override @Override
public boolean handle(Throwable ex, Event event) { public boolean handle(Throwable ex, Event event) {
TBMCCoreAPI.SendException("An error occured while executing " + event.getEventName() + "!", ex, false, Bukkit.getLogger()::warning); TBMCCoreAPI.SendException("An error occured while executing " + event.getEventName() + "!", ex);
return true; return true;
} }

View file

@ -30,6 +30,7 @@ public class TBMCChatEvent extends TBMCChatEventBase {
private boolean isIgnoreSenderPermissions() { private boolean isIgnoreSenderPermissions() {
return cm.getPermCheck() != cm.getSender(); return cm.getPermCheck() != cm.getSender();
} }
// TODO: Message object with data?
/** /**
* This will allow the sender of the message if {@link #isIgnoreSenderPermissions()} is true. * This will allow the sender of the message if {@link #isIgnoreSenderPermissions()} is true.

View file

@ -3,6 +3,7 @@ package buttondevteam.lib;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
@ -11,6 +12,7 @@ import org.bukkit.event.Event;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@Getter @Getter
@RequiredArgsConstructor
public abstract class TBMCChatEventBase extends Event implements Cancellable { public abstract class TBMCChatEventBase extends Event implements Cancellable {
private final Channel channel; private final Channel channel;
private @NonNull String message; private @NonNull String message;
@ -24,15 +26,6 @@ public abstract class TBMCChatEventBase extends Event implements Cancellable {
*/ */
private final String groupID; private final String groupID;
@java.beans.ConstructorProperties({"channel", "message", "score", "groupID"})
public TBMCChatEventBase(Channel channel, String message, int score, String groupID) {
super(true);
this.channel = channel;
this.message = message;
this.score = score;
this.groupID = groupID;
}
/** /**
* Note: Errors are sent to the sender automatically * Note: Errors are sent to the sender automatically
*/ */
@ -54,6 +47,6 @@ public abstract class TBMCChatEventBase extends Event implements Cancellable {
*/ */
@Nullable @Nullable
public String getGroupID(CommandSender sender) { public String getGroupID(CommandSender sender) {
return channel.getGroupID(sender); return channel.getGroupID(sender); //TODO: Performance-wise it'd be much better to use serialization for player data - it's only converted once
} }
} }

View file

@ -27,12 +27,15 @@ public class TBMCChatPreprocessEvent extends Event implements Cancellable {
private boolean cancelled; private boolean cancelled;
public TBMCChatPreprocessEvent(CommandSender sender, Channel channel, String message) { public TBMCChatPreprocessEvent(CommandSender sender, Channel channel, String message) {
super(true);
this.sender = sender; this.sender = sender;
this.channel = channel; this.channel = channel;
this.message = message; this.message = message; // TODO: Message object with data?
} }
/*
* public TBMCPlayer getPlayer() { return TBMCPlayer.getPlayer(sender); // TODO: Get Chroma user }
*/
@Override @Override
public HandlerList getHandlers() { public HandlerList getHandlers() {
return handlers; return handlers;

View file

@ -1,8 +1,6 @@
package buttondevteam.lib; package buttondevteam.lib;
import buttondevteam.core.component.channel.Channel;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
@ -16,18 +14,20 @@ import org.bukkit.event.HandlerList;
* @author NorbiPeti * @author NorbiPeti
*/ */
@Getter @Getter
@RequiredArgsConstructor
public class TBMCCommandPreprocessEvent extends Event implements Cancellable { public class TBMCCommandPreprocessEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
private final CommandSender sender; private final CommandSender sender;
private final Channel channel;
@Setter @Setter
private final String message; private String message;
private final CommandSender permCheck;
@Setter @Setter
private boolean cancelled; private boolean cancelled;
public TBMCCommandPreprocessEvent(CommandSender sender, String message) {
this.sender = sender;
this.message = message;
}
@Override @Override
public HandlerList getHandlers() { public HandlerList getHandlers() {
return handlers; return handlers;

View file

@ -1,28 +1,29 @@
package buttondevteam.lib; package buttondevteam.lib;
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin;
import buttondevteam.lib.architecture.Component; import buttondevteam.core.component.updater.PluginUpdater;
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.potato.DebugPotato; import buttondevteam.lib.potato.DebugPotato;
import org.apache.commons.io.IOUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class TBMCCoreAPI { public class TBMCCoreAPI {
static final List<String> coders = new ArrayList<String>() { static final List<String> coders = new ArrayList<String>() {
private static final long serialVersionUID = -4462159250738367334L; private static final long serialVersionUID = -4462159250738367334L;
{ {
add("Alisolarflare"); add("Alisolarflare");
add("NorbiPeti"); add("NorbiPeti");
@ -32,6 +33,33 @@ public class TBMCCoreAPI {
} }
}; };
/**
* Updates or installs the specified plugin. The plugin must use Maven.
*
* @param name
* The plugin's repository name.
* @param sender
* The command sender (if not console, messages will be printed to console as well).
*/
public static void UpdatePlugin(String name, CommandSender sender) {
UpdatePlugin(name, sender, "master");
}
/**
* Updates or installs the specified plugin from the specified branch. The plugin must use Maven.
*
* @param name
* The plugin's repository name.
* @param sender
* The command sender (if not console, messages will be printed to console as well).
* @param branch
* The branch to download the plugin from.
* @return Success or not
*/
public static boolean UpdatePlugin(String name, CommandSender sender, String branch) {
return PluginUpdater.UpdatePlugin(name, sender, branch);
}
public static String DownloadString(String urlstr) throws IOException { public static String DownloadString(String urlstr) throws IOException {
URL url = new URL(urlstr); URL url = new URL(urlstr);
URLConnection con = url.openConnection(); URLConnection con = url.openConnection();
@ -39,8 +67,7 @@ public class TBMCCoreAPI {
InputStream in = con.getInputStream(); InputStream in = con.getInputStream();
String encoding = con.getContentEncoding(); String encoding = con.getContentEncoding();
encoding = encoding == null ? "UTF-8" : encoding; encoding = encoding == null ? "UTF-8" : encoding;
Scanner s = new Scanner(in).useDelimiter("\\A"); String body = IOUtils.toString(in, encoding);
String body = s.hasNext() ? s.next() : "";
in.close(); in.close();
return body; return body;
} }
@ -51,25 +78,16 @@ public class TBMCCoreAPI {
/** /**
* Send exception to the {@link TBMCExceptionEvent}. * Send exception to the {@link TBMCExceptionEvent}.
* *
* @param sourcemsg A message that is shown at the top of the exception (before the exception's message) * @param sourcemsg
* @param e The exception to send * A message that is shown at the top of the exception (before the exception's message)
* @param e
* The exception to send
*/ */
public static void SendException(String sourcemsg, Throwable e, Component<?> component) { public static void SendException(String sourcemsg, Throwable e) {
SendException(sourcemsg, e, false, component::logWarn); SendException(sourcemsg, e, false);
} }
/** public static void SendException(String sourcemsg, Throwable e, boolean debugPotato) {
* Send exception to the {@link TBMCExceptionEvent}.
*
* @param sourcemsg A message that is shown at the top of the exception (before the exception's message)
* @param e The exception to send
*/
public static void SendException(String sourcemsg, Throwable e, JavaPlugin plugin) {
SendException(sourcemsg, e, false, plugin.getLogger()::warning);
}
public static void SendException(String sourcemsg, Throwable e, boolean debugPotato, Consumer<String> logWarn) {
try {
SendUnsentExceptions(); SendUnsentExceptions();
TBMCExceptionEvent event = new TBMCExceptionEvent(sourcemsg, e); TBMCExceptionEvent event = new TBMCExceptionEvent(sourcemsg, e);
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
@ -77,10 +95,10 @@ public class TBMCCoreAPI {
if (!event.isHandled()) if (!event.isHandled())
exceptionsToSend.put(sourcemsg, e); exceptionsToSend.put(sourcemsg, e);
} }
logWarn.accept(sourcemsg); Bukkit.getLogger().warning(sourcemsg);
e.printStackTrace(); e.printStackTrace();
if (debugPotato) { if (debugPotato) {
List<Player> devsOnline = new ArrayList<>(); List<Player> devsOnline = new ArrayList<Player>();
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
if (coders.contains(player.getName())) { if (coders.contains(player.getName())) {
devsOnline.add(player); devsOnline.add(player);
@ -88,10 +106,10 @@ public class TBMCCoreAPI {
} }
if (!devsOnline.isEmpty()) { if (!devsOnline.isEmpty()) {
DebugPotato potato = new DebugPotato() DebugPotato potato = new DebugPotato()
.setMessage(new String[]{ // .setMessage(new String[] { //
"§b§o" + e.getClass().getSimpleName(), // "§b§o" + e.getClass().getSimpleName(), //
"§c§o" + sourcemsg, // "§c§o" + sourcemsg, //
"§a§oFind a dev to fix this issue"}) "§a§oFind a dev to fix this issue" })
.setType(e instanceof IOException ? "Throwable Potato" .setType(e instanceof IOException ? "Throwable Potato"
: e instanceof ClassCastException ? "Squished Potato" : e instanceof ClassCastException ? "Squished Potato"
: e instanceof NullPointerException ? "Plain Potato" : e instanceof NullPointerException ? "Plain Potato"
@ -101,10 +119,6 @@ public class TBMCCoreAPI {
} }
} }
} }
} catch (Exception ee) {
System.err.println("Failed to send exception!");
ee.printStackTrace();
}
} }
public static void sendDebugMessage(String debugMessage) { public static void sendDebugMessage(String debugMessage) {
@ -117,21 +131,20 @@ public class TBMCCoreAPI {
} }
} }
private static EventExceptionCoreHandler eventExceptionCoreHandler;
/** /**
* Registers Bukkit events, handling the exceptions occurring in those events * Registers Bukkit events, handling the exceptions occurring in those events
* *
* @param listener The class that handles the events * @param listener
* @param plugin The plugin which the listener belongs to * The class that handles the events
* @param plugin
* The plugin which the listener belongs to
*/ */
public static void RegisterEventsForExceptions(Listener listener, Plugin plugin) { public static void RegisterEventsForExceptions(Listener listener, Plugin plugin) {
if (eventExceptionCoreHandler == null) eventExceptionCoreHandler = new EventExceptionCoreHandler(); EventExceptionHandler.registerEvents(listener, plugin, new EventExceptionCoreHandler());
EventExceptionHandler.registerEvents(listener, plugin, eventExceptionCoreHandler);
} }
public static <T extends ChromaGamerBase> void RegisterUserClass(Class<T> userclass, Supplier<T> constructor) { public static <T extends ChromaGamerBase> void RegisterUserClass(Class<T> userclass) {
ChromaGamerBase.RegisterPluginUserClass(userclass, constructor); ChromaGamerBase.RegisterPluginUserClass(userclass);
} }
/** /**
@ -170,7 +183,6 @@ public class TBMCCoreAPI {
} }
public static boolean IsTestServer() { public static boolean IsTestServer() {
if (MainPlugin.Instance == null) return true; return MainPlugin.Test;
return MainPlugin.Instance.test.get();
} }
} }

View file

@ -1,6 +1,5 @@
package buttondevteam.lib; package buttondevteam.lib;
import org.bukkit.Bukkit;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@ -10,7 +9,6 @@ public class TBMCDebugMessageEvent extends Event {
private boolean sent; private boolean sent;
public TBMCDebugMessageEvent(String message) { public TBMCDebugMessageEvent(String message) {
super(!Bukkit.isPrimaryThread());
this.message = message; this.message = message;
} }

View file

@ -1,7 +1,7 @@
package buttondevteam.lib; package buttondevteam.lib;
import lombok.Getter; import lombok.Getter;
import org.bukkit.Bukkit; import lombok.RequiredArgsConstructor;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@ -14,6 +14,7 @@ import org.bukkit.event.HandlerList;
* *
*/ */
@Getter @Getter
@RequiredArgsConstructor
public class TBMCExceptionEvent extends Event { public class TBMCExceptionEvent extends Event {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
@ -21,13 +22,6 @@ public class TBMCExceptionEvent extends Event {
private final Throwable exception; private final Throwable exception;
private boolean handled; private boolean handled;
@java.beans.ConstructorProperties({"sourceMessage", "exception"})
public TBMCExceptionEvent(String sourceMessage, Throwable exception) {
super(!Bukkit.isPrimaryThread());
this.sourceMessage = sourceMessage;
this.exception = exception;
}
public void setHandled() { public void setHandled() {
handled = true; handled = true;
} }

View file

@ -1,16 +1,10 @@
package buttondevteam.lib; package buttondevteam.lib;
import buttondevteam.core.MainPlugin;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import java.util.function.Supplier; public final class ThorpeUtils {
private ThorpeUtils() {}
public final class ChromaUtils {
private ChromaUtils() {}
public static String getDisplayName(CommandSender sender) { public static String getDisplayName(CommandSender sender) {
if (sender instanceof IHaveFancyName) if (sender instanceof IHaveFancyName)
@ -37,7 +31,7 @@ public final class ChromaUtils {
/** /**
* May return null. * May return null.
* *
* @return The full name that can be used to uniquely identify the user. * @return The full name that can be used to uniquely indentify the user.
*/ */
String getFancyFullName(); String getFancyFullName();
} }
@ -57,45 +51,4 @@ public final class ChromaUtils {
return number.doubleValue(); return number.doubleValue();
return number; return number;
} }
/**
* Calls the event always asynchronously. The return value is always false if async.
*
* @param event The event to call
* @return The event cancelled state or false if async.
*/
public static <T extends Event & Cancellable> boolean callEventAsync(T event) {
Supplier<Boolean> task = () -> {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
};
return doItAsync(task, false);
}
/**
* Does something always asynchronously. It will execute in the same thread if it's not the server thread.
*
* @param what What to do
* @param def Default if async
* @return The value supplied by the action or def if async.
*/
public static <T> T doItAsync(Supplier<T> what, T def) {
if (Bukkit.isPrimaryThread())
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, what::get);
else
return what.get();
return def;
}
private static boolean test = false;
/**
* Returns true while unit testing.
*/
public static boolean isTest() { return test; }
/**
* Call when unit testing.
*/
public static void setTest() { test = true; }
} }

View file

@ -0,0 +1,93 @@
package buttondevteam.lib.architecture;
import buttondevteam.core.ComponentManager;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2MC;
import buttondevteam.lib.chat.TBMCChatAPI;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.var;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Stack;
@HasConfig
public abstract class ButtonPlugin extends JavaPlugin {
@Getter
private static Command2MC command2MC = new Command2MC();
@Getter(AccessLevel.PROTECTED)
private IHaveConfig iConfig;
@Getter(AccessLevel.PROTECTED)
private IHaveConfig data; //TODO
private boolean loaded = false;
/**
* Used to unregister components in the right order - and to reload configs
*/
@Getter
private Stack<Component<?>> componentStack = new Stack<>();
protected abstract void pluginEnable();
/**
* Called after the components are unregistered
*/
protected abstract void pluginDisable();
/**
* Called before the components are unregistered
*/
protected void pluginPreDisable() {
}
@Override
public final void onEnable() {
loadConfig();
try {
pluginEnable();
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while enabling plugin " + getName() + "!", e);
}
}
private void loadConfig() {
var section = super.getConfig().getConfigurationSection("global");
if (section == null) section = super.getConfig().createSection("global");
iConfig = new IHaveConfig(section, this::saveConfig);
}
@Override
public final void onDisable() {
try {
pluginPreDisable();
ComponentManager.unregComponents(this);
pluginDisable();
saveConfig();
iConfig = null; //Clearing the hashmap is not enough, we need to update the section as well
TBMCChatAPI.RemoveCommands(this);
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e);
}
}
@Override
public void reloadConfig() {
tryReloadConfig();
}
public boolean tryReloadConfig() {
if (!justReload()) return false;
loadConfig();
componentStack.forEach(c -> Component.updateConfig(this, c));
return true;
}
public boolean justReload() {
if (loaded && ConfigData.saveNow(getConfig())) {
getLogger().warning("Saved pending configuration changes to the file, didn't reload (try again).");
return false;
}
super.reloadConfig();
loaded = true; //Needed because for the first time it uses reloadConfig() to load it
return true;
}
}

View file

@ -1,12 +1,14 @@
package buttondevteam.lib.architecture; package buttondevteam.lib.architecture;
import buttondevteam.buttonproc.HasConfig;
import buttondevteam.core.ComponentManager; import buttondevteam.core.ComponentManager;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException; import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException;
import buttondevteam.lib.chat.ICommand2MC; import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.chat.TBMCCommandBase;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.experimental.var;
import lombok.val; import lombok.val;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -15,27 +17,29 @@ import org.bukkit.plugin.java.JavaPlugin;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* Configuration is based on class name * Configuration is based on class name
*/ */
@HasConfig(global = false) //Used for obtaining javadoc @HasConfig //Used for obtaining javadoc
public abstract class Component<TP extends JavaPlugin> { public abstract class Component<TP extends JavaPlugin> {
@SuppressWarnings("rawtypes") private static HashMap<Class<? extends Component>, Component<? extends JavaPlugin>> components = new HashMap<>(); private static HashMap<Class<? extends Component>, Component<? extends JavaPlugin>> components = new HashMap<>();
@Getter @Getter
private boolean enabled = false; private boolean enabled = false;
@Getter @Getter
@NonNull @NonNull
private TP plugin; private TP plugin;
private @Getter final IHaveConfig config = new IHaveConfig(null); @NonNull
private @Getter
IHaveConfig config;
private @Getter IHaveConfig data; //TODO private @Getter IHaveConfig data; //TODO
public final ConfigData<Boolean> shouldBeEnabled = config.getData("enabled", public final ConfigData<Boolean> shouldBeEnabled() {
Optional.ofNullable(getClass().getAnnotation(ComponentMetadata.class)).map(ComponentMetadata::enabledByDefault).orElse(true)); return config.getData("enabled", true);
}
/** /**
* Registers a component checking it's dependencies and calling {@link #register(JavaPlugin)}.<br> * Registers a component checking it's dependencies and calling {@link #register(JavaPlugin)}.<br>
@ -66,7 +70,7 @@ public abstract class Component<TP extends JavaPlugin> {
try { try {
val metaAnn = component.getClass().getAnnotation(ComponentMetadata.class); val metaAnn = component.getClass().getAnnotation(ComponentMetadata.class);
if (metaAnn != null) { if (metaAnn != null) {
@SuppressWarnings("rawtypes") Class<? extends Component>[] dependencies = metaAnn.depends(); Class<? extends Component>[] dependencies = metaAnn.depends();
for (val dep : dependencies) { //TODO: Support dependencies at enable/disable as well for (val dep : dependencies) { //TODO: Support dependencies at enable/disable as well
if (!components.containsKey(dep)) { if (!components.containsKey(dep)) {
plugin.getLogger().warning("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + " as a required dependency is missing/disabled: " + dep.getSimpleName()); plugin.getLogger().warning("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + " as a required dependency is missing/disabled: " + dep.getSimpleName());
@ -76,25 +80,25 @@ public abstract class Component<TP extends JavaPlugin> {
} }
if (register) { if (register) {
if (components.containsKey(component.getClass())) { if (components.containsKey(component.getClass())) {
TBMCCoreAPI.SendException("Failed to register component " + component.getClassName(), new IllegalArgumentException("The component is already registered!"), plugin); TBMCCoreAPI.SendException("Failed to register component " + component.getClassName(), new IllegalArgumentException("The component is already registered!"));
return false; return false;
} }
component.plugin = plugin; component.plugin = plugin;
component.config.setSaveAction(plugin::saveConfig);
updateConfig(plugin, component); updateConfig(plugin, component);
component.register(plugin); component.register(plugin);
components.put(component.getClass(), component); components.put(component.getClass(), component);
if (plugin instanceof ButtonPlugin) if (plugin instanceof ButtonPlugin)
((ButtonPlugin) plugin).getComponentStack().push(component); ((ButtonPlugin) plugin).getComponentStack().push(component);
if (ComponentManager.areComponentsEnabled() && component.shouldBeEnabled.get()) { if (ComponentManager.areComponentsEnabled() && component.shouldBeEnabled().get()) {
try { //Enable components registered after the previous ones getting enabled try { //Enable components registered after the previous ones getting enabled
setComponentEnabled(component, true); setComponentEnabled(component, true);
return true; return true;
} catch (Exception | NoClassDefFoundError e) { } catch (Exception | NoClassDefFoundError e) {
TBMCCoreAPI.SendException("Failed to enable component " + component.getClassName() + "!", e, component); TBMCCoreAPI.SendException("Failed to enable component " + component.getClassName() + "!", e);
return true; return true;
} }
} }
return true; //Component shouldn't be enabled
} else { } else {
if (!components.containsKey(component.getClass())) if (!components.containsKey(component.getClass()))
return true; //Already unregistered return true; //Already unregistered
@ -102,65 +106,50 @@ public abstract class Component<TP extends JavaPlugin> {
try { try {
setComponentEnabled(component, false); setComponentEnabled(component, false);
} catch (Exception | NoClassDefFoundError e) { } catch (Exception | NoClassDefFoundError e) {
TBMCCoreAPI.SendException("Failed to disable component " + component.getClassName() + "!", e, component); TBMCCoreAPI.SendException("Failed to disable component " + component.getClassName() + "!", e);
return false; //If failed to disable, won't unregister either return false; //If failed to disable, won't unregister either
} }
} }
component.unregister(plugin); component.unregister(plugin);
components.remove(component.getClass()); components.remove(component.getClass());
}
return true; return true;
}
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + "!", e, plugin); TBMCCoreAPI.SendException("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + "!", e);
return false; return false;
} }
} }
/** /**
* Enables or disables the given component. If the component fails to enable, it will be disabled. * Registers a component checking it's dependencies and calling {@link #register(JavaPlugin)}.<br>
* Make sure to register the dependencies first.
* *
* @param component The component to register * @param component The component to register
* @param enabled Whether it's enabled or not
*/ */
public static void setComponentEnabled(Component<?> component, boolean enabled) throws UnregisteredComponentException { public static void setComponentEnabled(Component component, boolean enabled) throws UnregisteredComponentException {
if (!components.containsKey(component.getClass())) if (!components.containsKey(component.getClass()))
throw new UnregisteredComponentException(component); throw new UnregisteredComponentException(component);
if (component.enabled == enabled) return; //Don't do anything if (component.enabled == enabled) return; //Don't do anything
if (component.enabled = enabled) { if (component.enabled = enabled) {
try {
updateConfig(component.getPlugin(), component); updateConfig(component.getPlugin(), component);
component.enable(); component.enable();
if (ButtonPlugin.configGenAllowed(component)) {
IHaveConfig.pregenConfig(component, null);
}
} catch (Exception e) {
try { //Automatically disable components that fail to enable properly
setComponentEnabled(component, false);
throw e;
} catch (Exception ex) {
Throwable t = ex;
for (var th = t; th != null; th = th.getCause())
t = th; //Set if not null
if (t != e)
t.initCause(e);
throw ex;
}
}
} else { } else {
component.disable(); component.disable();
ButtonPlugin.getCommand2MC().unregisterCommands(component); component.plugin.saveConfig();
TBMCChatAPI.RemoveCommands(component);
} }
} }
public static void updateConfig(JavaPlugin plugin, Component<?> component) { public static void updateConfig(JavaPlugin plugin, Component component) {
if (plugin.getConfig() != null) { //Production if (plugin.getConfig() != null) { //Production
var compconf = plugin.getConfig().getConfigurationSection("components"); var compconf = plugin.getConfig().getConfigurationSection("components");
if (compconf == null) compconf = plugin.getConfig().createSection("components"); if (compconf == null) compconf = plugin.getConfig().createSection("components");
var configSect = compconf.getConfigurationSection(component.getClassName()); var configSect = compconf.getConfigurationSection(component.getClassName());
if (configSect == null) if (configSect == null)
configSect = compconf.createSection(component.getClassName()); configSect = compconf.createSection(component.getClassName());
component.config.reset(configSect); component.config = new IHaveConfig(configSect, plugin::saveConfig);
} //Testing: it's already set } else //Testing
component.config = new IHaveConfig(null, plugin::saveConfig);
} }
/** /**
@ -168,26 +157,17 @@ public abstract class Component<TP extends JavaPlugin> {
* *
* @return The currently registered components * @return The currently registered components
*/ */
@SuppressWarnings("rawtypes")
public static Map<Class<? extends Component>, Component<? extends JavaPlugin>> getComponents() { public static Map<Class<? extends Component>, Component<? extends JavaPlugin>> getComponents() {
return Collections.unmodifiableMap(components); return Collections.unmodifiableMap(components);
} }
public void log(String message) {
plugin.getLogger().info("[" + getClassName() + "] " + message);
}
public void logWarn(String message) {
plugin.getLogger().warning("[" + getClassName() + "] " + message);
}
/** /**
* Registers the module, when called by the JavaPlugin class. * Registers the module, when called by the JavaPlugin class.
* This gets fired when the plugin is enabled. Use {@link #enable()} to register commands and such. * This gets fired when the plugin is enabled. Use {@link #enable()} to register commands and such.
* *
* @param plugin Plugin object * @param plugin Plugin object
*/ */
@SuppressWarnings({"unused"}) @SuppressWarnings({"unused", "WeakerAccess"})
protected void register(JavaPlugin plugin) { protected void register(JavaPlugin plugin) {
} }
@ -198,7 +178,7 @@ public abstract class Component<TP extends JavaPlugin> {
* *
* @param plugin Plugin object * @param plugin Plugin object
*/ */
@SuppressWarnings({"unused"}) @SuppressWarnings({"WeakerAccess", "unused"})
protected void unregister(JavaPlugin plugin) { protected void unregister(JavaPlugin plugin) {
} }
@ -217,16 +197,22 @@ public abstract class Component<TP extends JavaPlugin> {
protected abstract void disable(); protected abstract void disable();
/** /**
* Registers a command to the component. Make sure to use {@link buttondevteam.lib.chat.CommandClass} and {@link buttondevteam.lib.chat.Command2.Subcommand}. * Registers a TBMCCommand to the component. Make sure to use {@link buttondevteam.lib.chat.CommandClass} and {@link buttondevteam.lib.chat.Command2.Subcommand}.
* You don't need to register the command in plugin.yml. * You don't need to register the command in plugin.yml.
* *
* @param command Custom coded command class * @param commandBase Custom coded command class
*/ */
protected final void registerCommand(ICommand2MC command) { protected final void registerCommand(ICommand2MC commandBase) {
if (plugin instanceof ButtonPlugin) ButtonPlugin.getCommand2MC().registerCommand(commandBase);
command.registerToPlugin((ButtonPlugin) plugin); }
command.registerToComponent(this);
ButtonPlugin.getCommand2MC().registerCommand(command); /**
* Registers a TBMCCommand to the component. Make sure to add it to plugin.yml and use {@link buttondevteam.lib.chat.CommandClass}.
*
* @param commandBase Custom coded command class
*/
protected final void registerCommand(TBMCCommandBase commandBase) {
TBMCChatAPI.AddCommand(this, commandBase);
} }
/** /**
@ -242,25 +228,19 @@ public abstract class Component<TP extends JavaPlugin> {
/** /**
* Returns a map of configs that are under the given key. * Returns a map of configs that are under the given key.
*
* @param key The key to use * @param key The key to use
* @param defaultProvider A mapping between config paths and config generators * @param defaultProvider A mapping between config paths and config generators
* @return A map containing configs * @return A map containing configs
*/ */
protected Map<String, IHaveConfig> getConfigMap(String key, Map<String, Consumer<IHaveConfig>> defaultProvider) { protected Map<String, IHaveConfig> getConfigMap(String key, Map<String, Consumer<IHaveConfig>> defaultProvider) {
val c = getConfig().getConfig(); val c=getConfig().getConfig();
var cs = c.getConfigurationSection(key); var cs=c.getConfigurationSection(key);
if (cs == null) cs = c.createSection(key); if(cs==null) cs=c.createSection(key);
val res = cs.getValues(false).entrySet().stream().filter(e -> e.getValue() instanceof ConfigurationSection) val res = cs.getValues(false).entrySet().stream().filter(e -> e.getValue() instanceof ConfigurationSection)
.collect(Collectors.toMap(Map.Entry::getKey, kv -> { .collect(Collectors.toMap(Map.Entry::getKey, kv -> new IHaveConfig((ConfigurationSection) kv.getValue(), getPlugin()::saveConfig)));
var conf = new IHaveConfig(getPlugin()::saveConfig);
conf.reset((ConfigurationSection) kv.getValue());
return conf;
}));
if (res.size() == 0) { if (res.size() == 0) {
for (val entry : defaultProvider.entrySet()) { for (val entry : defaultProvider.entrySet()) {
val conf = new IHaveConfig(getPlugin()::saveConfig); val conf = new IHaveConfig(cs.createSection(entry.getKey()), getPlugin()::saveConfig);
conf.reset(cs.createSection(entry.getKey()));
entry.getValue().accept(conf); entry.getValue().accept(conf);
res.put(entry.getKey(), conf); res.put(entry.getKey(), conf);
} }

View file

@ -9,6 +9,4 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface ComponentMetadata { public @interface ComponentMetadata {
Class<? extends Component>[] depends() default {}; Class<? extends Component>[] depends() default {};
boolean enabledByDefault() default true;
} }

View file

@ -0,0 +1,137 @@
package buttondevteam.lib.architecture;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.ThorpeUtils;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.scheduler.BukkitTask;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
/**
* Use the getter/setter constructor if {@link T} isn't a primitive type or String.<br>
* Use {@link Component#getConfig()} or {@link ButtonPlugin#getIConfig()} then {@link IHaveConfig#getData(String, Object)} to get an instance.
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
//@AllArgsConstructor(access = AccessLevel.PACKAGE)
public class ConfigData<T> {
private static final HashMap<Configuration, SaveTask> saveTasks = new HashMap<>();
/**
* May be null for testing
*/
private final ConfigurationSection config;
private final String path;
private final T def;
private final Object primitiveDef;
private final Runnable saveAction;
/**
* The parameter is of a primitive type as returned by {@link YamlConfiguration#get(String)}
*/
private Function<Object, T> getter;
/**
* The result should be a primitive type or string that can be retrieved correctly later
*/
private Function<T, Object> setter;
/**
* The config value should not change outside this instance
*/
private T value;
/**
* Whether the default value is saved in the yaml
*/
private boolean saved = false;
//This constructor is needed because it sets the getter and setter
public ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter, Runnable saveAction) {
this.config = config;
this.path = path;
this.def = def;
this.primitiveDef = primitiveDef;
this.getter = getter;
this.setter = setter;
this.saveAction=saveAction;
}
@Override
public String toString() {
return "ConfigData{" +
"path='" + path + '\'' +
", value=" + value +
'}';
}
@SuppressWarnings("unchecked")
public T get() {
if (value != null) return value; //Speed things up
Object val = config == null ? null : config.get(path); //config==null: testing
if (val == null) {
val = primitiveDef;
}
if (!saved && Objects.equals(val, primitiveDef)) { //String needs .equals()
if (def == null && config != null) //In Discord's case def may be null
config.set(path, primitiveDef);
else
set(def); //Save default value - def is always set
saved = true;
}
if (getter != null) {
T hmm = getter.apply(val);
if (hmm == null) hmm = def; //Set if the getter returned null
return hmm;
}
if (val instanceof Number && def != null)
val = ThorpeUtils.convertNumber((Number) val,
(Class<? extends Number>) def.getClass());
if (val instanceof List && def != null && def.getClass().isArray())
val = ((List<T>) val).toArray((T[]) Array.newInstance(def.getClass().getComponentType(), 0));
return value = (T) val; //Always cache, if not cached yet
}
public void set(T value) {
Object val;
if (setter != null && value != null)
val = setter.apply(value);
else val = value;
if (config != null) {
config.set(path, val);
if(!saveTasks.containsKey(config.getRoot())) {
synchronized (saveTasks) {
saveTasks.put(config.getRoot(), new SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, () -> {
synchronized (saveTasks) {
saveTasks.remove(config.getRoot());
saveAction.run();
}
}, 100), saveAction));
}
}
}
this.value = value;
}
@AllArgsConstructor
private static class SaveTask {
BukkitTask task;
Runnable saveAction;
}
public static boolean saveNow(Configuration config) {
SaveTask st = saveTasks.get(config);
if (st != null) {
st.task.cancel();
saveTasks.remove(config);
st.saveAction.run();
return true;
}
return false;
}
}

View file

@ -1,4 +1,4 @@
package buttondevteam.buttonproc; package buttondevteam.lib.architecture;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited; import java.lang.annotation.Inherited;
@ -10,5 +10,4 @@ import java.lang.annotation.Target;
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Inherited @Inherited
public @interface HasConfig { public @interface HasConfig {
boolean global();
} }

View file

@ -0,0 +1,120 @@
package buttondevteam.lib.architecture;
import lombok.Getter;
import lombok.val;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;
import java.util.HashMap;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* A config system
*/
public final class IHaveConfig {
private final HashMap<String, ConfigData<?>> datamap = new HashMap<>();
@Getter
private ConfigurationSection config;
private final Runnable saveAction;
/**
* May be used in testing.
*
* @param section May be null for testing
*/
IHaveConfig(ConfigurationSection section, Runnable saveAction) {
config = section;
this.saveAction=saveAction;
}
/**
* This method overload should only be used with primitves or String.
*
* @param path The path in config to use
* @param def The value to use by default
* @param <T> The type of this variable (only use primitives or String)
* @return The data object that can be used to get or set the value
*/
@SuppressWarnings("unchecked")
public <T> ConfigData<T> getData(String path, T def) {
ConfigData<?> data = datamap.get(path);
if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def, def, saveAction));
return (ConfigData<T>) data;
}
/**
* This method overload may be used with any class.
*
* @param path The path in config to use
* @param def The value to use by default
* @param getter A function that converts a primitive representation to the correct value
* @param setter A function that converts a value to a primitive representation
* @param <T> The type of this variable (can be any class)
* @return The data object that can be used to get or set the value
*/
@SuppressWarnings("unchecked")
public <T> ConfigData<T> getData(String path, T def, Function<Object, T> getter, Function<T, Object> setter) {
ConfigData<?> data = datamap.get(path);
if (data == null)
datamap.put(path, data = new ConfigData<>(config, path, def, setter.apply(def), getter, setter, saveAction));
return (ConfigData<T>) data;
}
/**
* This method overload may be used with any class. The given default value will be run through the getter.
*
* @param path The path in config to use
* @param primitiveDef The <b>primitive</b> value to use by default
* @param getter A function that converts a primitive representation to the correct value
* @param setter A function that converts a value to a primitive representation
* @param <T> The type of this variable (can be any class)
* @return The data object that can be used to get or set the value
*/
@SuppressWarnings("unchecked")
public <T> ConfigData<T> getDataPrimDef(String path, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
ConfigData<?> data = datamap.get(path);
if (data == null)
datamap.put(path, data = new ConfigData<>(config, path, getter.apply(primitiveDef), primitiveDef, getter, setter, saveAction));
return (ConfigData<T>) data;
}
/**
* This method overload should only be used with primitves or String.
*
* @param path The path in config to use
* @param def The value to use by default
* @param <T> The type of this variable (only use primitives or String)
* @return The data object that can be used to get or set the value
*/
@SuppressWarnings("unchecked")
public <T> ConfigData<T> getData(String path, Supplier<T> def) {
ConfigData<?> data = datamap.get(path);
if (data == null) {
val defval = def.get();
datamap.put(path, data = new ConfigData<>(config, path, defval, defval, saveAction));
}
return (ConfigData<T>) data;
}
/**
* This method overload may be used with any class.
*
* @param path The path in config to use
* @param def The value to use by default
* @param getter A function that converts a primitive representation to the correct value
* @param setter A function that converts a value to a primitive representation
* @param <T> The type of this variable (can be any class)
* @return The data object that can be used to get or set the value
*/
@SuppressWarnings("unchecked")
public <T> ConfigData<T> getData(String path, Supplier<T> def, Function<Object, T> getter, Function<T, Object> setter) {
ConfigData<?> data = datamap.get(path);
if (data == null) {
val defval = def.get();
datamap.put(path, data = new ConfigData<>(config, path, defval, setter.apply(defval), getter, setter, saveAction));
}
return (ConfigData<T>) data;
}
}

View file

@ -0,0 +1,20 @@
package buttondevteam.lib.chat;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Getter
public enum Color implements TellrawSerializableEnum {
Black("black", 0, 0, 0), DarkBlue("dark_blue", 0, 0, 170), DarkGreen("dark_green", 0, 170, 0), DarkAqua("dark_aqua",
0, 170, 170), DarkRed("dark_red", 170, 0, 0), DarkPurple("dark_purple", 0, 170, 0), Gold("gold", 255, 170,
0), Gray("gray", 170, 170, 170), DarkGray("dark_gray", 85, 85, 85), Blue("blue", 85, 85,
255), Green("green", 85, 255, 85), Aqua("aqua", 85, 255, 255), Red("red", 255, 85,
85), LightPurple("light_purple", 255, 85,
255), Yellow("yellow", 255, 255, 85), White("white", 255, 255, 255);
private final String name;
private final int red;
private final int green;
private final int blue;
}

View file

@ -0,0 +1,290 @@
package buttondevteam.lib.chat;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.ThorpeUtils;
import buttondevteam.lib.player.ChromaGamerBase;
import com.google.common.base.Defaults;
import com.google.common.primitives.Primitives;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.experimental.var;
import lombok.val;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.InputStreamReader;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.function.Function;
/**
* The method name is the subcommand, use underlines (_) to add further subcommands.
* The args may be null if the conversion failed and it's optional.
*/
public abstract class Command2<TC extends ICommand2, TP extends Command2Sender> {
protected Command2() {
commandHelp.add("§6---- Commands ----");
}
/**
* Parameters annotated with this receive all of the remaining arguments
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TextArg {
}
/**
* Methods annotated with this will be recognised as subcommands
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subcommand {
/**
* Help text to show players. A usage message will be also shown below it.
*/
String[] helpText() default {};
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface OptionalArg {
}
@AllArgsConstructor
protected static class SubcommandData<T extends ICommand2> {
public final Method method;
public final T command;
public String[] helpText;
}
@RequiredArgsConstructor
protected static class ParamConverter<T> {
public final Function<String, T> converter;
public final String errormsg;
}
private HashMap<String, SubcommandData<TC>> subcommands = new HashMap<>();
private HashMap<Class<?>, ParamConverter<?>> paramConverters = new HashMap<>();
private ArrayList<String> commandHelp = new ArrayList<>(); //Mainly needed by Discord
/**
* Adds a param converter that obtains a specific object from a string parameter.
* The converter may return null.
*
* @param cl The class of the result object
* @param converter The converter to use
* @param <T> The type of the result
*/
public <T> void addParamConverter(Class<T> cl, Function<String, T> converter, String errormsg) {
paramConverters.put(cl, new ParamConverter<>(converter, errormsg));
}
public boolean handleCommand(TP sender, String commandline) throws Exception {
for (int i = commandline.length(); i != -1; i = commandline.lastIndexOf(' ', i - 1)) {
String subcommand = commandline.substring(0, i).toLowerCase();
SubcommandData<TC> sd = subcommands.get(subcommand); //O(1)
if (sd == null) continue;
if (sd.method == null || sd.command == null) { //Main command not registered, but we have subcommands
sender.sendMessage(sd.helpText);
return true;
}
if (!hasPermission(sender, sd.command)) {
sender.sendMessage("§cYou don't have permission to use this command");
return true;
}
val params = new ArrayList<Object>(sd.method.getParameterCount());
int j = subcommand.length(), pj;
Class<?>[] parameterTypes = sd.method.getParameterTypes();
if (parameterTypes.length == 0)
throw new Exception("No sender parameter for method '" + sd.method + "'");
val sendertype = parameterTypes[0];
final ChromaGamerBase cg;
if (sendertype.isAssignableFrom(sender.getClass()))
params.add(sender); //The command either expects a CommandSender or it is a Player, or some other expected type
else if (sender instanceof Command2MCSender
&& sendertype.isAssignableFrom(((Command2MCSender) sender).getSender().getClass()))
params.add(((Command2MCSender) sender).getSender());
else if (ChromaGamerBase.class.isAssignableFrom(sendertype)
&& sender instanceof Command2MCSender
&& (cg = ChromaGamerBase.getFromSender(((Command2MCSender) sender).getSender())) != null
&& cg.getClass() == sendertype) //The command expects a user of our system
params.add(cg);
else {
sender.sendMessage("§cYou need to be a " + sendertype.getSimpleName() + " to use this command.");
return true;
}
val paramArr = sd.method.getParameters();
for (int i1 = 1; i1 < parameterTypes.length; i1++) {
Class<?> cl = parameterTypes[i1];
pj = j + 1; //Start index
if (pj == commandline.length() + 1) { //No param given
if (paramArr[i1].isAnnotationPresent(OptionalArg.class)) {
if (cl.isPrimitive())
params.add(Defaults.defaultValue(cl));
else if (Number.class.isAssignableFrom(cl)
|| Number.class.isAssignableFrom(cl))
params.add(Defaults.defaultValue(Primitives.unwrap(cl)));
else
params.add(null);
continue; //Fill the remaining params with nulls
} else {
sender.sendMessage(sd.helpText); //Required param missing
return true;
}
}
if (paramArr[i1].isVarArgs()) {
params.add(commandline.substring(j + 1).split(" +"));
continue;
}
j = commandline.indexOf(' ', j + 1); //End index
if (j == -1 || paramArr[i1].isAnnotationPresent(TextArg.class)) //Last parameter
j = commandline.length();
String param = commandline.substring(pj, j);
if (cl == String.class) {
params.add(param);
continue;
} else if (Number.class.isAssignableFrom(cl) || cl.isPrimitive()) {
try {
//System.out.println("Converting "+param+" param to "+cl.getSimpleName());
//noinspection unchecked
Number n = ThorpeUtils.convertNumber(NumberFormat.getInstance().parse(param), (Class<? extends Number>) cl);
//System.out.println(n.getClass().getSimpleName()+" with value "+n);
params.add(n);
} catch (ParseException e) {
sender.sendMessage("§c'" + param + "' is not a number.");
return true;
}
continue;
}
val conv = paramConverters.get(cl);
if (conv == null)
throw new Exception("No suitable converter found for parameter type '" + cl.getCanonicalName() + "' for command '" + sd.method.toString() + "'");
val cparam = conv.converter.apply(param);
if (cparam == null) {
sender.sendMessage(conv.errormsg); //Param conversion failed - ex. plugin not found
return true;
}
params.add(cparam);
}
//System.out.println("Our params: "+params);
try {
val ret = sd.method.invoke(sd.command, params.toArray()); //I FORGOT TO TURN IT INTO AN ARRAY (for a long time)
if (ret instanceof Boolean) {
if (!(boolean) ret) //Show usage
sender.sendMessage(sd.helpText);
} else if (ret != null)
throw new Exception("Wrong return type! Must return a boolean or void. Return value: " + ret);
return true; //We found a method
} catch (InvocationTargetException e) {
TBMCCoreAPI.SendException("An error occurred in a command handler!", e.getCause());
}
}
return false; //Didn't handle
} //TODO: Add to the help
public abstract void registerCommand(TC command);
protected void registerCommand(TC command, char commandChar) {
val path = command.getCommandPath();
int x = path.indexOf(' ');
val mainPath = commandChar + path.substring(0, x == -1 ? path.length() : x);
//var scmdmap = subcommandStrings.computeIfAbsent(mainPath, k -> new HashSet<>()); //Used to display subcommands
val scmdHelpList = new ArrayList<String>();
Method mainMethod = null;
boolean nosubs = true;
boolean isSubcommand = x != -1;
try { //Register the default handler first so it can be reliably overwritten
mainMethod = command.getClass().getMethod("def", Command2Sender.class, String.class);
val cc = command.getClass().getAnnotation(CommandClass.class);
var ht = cc == null || isSubcommand ? new String[0] : cc.helpText(); //If it's not the main command, don't add it
if (ht.length > 0)
ht[0] = "§6---- " + ht[0] + " ----";
scmdHelpList.addAll(Arrays.asList(ht));
if (!isSubcommand)
scmdHelpList.add("§6Subcommands:");
if (!commandHelp.contains(mainPath))
commandHelp.add(mainPath);
} catch (Exception e) {
TBMCCoreAPI.SendException("Could not register default handler for command /" + path, e);
}
for (val method : command.getClass().getMethods()) {
val ann = method.getAnnotation(Subcommand.class);
if (ann == null) continue; //Don't call the method on non-subcommands because they're not in the yaml
var ht = command.getHelpText(method, ann);
if (ht != null) {
val subcommand = commandChar + path + //Add command path (class name by default)
(method.getName().equals("def") ? "" : " " + method.getName().replace('_', ' ').toLowerCase()); //Add method name, unless it's 'def'
ht = getHelpText(method, ht, subcommand);
subcommands.put(subcommand, new SubcommandData<>(method, command, ht)); //Result of the above (def) is that it will show the help text
scmdHelpList.add(subcommand);
nosubs = false;
}
}
if (nosubs && scmdHelpList.size() > 0)
scmdHelpList.remove(scmdHelpList.size() - 1); //Remove Subcommands header
if (mainMethod != null && !subcommands.containsKey(commandChar + path)) //Command specified by the class
subcommands.put(commandChar + path, new SubcommandData<>(mainMethod, command, scmdHelpList.toArray(new String[0])));
if (mainMethod != null && !mainPath.equals(commandChar + path)) { //Main command, typically the same as the above
if (isSubcommand) { //The class itself is a subcommand
val scmd = subcommands.computeIfAbsent(mainPath, p -> new SubcommandData<>(null, null, new String[]{"§6---- Subcommands ----"}));
val scmdHelp = Arrays.copyOf(scmd.helpText, scmd.helpText.length + scmdHelpList.size());
for (int i = 0; i < scmdHelpList.size(); i++)
scmdHelp[scmd.helpText.length + i] = scmdHelpList.get(i);
scmd.helpText = scmdHelp;
} else if (!subcommands.containsKey(mainPath))
subcommands.put(mainPath, new SubcommandData<>(null, null, scmdHelpList.toArray(new String[0])));
}
}
private String[] getHelpText(Method method, String[] ht, String subcommand) {
val str = method.getDeclaringClass().getResourceAsStream("/commands.yml");
if (str == null)
TBMCCoreAPI.SendException("Error while getting command data!", new Exception("Resource not found!"));
else {
if (ht.length > 0)
ht[0] = "§6---- " + ht[0] + " ----";
YamlConfiguration yc = YamlConfiguration.loadConfiguration(new InputStreamReader(str)); //Generated by ButtonProcessor
val ccs = yc.getConfigurationSection(method.getDeclaringClass().getCanonicalName());
if (ccs != null) {
val cs = ccs.getConfigurationSection(method.getName());
if (cs != null) {
val mname = cs.getString("method");
val params = cs.getString("params");
//val goodname = method.getName() + "(" + Arrays.stream(method.getGenericParameterTypes()).map(cl -> cl.getTypeName()).collect(Collectors.joining(",")) + ")";
int i = mname.indexOf('('); //Check only the name - the whole method is still stored for backwards compatibility and in case it may be useful
if (i != -1 && method.getName().equals(mname.substring(0, i)) && params != null) {
String[] both = Arrays.copyOf(ht, ht.length + 1);
both[ht.length] = "§6Usage:§r " + subcommand + " " + params;
ht = both;
} else
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("Method '" + method.toString() + "' != " + mname + " or params is " + params));
} else
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("cs is " + cs));
} else
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("ccs is " + ccs + " - class: " + method.getDeclaringClass().getCanonicalName()));
}
return ht;
}
public abstract boolean hasPermission(TP sender, TC command);
public String[] getCommandsText() {
return commandHelp.toArray(new String[0]);
}
public String[] getHelpText(String path) {
val scmd = subcommands.get(path);
if (scmd == null) return null;
return scmd.helpText;
}
}

View file

@ -0,0 +1,43 @@
package buttondevteam.lib.chat;
import buttondevteam.core.MainPlugin;
import lombok.val;
import java.util.function.Function;
public class Command2MC extends Command2<ICommand2MC, Command2MCSender> {
@Override
public void registerCommand(ICommand2MC command) {
super.registerCommand(command, '/');
}
@Override
public boolean hasPermission(Command2MCSender sender, ICommand2MC command) {
return modOnly(command)
? MainPlugin.permission.has(sender.getSender(), "tbmc.admin") //TODO: Change when groups are implemented
: MainPlugin.permission.has(sender.getSender(), "thorpe.command." + command.getCommandPath().replace(' ', '.'));
}
/**
* Returns true if this class or <u>any</u> of the superclasses are mod only.
*
* @param command The command to check
* @return Whether the command is mod only
*/
private boolean modOnly(ICommand2MC command) {
for (Class<?> cl = command.getClass(); cl != null; cl = cl.getSuperclass()) {
val cc = command.getClass().getAnnotation(CommandClass.class);
if (cc != null && cc.modOnly()) return true;
}
return false;
}
/**
* Automatically colors the message red.
* {@see super#addParamConverter}
*/
@Override
public <T> void addParamConverter(Class<T> cl, Function<String, T> converter, String errormsg) {
super.addParamConverter(cl, converter, "§c" + errormsg);
}
}

View file

@ -1,6 +1,5 @@
package buttondevteam.lib.chat; package buttondevteam.lib.chat;
import buttondevteam.core.component.channel.Channel;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -8,8 +7,6 @@ import org.bukkit.command.CommandSender;
@RequiredArgsConstructor @RequiredArgsConstructor
public class Command2MCSender implements Command2Sender { public class Command2MCSender implements Command2Sender {
private @Getter final CommandSender sender; private @Getter final CommandSender sender;
private @Getter final Channel channel;
private @Getter final CommandSender permCheck;
@Override @Override
public void sendMessage(String message) { public void sendMessage(String message) {
@ -20,9 +17,4 @@ public class Command2MCSender implements Command2Sender {
public void sendMessage(String[] message) { public void sendMessage(String[] message) {
sender.sendMessage(message); sender.sendMessage(message);
} }
@Override
public String getName() {
return sender.getName();
}
} }

View file

@ -4,6 +4,4 @@ public interface Command2Sender { //We don't need the 'extras' of CommandSender
void sendMessage(String message); void sendMessage(String message);
void sendMessage(String[] message); void sendMessage(String[] message);
String getName();
} }

View file

@ -44,10 +44,4 @@ public @interface CommandClass {
* @return The help text * @return The help text
*/ */
String[] helpText() default {}; String[] helpText() default {};
/**
* The main permission which allows using this command (individual access can be still granted with "chroma.command.X").
* Used to be "tbmc.admin"
*/
String permGroup() default ""; //TODO: A single annotation instead of these two
} }

Some files were not shown because too many files have changed in this diff Show more