Compare commits
49 commits
Author | SHA1 | Date | |
---|---|---|---|
66c1b1b14f | |||
496af97e1c | |||
6ec63b5ae7 | |||
ea7520d99a | |||
|
47461d49c2 | ||
7120e3c40e | |||
|
c6b2dc3443 | ||
4e58ddd475 | |||
5c83b923da | |||
83b5f1fec4 | |||
|
59ccc55f98 | ||
f92cc773c1 | |||
2a008906f4 | |||
9fb35eb6cc | |||
66be5ab0dc | |||
5b5a8877cc | |||
09074f1a2e | |||
ff0d54e00b | |||
5199482053 | |||
c12a24895e | |||
51e0ca4f4c | |||
267a350473 | |||
132eba7db6 | |||
83ecf7717f | |||
893b24ed5f | |||
af0aed65e0 | |||
47dbb987eb | |||
|
28e26ad3d1 | ||
|
8e664d073f | ||
1aa9cd3552 | |||
731065fe2a | |||
01dd8a477e | |||
ab4dd75684 | |||
5b27af8925 | |||
28e44d5179 | |||
cedeca2f61 | |||
784ad5e1c7 | |||
9923f26698 | |||
b89391f84c | |||
8f610c9935 | |||
4310e45a6f | |||
43b7bd442a | |||
ef96ec2604 | |||
8815877bea | |||
58fcd4c145 | |||
1139f832b6 | |||
9dae442950 | |||
82858b0a41 | |||
7b505bb8e9 |
59 changed files with 1399 additions and 1404 deletions
75
.github/workflows/codeql-analysis.yml
vendored
Normal file
75
.github/workflows/codeql-analysis.yml
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
# 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
|
11
ButtonProcessor/pom.xml
Executable file → Normal file
11
ButtonProcessor/pom.xml
Executable file → Normal file
|
@ -11,7 +11,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
<artifactId>snakeyaml</artifactId>
|
<artifactId>snakeyaml</artifactId>
|
||||||
<version>1.21</version>
|
<version>1.32</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -33,14 +33,15 @@
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<compilerArgument>-proc:none</compilerArgument>
|
<compilerArgument>-proc:none</compilerArgument>
|
||||||
<source>8</source>
|
<source>8</source>
|
||||||
<target>8</target>
|
<target>8</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
|
|
@ -21,35 +21,29 @@ import java.util.stream.Collectors;
|
||||||
@SupportedAnnotationTypes("buttondevteam.*")
|
@SupportedAnnotationTypes("buttondevteam.*")
|
||||||
public class ButtonProcessor extends AbstractProcessor {
|
public class ButtonProcessor extends AbstractProcessor {
|
||||||
@Override
|
@Override
|
||||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||||
if (configProcessor == null)
|
if (configProcessor == null)
|
||||||
configProcessor = new ConfigProcessor(processingEnv);
|
configProcessor = new ConfigProcessor(processingEnv);
|
||||||
for (TypeElement te : annotations) {
|
for (TypeElement te : annotations) {
|
||||||
Set<? extends Element> classes = roundEnv.getElementsAnnotatedWith(te);
|
Set<? extends Element> classes = roundEnv.getElementsAnnotatedWith(te);
|
||||||
for (Element targetcl : classes) {
|
for (Element targetcl : classes) {
|
||||||
System.out.println("Processing " + targetcl);
|
List<? extends AnnotationMirror> annotationMirrors = processingEnv.getElementUtils()
|
||||||
List<? extends AnnotationMirror> annotationMirrors = processingEnv.getElementUtils()
|
.getAllAnnotationMirrors(targetcl);
|
||||||
.getAllAnnotationMirrors(targetcl);
|
Function<String, Boolean> hasAnnotation = ann -> annotationMirrors.stream()
|
||||||
//System.out.println("Annotations: " + annotationMirrors);
|
.anyMatch(am -> am.getAnnotationType().toString().contains(ann));
|
||||||
Function<String, Boolean> hasAnnotation = ann -> annotationMirrors.stream()
|
if (hasAnnotation.apply("ChromaGamerEnforcer") && !hasAnnotation.apply("UserClass")
|
||||||
.anyMatch(am -> am.getAnnotationType().toString().contains(ann));
|
&& !targetcl.getModifiers().contains(Modifier.ABSTRACT))
|
||||||
if (hasAnnotation.apply("ChromaGamerEnforcer") && !hasAnnotation.apply("UserClass")
|
processingEnv.getMessager().printMessage(Kind.ERROR,
|
||||||
&& !targetcl.getModifiers().contains(Modifier.ABSTRACT))
|
"No UserClass annotation found for " + targetcl.getSimpleName(), targetcl);
|
||||||
processingEnv.getMessager().printMessage(Kind.ERROR,
|
if (hasAnnotation.apply("TBMCPlayerEnforcer") && !hasAnnotation.apply("PlayerClass")
|
||||||
"No UserClass annotation found for " + targetcl.getSimpleName(), targetcl);
|
&& !targetcl.getModifiers().contains(Modifier.ABSTRACT))
|
||||||
if (hasAnnotation.apply("TBMCPlayerEnforcer") && !hasAnnotation.apply("PlayerClass")
|
processingEnv.getMessager().printMessage(Kind.ERROR,
|
||||||
&& !targetcl.getModifiers().contains(Modifier.ABSTRACT))
|
"No PlayerClass annotation found for " + targetcl.getSimpleName(), targetcl);
|
||||||
processingEnv.getMessager().printMessage(Kind.ERROR,
|
processSubcommands(targetcl, annotationMirrors);
|
||||||
"No PlayerClass annotation found for " + targetcl.getSimpleName(), targetcl);
|
if (hasAnnotation.apply("HasConfig"))
|
||||||
for (AnnotationMirror annotation : annotationMirrors) {
|
configProcessor.process(targetcl);
|
||||||
String type = annotation.getAnnotationType().toString();
|
}
|
||||||
//System.out.println("Type: " + type);
|
}
|
||||||
}
|
|
||||||
processSubcommands(targetcl, annotationMirrors);
|
|
||||||
if (hasAnnotation.apply("HasConfig"))
|
|
||||||
configProcessor.process(targetcl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
if (found) {
|
if (found) {
|
||||||
FileObject fo = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "commands.yml");
|
FileObject fo = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "commands.yml");
|
||||||
|
@ -59,38 +53,33 @@ public class ButtonProcessor extends AbstractProcessor {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return true; // claim the annotations
|
return true; // claim the annotations
|
||||||
}
|
}
|
||||||
|
|
||||||
private YamlConfiguration yc = new YamlConfiguration();
|
private final YamlConfiguration yc = new YamlConfiguration();
|
||||||
private boolean found = false;
|
private boolean found = false;
|
||||||
private ConfigProcessor configProcessor;
|
private ConfigProcessor configProcessor;
|
||||||
|
|
||||||
private void processSubcommands(Element targetcl, List<? extends AnnotationMirror> annotationMirrors) {
|
private void processSubcommands(Element method, List<? extends AnnotationMirror> annotationMirrors) {
|
||||||
if (!(targetcl instanceof ExecutableElement))
|
if (!(method instanceof ExecutableElement))
|
||||||
return;
|
return;
|
||||||
//System.out.println("Annotations: "+annotationMirrors);
|
|
||||||
if (annotationMirrors.stream().noneMatch(an -> an.getAnnotationType().toString().endsWith("Subcommand")))
|
if (annotationMirrors.stream().noneMatch(an -> an.getAnnotationType().toString().endsWith("Subcommand")))
|
||||||
return;
|
return;
|
||||||
//System.out.print("Processing method: " + targetcl.getEnclosingElement()+" "+targetcl);
|
ConfigurationSection cs = yc.createSection(method.getEnclosingElement().toString()
|
||||||
ConfigurationSection cs = yc.createSection(targetcl.getEnclosingElement().toString()
|
+ "." + method.getSimpleName().toString()); //Need to do the 2 config sections at once so it doesn't overwrite the class section
|
||||||
+ "." + targetcl.getSimpleName().toString()); //Need to do the 2 config sections at once so it doesn't overwrite the class section
|
System.out.println("Found subcommand: " + method);
|
||||||
System.out.println(targetcl);
|
cs.set("method", method.toString());
|
||||||
cs.set("method", targetcl.toString());
|
cs.set("params", ((ExecutableElement) method).getParameters().stream().skip(1).map(p -> {
|
||||||
cs.set("params", ((ExecutableElement) targetcl).getParameters().stream().skip(1).map(p -> {
|
|
||||||
//String tn=p.asType().toString();
|
|
||||||
//return tn.substring(tn.lastIndexOf('.')+1)+" "+p.getSimpleName();
|
|
||||||
boolean optional = p.getAnnotationMirrors().stream().anyMatch(am -> am.getAnnotationType().toString().endsWith("OptionalArg"));
|
boolean optional = p.getAnnotationMirrors().stream().anyMatch(am -> am.getAnnotationType().toString().endsWith("OptionalArg"));
|
||||||
if (optional)
|
if (optional)
|
||||||
return "[" + p.getSimpleName() + "]";
|
return "[" + p.getSimpleName() + "]";
|
||||||
return "<" + p.getSimpleName() + ">";
|
return "<" + p.getSimpleName() + ">";
|
||||||
}).collect(Collectors.joining(" ")));
|
}).collect(Collectors.joining(" ")));
|
||||||
//System.out.println();
|
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceVersion getSupportedSourceVersion() {
|
public SourceVersion getSupportedSourceVersion() {
|
||||||
return SourceVersion.latestSupported();
|
return SourceVersion.latestSupported();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,28 +48,27 @@ public class ConfigProcessor {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
for (Element e : targetcl.getEnclosedElements()) {
|
for (Element e : targetcl.getEnclosedElements()) {
|
||||||
/*System.out.println("Element: "+e);
|
TypeMirror tm;
|
||||||
System.out.println("Type: "+e.getClass()+" - "+e.getKind());
|
if (e instanceof ExecutableElement)
|
||||||
if(e instanceof ExecutableElement)
|
tm = ((ExecutableElement) e).getReturnType();
|
||||||
System.out.println("METHOD!");*/
|
else if (e.getKind().isField())
|
||||||
if (!(e instanceof ExecutableElement)) continue;
|
tm = e.asType();
|
||||||
TypeMirror tm = ((ExecutableElement) e).getReturnType();
|
else
|
||||||
|
continue;
|
||||||
if (tm.getKind() != TypeKind.DECLARED) continue;
|
if (tm.getKind() != TypeKind.DECLARED) continue;
|
||||||
DeclaredType dt = (DeclaredType) tm;
|
DeclaredType dt = (DeclaredType) tm;
|
||||||
if (!dt.asElement().getSimpleName().contentEquals("ConfigData"))
|
if (!dt.asElement().getSimpleName().toString().contains("ConfigData"))
|
||||||
continue; //Ahhha! There was a return here! (MinecraftChatModule getListener())
|
continue; //Ahhha! There was a return here! (MinecraftChatModule getListener())
|
||||||
System.out.println("Config: " + e.getSimpleName());
|
|
||||||
|
|
||||||
String doc = procEnv.getElementUtils().getDocComment(e);
|
String doc = procEnv.getElementUtils().getDocComment(e);
|
||||||
if (doc == null) continue;
|
if (doc == null) continue;
|
||||||
System.out.println("DOC: " + doc);
|
System.out.println("Adding docs for config: " + e.getSimpleName());
|
||||||
yc.set(path + "." + e.getSimpleName(), doc.trim());
|
yc.set(path + "." + e.getSimpleName(), doc.trim());
|
||||||
}
|
}
|
||||||
String javadoc = procEnv.getElementUtils().getDocComment(targetcl);
|
String javadoc = procEnv.getElementUtils().getDocComment(targetcl);
|
||||||
if (javadoc != null) {
|
if (javadoc != null) {
|
||||||
System.out.println("JAVADOC");
|
System.out.println("Adding docs for class: " + targetcl.getSimpleName());
|
||||||
System.out.println(javadoc.trim());
|
yc.set(path + ".generalDescriptionInsteadOfAConfig", javadoc.trim());
|
||||||
yc.set(path, javadoc.trim());
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
yc.save(file);
|
yc.save(file);
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<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">
|
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.ChromaCore</groupId>
|
||||||
<artifactId>CorePOM</artifactId>
|
<artifactId>CorePOM</artifactId>
|
||||||
<version>master-SNAPSHOT</version>
|
<version>master-SNAPSHOT</version>
|
||||||
<relativePath>../CorePOM</relativePath>
|
<relativePath>../CorePOM</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>Chroma-Core</artifactId>
|
<artifactId>Chroma-Core</artifactId>
|
||||||
<name>Chroma-Core</name>
|
<name>Chroma-Core</name>
|
||||||
<description>Chroma-Core</description>
|
<description>Chroma-Core</description>
|
||||||
|
<version>v${noprefix.version}-SNAPSHOT</version>
|
||||||
<build>
|
<build>
|
||||||
<sourceDirectory>src/main/java</sourceDirectory>
|
<sourceDirectory>src/main/java</sourceDirectory>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -118,13 +119,9 @@
|
||||||
<id>ess-repo</id>
|
<id>ess-repo</id>
|
||||||
<url>https://ci.ender.zone/plugin/repository/everything/</url>
|
<url>https://ci.ender.zone/plugin/repository/everything/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
|
||||||
<id>Votifier</id>
|
|
||||||
<url>https://dl.bintray.com/nuvotifier/maven/</url>
|
|
||||||
</repository>
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>Multiverse-Core</id>
|
<id>Multiverse-Core</id>
|
||||||
<url>http://repo.onarandombox.com/content/repositories/multiverse/</url>
|
<url>https://repo.onarandombox.com/content/groups/public/</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>minecraft-repo</id>
|
<id>minecraft-repo</id>
|
||||||
|
@ -135,7 +132,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.reflections</groupId>
|
<groupId>org.reflections</groupId>
|
||||||
<artifactId>reflections</artifactId>
|
<artifactId>reflections</artifactId>
|
||||||
<version>0.9.10</version>
|
<version>0.10.2</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -160,12 +157,12 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.javassist</groupId>
|
<groupId>org.javassist</groupId>
|
||||||
<artifactId>javassist</artifactId>
|
<artifactId>javassist</artifactId>
|
||||||
<version>3.20.0-GA</version>
|
<version>3.28.0-GA</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mockito</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<version>3.0.0</version>
|
<version>4.2.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
|
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
|
||||||
|
@ -179,22 +176,23 @@
|
||||||
<version>2.17.1</version>
|
<version>2.17.1</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.github.NuVotifier.NuVotifier/nuvotifier-bukkit -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.vexsoftware</groupId>
|
<groupId>com.github.NuVotifier.NuVotifier</groupId>
|
||||||
<artifactId>nuvotifier-universal</artifactId>
|
<artifactId>nuvotifier-bukkit</artifactId>
|
||||||
<version>2.3.4</version>
|
<version>v2.7.1</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.onarandombox.multiversecore</groupId>
|
<groupId>com.onarandombox.multiversecore</groupId>
|
||||||
<artifactId>Multiverse-Core</artifactId>
|
<artifactId>Multiverse-Core</artifactId>
|
||||||
<version>4.0.1</version>
|
<version>4.3.1</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>me.lucko</groupId>
|
<groupId>me.lucko</groupId>
|
||||||
<artifactId>commodore</artifactId>
|
<artifactId>commodore</artifactId>
|
||||||
<version>1.7</version>
|
<version>1.11</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -218,6 +216,7 @@
|
||||||
<!-- 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>
|
||||||
|
|
|
@ -4,18 +4,28 @@ import buttondevteam.lib.architecture.ButtonPlugin;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
import buttondevteam.lib.chat.ICommand2MC;
|
import buttondevteam.lib.chat.ICommand2MC;
|
||||||
|
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 java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@CommandClass
|
@CommandClass
|
||||||
public class ChromaCommand extends ICommand2MC {
|
public class ChromaCommand extends ICommand2MC {
|
||||||
|
public ChromaCommand() {
|
||||||
|
getManager().addParamConverter(ButtonPlugin.class, name ->
|
||||||
|
(ButtonPlugin) Optional.ofNullable(Bukkit.getPluginManager().getPlugin(name))
|
||||||
|
.filter(plugin -> plugin instanceof ButtonPlugin).orElse(null),
|
||||||
|
"No Chroma plugin found by that name.", () -> Arrays.stream(Bukkit.getPluginManager().getPlugins())
|
||||||
|
.filter(plugin -> plugin instanceof ButtonPlugin).map(Plugin::getName)::iterator);
|
||||||
|
}
|
||||||
|
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
public void reload(CommandSender sender, @Command2.OptionalArg Plugin plugin) {
|
public void reload(CommandSender sender, @Command2.OptionalArg ButtonPlugin plugin) {
|
||||||
if (plugin == null)
|
if (plugin == null)
|
||||||
plugin = MainPlugin.Instance;
|
plugin = MainPlugin.Instance;
|
||||||
if (!(plugin instanceof ButtonPlugin)) //Probably not a good idea to allow reloading any plugin's config
|
if (plugin.tryReloadConfig())
|
||||||
sender.sendMessage("§c" + plugin.getName() + " doesn't support this.");
|
|
||||||
else if (((ButtonPlugin) plugin).tryReloadConfig())
|
|
||||||
sender.sendMessage("§b" + plugin.getName() + " config reloaded.");
|
sender.sendMessage("§b" + plugin.getName() + " config reloaded.");
|
||||||
else
|
else
|
||||||
sender.sendMessage("§cFailed to reload config. Check console.");
|
sender.sendMessage("§cFailed to reload config. Check console.");
|
||||||
|
|
|
@ -67,9 +67,10 @@ public class ComponentCommand extends ICommand2MC {
|
||||||
return getPluginComponents(plugin).map(c -> c.getClass().getSimpleName())::iterator;
|
return getPluginComponents(plugin).map(c -> c.getClass().getSimpleName())::iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@CustomTabCompleteMethod(param = "plugin")
|
@CustomTabCompleteMethod(param = "plugin", subcommand = {"list", "enable", "disable"}, ignoreTypeCompletion = true)
|
||||||
public Iterable<String> list() {
|
public Iterable<String> pluginTabcomplete() {
|
||||||
return Arrays.stream(Bukkit.getPluginManager().getPlugins()).map(Plugin::getName)::iterator;
|
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) {
|
private boolean enable_disable(CommandSender sender, Plugin plugin, String component, boolean enable, boolean permanent) {
|
||||||
|
@ -79,10 +80,10 @@ public class ComponentCommand extends ICommand2MC {
|
||||||
return true;
|
return true;
|
||||||
Component.setComponentEnabled(oc.get(), enable);
|
Component.setComponentEnabled(oc.get(), enable);
|
||||||
if (permanent)
|
if (permanent)
|
||||||
oc.get().shouldBeEnabled().set(enable);
|
oc.get().shouldBeEnabled.set(enable);
|
||||||
sender.sendMessage(oc.get().getClass().getSimpleName() + " " + (enable ? "en" : "dis") + "abled " + (permanent ? "permanently" : "temporarily") + ".");
|
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);
|
TBMCCoreAPI.SendException("Couldn't " + (enable ? "en" : "dis") + "able component " + component + "!", e, (JavaPlugin) plugin);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 | NoClassDefFoundError e) {
|
||||||
TBMCCoreAPI.SendException("Failed to enable one of the components: " + c.getClass().getSimpleName(), e);
|
TBMCCoreAPI.SendException("Failed to enable one of the components: " + c.getClass().getSimpleName(), e, c);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
componentsEnabled = true;
|
componentsEnabled = true;
|
||||||
|
|
|
@ -8,7 +8,6 @@ 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.spawn.SpawnComponent;
|
||||||
import buttondevteam.core.component.towny.TownyComponent;
|
import buttondevteam.core.component.towny.TownyComponent;
|
||||||
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;
|
||||||
|
@ -65,37 +64,26 @@ public class MainPlugin extends ButtonPlugin {
|
||||||
* Sets whether the plugin should write a list of installed plugins in a txt file.
|
* Sets whether the plugin should write a list of installed plugins in a txt file.
|
||||||
* It can be useful if some other software needs to know the plugins.
|
* It can be useful if some other software needs to know the plugins.
|
||||||
*/
|
*/
|
||||||
private ConfigData<Boolean> writePluginList() {
|
private final ConfigData<Boolean> writePluginList = getIConfig().getData("writePluginList", false);
|
||||||
return getIConfig().getData("writePluginList", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The chat format to use for messages from other platforms if Chroma-Chat is not installed.
|
* The chat format to use for messages from other platforms if Chroma-Chat is not installed.
|
||||||
*/
|
*/
|
||||||
ConfigData<String> chatFormat() {
|
ConfigData<String> chatFormat = getIConfig().getData("chatFormat", "[{origin}|" +
|
||||||
return getIConfig().getData("chatFormat", "[{origin}|" +
|
"{channel}] <{name}> {message}");
|
||||||
"{channel}] <{name}> {message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print some debug information.
|
* Print some debug information.
|
||||||
*/
|
*/
|
||||||
public ConfigData<Boolean> test() {
|
public final ConfigData<Boolean> test = getIConfig().getData("test", false);
|
||||||
return getIConfig().getData("test", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* By default, the plugin uses Vault for all command permission checks, but this can have issues (with PEX for example) where default permissions aren't granted.
|
* 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.
|
||||||
* When this setting is off, the plugin uses Bukkit's built-in way of handling permissions, which usually works fine for players.
|
|
||||||
* You can also grant chroma.command.* to each player (mod-only commands need another permission, chroma.mod).
|
|
||||||
*/
|
*/
|
||||||
/*public ConfigData<Boolean> useVaultForCommands() {
|
public final ConfigData<Boolean> prioritizeCustomCommands = getIConfig().getData("prioritizeCustomCommands", false);
|
||||||
return getIConfig().getData("useVaultForCommands", true);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@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();
|
||||||
|
@ -105,7 +93,6 @@ public class MainPlugin extends ButtonPlugin {
|
||||||
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.");
|
||||||
saveConfig();
|
saveConfig();
|
||||||
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());
|
||||||
|
@ -113,8 +100,8 @@ public class MainPlugin extends ButtonPlugin {
|
||||||
Component.registerComponent(this, new SpawnComponent());
|
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());
|
registerCommand(new ComponentCommand());
|
||||||
registerCommand(new ChromaCommand());
|
registerCommand(new ChromaCommand());
|
||||||
|
@ -124,7 +111,7 @@ public class MainPlugin extends ButtonPlugin {
|
||||||
? 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);
|
TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase.class, TBMCPlayer::new);
|
||||||
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)));
|
||||||
|
@ -140,22 +127,22 @@ public class MainPlugin extends ButtonPlugin {
|
||||||
Supplier<Iterable<String>> playerSupplier = () -> Bukkit.getOnlinePlayers().stream().map(HumanEntity::getName)::iterator;
|
Supplier<Iterable<String>> playerSupplier = () -> Bukkit.getOnlinePlayers().stream().map(HumanEntity::getName)::iterator;
|
||||||
getCommand2MC().addParamConverter(OfflinePlayer.class, Bukkit::getOfflinePlayer, "Player not found!", playerSupplier);
|
getCommand2MC().addParamConverter(OfflinePlayer.class, Bukkit::getOfflinePlayer, "Player not found!", playerSupplier);
|
||||||
getCommand2MC().addParamConverter(Player.class, Bukkit::getPlayer, "Online player not found!", playerSupplier);
|
getCommand2MC().addParamConverter(Player.class, Bukkit::getPlayer, "Online player not found!", playerSupplier);
|
||||||
if (writePluginList().get()) {
|
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);
|
TBMCCoreAPI.SendException("Failed to write plugin list!", e, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (getServer().getPluginManager().isPluginEnabled("Essentials"))
|
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.get() + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pluginDisable() {
|
public void pluginDisable() {
|
||||||
logger.info("Saving player data...");
|
logger.info("Saving player data...");
|
||||||
TBMCPlayerBase.savePlayers();
|
ChromaGamerBase.saveUsers();
|
||||||
logger.info("Player data saved.");
|
logger.info("Player data saved.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,12 +28,21 @@ public class PlayerListener implements Listener {
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL)
|
@EventHandler(priority = EventPriority.NORMAL)
|
||||||
public void OnPlayerJoin(PlayerJoinEvent event) {
|
public void OnPlayerJoin(PlayerJoinEvent event) {
|
||||||
TBMCPlayerBase.joinPlayer(event.getPlayer());
|
var p = 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.quitPlayer(event.getPlayer());
|
TBMCPlayerBase.getPlayer(event.getPlayer().getUniqueId(), TBMCPlayer.class).uncache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST)
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
|
@ -43,7 +52,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
|
||||||
|
@ -59,13 +68,9 @@ 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 = Optional.ofNullable(ChromaGamerBase.getFromSender(sender));
|
|
||||||
val ch = cg.map(ChromaGamerBase::channel).map(ChannelPlayerData::get);
|
|
||||||
val rtr = ch.map(c -> c.getRTR(sender)).orElseGet(() -> new Channel.RecipientTestResult("Failed to get user"));
|
|
||||||
val ev = new TBMCCommandPreprocessEvent(sender, ch.orElse(Channel.GlobalChat), message, rtr.score, rtr.groupID);*/
|
|
||||||
val cg = ChromaGamerBase.getFromSender(sender);
|
val cg = ChromaGamerBase.getFromSender(sender);
|
||||||
if (cg == null) throw new RuntimeException("Couldn't get user from sender for " + sender.getName() + "!");
|
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);
|
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
|
||||||
|
@ -77,7 +82,7 @@ public class PlayerListener implements Listener {
|
||||||
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.getChannel(), event.getPermCheck()), event.getMessage()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Command processing failed for sender '" + event.getSender() + "' and message '" + event.getMessage() + "'", e);
|
TBMCCoreAPI.SendException("Command processing failed for sender '" + event.getSender() + "' and message '" + event.getMessage() + "'", e, MainPlugin.Instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,11 +103,11 @@ public class PlayerListener implements Listener {
|
||||||
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();
|
var channel = event.getChannel();
|
||||||
String msg = MainPlugin.Instance.chatFormat().get()
|
String msg = MainPlugin.Instance.chatFormat.get()
|
||||||
.replace("{channel}", channel.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}", ChromaUtils.getDisplayName(event.getSender()))
|
||||||
.replace("{message}", "§" + channel.Color().get().ordinal() + event.getMessage());
|
.replace("{message}", String.format("§%x%s", channel.Color.get().ordinal(), 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);
|
||||||
|
|
|
@ -2,6 +2,7 @@ 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;
|
||||||
|
@ -19,11 +20,13 @@ 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
|
||||||
public Object answer(InvocationOnMock invocation) {
|
public Object answer(InvocationOnMock invocation) {
|
||||||
if (returns(invocation, String.class))
|
if (returns(invocation, String.class))
|
||||||
return "test";
|
return "test";
|
||||||
if (returns(invocation, Logger.class))
|
if (returns(invocation, Logger.class))
|
||||||
|
@ -41,7 +44,6 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import buttondevteam.core.ComponentManager;
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
|
import buttondevteam.lib.architecture.IHaveConfig;
|
||||||
import buttondevteam.lib.chat.Color;
|
import buttondevteam.lib.chat.Color;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
@ -20,127 +21,135 @@ import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a chat channel. May only be instantiated after the channel component is registered.
|
||||||
|
*/
|
||||||
public class Channel {
|
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}
|
* 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;
|
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.
|
* 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;
|
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
|
* 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";
|
public static final String GROUP_EVERYONE = "everyone";
|
||||||
|
|
||||||
private static ChannelComponent component;
|
private static ChannelComponent component;
|
||||||
|
|
||||||
private String defDisplayName;
|
private String defDisplayName;
|
||||||
private Color defColor;
|
private Color defColor;
|
||||||
|
|
||||||
private void throwGame() {
|
private IHaveConfig config;
|
||||||
if (component == null) throw new RuntimeException("Cannot access channel properties until registered!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ConfigData<Boolean> Enabled() {
|
public final ConfigData<Boolean> Enabled;
|
||||||
throwGame();
|
|
||||||
return component.getConfig().getData(ID + ".enabled", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Must start with a color code
|
* Must start with a color code
|
||||||
*/
|
*/
|
||||||
public final ConfigData<String> DisplayName() {
|
public final ConfigData<String> DisplayName;
|
||||||
throwGame();
|
|
||||||
return component.getConfig().getData(ID + ".displayName", defDisplayName); //TODO: Use config map
|
public final ConfigData<Color> Color;
|
||||||
|
public final String ID;
|
||||||
|
|
||||||
|
public ConfigData<String[]> IDs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
init();
|
||||||
|
Enabled = component.getConfig().getData(ID + ".enabled", true);
|
||||||
|
DisplayName = component.getConfig().getData(ID + ".displayName", defDisplayName);
|
||||||
|
Color = component.getConfig().getData(ID + ".color", defColor, c -> buttondevteam.lib.chat.Color.valueOf((String) c), Enum::toString);
|
||||||
|
//noinspection unchecked
|
||||||
|
IDs = component.getConfig().getData(ID + ".IDs", new String[0], l -> ((List<String>) l).toArray(new String[0]), Lists::newArrayList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ConfigData<Color> Color() {
|
/**
|
||||||
throwGame();
|
* Must be only called from a subclass - otherwise it'll throw an exception.
|
||||||
return component.getConfig().getData(ID + ".color", defColor, c -> Color.valueOf((String) c), Enum::toString);
|
*
|
||||||
}
|
* @see Channel#Channel(String, Color, String, Function)
|
||||||
public final String ID;
|
*/
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ConfigData<String[]> IDs() {
|
protected <T extends Channel> Channel(String displayname, Color color, String command,
|
||||||
throwGame();
|
BiFunction<T, CommandSender, RecipientTestResult> filteranderrormsg) {
|
||||||
return component.getConfig().getData(ID + ".IDs", new String[0], l -> ((List<String>) l).toArray(new String[0]), Lists::newArrayList);
|
defDisplayName = displayname;
|
||||||
|
defColor = color;
|
||||||
|
ID = command;
|
||||||
|
this.filteranderrormsg = s -> filteranderrormsg.apply((T) this, s);
|
||||||
|
init();
|
||||||
|
Enabled = component.getConfig().getData(ID + ".enabled", true);
|
||||||
|
DisplayName = component.getConfig().getData(ID + ".displayName", defDisplayName);
|
||||||
|
Color = component.getConfig().getData(ID + ".color", defColor, c -> buttondevteam.lib.chat.Color.valueOf((String) c), Enum::toString);
|
||||||
|
//noinspection unchecked
|
||||||
|
IDs = 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<>();
|
private static void init() {
|
||||||
|
if (component == null)
|
||||||
|
component = (ChannelComponent) Component.getComponents().get(ChannelComponent.class);
|
||||||
|
if (component == null)
|
||||||
|
throw new RuntimeException("Attempting to create a channel before the component is registered!");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
public boolean isGlobal() {
|
||||||
* Creates a channel.
|
return filteranderrormsg == null;
|
||||||
*
|
}
|
||||||
* @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.
|
* Note: Errors are sent to the sender automatically
|
||||||
*
|
*
|
||||||
* @see Channel#Channel(String, Color, String, Function)
|
* @param sender The user we're sending to
|
||||||
*/
|
* @param score The (source) score to compare with the user's
|
||||||
@SuppressWarnings("unchecked")
|
*/
|
||||||
protected <T extends Channel> Channel(String displayname, Color color, String command,
|
public boolean shouldSendTo(CommandSender sender, int score) {
|
||||||
BiFunction<T, CommandSender, RecipientTestResult> filteranderrormsg) {
|
return score == getMCScore(sender); //If there's any error, the score won't be equal
|
||||||
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
|
||||||
}
|
*/
|
||||||
|
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
|
* Note: Errors are sent to the sender automatically<br>
|
||||||
*
|
* <p>
|
||||||
* @param sender The user we're sending to
|
* Null means don't send
|
||||||
* @param score The (source) score to compare with the user's
|
*/
|
||||||
*/
|
@Nullable
|
||||||
public boolean shouldSendTo(CommandSender sender, int score) {
|
public String getGroupID(CommandSender sender) {
|
||||||
return score == getMCScore(sender); //If there's any error, the score won't be equal
|
return getRTR(sender).groupID; //No need to check if there was an error
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public RecipientTestResult getRTR(CommandSender sender) {
|
||||||
* Note: Errors are sent to the sender automatically
|
if (filteranderrormsg == null)
|
||||||
*/
|
return new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE);
|
||||||
public int getMCScore(CommandSender sender) {
|
return filteranderrormsg.apply(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
|
* Get a stream of the enabled channels
|
||||||
|
@ -148,7 +157,7 @@ public class Channel {
|
||||||
* @return Only the enabled channels
|
* @return Only the enabled channels
|
||||||
*/
|
*/
|
||||||
public static Stream<Channel> getChannels() {
|
public static Stream<Channel> getChannels() {
|
||||||
return channels.stream().filter(ch -> ch.Enabled().get());
|
return channels.stream().filter(ch -> ch.Enabled.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,73 +169,69 @@ public class Channel {
|
||||||
return Collections.unmodifiableList(channels);
|
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
|
* 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.
|
* generated automatically.
|
||||||
*
|
*
|
||||||
* @param permgroup The group that can access the channel or <b>null</b> to only allow OPs.
|
* @param permgroup The group that can access the channel or <b>null</b> to only allow OPs.
|
||||||
* @return If has access
|
* @return If has access
|
||||||
*/
|
*/
|
||||||
public static Function<CommandSender, RecipientTestResult> inGroupFilter(String permgroup) {
|
public static Function<CommandSender, RecipientTestResult> inGroupFilter(String permgroup) {
|
||||||
return noScoreResult(
|
return noScoreResult(
|
||||||
s -> s.isOp() || (permgroup != null && (s instanceof Player && MainPlugin.permission != null && MainPlugin.permission.playerInGroup((Player) s, permgroup))),
|
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.");
|
"You need to be a(n) " + (permgroup != null ? permgroup : "OP") + " to use this channel.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function<CommandSender, RecipientTestResult> noScoreResult(Predicate<CommandSender> filter,
|
public static Function<CommandSender, RecipientTestResult> noScoreResult(Predicate<CommandSender> filter,
|
||||||
String errormsg) {
|
String errormsg) {
|
||||||
return s -> filter.test(s) ? new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE) : new RecipientTestResult(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(
|
public static <T extends Channel> BiFunction<T, CommandSender, RecipientTestResult> noScoreResult(
|
||||||
BiPredicate<T, CommandSender> filter, String errormsg) {
|
BiPredicate<T, CommandSender> filter, String errormsg) {
|
||||||
return (this_, s) -> filter.test(this_, s) ? new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE) : new RecipientTestResult(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 GlobalChat;
|
||||||
public static Channel AdminChat;
|
public static Channel AdminChat;
|
||||||
public static Channel ModChat;
|
public static Channel ModChat;
|
||||||
|
|
||||||
public static void RegisterChannel(Channel channel) {
|
public static void RegisterChannel(Channel channel) {
|
||||||
if (!channel.isGlobal() && !ComponentManager.isEnabled(ChannelComponent.class))
|
if (!channel.isGlobal() && !ComponentManager.isEnabled(ChannelComponent.class))
|
||||||
return; //Allow registering the global chat (and I guess other chats like the RP chat)
|
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);
|
channels.add(channel);
|
||||||
component.registerChannelCommand(channel);
|
component.registerChannelCommand(channel);
|
||||||
Bukkit.getScheduler().runTask(MainPlugin.Instance, () -> Bukkit.getPluginManager().callEvent(new ChatChannelRegisterEvent(channel))); // Wait for server start
|
Bukkit.getScheduler().runTask(MainPlugin.Instance, () -> Bukkit.getPluginManager().callEvent(new ChatChannelRegisterEvent(channel))); // Wait for server start
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RecipientTestResult {
|
public static class RecipientTestResult {
|
||||||
public final String errormessage;
|
public final String errormessage;
|
||||||
public final int score; // Anything below 0 is "never send"
|
public final int score; // Anything below 0 is "never send"
|
||||||
public final String groupID;
|
public final String groupID;
|
||||||
public static final RecipientTestResult ALL = new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE);
|
public static final RecipientTestResult ALL = new RecipientTestResult(SCORE_SEND_OK, GROUP_EVERYONE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a result that indicates an <b>error</b>
|
* 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.
|
* @param errormessage The error message to show the sender if they don't meet the criteria.
|
||||||
*/
|
*/
|
||||||
public RecipientTestResult(String errormessage) {
|
public RecipientTestResult(String errormessage) {
|
||||||
this.errormessage = errormessage;
|
this.errormessage = errormessage;
|
||||||
this.score = SCORE_SEND_NOPE;
|
this.score = SCORE_SEND_NOPE;
|
||||||
this.groupID = null;
|
this.groupID = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a result that indicates a <b>success</b>
|
* 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 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.
|
* @param groupID The ID of the target group.
|
||||||
*/
|
*/
|
||||||
public RecipientTestResult(int score, String groupID) {
|
public RecipientTestResult(int score, String groupID) {
|
||||||
if (score < 0) throw new IllegalArgumentException("Score must be non-negative!");
|
if (score < 0) throw new IllegalArgumentException("Score must be non-negative!");
|
||||||
this.score = score;
|
this.score = score;
|
||||||
this.groupID = groupID;
|
this.groupID = groupID;
|
||||||
this.errormessage = null;
|
this.errormessage = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package buttondevteam.core.component.channel;
|
package buttondevteam.core.component.channel;
|
||||||
|
|
||||||
|
import buttondevteam.lib.ChromaUtils;
|
||||||
import buttondevteam.lib.TBMCSystemChatEvent;
|
import buttondevteam.lib.TBMCSystemChatEvent;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.chat.*;
|
import buttondevteam.lib.chat.*;
|
||||||
|
@ -10,7 +11,7 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||||
/**
|
/**
|
||||||
* Manages chat channels. If disabled, only global channels will be registered.
|
* Manages chat channels. If disabled, only global channels will be registered.
|
||||||
*/
|
*/
|
||||||
public class ChannelComponent extends Component {
|
public class ChannelComponent extends Component<JavaPlugin> {
|
||||||
static TBMCSystemChatEvent.BroadcastTarget roomJoinLeave;
|
static TBMCSystemChatEvent.BroadcastTarget roomJoinLeave;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -35,7 +36,8 @@ public class ChannelComponent extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerChannelCommand(Channel channel) {
|
void registerChannelCommand(Channel channel) {
|
||||||
registerCommand(new ChannelCommand(channel));
|
if (!ChromaUtils.isTest())
|
||||||
|
registerCommand(new ChannelCommand(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandClass
|
@CommandClass
|
||||||
|
@ -45,7 +47,12 @@ public class ChannelComponent extends Component {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCommandPath() {
|
public String getCommandPath() {
|
||||||
return channel.ID; //TODO: IDs
|
return channel.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getCommandPaths() {
|
||||||
|
return channel.IDs.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
|
@ -57,17 +64,17 @@ public class ChannelComponent extends Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
Channel oldch = user.channel().get();
|
Channel oldch = user.channel.get();
|
||||||
if (oldch instanceof ChatRoom)
|
if (oldch instanceof ChatRoom)
|
||||||
((ChatRoom) oldch).leaveRoom(sender);
|
((ChatRoom) oldch).leaveRoom(sender);
|
||||||
if (oldch.equals(channel))
|
if (oldch.equals(channel))
|
||||||
user.channel().set(Channel.GlobalChat);
|
user.channel.set(Channel.GlobalChat);
|
||||||
else {
|
else {
|
||||||
user.channel().set(channel);
|
user.channel.set(channel);
|
||||||
if (channel instanceof ChatRoom)
|
if (channel instanceof ChatRoom)
|
||||||
((ChatRoom) channel).joinRoom(sender);
|
((ChatRoom) channel).joinRoom(sender);
|
||||||
}
|
}
|
||||||
sender.sendMessage("§6You are now talking in: §b" + user.channel().get().DisplayName().get());
|
sender.sendMessage("§6You are now talking in: §b" + user.channel.get().DisplayName.get());
|
||||||
} else
|
} else
|
||||||
TBMCChatAPI.SendChatMessage(ChatMessage.builder(sender, user, message).fromCommand(true)
|
TBMCChatAPI.SendChatMessage(ChatMessage.builder(sender, user, message).fromCommand(true)
|
||||||
.permCheck(senderMC.getPermCheck()).build(), channel);
|
.permCheck(senderMC.getPermCheck()).build(), channel);
|
||||||
|
|
|
@ -38,8 +38,8 @@ 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!");
|
||||||
|
|
|
@ -25,23 +25,17 @@ public class MemberComponent extends Component<MainPlugin> implements Listener {
|
||||||
/**
|
/**
|
||||||
* The permission group to give to the player
|
* The permission group to give to the player
|
||||||
*/
|
*/
|
||||||
ConfigData<String> memberGroup() {
|
final ConfigData<String> memberGroup = getConfig().getData("memberGroup", "member");
|
||||||
return getConfig().getData("memberGroup", "member");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of hours needed to play before promotion
|
* The amount of hours needed to play before promotion
|
||||||
*/
|
*/
|
||||||
private ConfigData<Integer> playedHours() {
|
private final ConfigData<Integer> playedHours = getConfig().getData("playedHours", 12);
|
||||||
return getConfig().getData("playedHours", 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of days passed since first login
|
* The amount of days passed since first login
|
||||||
*/
|
*/
|
||||||
private ConfigData<Integer> registeredForDays() {
|
private final ConfigData<Integer> registeredForDays = getConfig().getData("registeredForDays", 7);
|
||||||
return getConfig().getData("registeredForDays", 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AbstractMap.SimpleEntry<Statistic, Integer> playtime;
|
private AbstractMap.SimpleEntry<Statistic, Integer> playtime;
|
||||||
|
|
||||||
|
@ -69,22 +63,22 @@ public class MemberComponent extends Component<MainPlugin> implements Listener {
|
||||||
|
|
||||||
public Boolean addPlayerAsMember(Player player) {
|
public Boolean addPlayerAsMember(Player player) {
|
||||||
try {
|
try {
|
||||||
if (permission.playerAddGroup(null, player, memberGroup().get())) {
|
if (permission.playerAddGroup(null, player, memberGroup.get())) {
|
||||||
player.sendMessage("§bYou are a member now!");
|
player.sendMessage("§bYou are a member now!");
|
||||||
MainPlugin.Instance.getLogger().info("Added " + player.getName() + " as a member.");
|
log("Added " + player.getName() + " as a member.");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
MainPlugin.Instance.getLogger().warning("Failed to assign the member role! Please make sure the member group exists or disable the component if it's unused.");
|
logWarn("Failed to assign the member role! Please make sure the member group exists or disable the component if it's unused.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (UnsupportedOperationException e) {
|
} catch (UnsupportedOperationException e) {
|
||||||
MainPlugin.Instance.getLogger().warning("Failed to assign the member role! Groups are not supported by the permissions implementation.");
|
logWarn("Failed to assign the member role! Groups are not supported by the permissions implementation.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkNotMember(Player player) {
|
public boolean checkNotMember(Player player) {
|
||||||
return permission != null && !permission.playerInGroup(player, memberGroup().get());
|
return permission != null && !permission.playerInGroup(player, memberGroup.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkRegTime(Player player) {
|
public boolean checkRegTime(Player player) {
|
||||||
|
@ -92,14 +86,14 @@ public class MemberComponent extends Component<MainPlugin> implements Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkPlayTime(Player player) {
|
public boolean checkPlayTime(Player player) {
|
||||||
return getPlayTime(player) > playtime.getValue() * playedHours().get();
|
return getPlayTime(player) > playtime.getValue() * playedHours.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns milliseconds
|
* Returns milliseconds
|
||||||
*/
|
*/
|
||||||
public long getRegTime(Player player) {
|
public long getRegTime(Player player) {
|
||||||
Instant date = new Date(player.getFirstPlayed()).toInstant().plus(registeredForDays().get(), ChronoUnit.DAYS);
|
Instant date = new Date(player.getFirstPlayed()).toInstant().plus(registeredForDays.get(), ChronoUnit.DAYS);
|
||||||
if (date.isAfter(Instant.now()))
|
if (date.isAfter(Instant.now()))
|
||||||
return date.toEpochMilli() - Instant.now().toEpochMilli();
|
return date.toEpochMilli() - Instant.now().toEpochMilli();
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -113,7 +107,7 @@ public class MemberComponent extends Component<MainPlugin> implements Listener {
|
||||||
* Returns hours
|
* Returns hours
|
||||||
*/
|
*/
|
||||||
public double getPlayTime(Player player) {
|
public double getPlayTime(Player player) {
|
||||||
double pt = playedHours().get() - (double) getPlayTimeTotal(player) / playtime.getValue();
|
double pt = playedHours.get() - (double) getPlayTimeTotal(player) / playtime.getValue();
|
||||||
if (pt < 0) return -1;
|
if (pt < 0) return -1;
|
||||||
return pt;
|
return pt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,11 +59,10 @@ public class RandomTP extends ICommand2MC
|
||||||
{
|
{
|
||||||
world = Bukkit.getWorld("World");
|
world = Bukkit.getWorld("World");
|
||||||
border = world.getWorldBorder();
|
border = world.getWorldBorder();
|
||||||
Logger logger = component.getPlugin().getLogger();
|
component.log("Getting new location");
|
||||||
logger.info("Getting new location");
|
|
||||||
if(border.getSize() > 100000)
|
if(border.getSize() > 100000)
|
||||||
logger.warning("World border is wide, it may take a minute...");
|
component.logWarn("World border is wide, it may take a minute...");
|
||||||
logger.info("Success: "+newLocation());
|
component.log("Success: "+newLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*================================================================================================*/
|
/*================================================================================================*/
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.bukkit.command.CommandSender;
|
||||||
public class PrimeRestartCommand extends ICommand2MC {
|
public class PrimeRestartCommand extends ICommand2MC {
|
||||||
private final RestartComponent component;
|
private final RestartComponent component;
|
||||||
|
|
||||||
|
@Command2.Subcommand
|
||||||
public void def(CommandSender sender, @Command2.TextArg @Command2.OptionalArg String somethingrandom) {
|
public void def(CommandSender sender, @Command2.TextArg @Command2.OptionalArg String somethingrandom) {
|
||||||
loud = somethingrandom != null;
|
loud = somethingrandom != null;
|
||||||
if (Bukkit.getOnlinePlayers().size() > 0) {
|
if (Bukkit.getOnlinePlayers().size() > 0) {
|
||||||
|
|
|
@ -4,6 +4,8 @@ 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;
|
||||||
|
@ -13,16 +15,29 @@ 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() {
|
||||||
registerCommand(new ScheduledRestartCommand(this));
|
var scheduledRestartCommand = new ScheduledRestartCommand(this);
|
||||||
|
registerCommand(scheduledRestartCommand);
|
||||||
registerCommand(new PrimeRestartCommand(this));
|
registerCommand(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
|
||||||
|
@ -30,10 +45,28 @@ 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()
|
||||||
|
|
|
@ -45,20 +45,20 @@ public class ScheduledRestartCommand extends ICommand2MC {
|
||||||
}
|
}
|
||||||
final int restarttime = restartCounter = seconds * 20;
|
final int restarttime = restartCounter = seconds * 20;
|
||||||
restartbar = Bukkit.createBossBar("Server restart in " + seconds + "s", BarColor.RED, BarStyle.SOLID,
|
restartbar = Bukkit.createBossBar("Server restart in " + seconds + "s", BarColor.RED, BarStyle.SOLID,
|
||||||
BarFlag.DARKEN_SKY);
|
BarFlag.DARKEN_SKY);
|
||||||
restartbar.setProgress(1);
|
restartbar.setProgress(1);
|
||||||
Bukkit.getOnlinePlayers().forEach(p -> restartbar.addPlayer(p));
|
Bukkit.getOnlinePlayers().forEach(p -> restartbar.addPlayer(p));
|
||||||
sender.sendMessage("Scheduled restart in " + seconds);
|
sender.sendMessage("Scheduled restart in " + seconds);
|
||||||
ScheduledServerRestartEvent e = new ScheduledServerRestartEvent(restarttime, this);
|
ScheduledServerRestartEvent e = new ScheduledServerRestartEvent(restarttime, this);
|
||||||
Bukkit.getPluginManager().callEvent(e);
|
Bukkit.getPluginManager().callEvent(e);
|
||||||
restarttask = Bukkit.getScheduler().runTaskTimer(MainPlugin.Instance, () -> {
|
restarttask = Bukkit.getScheduler().runTaskTimer(MainPlugin.Instance, () -> {
|
||||||
if (restartCounter < 0) {
|
if (restartCounter < 0) {
|
||||||
restarttask.cancel();
|
restarttask.cancel();
|
||||||
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 && Bukkit.getOnlinePlayers().size() > 0)
|
||||||
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§c-- The server is restarting in " + restartCounter / 20 + " seconds! (/press)", component.getRestartBroadcast());
|
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, "§c-- The server is restarting in " + restartCounter / 20 + " seconds!", 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--;
|
||||||
|
|
|
@ -2,6 +2,7 @@ package buttondevteam.core.component.spawn;
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
|
import buttondevteam.lib.architecture.ComponentMetadata;
|
||||||
import buttondevteam.lib.architecture.ConfigData;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
|
@ -23,11 +24,12 @@ import java.math.BigDecimal;
|
||||||
/**
|
/**
|
||||||
* Provides a /spawn command that works with BungeeCord. Make sure to set up on each server.
|
* Provides a /spawn command that works with BungeeCord. Make sure to set up on each server.
|
||||||
*/
|
*/
|
||||||
|
@ComponentMetadata(enabledByDefault = false)
|
||||||
public class SpawnComponent extends Component<MainPlugin> implements PluginMessageListener {
|
public class SpawnComponent extends Component<MainPlugin> implements PluginMessageListener {
|
||||||
@Override
|
@Override
|
||||||
protected void enable() {
|
protected void enable() {
|
||||||
registerCommand(new SpawnCommand());
|
registerCommand(new SpawnCommand());
|
||||||
if (targetServer().get().length() == 0) {
|
if (targetServer.get().length() == 0) {
|
||||||
spawnloc = MultiverseCore.getPlugin(MultiverseCore.class).getMVWorldManager().getFirstSpawnWorld()
|
spawnloc = MultiverseCore.getPlugin(MultiverseCore.class).getMVWorldManager().getFirstSpawnWorld()
|
||||||
.getSpawnLocation();
|
.getSpawnLocation();
|
||||||
}
|
}
|
||||||
|
@ -47,7 +49,7 @@ public class SpawnComponent extends Component<MainPlugin> implements PluginMessa
|
||||||
if (!channel.equals("BungeeCord")) {
|
if (!channel.equals("BungeeCord")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (targetServer().get().length() != 0)
|
if (targetServer.get().length() != 0)
|
||||||
return;
|
return;
|
||||||
ByteArrayDataInput in = ByteStreams.newDataInput(message);
|
ByteArrayDataInput in = ByteStreams.newDataInput(message);
|
||||||
String subchannel = in.readUTF();
|
String subchannel = in.readUTF();
|
||||||
|
@ -76,9 +78,7 @@ public class SpawnComponent extends Component<MainPlugin> implements PluginMessa
|
||||||
/**
|
/**
|
||||||
* The BungeeCord server that has the spawn. Set to empty if this server is the target.
|
* The BungeeCord server that has the spawn. Set to empty if this server is the target.
|
||||||
*/
|
*/
|
||||||
private ConfigData<String> targetServer() {
|
private final ConfigData<String> targetServer = getConfig().getData("targetServer", "");
|
||||||
return getConfig().getData("targetServer", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Location spawnloc;
|
private Location spawnloc;
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ public class SpawnComponent extends Component<MainPlugin> implements PluginMessa
|
||||||
@SuppressWarnings("UnstableApiUsage")
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
public void def(Player player) {
|
public void def(Player player) {
|
||||||
if (targetServer().get().length() == 0) {
|
if (targetServer.get().length() == 0) {
|
||||||
player.sendMessage("§bTeleporting to spawn...");
|
player.sendMessage("§bTeleporting to spawn...");
|
||||||
try {
|
try {
|
||||||
if (MainPlugin.ess != null)
|
if (MainPlugin.ess != null)
|
||||||
|
@ -105,7 +105,7 @@ public class SpawnComponent extends Component<MainPlugin> implements PluginMessa
|
||||||
}
|
}
|
||||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||||
out.writeUTF("Connect");
|
out.writeUTF("Connect");
|
||||||
out.writeUTF(targetServer().get());
|
out.writeUTF(targetServer.get());
|
||||||
|
|
||||||
player.sendPluginMessage(getPlugin(), "BungeeCord", out.toByteArray());
|
player.sendPluginMessage(getPlugin(), "BungeeCord", out.toByteArray());
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
package buttondevteam.core.component.towny;
|
package buttondevteam.core.component.towny;
|
||||||
|
|
||||||
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
import buttondevteam.lib.chat.CustomTabComplete;
|
import buttondevteam.lib.chat.CustomTabComplete;
|
||||||
import buttondevteam.lib.chat.ICommand2MC;
|
import buttondevteam.lib.chat.ICommand2MC;
|
||||||
|
import buttondevteam.lib.player.TBMCPlayer;
|
||||||
|
import com.earth2me.essentials.Essentials;
|
||||||
|
import com.earth2me.essentials.User;
|
||||||
import com.palmergames.bukkit.towny.TownySettings;
|
import com.palmergames.bukkit.towny.TownySettings;
|
||||||
import com.palmergames.bukkit.towny.TownyUniverse;
|
import com.palmergames.bukkit.towny.TownyUniverse;
|
||||||
|
import com.palmergames.bukkit.towny.object.Resident;
|
||||||
import com.palmergames.bukkit.towny.object.TownyObject;
|
import com.palmergames.bukkit.towny.object.TownyObject;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@ -26,10 +33,17 @@ public class RemoveResidentsCommand extends ICommand2MC {
|
||||||
sender.sendMessage("Starting...");
|
sender.sendMessage("Starting...");
|
||||||
var ds = TownyUniverse.getInstance().getDataSource();
|
var ds = TownyUniverse.getInstance().getDataSource();
|
||||||
var res = ds.getResidents().stream()
|
var res = ds.getResidents().stream()
|
||||||
.flatMap(r -> Stream.of(r) //https://stackoverflow.com/questions/37299312/in-java-8-lambdas-how-to-access-original-object-in-the-stream
|
.flatMap(r -> {
|
||||||
.map(TownyObject::getName).map(Bukkit::getOfflinePlayer).filter(p -> !p.hasPlayedBefore())
|
var st = Stream.of(r) //https://stackoverflow.com/questions/37299312/in-java-8-lambdas-how-to-access-original-object-in-the-stream
|
||||||
.map(p -> new AbstractMap.SimpleEntry<>(r, p)))
|
.map(TownyObject::getName);
|
||||||
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
|
return (MainPlugin.ess == null
|
||||||
|
? st.map(Bukkit::getOfflinePlayer)
|
||||||
|
: st.map(MainPlugin.ess::getOfflineUser).map(User::getBase))
|
||||||
|
.filter(p -> !p.hasPlayedBefore())
|
||||||
|
.map(p -> new AbstractMap.SimpleEntry<>(r, p));
|
||||||
|
}).collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
|
||||||
|
if (MainPlugin.ess == null)
|
||||||
|
sender.sendMessage("§cEssentials not found, players who haven't joined after changing their names are also listed here.");
|
||||||
sender.sendMessage("Residents to remove:");
|
sender.sendMessage("Residents to remove:");
|
||||||
res.values().forEach(op -> sender.sendMessage(op.getName()));
|
res.values().forEach(op -> sender.sendMessage(op.getName()));
|
||||||
if (TownySettings.isDeleteEcoAccount())
|
if (TownySettings.isDeleteEcoAccount())
|
||||||
|
|
|
@ -1,18 +1,10 @@
|
||||||
package buttondevteam.core.component.towny;
|
package buttondevteam.core.component.towny;
|
||||||
|
|
||||||
import buttondevteam.core.ComponentManager;
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.architecture.Component;
|
||||||
import com.palmergames.bukkit.towny.Towny;
|
|
||||||
import com.palmergames.bukkit.towny.TownyUniverse;
|
|
||||||
import com.palmergames.bukkit.towny.exceptions.AlreadyRegisteredException;
|
|
||||||
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
|
|
||||||
import com.palmergames.bukkit.towny.object.Resident;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically renames Towny players if they changed their Minecraft name
|
* Provides a command to remove invalid Towny residents.
|
||||||
*/
|
*/
|
||||||
public class TownyComponent extends Component<MainPlugin> {
|
public class TownyComponent extends Component<MainPlugin> {
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,33 +15,4 @@ public class TownyComponent extends Component<MainPlugin> {
|
||||||
@Override
|
@Override
|
||||||
protected void disable() {
|
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;
|
|
||||||
Bukkit.getLogger().info("Renaming" + oldName + " in Towny to " + newName);
|
|
||||||
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.getDataSource().hasResident(newName)) {
|
|
||||||
Bukkit.getLogger().warning("Target resident name is already in use.");
|
|
||||||
TBMCCoreAPI.sendDebugMessage("Target resident name is already in use. (" + oldName + " -> " + newName + ")");
|
|
||||||
} else
|
|
||||||
try {
|
|
||||||
tu.getDataSource().renamePlayer(resident, newName); //Fixed in Towny 0.91.1.2
|
|
||||||
Bukkit.getLogger().info("Renaming done.");
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
package buttondevteam.core.component.votifier;
|
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
|
||||||
import buttondevteam.lib.architecture.Component;
|
|
||||||
import buttondevteam.lib.architecture.ComponentMetadata;
|
|
||||||
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
|
|
||||||
@ComponentMetadata(enabledByDefault = false)
|
|
||||||
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.");
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,7 +37,7 @@ public final class ChromaUtils {
|
||||||
/**
|
/**
|
||||||
* May return null.
|
* May return null.
|
||||||
*
|
*
|
||||||
* @return The full name that can be used to uniquely indentify the user.
|
* @return The full name that can be used to uniquely identify the user.
|
||||||
*/
|
*/
|
||||||
String getFancyFullName();
|
String getFancyFullName();
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ public final class ChromaUtils {
|
||||||
*
|
*
|
||||||
* @param what What to do
|
* @param what What to do
|
||||||
* @param def Default if async
|
* @param def Default if async
|
||||||
* @return The event cancelled state or false if async.
|
* @return The value supplied by the action or def if async.
|
||||||
*/
|
*/
|
||||||
public static <T> T doItAsync(Supplier<T> what, T def) {
|
public static <T> T doItAsync(Supplier<T> what, T def) {
|
||||||
if (Bukkit.isPrimaryThread())
|
if (Bukkit.isPrimaryThread())
|
||||||
|
@ -86,4 +86,16 @@ public final class ChromaUtils {
|
||||||
return what.get();
|
return what.get();
|
||||||
return def;
|
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; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package buttondevteam.lib;
|
package buttondevteam.lib;
|
||||||
|
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
class EventExceptionCoreHandler extends EventExceptionHandler {
|
|
||||||
|
class EventExceptionCoreHandler extends EventExceptionHandler {
|
||||||
@Override
|
|
||||||
public boolean handle(Throwable ex, Event event) {
|
@Override
|
||||||
TBMCCoreAPI.SendException("An error occured while executing " + event.getEventName() + "!", ex);
|
public boolean handle(Throwable ex, Event event) {
|
||||||
return true;
|
TBMCCoreAPI.SendException("An error occured while executing " + event.getEventName() + "!", ex, false, Bukkit.getLogger()::warning);
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ 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.
|
||||||
|
|
|
@ -54,6 +54,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); //TODO: Performance-wise it'd be much better to use serialization for player data - it's only converted once
|
return channel.getGroupID(sender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,13 +30,9 @@ public class TBMCChatPreprocessEvent extends Event implements Cancellable {
|
||||||
super(true);
|
super(true);
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.message = message; // TODO: Message object with data?
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* public TBMCPlayer getPlayer() { return TBMCPlayer.getPlayer(sender); // TODO: Get Chroma user }
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HandlerList getHandlers() {
|
public HandlerList getHandlers() {
|
||||||
return handlers;
|
return handlers;
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package buttondevteam.lib;
|
package buttondevteam.lib;
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
|
import buttondevteam.lib.architecture.Component;
|
||||||
import buttondevteam.lib.player.ChromaGamerBase;
|
import buttondevteam.lib.player.ChromaGamerBase;
|
||||||
import buttondevteam.lib.potato.DebugPotato;
|
import buttondevteam.lib.potato.DebugPotato;
|
||||||
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;
|
||||||
|
@ -15,6 +16,8 @@ import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
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>() {
|
||||||
|
@ -51,11 +54,21 @@ public class TBMCCoreAPI {
|
||||||
* @param sourcemsg A message that is shown at the top of the exception (before the exception's message)
|
* @param sourcemsg A message that is shown at the top of the exception (before the exception's message)
|
||||||
* @param e The exception to send
|
* @param e The exception to send
|
||||||
*/
|
*/
|
||||||
public static void SendException(String sourcemsg, Throwable e) {
|
public static void SendException(String sourcemsg, Throwable e, Component<?> component) {
|
||||||
SendException(sourcemsg, e, false);
|
SendException(sourcemsg, e, false, component::logWarn);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
try {
|
||||||
SendUnsentExceptions();
|
SendUnsentExceptions();
|
||||||
TBMCExceptionEvent event = new TBMCExceptionEvent(sourcemsg, e);
|
TBMCExceptionEvent event = new TBMCExceptionEvent(sourcemsg, e);
|
||||||
|
@ -64,7 +77,7 @@ public class TBMCCoreAPI {
|
||||||
if (!event.isHandled())
|
if (!event.isHandled())
|
||||||
exceptionsToSend.put(sourcemsg, e);
|
exceptionsToSend.put(sourcemsg, e);
|
||||||
}
|
}
|
||||||
Bukkit.getLogger().warning(sourcemsg);
|
logWarn.accept(sourcemsg);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
if (debugPotato) {
|
if (debugPotato) {
|
||||||
List<Player> devsOnline = new ArrayList<>();
|
List<Player> devsOnline = new ArrayList<>();
|
||||||
|
@ -105,6 +118,7 @@ public class TBMCCoreAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EventExceptionCoreHandler eventExceptionCoreHandler;
|
private static EventExceptionCoreHandler eventExceptionCoreHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers Bukkit events, handling the exceptions occurring in those events
|
* Registers Bukkit events, handling the exceptions occurring in those events
|
||||||
*
|
*
|
||||||
|
@ -116,8 +130,8 @@ public class TBMCCoreAPI {
|
||||||
EventExceptionHandler.registerEvents(listener, plugin, eventExceptionCoreHandler);
|
EventExceptionHandler.registerEvents(listener, plugin, eventExceptionCoreHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends ChromaGamerBase> void RegisterUserClass(Class<T> userclass) {
|
public static <T extends ChromaGamerBase> void RegisterUserClass(Class<T> userclass, Supplier<T> constructor) {
|
||||||
ChromaGamerBase.RegisterPluginUserClass(userclass);
|
ChromaGamerBase.RegisterPluginUserClass(userclass, constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,6 +171,6 @@ public class TBMCCoreAPI {
|
||||||
|
|
||||||
public static boolean IsTestServer() {
|
public static boolean IsTestServer() {
|
||||||
if (MainPlugin.Instance == null) return true;
|
if (MainPlugin.Instance == null) return true;
|
||||||
return MainPlugin.Instance.test().get();
|
return MainPlugin.Instance.test.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,9 +25,9 @@ import java.util.Stack;
|
||||||
@HasConfig(global = true)
|
@HasConfig(global = true)
|
||||||
public abstract class ButtonPlugin extends JavaPlugin {
|
public abstract class ButtonPlugin extends JavaPlugin {
|
||||||
@Getter //Needs to be static as we don't know the plugin when a command is handled
|
@Getter //Needs to be static as we don't know the plugin when a command is handled
|
||||||
private static Command2MC command2MC = new Command2MC();
|
private static final Command2MC command2MC = new Command2MC();
|
||||||
@Getter(AccessLevel.PROTECTED)
|
@Getter(AccessLevel.PROTECTED)
|
||||||
private IHaveConfig iConfig;
|
private final IHaveConfig iConfig = new IHaveConfig(this::saveConfig);
|
||||||
private CommentedConfiguration yaml;
|
private CommentedConfiguration yaml;
|
||||||
@Getter(AccessLevel.PROTECTED)
|
@Getter(AccessLevel.PROTECTED)
|
||||||
private IHaveConfig data; //TODO
|
private IHaveConfig data; //TODO
|
||||||
|
@ -35,8 +35,7 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
||||||
* Used to unregister components in the right order - and to reload configs
|
* Used to unregister components in the right order - and to reload configs
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private Stack<Component<?>> componentStack = new Stack<>();
|
private final Stack<Component<?>> componentStack = new Stack<>();
|
||||||
;
|
|
||||||
|
|
||||||
protected abstract void pluginEnable();
|
protected abstract void pluginEnable();
|
||||||
|
|
||||||
|
@ -60,7 +59,7 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
||||||
try {
|
try {
|
||||||
pluginEnable();
|
pluginEnable();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Error while enabling plugin " + getName() + "!", e);
|
TBMCCoreAPI.SendException("Error while enabling plugin " + getName() + "!", e, this);
|
||||||
}
|
}
|
||||||
if (configGenAllowed(this)) //If it's not disabled (by default it's not)
|
if (configGenAllowed(this)) //If it's not disabled (by default it's not)
|
||||||
IHaveConfig.pregenConfig(this, null);
|
IHaveConfig.pregenConfig(this, null);
|
||||||
|
@ -72,7 +71,7 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
||||||
return false;
|
return false;
|
||||||
var section = config.getConfigurationSection("global");
|
var section = config.getConfigurationSection("global");
|
||||||
if (section == null) section = config.createSection("global");
|
if (section == null) section = config.createSection("global");
|
||||||
iConfig = new IHaveConfig(section, this::saveConfig);
|
iConfig.reset(section);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,10 +83,9 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
||||||
pluginDisable();
|
pluginDisable();
|
||||||
if (ConfigData.saveNow(getConfig()))
|
if (ConfigData.saveNow(getConfig()))
|
||||||
getLogger().info("Saved configuration changes.");
|
getLogger().info("Saved configuration changes.");
|
||||||
iConfig = null; //Clearing the hashmap is not enough, we need to update the section as well
|
|
||||||
getCommand2MC().unregisterCommands(this);
|
getCommand2MC().unregisterCommands(this);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e);
|
TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,15 +124,17 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
||||||
var yc = YamlConfiguration.loadConfiguration(res);
|
var yc = YamlConfiguration.loadConfiguration(res);
|
||||||
for (var kv : yc.getValues(true).entrySet())
|
for (var kv : yc.getValues(true).entrySet())
|
||||||
if (kv.getValue() instanceof String)
|
if (kv.getValue() instanceof String)
|
||||||
yaml.addComment(kv.getKey(), Arrays.stream(((String) kv.getValue()).split("\n"))
|
yaml.addComment(kv.getKey().replace(".generalDescriptionInsteadOfAConfig", ""),
|
||||||
.map(str -> "# " + str.trim()).toArray(String[]::new));
|
Arrays.stream(((String) kv.getValue()).split("\n"))
|
||||||
|
.map(str -> "# " + str.trim()).toArray(String[]::new));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileConfiguration getConfig() {
|
public FileConfiguration getConfig() {
|
||||||
if (yaml == null)
|
if (yaml == null)
|
||||||
justReload(); //TODO: If it fails to load, it'll probably throw an NPE
|
justReload();
|
||||||
|
if (yaml == null) return new YamlConfiguration(); //Return a temporary instance
|
||||||
return yaml;
|
return yaml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ public abstract class ButtonPlugin extends JavaPlugin {
|
||||||
if (yaml != null)
|
if (yaml != null)
|
||||||
yaml.save();
|
yaml.save();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Failed to save config", e);
|
TBMCCoreAPI.SendException("Failed to save config", e, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.yaml.snakeyaml.representer.Representer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ import java.util.HashMap;
|
||||||
*
|
*
|
||||||
* @author dumptruckman & Articdive
|
* @author dumptruckman & Articdive
|
||||||
*/
|
*/
|
||||||
public class CommentedConfiguration extends YamlConfiguration { //TODO: Remove FileMgmt dependency
|
public class CommentedConfiguration extends YamlConfiguration {
|
||||||
private HashMap<String, String> comments;
|
private HashMap<String, String> comments;
|
||||||
private File file;
|
private File file;
|
||||||
|
|
||||||
|
@ -174,7 +175,7 @@ public class CommentedConfiguration extends YamlConfiguration { //TODO: Remove F
|
||||||
while (newContents.toString().startsWith(" " + System.getProperty("line.separator"))) {
|
while (newContents.toString().startsWith(" " + System.getProperty("line.separator"))) {
|
||||||
newContents = new StringBuilder(newContents.toString().replaceFirst(" " + System.getProperty("line.separator"), ""));
|
newContents = new StringBuilder(newContents.toString().replaceFirst(" " + System.getProperty("line.separator"), ""));
|
||||||
}
|
}
|
||||||
Files.write(file.toPath(), newContents.toString().getBytes());
|
Files.write(file.toPath(), newContents.toString().getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,14 +31,11 @@ public abstract class Component<TP extends JavaPlugin> {
|
||||||
@Getter
|
@Getter
|
||||||
@NonNull
|
@NonNull
|
||||||
private TP plugin;
|
private TP plugin;
|
||||||
@NonNull
|
private @Getter final IHaveConfig config = new IHaveConfig(null);
|
||||||
private @Getter
|
|
||||||
IHaveConfig config;
|
|
||||||
private @Getter IHaveConfig data; //TODO
|
private @Getter IHaveConfig data; //TODO
|
||||||
|
|
||||||
public final ConfigData<Boolean> shouldBeEnabled() {
|
public final ConfigData<Boolean> shouldBeEnabled = config.getData("enabled",
|
||||||
return config.getData("enabled", Optional.ofNullable(getClass().getAnnotation(ComponentMetadata.class)).map(ComponentMetadata::enabledByDefault).orElse(true));
|
Optional.ofNullable(getClass().getAnnotation(ComponentMetadata.class)).map(ComponentMetadata::enabledByDefault).orElse(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>
|
||||||
|
@ -61,7 +58,7 @@ public abstract class Component<TP extends JavaPlugin> {
|
||||||
* @param component The component to unregister
|
* @param component The component to unregister
|
||||||
* @return Whether the component is unregistered successfully (it also got disabled)
|
* @return Whether the component is unregistered successfully (it also got disabled)
|
||||||
*/
|
*/
|
||||||
public static <T extends ButtonPlugin> boolean unregisterComponent(T plugin, Component<T> component) {
|
public static <T extends JavaPlugin> boolean unregisterComponent(T plugin, Component<T> component) {
|
||||||
return registerUnregisterComponent(plugin, component, false);
|
return registerUnregisterComponent(plugin, component, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,21 +76,22 @@ 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!"));
|
TBMCCoreAPI.SendException("Failed to register component " + component.getClassName(), new IllegalArgumentException("The component is already registered!"), plugin);
|
||||||
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);
|
TBMCCoreAPI.SendException("Failed to enable component " + component.getClassName() + "!", e, component);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +102,7 @@ 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);
|
TBMCCoreAPI.SendException("Failed to disable component " + component.getClassName() + "!", e, component);
|
||||||
return false; //If failed to disable, won't unregister either
|
return false; //If failed to disable, won't unregister either
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,26 +111,40 @@ public abstract class Component<TP extends JavaPlugin> {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + "!", e);
|
TBMCCoreAPI.SendException("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + "!", e, plugin);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a component checking it's dependencies and calling {@link #register(JavaPlugin)}.<br>
|
* Enables or disables the given component. If the component fails to enable, it will be disabled.
|
||||||
* 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) {
|
||||||
updateConfig(component.getPlugin(), component);
|
try {
|
||||||
component.enable();
|
updateConfig(component.getPlugin(), component);
|
||||||
if (ButtonPlugin.configGenAllowed(component)) {
|
component.enable();
|
||||||
IHaveConfig.pregenConfig(component, null);
|
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();
|
||||||
|
@ -140,16 +152,15 @@ public abstract class Component<TP extends JavaPlugin> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = new IHaveConfig(configSect, plugin::saveConfig);
|
component.config.reset(configSect);
|
||||||
} else //Testing
|
} //Testing: it's already set
|
||||||
component.config = new IHaveConfig(null, plugin::saveConfig);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,13 +173,21 @@ public abstract class Component<TP extends JavaPlugin> {
|
||||||
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", "WeakerAccess"})
|
@SuppressWarnings({"unused"})
|
||||||
protected void register(JavaPlugin plugin) {
|
protected void register(JavaPlugin plugin) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +198,7 @@ public abstract class Component<TP extends JavaPlugin> {
|
||||||
*
|
*
|
||||||
* @param plugin Plugin object
|
* @param plugin Plugin object
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
@SuppressWarnings({"unused"})
|
||||||
protected void unregister(JavaPlugin plugin) {
|
protected void unregister(JavaPlugin plugin) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,10 +252,15 @@ public abstract class Component<TP extends JavaPlugin> {
|
||||||
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 -> new IHaveConfig((ConfigurationSection) kv.getValue(), getPlugin()::saveConfig)));
|
.collect(Collectors.toMap(Map.Entry::getKey, kv -> {
|
||||||
|
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(cs.createSection(entry.getKey()), getPlugin()::saveConfig);
|
val conf = new IHaveConfig(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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,9 @@ package buttondevteam.lib.architecture;
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.lib.ChromaUtils;
|
import buttondevteam.lib.ChromaUtils;
|
||||||
import lombok.AccessLevel;
|
import lombok.*;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.configuration.Configuration;
|
import org.bukkit.configuration.Configuration;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
|
@ -22,64 +18,67 @@ import java.util.function.Function;
|
||||||
* Use the getter/setter constructor if {@link T} isn't a primitive type or String.<br>
|
* 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.
|
* Use {@link Component#getConfig()} or {@link ButtonPlugin#getIConfig()} then {@link IHaveConfig#getData(String, Object)} to get an instance.
|
||||||
*/
|
*/
|
||||||
//@AllArgsConstructor(access = AccessLevel.PACKAGE)
|
|
||||||
public class ConfigData<T> {
|
public class ConfigData<T> {
|
||||||
private static final HashMap<Configuration, SaveTask> saveTasks = new HashMap<>();
|
private static final HashMap<Configuration, SaveTask> saveTasks = new HashMap<>();
|
||||||
/**
|
/**
|
||||||
* May be null for testing
|
* May be null for testing
|
||||||
*/
|
*/
|
||||||
private final ConfigurationSection config;
|
private IHaveConfig config;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter(AccessLevel.PACKAGE)
|
@Setter(AccessLevel.PACKAGE)
|
||||||
private String path;
|
private String path;
|
||||||
protected final T def;
|
protected final T def;
|
||||||
private final Object primitiveDef;
|
private final Object primitiveDef;
|
||||||
private final Runnable saveAction;
|
|
||||||
/**
|
/**
|
||||||
* The parameter is of a primitive type as returned by {@link YamlConfiguration#get(String)}
|
* The parameter is of a primitive type as returned by {@link YamlConfiguration#get(String)}
|
||||||
*/
|
*/
|
||||||
private Function<Object, T> getter;
|
private final Function<Object, T> getter;
|
||||||
/**
|
/**
|
||||||
* The result should be a primitive type or string that can be retrieved correctly later
|
* The result should be a primitive type or string that can be retrieved correctly later
|
||||||
*/
|
*/
|
||||||
private Function<T, Object> setter;
|
private final Function<T, Object> setter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The config value should not change outside this instance
|
* The config value should not change outside this instance
|
||||||
*/
|
*/
|
||||||
private T value;
|
private T value;
|
||||||
|
|
||||||
//This constructor is needed because it sets the getter and setter
|
ConfigData(IHaveConfig config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
||||||
ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter, Runnable saveAction) {
|
if (def == null) {
|
||||||
|
if (primitiveDef == null)
|
||||||
|
throw new IllegalArgumentException("Either def or primitiveDef must be set.");
|
||||||
|
if (getter == null)
|
||||||
|
throw new IllegalArgumentException("A getter and setter must be present when using primitiveDef.");
|
||||||
|
def = getter.apply(primitiveDef);
|
||||||
|
} else if (primitiveDef == null)
|
||||||
|
if (setter == null)
|
||||||
|
primitiveDef = def;
|
||||||
|
else
|
||||||
|
primitiveDef = setter.apply(def);
|
||||||
|
if ((getter == null) != (setter == null))
|
||||||
|
throw new IllegalArgumentException("Both setters and getters must be present (or none if def is primitive).");
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.def = def;
|
this.def = def;
|
||||||
this.primitiveDef = primitiveDef;
|
this.primitiveDef = primitiveDef;
|
||||||
this.getter = getter;
|
this.getter = getter;
|
||||||
this.setter = setter;
|
this.setter = setter;
|
||||||
this.saveAction = saveAction;
|
get(); //Generate config automatically
|
||||||
}
|
|
||||||
|
|
||||||
@java.beans.ConstructorProperties({"config", "path", "def", "primitiveDef", "saveAction"})
|
|
||||||
ConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Runnable saveAction) {
|
|
||||||
this.config = config;
|
|
||||||
this.path = path;
|
|
||||||
this.def = def;
|
|
||||||
this.primitiveDef = primitiveDef;
|
|
||||||
this.saveAction = saveAction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ConfigData{" +
|
return "ConfigData{" + "path='" + path + '\'' + ", value=" + value + '}';
|
||||||
"path='" + path + '\'' +
|
}
|
||||||
", value=" + value +
|
|
||||||
'}';
|
void reset() {
|
||||||
|
value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public T get() {
|
public T get() {
|
||||||
if (value != null) return value; //Speed things up
|
if (value != null) return value; //Speed things up
|
||||||
|
var config = this.config.getConfig();
|
||||||
Object val;
|
Object val;
|
||||||
if (config == null || !config.isSet(path)) { //Call set() if config == null
|
if (config == null || !config.isSet(path)) { //Call set() if config == null
|
||||||
val = primitiveDef;
|
val = primitiveDef;
|
||||||
|
@ -119,21 +118,27 @@ public class ConfigData<T> {
|
||||||
if (setter != null && value != null)
|
if (setter != null && value != null)
|
||||||
val = setter.apply(value);
|
val = setter.apply(value);
|
||||||
else val = value;
|
else val = value;
|
||||||
if (config != null)
|
if (config.getConfig() != null)
|
||||||
setInternal(val);
|
setInternal(val);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInternal(Object val) {
|
private void setInternal(Object val) {
|
||||||
config.set(path, val);
|
config.getConfig().set(path, val);
|
||||||
if (!saveTasks.containsKey(config.getRoot())) {
|
signalChange(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signalChange(IHaveConfig config) {
|
||||||
|
var cc = config.getConfig();
|
||||||
|
var sa = config.getSaveAction();
|
||||||
|
if (!saveTasks.containsKey(cc.getRoot())) {
|
||||||
synchronized (saveTasks) {
|
synchronized (saveTasks) {
|
||||||
saveTasks.put(config.getRoot(), new SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, () -> {
|
saveTasks.put(cc.getRoot(), new SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, () -> {
|
||||||
synchronized (saveTasks) {
|
synchronized (saveTasks) {
|
||||||
saveTasks.remove(config.getRoot());
|
saveTasks.remove(cc.getRoot());
|
||||||
saveAction.run();
|
sa.run();
|
||||||
}
|
}
|
||||||
}, 100), saveAction));
|
}, 100), sa));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,4 +161,92 @@ public class ConfigData<T> {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> ConfigData.ConfigDataBuilder<T> builder(IHaveConfig config, String path) {
|
||||||
|
return new ConfigDataBuilder<T>(config, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||||
|
public static class ConfigDataBuilder<T> {
|
||||||
|
private final IHaveConfig config;
|
||||||
|
private final String path;
|
||||||
|
private T def;
|
||||||
|
private Object primitiveDef;
|
||||||
|
private Function<Object, T> getter;
|
||||||
|
private Function<T, Object> setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default value to use, as used in code. If not a primitive type, use the {@link #getter(Function)} and {@link #setter(Function)} methods.
|
||||||
|
* <br/>
|
||||||
|
* To set the value as it is stored, use {@link #primitiveDef(Object)}.
|
||||||
|
*
|
||||||
|
* @param def The default value
|
||||||
|
* @return This builder
|
||||||
|
*/
|
||||||
|
public ConfigDataBuilder<T> def(T def) {
|
||||||
|
this.def = def;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default value to use, as stored in yaml. Must be a primitive type. Make sure to use the {@link #getter(Function)} and {@link #setter(Function)} methods.
|
||||||
|
* <br/>
|
||||||
|
* To set the value as used in the code, use {@link #def(Object)}.
|
||||||
|
*
|
||||||
|
* @param primitiveDef The default value
|
||||||
|
* @return This builder
|
||||||
|
*/
|
||||||
|
public ConfigDataBuilder<T> primitiveDef(Object primitiveDef) {
|
||||||
|
this.primitiveDef = primitiveDef;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to use to obtain the runtime object from the yaml representation (usually string).
|
||||||
|
* The {@link #setter(Function)} must also be set.
|
||||||
|
*
|
||||||
|
* @param getter A function that receives the primitive type and returns the runtime type
|
||||||
|
* @return This builder
|
||||||
|
*/
|
||||||
|
public ConfigDataBuilder<T> getter(Function<Object, T> getter) {
|
||||||
|
this.getter = getter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to use to obtain the yaml representation (usually string) from the runtime object.
|
||||||
|
* The {@link #getter(Function)} must also be set.
|
||||||
|
*
|
||||||
|
* @param setter A function that receives the runtime type and returns the primitive type
|
||||||
|
* @return This builder
|
||||||
|
*/
|
||||||
|
public ConfigDataBuilder<T> setter(Function<T, Object> setter) {
|
||||||
|
this.setter = setter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a modifiable config representation. Use if you want to change the value <i>in code</i>.
|
||||||
|
*
|
||||||
|
* @return A ConfigData instance.
|
||||||
|
*/
|
||||||
|
public ConfigData<T> build() {
|
||||||
|
ConfigData<T> config = new ConfigData<>(this.config, path, def, primitiveDef, getter, setter);
|
||||||
|
this.config.onConfigBuild(config);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a read-only config representation. Use if you only want the value to be changed <i>in the config</i>.
|
||||||
|
*
|
||||||
|
* @return A ReadOnlyConfigData instance.
|
||||||
|
*/
|
||||||
|
public ReadOnlyConfigData<T> buildReadOnly() {
|
||||||
|
ReadOnlyConfigData<T> config = new ReadOnlyConfigData<>(this.config, path, def, primitiveDef, getter, setter);
|
||||||
|
this.config.onConfigBuild(config);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {return "ConfigData.ConfigDataBuilder(config=" + this.config + ", path=" + this.path + ", def=" + this.def + ", primitiveDef=" + this.primitiveDef + ", getter=" + this.getter + ", setter=" + this.setter + ")";}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,11 @@ package buttondevteam.lib.architecture;
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
@ -18,22 +21,41 @@ import java.util.stream.Collectors;
|
||||||
*/
|
*/
|
||||||
public final class IHaveConfig {
|
public final class IHaveConfig {
|
||||||
private final HashMap<String, ConfigData<?>> datamap = new HashMap<>();
|
private final HashMap<String, ConfigData<?>> datamap = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* Returns the Bukkit ConfigurationSection. Use {@link #signalChange()} after changing it.
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private ConfigurationSection config;
|
private ConfigurationSection config;
|
||||||
private final Runnable saveAction;
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private Runnable saveAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* May be used in testing.
|
* May be used in testing.
|
||||||
*
|
*
|
||||||
* @param section May be null for testing
|
* @param saveAction What to do to save the config to disk. Don't use get methods until it's non-null.
|
||||||
*/
|
*/
|
||||||
IHaveConfig(ConfigurationSection section, Runnable saveAction) {
|
public IHaveConfig(Runnable saveAction) {
|
||||||
config = section;
|
|
||||||
this.saveAction = saveAction;
|
this.saveAction = saveAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method overload should only be used with primitves or String.
|
* Gets a config object for the given path. The def or primitiveDef must be set. If a getter is present, a setter must be present as well.
|
||||||
|
*
|
||||||
|
* @param path The dot-separated path relative to this config instance
|
||||||
|
* @param <T> The runtime type of the config value
|
||||||
|
* @return A ConfigData builder to set how to obtain the value
|
||||||
|
*/
|
||||||
|
public <T> ConfigData.ConfigDataBuilder<T> getConfig(String path) {
|
||||||
|
return ConfigData.builder(this, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onConfigBuild(ConfigData<?> config) {
|
||||||
|
datamap.put(config.getPath(), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method overload should only be used with primitives or String.
|
||||||
*
|
*
|
||||||
* @param path The path in config to use
|
* @param path The path in config to use
|
||||||
* @param def The value to use by default
|
* @param def The value to use by default
|
||||||
|
@ -43,7 +65,7 @@ public final class IHaveConfig {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> ConfigData<T> getData(String path, T def) {
|
public <T> ConfigData<T> getData(String path, T def) {
|
||||||
ConfigData<?> data = datamap.get(path);
|
ConfigData<?> data = datamap.get(path);
|
||||||
if (data == null) datamap.put(path, data = new ConfigData<>(config, path, def, def, saveAction));
|
if (data == null) datamap.put(path, data = new ConfigData<>(this, path, def, def, null, null));
|
||||||
return (ConfigData<T>) data;
|
return (ConfigData<T>) data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +83,7 @@ public final class IHaveConfig {
|
||||||
public <T> ConfigData<T> getData(String path, T def, Function<Object, T> getter, Function<T, Object> setter) {
|
public <T> ConfigData<T> getData(String path, T def, Function<Object, T> getter, Function<T, Object> setter) {
|
||||||
ConfigData<?> data = datamap.get(path);
|
ConfigData<?> data = datamap.get(path);
|
||||||
if (data == null)
|
if (data == null)
|
||||||
datamap.put(path, data = new ConfigData<>(config, path, def, setter.apply(def), getter, setter, saveAction));
|
datamap.put(path, data = new ConfigData<>(this, path, def, setter.apply(def), getter, setter));
|
||||||
return (ConfigData<T>) data;
|
return (ConfigData<T>) data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +101,7 @@ public final class IHaveConfig {
|
||||||
public <T> ConfigData<T> getDataPrimDef(String path, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
public <T> ConfigData<T> getDataPrimDef(String path, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
||||||
ConfigData<?> data = datamap.get(path);
|
ConfigData<?> data = datamap.get(path);
|
||||||
if (data == null)
|
if (data == null)
|
||||||
datamap.put(path, data = new ConfigData<>(config, path, getter.apply(primitiveDef), primitiveDef, getter, setter, saveAction));
|
datamap.put(path, data = new ConfigData<>(this, path, getter.apply(primitiveDef), primitiveDef, getter, setter));
|
||||||
return (ConfigData<T>) data;
|
return (ConfigData<T>) data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +119,7 @@ public final class IHaveConfig {
|
||||||
public <T> ReadOnlyConfigData<T> getReadOnlyDataPrimDef(String path, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
public <T> ReadOnlyConfigData<T> getReadOnlyDataPrimDef(String path, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
||||||
ConfigData<?> data = datamap.get(path);
|
ConfigData<?> data = datamap.get(path);
|
||||||
if (data == null)
|
if (data == null)
|
||||||
datamap.put(path, data = new ReadOnlyConfigData<>(config, path, getter.apply(primitiveDef), primitiveDef, getter, setter, saveAction));
|
datamap.put(path, data = new ReadOnlyConfigData<>(this, path, getter.apply(primitiveDef), primitiveDef, getter, setter));
|
||||||
return (ReadOnlyConfigData<T>) data;
|
return (ReadOnlyConfigData<T>) data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +136,7 @@ public final class IHaveConfig {
|
||||||
ConfigData<?> data = datamap.get(path);
|
ConfigData<?> data = datamap.get(path);
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
val defval = def.get();
|
val defval = def.get();
|
||||||
datamap.put(path, data = new ConfigData<>(config, path, defval, defval, saveAction));
|
datamap.put(path, data = new ConfigData<>(this, path, defval, defval, null, null));
|
||||||
}
|
}
|
||||||
return (ConfigData<T>) data;
|
return (ConfigData<T>) data;
|
||||||
}
|
}
|
||||||
|
@ -134,7 +156,7 @@ public final class IHaveConfig {
|
||||||
ConfigData<?> data = datamap.get(path);
|
ConfigData<?> data = datamap.get(path);
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
val defval = def.get();
|
val defval = def.get();
|
||||||
datamap.put(path, data = new ConfigData<>(config, path, defval, setter.apply(defval), getter, setter, saveAction));
|
datamap.put(path, data = new ConfigData<>(this, path, defval, setter.apply(defval), getter, setter));
|
||||||
}
|
}
|
||||||
return (ConfigData<T>) data;
|
return (ConfigData<T>) data;
|
||||||
}
|
}
|
||||||
|
@ -150,10 +172,25 @@ public final class IHaveConfig {
|
||||||
public <T> ListConfigData<T> getListData(String path) {
|
public <T> ListConfigData<T> getListData(String path) {
|
||||||
ConfigData<?> data = datamap.get(path);
|
ConfigData<?> data = datamap.get(path);
|
||||||
if (data == null)
|
if (data == null)
|
||||||
datamap.put(path, data = new ListConfigData<>(config, path, new ListConfigData.List<T>(), saveAction));
|
datamap.put(path, data = new ListConfigData<>(this, path, new ListConfigData.List<T>()));
|
||||||
return (ListConfigData<T>) data;
|
return (ListConfigData<T>) data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a save operation. Use after changing the ConfigurationSection directly.
|
||||||
|
*/
|
||||||
|
public void signalChange() {
|
||||||
|
ConfigData.signalChange(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all caches and loads everything from yaml.
|
||||||
|
*/
|
||||||
|
public void reset(ConfigurationSection config) {
|
||||||
|
this.config = config;
|
||||||
|
datamap.forEach((path, data) -> data.reset());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the config YAML.
|
* Generates the config YAML.
|
||||||
*
|
*
|
||||||
|
@ -164,6 +201,13 @@ public final class IHaveConfig {
|
||||||
val ms = obj.getClass().getDeclaredMethods();
|
val ms = obj.getClass().getDeclaredMethods();
|
||||||
for (val m : ms) {
|
for (val m : ms) {
|
||||||
if (!m.getReturnType().getName().equals(ConfigData.class.getName())) continue;
|
if (!m.getReturnType().getName().equals(ConfigData.class.getName())) continue;
|
||||||
|
final String mName;
|
||||||
|
{
|
||||||
|
var name = m.getName();
|
||||||
|
var ind = name.lastIndexOf('$');
|
||||||
|
if (ind == -1) mName = name;
|
||||||
|
else mName = name.substring(ind + 1);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
m.setAccessible(true);
|
m.setAccessible(true);
|
||||||
List<ConfigData<?>> configList;
|
List<ConfigData<?>> configList;
|
||||||
|
@ -176,24 +220,36 @@ public final class IHaveConfig {
|
||||||
try {
|
try {
|
||||||
return (ConfigData<?>) m.invoke(obj, kv.getValue());
|
return (ConfigData<?>) m.invoke(obj, kv.getValue());
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||||
TBMCCoreAPI.SendException("Failed to pregenerate " + m.getName() + " for " + obj + " using config " + kv.getKey() + "!", e);
|
String msg = "Failed to pregenerate " + mName + " for " + obj + " using config " + kv.getKey() + "!";
|
||||||
|
if (obj instanceof Component<?>)
|
||||||
|
TBMCCoreAPI.SendException(msg, e, (Component<?>) obj);
|
||||||
|
else if (obj instanceof JavaPlugin)
|
||||||
|
TBMCCoreAPI.SendException(msg, e, (JavaPlugin) obj);
|
||||||
|
else
|
||||||
|
TBMCCoreAPI.SendException(msg, e, false, Bukkit.getLogger()::warning);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
} else {
|
} else {
|
||||||
if (TBMCCoreAPI.IsTestServer())
|
if (TBMCCoreAPI.IsTestServer())
|
||||||
MainPlugin.Instance.getLogger().warning("Method " + m.getName() + " returns a config but its parameters are unknown: " + Arrays.toString(m.getParameterTypes()));
|
MainPlugin.Instance.getLogger().warning("Method " + mName + " returns a config but its parameters are unknown: " + Arrays.toString(m.getParameterTypes()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (val c : configList) {
|
for (val c : configList) {
|
||||||
if (c.getPath().length() == 0)
|
if (c.getPath().length() == 0)
|
||||||
c.setPath(m.getName());
|
c.setPath(mName);
|
||||||
else if (!c.getPath().equals(m.getName()))
|
else if (!c.getPath().equals(mName))
|
||||||
MainPlugin.Instance.getLogger().warning("Config name does not match: " + c.getPath() + " instead of " + m.getName());
|
MainPlugin.Instance.getLogger().warning("Config name does not match: " + c.getPath() + " instead of " + mName);
|
||||||
c.get(); //Saves the default value if needed - also checks validity
|
c.get(); //Saves the default value if needed - also checks validity
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Failed to pregenerate " + m.getName() + " for " + obj + "!", e);
|
String msg = "Failed to pregenerate " + mName + " for " + obj + "!";
|
||||||
|
if (obj instanceof Component<?>)
|
||||||
|
TBMCCoreAPI.SendException(msg, e, (Component<?>) obj);
|
||||||
|
else if (obj instanceof JavaPlugin)
|
||||||
|
TBMCCoreAPI.SendException(msg, e, (JavaPlugin) obj);
|
||||||
|
else
|
||||||
|
TBMCCoreAPI.SendException(msg, e, false, Bukkit.getLogger()::warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package buttondevteam.lib.architecture;
|
package buttondevteam.lib.architecture;
|
||||||
|
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -12,12 +11,12 @@ import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
public class ListConfigData<T> extends ConfigData<ListConfigData.List<T>> {
|
public class ListConfigData<T> extends ConfigData<ListConfigData.List<T>> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
ListConfigData(ConfigurationSection config, String path, List<T> def, Runnable saveAction) {
|
ListConfigData(IHaveConfig config, String path, List<T> def) {
|
||||||
super(config, path, def, new ArrayList<>(def), list -> {
|
super(config, path, def, new ArrayList<>(def), list -> {
|
||||||
var l = new List<>((ArrayList<T>) list);
|
var l = new List<>((ArrayList<T>) list);
|
||||||
l.listConfig = def.listConfig;
|
l.listConfig = def.listConfig;
|
||||||
return l;
|
return l;
|
||||||
}, ArrayList::new, saveAction);
|
}, ArrayList::new);
|
||||||
def.listConfig = this; //Can't make the List class non-static or pass this in the super() constructor
|
def.listConfig = this; //Can't make the List class non-static or pass this in the super() constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
package buttondevteam.lib.architecture;
|
package buttondevteam.lib.architecture;
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class ReadOnlyConfigData<T> extends ConfigData<T> {
|
public class ReadOnlyConfigData<T> extends ConfigData<T> {
|
||||||
ReadOnlyConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter, Runnable saveAction) {
|
ReadOnlyConfigData(IHaveConfig config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
||||||
super(config, path, def, primitiveDef, getter, setter, saveAction);
|
super(config, path, def, primitiveDef, getter, setter);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlyConfigData(ConfigurationSection config, String path, T def, Object primitiveDef, Runnable saveAction) {
|
ReadOnlyConfigData(IHaveConfig config, String path, T def, Object primitiveDef) {
|
||||||
super(config, path, def, primitiveDef, saveAction);
|
super(config, path, def, primitiveDef, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,25 @@ import lombok.RequiredArgsConstructor;
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public enum Color implements TellrawSerializableEnum {
|
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",
|
Black("black", 0, 0, 0),
|
||||||
0, 170, 170), DarkRed("dark_red", 170, 0, 0), DarkPurple("dark_purple", 0, 170, 0), Gold("gold", 255, 170,
|
DarkBlue("dark_blue", 0, 0, 170),
|
||||||
0), Gray("gray", 170, 170, 170), DarkGray("dark_gray", 85, 85, 85), Blue("blue", 85, 85,
|
DarkGreen("dark_green", 0, 170, 0),
|
||||||
255), Green("green", 85, 255, 85), Aqua("aqua", 85, 255, 255), Red("red", 255, 85,
|
DarkAqua("dark_aqua", 0, 170, 170),
|
||||||
85), LightPurple("light_purple", 255, 85,
|
DarkRed("dark_red", 170, 0, 0),
|
||||||
255), Yellow("yellow", 255, 255, 85), White("white", 255, 255, 255);
|
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 String name;
|
||||||
private final int red;
|
private final int red;
|
||||||
private final int green;
|
private final int green;
|
||||||
private final int blue;
|
private final int blue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,8 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
* Used to be "tbmc.admin". The {@link #MOD_GROUP} is provided to use with this.
|
* Used to be "tbmc.admin". The {@link #MOD_GROUP} is provided to use with this.
|
||||||
*/
|
*/
|
||||||
String permGroup() default "";
|
String permGroup() default "";
|
||||||
|
|
||||||
|
String[] aliases() default {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Target(ElementType.PARAMETER)
|
@Target(ElementType.PARAMETER)
|
||||||
|
@ -117,7 +119,7 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
protected final HashMap<String, SubcommandData<TC>> subcommands = new HashMap<>();
|
protected final HashMap<String, SubcommandData<TC>> subcommands = new HashMap<>();
|
||||||
protected final HashMap<Class<?>, ParamConverter<?>> paramConverters = new HashMap<>();
|
protected final HashMap<Class<?>, ParamConverter<?>> paramConverters = new HashMap<>();
|
||||||
|
|
||||||
private ArrayList<String> commandHelp = new ArrayList<>(); //Mainly needed by Discord
|
private final ArrayList<String> commandHelp = new ArrayList<>(); //Mainly needed by Discord
|
||||||
|
|
||||||
private char commandChar;
|
private char commandChar;
|
||||||
|
|
||||||
|
@ -145,7 +147,7 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
try {
|
try {
|
||||||
handleCommandAsync(sender, commandline, sd, subcommand, sync);
|
handleCommandAsync(sender, commandline, sd, subcommand, sync);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Command execution failed for sender " + sender.getName() + "(" + sender.getClass().getCanonicalName() + ") and message " + commandline, e);
|
TBMCCoreAPI.SendException("Command execution failed for sender " + sender.getName() + "(" + sender.getClass().getCanonicalName() + ") and message " + commandline, e, MainPlugin.Instance);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true; //We found a method
|
return true; //We found a method
|
||||||
|
@ -165,7 +167,7 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
* @param sync Whether the command was originally sync
|
* @param sync Whether the command was originally sync
|
||||||
* @throws Exception If something's not right
|
* @throws Exception If something's not right
|
||||||
*/
|
*/
|
||||||
public void handleCommandAsync(TP sender, String commandline, SubcommandData<TC> sd, String subcommand, boolean sync) throws Exception {
|
private void handleCommandAsync(TP sender, String commandline, SubcommandData<TC> sd, String subcommand, boolean sync) throws Exception {
|
||||||
if (sd.method == null || sd.command == null) { //Main command not registered, but we have subcommands
|
if (sd.method == null || sd.command == null) { //Main command not registered, but we have subcommands
|
||||||
sender.sendMessage(sd.helpText);
|
sender.sendMessage(sd.helpText);
|
||||||
return;
|
return;
|
||||||
|
@ -251,7 +253,7 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
}
|
}
|
||||||
val conv = paramConverters.get(cl);
|
val conv = paramConverters.get(cl);
|
||||||
if (conv == null)
|
if (conv == null)
|
||||||
throw new Exception("No suitable converter found for parameter type '" + cl.getCanonicalName() + "' for command '" + sd.method.toString() + "'");
|
throw new Exception("No suitable converter found for parameter type '" + cl.getCanonicalName() + "' for command '" + sd.method + "'");
|
||||||
val cparam = conv.converter.apply(param);
|
val cparam = conv.converter.apply(param);
|
||||||
if (cparam == null) {
|
if (cparam == null) {
|
||||||
sender.sendMessage(conv.errormsg); //Param conversion failed - ex. plugin not found
|
sender.sendMessage(conv.errormsg); //Param conversion failed - ex. plugin not found
|
||||||
|
@ -269,9 +271,9 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
} else if (ret != null)
|
} else if (ret != null)
|
||||||
throw new Exception("Wrong return type! Must return a boolean or void. Return value: " + ret);
|
throw new Exception("Wrong return type! Must return a boolean or void. Return value: " + ret);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
TBMCCoreAPI.SendException("An error occurred in a command handler!", e.getCause());
|
TBMCCoreAPI.SendException("An error occurred in a command handler for " + subcommand + "!", e.getCause(), MainPlugin.Instance);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Command handling failed for sender " + sender + " and subcommand " + subcommand, e);
|
TBMCCoreAPI.SendException("Command handling failed for sender " + sender + " and subcommand " + subcommand, e, MainPlugin.Instance);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (sync)
|
if (sync)
|
||||||
|
@ -282,9 +284,12 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
|
|
||||||
public abstract void registerCommand(TC command);
|
public abstract void registerCommand(TC command);
|
||||||
|
|
||||||
protected List<SubcommandData<TC>> registerCommand(TC command, @SuppressWarnings("SameParameterValue") char commandChar) {
|
protected List<SubcommandData<TC>> registerCommand(TC command, char commandChar) {
|
||||||
|
return registerCommand(command, command.getCommandPath(), commandChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<SubcommandData<TC>> registerCommand(TC command, String path, @SuppressWarnings("SameParameterValue") char commandChar) {
|
||||||
this.commandChar = commandChar;
|
this.commandChar = commandChar;
|
||||||
val path = command.getCommandPath();
|
|
||||||
int x = path.indexOf(' ');
|
int x = path.indexOf(' ');
|
||||||
val mainPath = commandChar + path.substring(0, x == -1 ? path.length() : x);
|
val mainPath = commandChar + path.substring(0, x == -1 ? path.length() : x);
|
||||||
//var scmdmap = subcommandStrings.computeIfAbsent(mainPath, k -> new HashSet<>()); //Used to display subcommands
|
//var scmdmap = subcommandStrings.computeIfAbsent(mainPath, k -> new HashSet<>()); //Used to display subcommands
|
||||||
|
@ -304,7 +309,7 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
if (!commandHelp.contains(mainPath))
|
if (!commandHelp.contains(mainPath))
|
||||||
commandHelp.add(mainPath);
|
commandHelp.add(mainPath);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Could not register default handler for command /" + path, e);
|
TBMCCoreAPI.SendException("Could not register default handler for command /" + path, e, MainPlugin.Instance);
|
||||||
}
|
}
|
||||||
var addedSubcommands = new ArrayList<SubcommandData<TC>>();
|
var addedSubcommands = new ArrayList<SubcommandData<TC>>();
|
||||||
for (val method : command.getClass().getMethods()) {
|
for (val method : command.getClass().getMethods()) {
|
||||||
|
@ -317,7 +322,9 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
var params = new String[method.getParameterCount() - 1];
|
var params = new String[method.getParameterCount() - 1];
|
||||||
ht = getParameterHelp(method, ht, subcommand, params);
|
ht = getParameterHelp(method, ht, subcommand, params);
|
||||||
var sd = new SubcommandData<>(method, command, params, ht);
|
var sd = new SubcommandData<>(method, command, params, ht);
|
||||||
subcommands.put(subcommand, sd); //Result of the above (def) is that it will show the help text
|
registerCommand(path, method.getName(), ann, sd);
|
||||||
|
for (String p : command.getCommandPaths())
|
||||||
|
registerCommand(p, method.getName(), ann, sd);
|
||||||
addedSubcommands.add(sd);
|
addedSubcommands.add(sd);
|
||||||
scmdHelpList.add(subcommand);
|
scmdHelpList.add(subcommand);
|
||||||
nosubs = false;
|
nosubs = false;
|
||||||
|
@ -330,15 +337,12 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
subcommands.put(commandChar + path, sd);
|
subcommands.put(commandChar + path, sd);
|
||||||
addedSubcommands.add(sd);
|
addedSubcommands.add(sd);
|
||||||
}
|
}
|
||||||
if (mainMethod != null && !mainPath.equals(commandChar + path)) { //Main command, typically the same as the above
|
if (isSubcommand) { //The class itself is a subcommand
|
||||||
if (isSubcommand) { //The class itself is a subcommand
|
val scmd = subcommands.computeIfAbsent(mainPath, p -> new SubcommandData<>(null, null, new String[0], new String[]{"§6---- Subcommands ----"}));
|
||||||
val scmd = subcommands.computeIfAbsent(mainPath, p -> new SubcommandData<>(null, null, new String[0], new String[]{"§6---- Subcommands ----"}));
|
val scmdHelp = Arrays.copyOf(scmd.helpText, scmd.helpText.length + scmdHelpList.size());
|
||||||
val scmdHelp = Arrays.copyOf(scmd.helpText, scmd.helpText.length + scmdHelpList.size());
|
for (int i = 0; i < scmdHelpList.size(); i++)
|
||||||
for (int i = 0; i < scmdHelpList.size(); i++)
|
scmdHelp[scmd.helpText.length + i] = scmdHelpList.get(i);
|
||||||
scmdHelp[scmd.helpText.length + i] = scmdHelpList.get(i);
|
scmd.helpText = scmdHelp;
|
||||||
scmd.helpText = scmdHelp;
|
|
||||||
} else if (!subcommands.containsKey(mainPath))
|
|
||||||
subcommands.put(mainPath, new SubcommandData<>(null, null, new String[0], scmdHelpList.toArray(new String[0])));
|
|
||||||
}
|
}
|
||||||
return addedSubcommands;
|
return addedSubcommands;
|
||||||
}
|
}
|
||||||
|
@ -346,12 +350,12 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
private String[] getParameterHelp(Method method, String[] ht, String subcommand, String[] parameters) {
|
private String[] getParameterHelp(Method method, String[] ht, String subcommand, String[] parameters) {
|
||||||
val str = method.getDeclaringClass().getResourceAsStream("/commands.yml");
|
val str = method.getDeclaringClass().getResourceAsStream("/commands.yml");
|
||||||
if (str == null)
|
if (str == null)
|
||||||
TBMCCoreAPI.SendException("Error while getting command data!", new Exception("Resource not found!"));
|
TBMCCoreAPI.SendException("Error while getting command data!", new Exception("Resource not found!"), MainPlugin.Instance);
|
||||||
else {
|
else {
|
||||||
if (ht.length > 0)
|
if (ht.length > 0)
|
||||||
ht[0] = "§6---- " + ht[0] + " ----";
|
ht[0] = "§6---- " + ht[0] + " ----";
|
||||||
YamlConfiguration yc = YamlConfiguration.loadConfiguration(new InputStreamReader(str)); //Generated by ButtonProcessor
|
YamlConfiguration yc = YamlConfiguration.loadConfiguration(new InputStreamReader(str)); //Generated by ButtonProcessor
|
||||||
val ccs = yc.getConfigurationSection(method.getDeclaringClass().getCanonicalName());
|
val ccs = yc.getConfigurationSection(method.getDeclaringClass().getCanonicalName().replace('$', '.'));
|
||||||
if (ccs != null) {
|
if (ccs != null) {
|
||||||
val cs = ccs.getConfigurationSection(method.getName());
|
val cs = ccs.getConfigurationSection(method.getName());
|
||||||
if (cs != null) {
|
if (cs != null) {
|
||||||
|
@ -367,15 +371,22 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
for (int j = 0; j < paramArray.length && j < parameters.length; j++)
|
for (int j = 0; j < paramArray.length && j < parameters.length; j++)
|
||||||
parameters[j] = paramArray[j];
|
parameters[j] = paramArray[j];
|
||||||
} else
|
} else
|
||||||
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("Method '" + method.toString() + "' != " + mname + " or params is " + params));
|
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("Method '" + method + "' != " + mname + " or params is " + params), MainPlugin.Instance);
|
||||||
} else
|
} else
|
||||||
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("cs is " + cs));
|
MainPlugin.Instance.getLogger().warning("Failed to get command data for " + method + " (cs is null)! Make sure to use 'clean install' when building the project.");
|
||||||
} else
|
} else
|
||||||
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("ccs is " + ccs + " - class: " + method.getDeclaringClass().getCanonicalName()));
|
MainPlugin.Instance.getLogger().warning("Failed to get command data for " + method + " (ccs is null)! Make sure to use 'clean install' when building the project.");
|
||||||
}
|
}
|
||||||
return ht;
|
return ht;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerCommand(String path, String methodName, Subcommand ann, SubcommandData<TC> sd) {
|
||||||
|
val subcommand = commandChar + path + getCommandPath(methodName, ' ');
|
||||||
|
subcommands.put(subcommand, sd);
|
||||||
|
for (String alias : ann.aliases())
|
||||||
|
subcommands.put(commandChar + path + alias, sd);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract boolean hasPermission(TP sender, TC command, Method subcommand);
|
public abstract boolean hasPermission(TP sender, TC command, Method subcommand);
|
||||||
|
|
||||||
public String[] getCommandsText() {
|
public String[] getCommandsText() {
|
||||||
|
@ -402,11 +413,19 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
for (val method : command.getClass().getMethods()) {
|
for (val method : command.getClass().getMethods()) {
|
||||||
val ann = method.getAnnotation(Subcommand.class);
|
val ann = method.getAnnotation(Subcommand.class);
|
||||||
if (ann == null) continue;
|
if (ann == null) continue;
|
||||||
val subcommand = commandChar + path + getCommandPath(method.getName(), ' ');
|
unregisterCommand(path, method.getName(), ann);
|
||||||
subcommands.remove(subcommand);
|
for (String p : command.getCommandPaths())
|
||||||
|
unregisterCommand(p, method.getName(), ann);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void unregisterCommand(String path, String methodName, Subcommand ann) {
|
||||||
|
val subcommand = commandChar + path + getCommandPath(methodName, ' ');
|
||||||
|
subcommands.remove(subcommand);
|
||||||
|
for (String alias : ann.aliases())
|
||||||
|
subcommands.remove(commandChar + path + alias);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It will start with the given replace char.
|
* It will start with the given replace char.
|
||||||
*
|
*
|
||||||
|
|
|
@ -4,10 +4,10 @@ import buttondevteam.core.MainPlugin;
|
||||||
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;
|
||||||
|
import buttondevteam.lib.player.ChromaGamerBase;
|
||||||
import com.mojang.brigadier.arguments.*;
|
import com.mojang.brigadier.arguments.*;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
@ -16,14 +16,9 @@ import me.lucko.commodore.CommodoreProvider;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.*;
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.command.ConsoleCommandSender;
|
|
||||||
import org.bukkit.command.SimpleCommandMap;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.server.TabCompleteEvent;
|
|
||||||
import org.bukkit.permissions.Permission;
|
import org.bukkit.permissions.Permission;
|
||||||
import org.bukkit.permissions.PermissionDefault;
|
import org.bukkit.permissions.PermissionDefault;
|
||||||
import org.javatuples.Triplet;
|
import org.javatuples.Triplet;
|
||||||
|
@ -35,8 +30,10 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@ -48,7 +45,19 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void registerCommand(ICommand2MC command) {
|
public void registerCommand(ICommand2MC command) {
|
||||||
|
/*String mainpath;
|
||||||
|
var plugin = command.getPlugin();
|
||||||
|
{
|
||||||
|
String cpath = command.getCommandPath();
|
||||||
|
int i = cpath.indexOf(' ');
|
||||||
|
mainpath = cpath.substring(0, i == -1 ? cpath.length() : i);
|
||||||
|
}*/
|
||||||
var subcmds = super.registerCommand(command, '/');
|
var subcmds = super.registerCommand(command, '/');
|
||||||
|
var bcmd = registerOfficially(command, subcmds);
|
||||||
|
if (bcmd != null)
|
||||||
|
for (String alias : bcmd.getAliases())
|
||||||
|
super.registerCommand(command, command.getCommandPath().replaceFirst("^" + bcmd.getName(), Matcher.quoteReplacement(alias)), '/');
|
||||||
|
|
||||||
var perm = "chroma.command." + command.getCommandPath().replace(' ', '.');
|
var perm = "chroma.command." + command.getCommandPath().replace(' ', '.');
|
||||||
if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset
|
if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset
|
||||||
Bukkit.getPluginManager().addPermission(new Permission(perm,
|
Bukkit.getPluginManager().addPermission(new Permission(perm,
|
||||||
|
@ -64,13 +73,11 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
}
|
}
|
||||||
String pg = permGroup(command, method);
|
String pg = permGroup(command, method);
|
||||||
if (pg.length() == 0) continue;
|
if (pg.length() == 0) continue;
|
||||||
perm = "chroma." + pg;
|
String permGroup = "chroma." + pg;
|
||||||
if (Bukkit.getPluginManager().getPermission(perm) == null) //It may occur multiple times
|
if (Bukkit.getPluginManager().getPermission(permGroup) == null) //It may occur multiple times
|
||||||
Bukkit.getPluginManager().addPermission(new Permission(perm,
|
Bukkit.getPluginManager().addPermission(new Permission(permGroup,
|
||||||
PermissionDefault.OP)); //Do not allow any commands that belong to a group
|
PermissionDefault.OP)); //Do not allow any commands that belong to a group
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOfficially(command, subcmds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -166,32 +173,82 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
.map(comp -> component.getClass().getSimpleName().equals(comp.getClass().getSimpleName())).orElse(false));
|
.map(comp -> component.getClass().getSimpleName().equals(comp.getClass().getSimpleName())).orElse(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
/*@EventHandler
|
||||||
public void onTabComplete(TabCompleteEvent event) {
|
public void onTabComplete(TabCompleteEvent event) {
|
||||||
//System.out.println("Tabcomplete: " + event.getBuffer());
|
try {
|
||||||
//System.out.println("First completion: " + event.getCompletions().stream().findFirst().orElse("no completions"));
|
event.getCompletions().clear(); //Remove player names
|
||||||
event.getCompletions().clear();
|
} catch (UnsupportedOperationException e) {
|
||||||
|
//System.out.println("Tabcomplete: " + event.getBuffer());
|
||||||
|
//System.out.println("First completion: " + event.getCompletions().stream().findFirst().orElse("no completions"));
|
||||||
|
//System.out.println("Listeners: " + Arrays.toString(event.getHandlers().getRegisteredListeners()));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleCommand(Command2MCSender sender, String commandline) {
|
||||||
|
return handleCommand(sender, commandline, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleCommand(Command2MCSender sender, String commandline, boolean checkPlugin) {
|
||||||
|
int i = commandline.indexOf(' ');
|
||||||
|
String mainpath = commandline.substring(1, i == -1 ? commandline.length() : i); //Without the slash
|
||||||
|
PluginCommand pcmd;
|
||||||
|
if (!checkPlugin
|
||||||
|
|| MainPlugin.Instance.prioritizeCustomCommands.get()
|
||||||
|
|| (pcmd = Bukkit.getPluginCommand(mainpath)) == null //Our commands aren't PluginCommands
|
||||||
|
|| pcmd.getPlugin() instanceof ButtonPlugin) //Unless it's specified in the plugin.yml
|
||||||
|
return super.handleCommand(sender, commandline);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldRegisterOfficially = true;
|
private boolean shouldRegisterOfficially = true;
|
||||||
|
|
||||||
private void registerOfficially(ICommand2MC command, List<SubcommandData<ICommand2MC>> subcmds) {
|
private Command registerOfficially(ICommand2MC command, List<SubcommandData<ICommand2MC>> subcmds) {
|
||||||
if (!shouldRegisterOfficially) return;
|
if (!shouldRegisterOfficially || command.getPlugin() == null) return null;
|
||||||
try {
|
try {
|
||||||
var cmdmap = (SimpleCommandMap) Bukkit.getServer().getClass().getMethod("getCommandMap").invoke(Bukkit.getServer());
|
var cmdmap = (SimpleCommandMap) Bukkit.getServer().getClass().getMethod("getCommandMap").invoke(Bukkit.getServer());
|
||||||
var path = command.getCommandPath();
|
var path = command.getCommandPath();
|
||||||
int x = path.indexOf(' ');
|
int x = path.indexOf(' ');
|
||||||
var mainPath = path.substring(0, x == -1 ? path.length() : x);
|
var mainPath = path.substring(0, x == -1 ? path.length() : x);
|
||||||
var bukkitCommand = new BukkitCommand(mainPath);
|
Command bukkitCommand;
|
||||||
cmdmap.register(command.getPlugin().getName(), bukkitCommand);
|
{ //Commands conflicting with Essentials have to be registered in plugin.yml
|
||||||
|
var oldcmd = cmdmap.getCommand(command.getPlugin().getName() + ":" + mainPath); //The label with the fallback prefix is always registered
|
||||||
|
if (oldcmd == null) {
|
||||||
|
bukkitCommand = new BukkitCommand(mainPath);
|
||||||
|
cmdmap.register(command.getPlugin().getName(), bukkitCommand);
|
||||||
|
} else {
|
||||||
|
bukkitCommand = oldcmd;
|
||||||
|
if (bukkitCommand instanceof PluginCommand)
|
||||||
|
((PluginCommand) bukkitCommand).setExecutor(this::executeCommand);
|
||||||
|
}
|
||||||
|
bukkitCommand = oldcmd == null ? new BukkitCommand(mainPath) : oldcmd;
|
||||||
|
}
|
||||||
if (CommodoreProvider.isSupported())
|
if (CommodoreProvider.isSupported())
|
||||||
TabcompleteHelper.registerTabcomplete(command, subcmds, bukkitCommand);
|
TabcompleteHelper.registerTabcomplete(command, subcmds, bukkitCommand);
|
||||||
|
return bukkitCommand;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Failed to register command in command map!", e);
|
if (command.getComponent() == null)
|
||||||
|
TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.getPlugin());
|
||||||
|
else
|
||||||
|
TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.getComponent());
|
||||||
shouldRegisterOfficially = false;
|
shouldRegisterOfficially = false;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean executeCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
var user = ChromaGamerBase.getFromSender(sender);
|
||||||
|
if (user == null) {
|
||||||
|
TBMCCoreAPI.SendException("Failed to run Bukkit command for user!", new Throwable("No Chroma user found"), MainPlugin.Instance);
|
||||||
|
sender.sendMessage("§cAn internal error occurred.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
handleCommand(new Command2MCSender(sender, user.channel.get(), sender),
|
||||||
|
("/" + command.getName() + " " + String.join(" ", args)).trim(), false); ///trim(): remove space if there are no args
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static class BukkitCommand extends Command {
|
private static class BukkitCommand extends Command {
|
||||||
protected BukkitCommand(String name) {
|
protected BukkitCommand(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
|
@ -199,8 +256,7 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
||||||
sender.sendMessage("§cThe command wasn't executed for some reason... (command processing failed)");
|
return ButtonPlugin.getCommand2MC().executeCommand(sender, this, commandLabel, args);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -219,22 +275,33 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
|
|
||||||
private static LiteralCommandNode<Object> appendSubcommand(String path, CommandNode<Object> parent,
|
private static LiteralCommandNode<Object> appendSubcommand(String path, CommandNode<Object> parent,
|
||||||
SubcommandData<ICommand2MC> subcommand) {
|
SubcommandData<ICommand2MC> subcommand) {
|
||||||
|
LiteralCommandNode<Object> scmd;
|
||||||
|
if ((scmd = (LiteralCommandNode<Object>) parent.getChild(path)) != null)
|
||||||
|
return scmd;
|
||||||
var scmdBuilder = LiteralArgumentBuilder.literal(path);
|
var scmdBuilder = LiteralArgumentBuilder.literal(path);
|
||||||
if (subcommand != null)
|
if (subcommand != null)
|
||||||
scmdBuilder.requires(o -> {
|
scmdBuilder.requires(o -> {
|
||||||
var sender = commodore.getBukkitSender(o);
|
var sender = commodore.getBukkitSender(o);
|
||||||
return ButtonPlugin.getCommand2MC().hasPermission(sender, subcommand.command, subcommand.method);
|
return ButtonPlugin.getCommand2MC().hasPermission(sender, subcommand.command, subcommand.method);
|
||||||
});
|
});
|
||||||
var scmd = scmdBuilder.build();
|
scmd = scmdBuilder.build();
|
||||||
parent.addChild(scmd);
|
parent.addChild(scmd);
|
||||||
return scmd;
|
return scmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerTabcomplete(ICommand2MC command2MC, List<SubcommandData<ICommand2MC>> subcmds, Command bukkitCommand) {
|
private static void registerTabcomplete(ICommand2MC command2MC, List<SubcommandData<ICommand2MC>> subcmds, Command bukkitCommand) {
|
||||||
if (commodore == null)
|
if (commodore == null) {
|
||||||
commodore = CommodoreProvider.getCommodore(MainPlugin.Instance); //Register all to the Core, it's easier
|
commodore = CommodoreProvider.getCommodore(MainPlugin.Instance); //Register all to the Core, it's easier
|
||||||
|
commodore.register(LiteralArgumentBuilder.literal("un").redirect(RequiredArgumentBuilder.argument("unsomething",
|
||||||
|
StringArgumentType.word()).suggests((context, builder) -> builder.suggest("untest").buildFuture()).build()));
|
||||||
|
}
|
||||||
String[] path = command2MC.getCommandPath().split(" ");
|
String[] path = command2MC.getCommandPath().split(" ");
|
||||||
var maincmd = LiteralArgumentBuilder.literal(path[0]).build();
|
var shouldRegister = new AtomicBoolean(true);
|
||||||
|
@SuppressWarnings("unchecked") var maincmd = commodore.getRegisteredNodes().stream()
|
||||||
|
.filter(node -> node.getLiteral().equalsIgnoreCase(path[0]))
|
||||||
|
.filter(node -> { shouldRegister.set(false); return true; })
|
||||||
|
.map(node -> (LiteralCommandNode<Object>) node).findAny()
|
||||||
|
.orElseGet(() -> LiteralArgumentBuilder.literal(path[0]).build()); //Commodore 1.8 removes previous nodes
|
||||||
var cmd = maincmd;
|
var cmd = maincmd;
|
||||||
for (int i = 1; i < path.length; i++) {
|
for (int i = 1; i < path.length; i++) {
|
||||||
var scmd = subcmds.stream().filter(sd -> sd.method.getName().equals("def")).findAny().orElse(null);
|
var scmd = subcmds.stream().filter(sd -> sd.method.getName().equals("def")).findAny().orElse(null);
|
||||||
|
@ -248,7 +315,7 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
.orElseGet(() -> new String[]{
|
.orElseGet(() -> new String[]{
|
||||||
ButtonPlugin.getCommand2MC().getCommandPath(method.getName(), ' ').trim()
|
ButtonPlugin.getCommand2MC().getCommandPath(method.getName(), ' ').trim()
|
||||||
});
|
});
|
||||||
return Arrays.stream(paths).map(name -> new Triplet<>(name, ctcm.param(), method));
|
return Arrays.stream(paths).map(name -> new Triplet<>(name, ctcm, method));
|
||||||
})).collect(Collectors.toList());
|
})).collect(Collectors.toList());
|
||||||
for (SubcommandData<ICommand2MC> subcmd : subcmds) {
|
for (SubcommandData<ICommand2MC> subcmd : subcmds) {
|
||||||
String subpathAsOne = ButtonPlugin.getCommand2MC().getCommandPath(subcmd.method.getName(), ' ').trim();
|
String subpathAsOne = ButtonPlugin.getCommand2MC().getCommandPath(subcmd.method.getName(), ' ').trim();
|
||||||
|
@ -297,20 +364,24 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
val param = subcmd.parameters[i - 1];
|
val param = subcmd.parameters[i - 1];
|
||||||
val customTC = Optional.ofNullable(parameter.getAnnotation(CustomTabComplete.class))
|
val customTC = Optional.ofNullable(parameter.getAnnotation(CustomTabComplete.class))
|
||||||
.map(CustomTabComplete::value);
|
.map(CustomTabComplete::value);
|
||||||
final Optional<Method> customTCmethod = customTCmethods.stream().filter(t -> subpathAsOne.equalsIgnoreCase(t.getValue0()))
|
var customTCmethod = customTCmethods.stream().filter(t -> subpathAsOne.equalsIgnoreCase(t.getValue0()))
|
||||||
.filter(t -> param.replaceAll("[\\[\\]<>]", "").equalsIgnoreCase(t.getValue1()))
|
.filter(t -> param.replaceAll("[\\[\\]<>]", "").equalsIgnoreCase(t.getValue1().param()))
|
||||||
.map(Triplet::getValue2).findAny();
|
.findAny();
|
||||||
var argb = RequiredArgumentBuilder.argument(param, type)
|
var argb = RequiredArgumentBuilder.argument(param, type)
|
||||||
.suggests((SuggestionProvider<Object>) (context, builder) -> {
|
.suggests((context, builder) -> {
|
||||||
if (parameter.isVarArgs()) { //Do it before the builder is used
|
if (parameter.isVarArgs()) { //Do it before the builder is used
|
||||||
int x = context.getInput().lastIndexOf(' ') + 1;
|
int nextTokenStart = context.getInput().lastIndexOf(' ') + 1;
|
||||||
builder = builder.createOffset(x);
|
builder = builder.createOffset(nextTokenStart);
|
||||||
}
|
}
|
||||||
if (customTC.isPresent())
|
if (customTC.isPresent())
|
||||||
for (val ctc : customTC.get())
|
for (val ctc : customTC.get())
|
||||||
builder.suggest(ctc);
|
builder.suggest(ctc);
|
||||||
|
boolean ignoreCustomParamType = false;
|
||||||
if (customTCmethod.isPresent()) {
|
if (customTCmethod.isPresent()) {
|
||||||
final var method = customTCmethod.get();
|
var tr = customTCmethod.get();
|
||||||
|
if (tr.getValue1().ignoreTypeCompletion())
|
||||||
|
ignoreCustomParamType = true;
|
||||||
|
final var method = tr.getValue2();
|
||||||
val params = method.getParameters();
|
val params = method.getParameters();
|
||||||
val args = new Object[params.length];
|
val args = new Object[params.length];
|
||||||
for (int j = 0, k = 0; j < args.length && k < subcmd.parameters.length; j++) {
|
for (int j = 0, k = 0; j < args.length && k < subcmd.parameters.length; j++) {
|
||||||
|
@ -324,12 +395,9 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
args[j] = paramValueString;
|
args[j] = paramValueString;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
val converter = ButtonPlugin.getCommand2MC().paramConverters.get(params[j].getType());
|
val converter = getParamConverter(params[j].getType(), command2MC);
|
||||||
if (converter == null) {
|
if (converter == null)
|
||||||
TBMCCoreAPI.SendException("Could not find a suitable converter for type " + params[j].getType().getSimpleName(),
|
|
||||||
new NullPointerException("converter is null"));
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
val paramValue = converter.converter.apply(paramValueString);
|
val paramValue = converter.converter.apply(paramValueString);
|
||||||
if (paramValue == null) //For example, the player provided an invalid plugin name
|
if (paramValue == null) //For example, the player provided an invalid plugin name
|
||||||
break;
|
break;
|
||||||
|
@ -352,16 +420,17 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
else
|
else
|
||||||
throw new ClassCastException("Bad return type! It should return a String[] or an Iterable<String>.");
|
throw new ClassCastException("Bad return type! It should return a String[] or an Iterable<String>.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Failed to run tabcomplete method " + method.getName() + " for command " + command2MC.getClass().getSimpleName(), e);
|
String msg = "Failed to run tabcomplete method " + method.getName() + " for command " + command2MC.getClass().getSimpleName();
|
||||||
|
if (command2MC.getComponent() == null)
|
||||||
|
TBMCCoreAPI.SendException(msg, e, command2MC.getPlugin());
|
||||||
|
else
|
||||||
|
TBMCCoreAPI.SendException(msg, e, command2MC.getComponent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (customParamType) {
|
if (!ignoreCustomParamType && customParamType) {
|
||||||
val converter = ButtonPlugin.getCommand2MC().paramConverters.get(ptype);
|
val converter = getParamConverter(ptype, command2MC);
|
||||||
if (converter == null)
|
if (converter != null) {
|
||||||
TBMCCoreAPI.SendException("Could not find a suitable converter for type " + ptype.getSimpleName(),
|
|
||||||
new NullPointerException("converter is null"));
|
|
||||||
else {
|
|
||||||
var suggestions = converter.allSupplier.get();
|
var suggestions = converter.allSupplier.get();
|
||||||
for (String suggestion : suggestions)
|
for (String suggestion : suggestions)
|
||||||
builder.suggest(suggestion);
|
builder.suggest(suggestion);
|
||||||
|
@ -369,15 +438,45 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
}
|
}
|
||||||
if (ptype == boolean.class || ptype == Boolean.class)
|
if (ptype == boolean.class || ptype == Boolean.class)
|
||||||
builder.suggest("true").suggest("false");
|
builder.suggest("true").suggest("false");
|
||||||
|
final String loweredInput = builder.getRemaining().toLowerCase();
|
||||||
return builder.suggest(param).buildFuture().whenComplete((s, e) -> //The list is automatically ordered
|
return builder.suggest(param).buildFuture().whenComplete((s, e) -> //The list is automatically ordered
|
||||||
s.getList().add(s.getList().remove(0))); //So we need to put the <param> at the end after that
|
s.getList().add(s.getList().remove(0))) //So we need to put the <param> at the end after that
|
||||||
|
.whenComplete((ss, e) -> ss.getList().removeIf(s -> {
|
||||||
|
String text = s.getText();
|
||||||
|
return !text.startsWith("<") && !text.startsWith("[") && !text.toLowerCase().startsWith(loweredInput);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
var arg = argb.build();
|
var arg = argb.build();
|
||||||
scmd.addChild(arg);
|
scmd.addChild(arg);
|
||||||
scmd = arg;
|
scmd = arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commodore.register(maincmd);
|
if (shouldRegister.get()) {
|
||||||
|
commodore.register(maincmd);
|
||||||
|
//MinecraftArgumentTypes.getByKey(NamespacedKey.minecraft(""))
|
||||||
|
String pluginName = command2MC.getPlugin().getName().toLowerCase();
|
||||||
|
var prefixedcmd = LiteralArgumentBuilder.literal(pluginName + ":" + path[0])
|
||||||
|
.redirect(maincmd).build();
|
||||||
|
commodore.register(prefixedcmd);
|
||||||
|
for (String alias : bukkitCommand.getAliases()) {
|
||||||
|
commodore.register(LiteralArgumentBuilder.literal(alias).redirect(maincmd).build());
|
||||||
|
commodore.register(LiteralArgumentBuilder.literal(pluginName + ":" + alias).redirect(maincmd).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ParamConverter<?> getParamConverter(Class<?> cl, ICommand2MC command2MC) {
|
||||||
|
val converter = ButtonPlugin.getCommand2MC().paramConverters.get(cl);
|
||||||
|
if (converter == null) {
|
||||||
|
String msg = "Could not find a suitable converter for type " + cl.getSimpleName();
|
||||||
|
Exception exception = new NullPointerException("converter is null");
|
||||||
|
if (command2MC.getComponent() == null)
|
||||||
|
TBMCCoreAPI.SendException(msg, exception, command2MC.getPlugin());
|
||||||
|
else
|
||||||
|
TBMCCoreAPI.SendException(msg, exception, command2MC.getComponent());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,9 @@ public @interface CustomTabCompleteMethod {
|
||||||
* The subcommand(s) which have the parameter, by default the method's name
|
* The subcommand(s) which have the parameter, by default the method's name
|
||||||
*/
|
*/
|
||||||
String[] subcommand() default {};
|
String[] subcommand() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter types can provide tab completions. This allows disabling that.
|
||||||
|
*/
|
||||||
|
boolean ignoreTypeCompletion() default false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,18 @@ public abstract class ICommand2<TP extends Command2Sender> {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String[] EMPTY_PATHS = new String[0];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All of the command's paths it will be invoked on. Does not include aliases or the default path.
|
||||||
|
* Must be lowercase and must include the full path.
|
||||||
|
*
|
||||||
|
* @return The full command paths that this command should be registered under in addition to the default one.
|
||||||
|
*/
|
||||||
|
public String[] getCommandPaths() {
|
||||||
|
return EMPTY_PATHS;
|
||||||
|
}
|
||||||
|
|
||||||
private String getcmdpath() {
|
private String getcmdpath() {
|
||||||
if (!getClass().isAnnotationPresent(CommandClass.class))
|
if (!getClass().isAnnotationPresent(CommandClass.class))
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class TBMCChatAPI {
|
||||||
* @return The event cancelled state
|
* @return The event cancelled state
|
||||||
*/
|
*/
|
||||||
public static boolean SendChatMessage(ChatMessage cm) {
|
public static boolean SendChatMessage(ChatMessage cm) {
|
||||||
return SendChatMessage(cm, cm.getUser().channel().get());
|
return SendChatMessage(cm, cm.getUser().channel.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,9 +35,9 @@ public class TBMCChatAPI {
|
||||||
*/
|
*/
|
||||||
public static boolean SendChatMessage(ChatMessage cm, Channel channel) {
|
public static boolean SendChatMessage(ChatMessage cm, Channel channel) {
|
||||||
if (!Channel.getChannelList().contains(channel))
|
if (!Channel.getChannelList().contains(channel))
|
||||||
throw new RuntimeException("Channel " + channel.DisplayName().get() + " not registered!");
|
throw new RuntimeException("Channel " + channel.DisplayName.get() + " not registered!");
|
||||||
if (!channel.Enabled().get()) {
|
if (!channel.Enabled.get()) {
|
||||||
cm.getSender().sendMessage("§cThe channel '" + channel.DisplayName().get() + "' is disabled!");
|
cm.getSender().sendMessage("§cThe channel '" + channel.DisplayName.get() + "' is disabled!");
|
||||||
return true; //Cancel sending if channel is disabled
|
return true; //Cancel sending if channel is disabled
|
||||||
}
|
}
|
||||||
Supplier<Boolean> task = () -> {
|
Supplier<Boolean> task = () -> {
|
||||||
|
@ -70,11 +70,11 @@ public class TBMCChatAPI {
|
||||||
*/
|
*/
|
||||||
public static boolean SendSystemMessage(Channel channel, RecipientTestResult rtr, String message, TBMCSystemChatEvent.BroadcastTarget target, String... exceptions) {
|
public static boolean SendSystemMessage(Channel channel, RecipientTestResult rtr, String message, TBMCSystemChatEvent.BroadcastTarget target, String... exceptions) {
|
||||||
if (!Channel.getChannelList().contains(channel))
|
if (!Channel.getChannelList().contains(channel))
|
||||||
throw new RuntimeException("Channel " + channel.DisplayName().get() + " not registered!");
|
throw new RuntimeException("Channel " + channel.DisplayName.get() + " not registered!");
|
||||||
if (!channel.Enabled().get())
|
if (!channel.Enabled.get())
|
||||||
return true; //Cancel sending
|
return true; //Cancel sending
|
||||||
if (!Arrays.asList(exceptions).contains("Minecraft"))
|
if (!Arrays.asList(exceptions).contains("Minecraft"))
|
||||||
Bukkit.getConsoleSender().sendMessage("[" + channel.DisplayName().get() + "] " + message);
|
Bukkit.getConsoleSender().sendMessage("[" + channel.DisplayName.get() + "] " + message);
|
||||||
TBMCSystemChatEvent event = new TBMCSystemChatEvent(channel, message, rtr.score, rtr.groupID, exceptions, target);
|
TBMCSystemChatEvent event = new TBMCSystemChatEvent(channel, message, rtr.score, rtr.groupID, exceptions, target);
|
||||||
return ChromaUtils.callEventAsync(event);
|
return ChromaUtils.callEventAsync(event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package buttondevteam.lib.player;
|
|
||||||
|
|
||||||
import buttondevteam.core.component.channel.Channel;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
|
|
||||||
public class ChannelPlayerData { //I just want this to work
|
|
||||||
private final PlayerData<String> data;
|
|
||||||
private final Channel def;
|
|
||||||
|
|
||||||
public ChannelPlayerData(String name, YamlConfiguration yaml, Channel def) {
|
|
||||||
data = new PlayerData<>(name, yaml, "");
|
|
||||||
this.def = def;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Channel get() {
|
|
||||||
String str = data.get();
|
|
||||||
if (str.isEmpty())
|
|
||||||
return def;
|
|
||||||
return Channel.getChannels().filter(c -> str.equals(c.ID)).findAny().orElse(def);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(Channel value) {
|
|
||||||
data.set(value.ID);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,11 @@
|
||||||
package buttondevteam.lib.player;
|
package buttondevteam.lib.player;
|
||||||
|
|
||||||
|
import buttondevteam.core.MainPlugin;
|
||||||
import buttondevteam.core.component.channel.Channel;
|
import buttondevteam.core.component.channel.Channel;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
import com.google.common.collect.HashBiMap;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
|
import buttondevteam.lib.architecture.IHaveConfig;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
@ -12,37 +15,60 @@ import javax.annotation.Nullable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@ChromaGamerEnforcer
|
@ChromaGamerEnforcer
|
||||||
public abstract class ChromaGamerBase implements AutoCloseable {
|
public abstract class ChromaGamerBase {
|
||||||
public static final String TBMC_PLAYERS_DIR = "TBMC/players/";
|
private static final String TBMC_PLAYERS_DIR = "TBMC/players/";
|
||||||
|
private static final ArrayList<Function<CommandSender, ? extends Optional<? extends ChromaGamerBase>>> senderConverters = new ArrayList<>();
|
||||||
private static final HashBiMap<Class<? extends ChromaGamerBase>, String> playerTypes = HashBiMap.create();
|
/**
|
||||||
|
* Holds data per user class
|
||||||
|
*/
|
||||||
|
private static final HashMap<Class<? extends ChromaGamerBase>, StaticUserData<?>> staticDataMap = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for connecting with every type of user ({@link #connectWith(ChromaGamerBase)})
|
* Use {@link #getConfig()} where possible; the 'id' must be always set
|
||||||
*/
|
*/
|
||||||
public static void RegisterPluginUserClass(Class<? extends ChromaGamerBase> userclass) {
|
//protected YamlConfiguration plugindata;
|
||||||
if (userclass.isAnnotationPresent(UserClass.class))
|
|
||||||
playerTypes.put(userclass, userclass.getAnnotation(UserClass.class).foldername());
|
@Getter
|
||||||
else if (userclass.isAnnotationPresent(AbstractUserClass.class))
|
protected final IHaveConfig config = new IHaveConfig(this::save);
|
||||||
playerTypes.put(userclass.getAnnotation(AbstractUserClass.class).prototype(),
|
protected CommonUserData<?> commonUserData;
|
||||||
userclass.getAnnotation(AbstractUserClass.class).foldername());
|
|
||||||
else // <-- Really important
|
/**
|
||||||
|
* Used for connecting with every type of user ({@link #connectWith(ChromaGamerBase)}) and to init the configs.
|
||||||
|
*/
|
||||||
|
public static <T extends ChromaGamerBase> void RegisterPluginUserClass(Class<T> userclass, Supplier<T> constructor) {
|
||||||
|
Class<? extends T> cl;
|
||||||
|
String folderName;
|
||||||
|
if (userclass.isAnnotationPresent(UserClass.class)) {
|
||||||
|
cl = userclass;
|
||||||
|
folderName = userclass.getAnnotation(UserClass.class).foldername();
|
||||||
|
} else if (userclass.isAnnotationPresent(AbstractUserClass.class)) {
|
||||||
|
var ucl = userclass.getAnnotation(AbstractUserClass.class).prototype();
|
||||||
|
if (!userclass.isAssignableFrom(ucl))
|
||||||
|
throw new RuntimeException("The prototype class (" + ucl.getSimpleName() + ") must be a subclass of the userclass parameter (" + userclass.getSimpleName() + ")!");
|
||||||
|
//noinspection unchecked
|
||||||
|
cl = (Class<? extends T>) ucl;
|
||||||
|
folderName = userclass.getAnnotation(AbstractUserClass.class).foldername();
|
||||||
|
} else // <-- Really important
|
||||||
throw new RuntimeException("Class not registered as a user class! Use @UserClass or TBMCPlayerBase");
|
throw new RuntimeException("Class not registered as a user class! Use @UserClass or TBMCPlayerBase");
|
||||||
|
var sud = new StaticUserData<T>(folderName);
|
||||||
|
sud.getConstructors().put(cl, constructor);
|
||||||
|
sud.getConstructors().put(userclass, constructor); // Alawys register abstract and prototype class (TBMCPlayerBase and TBMCPlayer)
|
||||||
|
staticDataMap.put(userclass, sud);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the folder name for the given player class.
|
* Returns the folder name for the given player class.
|
||||||
*
|
*
|
||||||
* @param cl
|
* @param cl The class to get the folder from (like {@link TBMCPlayerBase} or one of it's subclasses)
|
||||||
* The class to get the folder from (like {@link TBMCPlayerBase} or one of it's subclasses)
|
|
||||||
* @return The folder name for the given type
|
* @return The folder name for the given type
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException If the class doesn't have the {@link UserClass} annotation.
|
||||||
* If the class doesn't have the {@link UserClass} annotation.
|
|
||||||
*/
|
*/
|
||||||
public static <T extends ChromaGamerBase> String getFolderForType(Class<T> cl) {
|
public static <T extends ChromaGamerBase> String getFolderForType(Class<T> cl) {
|
||||||
if (cl.isAnnotationPresent(UserClass.class))
|
if (cl.isAnnotationPresent(UserClass.class))
|
||||||
|
@ -54,52 +80,63 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the player class for the given folder name.
|
* Returns the player class for the given folder name.
|
||||||
*
|
*
|
||||||
* @param foldername
|
* @param foldername The folder to get the class from (like "minecraft")
|
||||||
* The folder to get the class from (like "minecraft")
|
|
||||||
* @return The type for the given folder name or null if not found
|
* @return The type for the given folder name or null if not found
|
||||||
*/
|
*/
|
||||||
public static Class<? extends ChromaGamerBase> getTypeForFolder(String foldername) {
|
public static Class<? extends ChromaGamerBase> getTypeForFolder(String foldername) {
|
||||||
return playerTypes.inverse().get(foldername);
|
synchronized (staticDataMap) {
|
||||||
|
return staticDataMap.entrySet().stream().filter(e -> e.getValue().getFolder().equalsIgnoreCase(foldername))
|
||||||
|
.map(Map.Entry::getKey).findAny().orElse(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns the filename for this player data. For example, for Minecraft-related data, MC UUIDs, for Discord data, use Discord IDs, etc.<br>
|
|
||||||
* <b>Does not include .yml</b>
|
|
||||||
*/
|
|
||||||
public final String getFileName() {
|
|
||||||
return plugindata.getString(getFolder() + "_id");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use {@link #data(Object)} or {@link #data(String, Object)} where possible; the 'id' must be always set
|
|
||||||
*/
|
|
||||||
protected YamlConfiguration plugindata;
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Loads a user from disk and returns the user object. Make sure to use the subclasses' methods, where possible, like {@link TBMCPlayerBase#getPlayer(java.util.UUID, Class)}
|
* Retrieves a user from cache or loads it from disk.
|
||||||
*
|
*
|
||||||
* @param fname Filename without .yml, usually UUID
|
* @param fname Filename without .yml, the user's identifier for that type
|
||||||
* @param cl User class
|
* @param cl User class
|
||||||
* @return The user object
|
* @return The user object
|
||||||
*/
|
*/
|
||||||
public static <T extends ChromaGamerBase> T getUser(String fname, Class<T> cl) {
|
public static synchronized <T extends ChromaGamerBase> T getUser(String fname, Class<T> cl) {
|
||||||
try {
|
StaticUserData<?> staticUserData = null;
|
||||||
T obj = cl.newInstance();
|
for (var sud : staticDataMap.entrySet()) {
|
||||||
final String folder = getFolderForType(cl);
|
if (sud.getKey().isAssignableFrom(cl)) {
|
||||||
|
staticUserData = sud.getValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (staticUserData == null)
|
||||||
|
throw new RuntimeException("User class not registered! Use @UserClass or @AbstractUserClass");
|
||||||
|
var commonUserData = staticUserData.getUserDataMap().get(fname);
|
||||||
|
if (commonUserData == null) {
|
||||||
|
final String folder = staticUserData.getFolder();
|
||||||
final File file = new File(TBMC_PLAYERS_DIR + folder, fname + ".yml");
|
final File file = new File(TBMC_PLAYERS_DIR + folder, fname + ".yml");
|
||||||
file.getParentFile().mkdirs();
|
file.getParentFile().mkdirs();
|
||||||
obj.plugindata = YamlConfiguration.loadConfiguration(file);
|
var playerData = YamlConfiguration.loadConfiguration(file);
|
||||||
obj.plugindata.set(folder + "_id", fname);
|
commonUserData = new CommonUserData<>(playerData);
|
||||||
return obj;
|
playerData.set(staticUserData.getFolder() + "_id", fname);
|
||||||
} catch (Exception e) {
|
staticUserData.getUserDataMap().put(fname, commonUserData);
|
||||||
TBMCCoreAPI.SendException("An error occured while loading a " + cl.getSimpleName() + "!", e);
|
|
||||||
}
|
}
|
||||||
return null;
|
if (commonUserData.getUserCache().containsKey(cl))
|
||||||
|
return (T) commonUserData.getUserCache().get(cl);
|
||||||
|
T obj;
|
||||||
|
if (staticUserData.getConstructors().containsKey(cl))
|
||||||
|
//noinspection unchecked
|
||||||
|
obj = (T) staticUserData.getConstructors().get(cl).get();
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
obj = cl.getConstructor().newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to create new instance of user of type " + cl.getSimpleName() + "!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj.commonUserData = commonUserData;
|
||||||
|
obj.init();
|
||||||
|
obj.scheduleUncache();
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArrayList<Function<CommandSender, ? extends Optional<? extends ChromaGamerBase>>> senderConverters = new ArrayList<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a converter to the start of the list.
|
* Adds a converter to the start of the list.
|
||||||
*
|
*
|
||||||
|
@ -124,88 +161,110 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static void saveUsers() {
|
||||||
* Saves the player. It'll pass all exceptions to the caller. To automatically handle the exception, use {@link #save()} instead.
|
synchronized (staticDataMap) {
|
||||||
*/
|
for (var sud : staticDataMap.values())
|
||||||
@Override
|
for (var cud : sud.getUserDataMap().values())
|
||||||
public void close() throws Exception {
|
ConfigData.saveNow(cud.getPlayerData()); //Calls save()
|
||||||
if (plugindata.getKeys(false).size() > 0)
|
}
|
||||||
plugindata.save(new File(TBMC_PLAYERS_DIR + getFolder(), getFileName() + ".yml"));
|
}
|
||||||
|
|
||||||
|
protected void init() {
|
||||||
|
config.reset(commonUserData.getPlayerData());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the player. It'll handle all exceptions that may happen. To catch the exception, use {@link #close()} instead.
|
* Saves the player. It'll handle all exceptions that may happen. Called automatically.
|
||||||
*/
|
*/
|
||||||
public void save() {
|
protected void save() {
|
||||||
try {
|
try {
|
||||||
close();
|
if (commonUserData.getPlayerData().getKeys(false).size() > 0)
|
||||||
|
commonUserData.getPlayerData().save(new File(TBMC_PLAYERS_DIR + getFolder(), getFileName() + ".yml"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
TBMCCoreAPI.SendException("Error while saving player to " + getFolder() + "/" + getFileName() + ".yml!", e);
|
TBMCCoreAPI.SendException("Error while saving player to " + getFolder() + "/" + getFileName() + ".yml!", e, MainPlugin.Instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect two accounts. Do not use for connecting two Minecraft accounts or similar. Also make sure you have the "id" tag set
|
* Removes the user from the cache. This will be called automatically after some time by default.
|
||||||
*
|
|
||||||
* @param user
|
|
||||||
* The account to connect with
|
|
||||||
*/
|
*/
|
||||||
public <T extends ChromaGamerBase> void connectWith(T user) {
|
public void uncache() {
|
||||||
|
final var userCache = commonUserData.getUserCache();
|
||||||
|
//noinspection SynchronizationOnLocalVariableOrMethodParameter
|
||||||
|
synchronized (userCache) {
|
||||||
|
if (userCache.containsKey(getClass()))
|
||||||
|
if (userCache.remove(getClass()) != this)
|
||||||
|
throw new IllegalStateException("A different player instance was cached!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void scheduleUncache() {
|
||||||
|
Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, this::uncache, 2 * 60 * 60 * 20); //2 hours
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect two accounts. Do not use for connecting two Minecraft accounts or similar. Also make sure you have the "id" tag set.
|
||||||
|
*
|
||||||
|
* @param user The account to connect with
|
||||||
|
*/
|
||||||
|
public final <T extends ChromaGamerBase> void connectWith(T user) {
|
||||||
// Set the ID, go through all linked files and connect them as well
|
// Set the ID, go through all linked files and connect them as well
|
||||||
if (!playerTypes.containsKey(getClass()))
|
|
||||||
throw new RuntimeException("Class not registered as a user class! Use TBMCCoreAPI.RegisterUserClass");
|
|
||||||
final String ownFolder = getFolder();
|
final String ownFolder = getFolder();
|
||||||
final String userFolder = user.getFolder();
|
final String userFolder = user.getFolder();
|
||||||
if (ownFolder.equalsIgnoreCase(userFolder))
|
if (ownFolder.equalsIgnoreCase(userFolder))
|
||||||
throw new RuntimeException("Do not connect two accounts of the same type! Type: "+ownFolder);
|
throw new RuntimeException("Do not connect two accounts of the same type! Type: " + ownFolder);
|
||||||
user.plugindata.set(ownFolder + "_id", plugindata.getString(ownFolder + "_id"));
|
var ownData = commonUserData.getPlayerData();
|
||||||
plugindata.set(userFolder + "_id", user.plugindata.getString(userFolder + "_id"));
|
var userData = user.commonUserData.getPlayerData();
|
||||||
|
userData.set(ownFolder + "_id", ownData.getString(ownFolder + "_id"));
|
||||||
|
ownData.set(userFolder + "_id", userData.getString(userFolder + "_id"));
|
||||||
|
config.signalChange();
|
||||||
|
user.config.signalChange();
|
||||||
Consumer<YamlConfiguration> sync = sourcedata -> {
|
Consumer<YamlConfiguration> sync = sourcedata -> {
|
||||||
final String sourcefolder = sourcedata == plugindata ? ownFolder : userFolder;
|
final String sourcefolder = sourcedata == ownData ? ownFolder : userFolder;
|
||||||
final String id = sourcedata.getString(sourcefolder + "_id");
|
final String id = sourcedata.getString(sourcefolder + "_id");
|
||||||
for (val entry : playerTypes.entrySet()) { // Set our ID in all files we can find, both from our connections and the new ones
|
for (val entry : staticDataMap.entrySet()) { // Set our ID in all files we can find, both from our connections and the new ones
|
||||||
if (entry.getKey() == getClass() || entry.getKey() == user.getClass())
|
if (entry.getKey() == getClass() || entry.getKey() == user.getClass())
|
||||||
continue;
|
continue;
|
||||||
final String otherid = sourcedata.getString(entry.getValue() + "_id");
|
var entryFolder = entry.getValue().getFolder();
|
||||||
|
final String otherid = sourcedata.getString(entryFolder + "_id");
|
||||||
if (otherid == null)
|
if (otherid == null)
|
||||||
continue;
|
continue;
|
||||||
try (ChromaGamerBase cg = getUser(otherid, entry.getKey())) {
|
ChromaGamerBase cg = getUser(otherid, entry.getKey());
|
||||||
cg.plugindata.set(sourcefolder + "_id", id); // Set new IDs
|
var cgData = cg.commonUserData.getPlayerData();
|
||||||
for (val item : playerTypes.entrySet())
|
cgData.set(sourcefolder + "_id", id); // Set new IDs
|
||||||
if (sourcedata.contains(item.getValue() + "_id"))
|
for (val item : staticDataMap.entrySet()) {
|
||||||
cg.plugindata.set(item.getValue() + "_id", sourcedata.getString(item.getValue() + "_id")); // Set all existing IDs
|
var itemFolder = item.getValue().getFolder();
|
||||||
} catch (Exception e) {
|
if (sourcedata.contains(itemFolder + "_id")) {
|
||||||
TBMCCoreAPI.SendException("Failed to update " + sourcefolder + " ID in player files for " + id
|
cgData.set(itemFolder + "_id", sourcedata.getString(itemFolder + "_id")); // Set all existing IDs
|
||||||
+ " in folder with " + entry.getValue() + " id " + otherid + "!", e);
|
}
|
||||||
}
|
}
|
||||||
|
cg.config.signalChange();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
sync.accept(plugindata);
|
sync.accept(ownData);
|
||||||
sync.accept(user.plugindata);
|
sync.accept(userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retunrs the ID for the T typed player object connected with this one or null if no connection found.
|
* Returns the ID for the T typed player object connected with this one or null if no connection found.
|
||||||
*
|
*
|
||||||
* @param cl
|
* @param cl The player class to get the ID from
|
||||||
* The player class to get the ID from
|
|
||||||
* @return The ID or null if not found
|
* @return The ID or null if not found
|
||||||
*/
|
*/
|
||||||
public <T extends ChromaGamerBase> String getConnectedID(Class<T> cl) {
|
public final <T extends ChromaGamerBase> String getConnectedID(Class<T> cl) {
|
||||||
return plugindata.getString(getFolderForType(cl) + "_id");
|
return commonUserData.getPlayerData().getString(getFolderForType(cl) + "_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this player as a plugin player. This will return a new instance unless the player is online.<br>
|
* Returns a player instance of the given type that represents the same player. This will return a new instance unless the player is cached.<br>
|
||||||
* Make sure to close both the returned and this object. A try-with-resources block or two can help.<br>
|
* If the class is a subclass of the current class then the same ID is used, otherwise, a connected ID is used, if found.
|
||||||
*
|
*
|
||||||
* @param cl
|
* @param cl The target player class
|
||||||
* The target player class
|
* @return The player as a {@link T} object or null if the user doesn't have an account there
|
||||||
* @return The player as a {@link T} object or null if not having an account there
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T extends ChromaGamerBase> T getAs(Class<T> cl) { // TODO: Provide a way to use TBMCPlayerBase's loaded players
|
public final <T extends ChromaGamerBase> T getAs(Class<T> cl) {
|
||||||
if (cl.getSimpleName().equals(getClass().getSimpleName()))
|
if (cl.getSimpleName().equals(getClass().getSimpleName()))
|
||||||
return (T) this;
|
return (T) this;
|
||||||
String newfolder = getFolderForType(cl);
|
String newfolder = getFolderForType(cl);
|
||||||
|
@ -213,105 +272,34 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
throw new RuntimeException("The specified class " + cl.getSimpleName() + " isn't registered!");
|
throw new RuntimeException("The specified class " + cl.getSimpleName() + " isn't registered!");
|
||||||
if (newfolder.equals(getFolder())) // If in the same folder, the same filename is used
|
if (newfolder.equals(getFolder())) // If in the same folder, the same filename is used
|
||||||
return getUser(getFileName(), cl);
|
return getUser(getFileName(), cl);
|
||||||
if (!plugindata.contains(newfolder + "_id"))
|
var playerData = commonUserData.getPlayerData();
|
||||||
|
if (!playerData.contains(newfolder + "_id"))
|
||||||
return null;
|
return null;
|
||||||
return getUser(plugindata.getString(newfolder + "_id"), cl);
|
return getUser(playerData.getString(newfolder + "_id"), cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFolder() {
|
/**
|
||||||
|
* This method returns the filename for this player data. For example, for Minecraft-related data, MC UUIDs, for Discord data, Discord IDs, etc.<br>
|
||||||
|
* <b>Does not include .yml</b>
|
||||||
|
*/
|
||||||
|
public final String getFileName() {
|
||||||
|
return commonUserData.getPlayerData().getString(getFolder() + "_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the folder that this player data is stored in. For example: "minecraft".
|
||||||
|
*/
|
||||||
|
public final String getFolder() {
|
||||||
return getFolderForType(getClass());
|
return getFolderForType(getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThrowIfNoUser() {
|
|
||||||
if (!getClass().isAnnotationPresent(UserClass.class)
|
|
||||||
&& !getClass().isAnnotationPresent(AbstractUserClass.class))
|
|
||||||
throw new RuntimeException("Class not registered as a user class! Use @UserClass");
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private final HashMap<String, PlayerData> datamap = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use from a data() method, which is in a method with the name of the key. For example, use flair() for the enclosing method of the outer data() to save to and load from "flair"
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T> PlayerData<T> data(String sectionname, T def) {
|
|
||||||
ThrowIfNoUser();
|
|
||||||
String mname = sectionname + "." + new Exception().getStackTrace()[2].getMethodName();
|
|
||||||
if (!datamap.containsKey(mname))
|
|
||||||
datamap.put(mname, new PlayerData<T>(mname, plugindata, def));
|
|
||||||
return datamap.get(mname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use from a method with the name of the key. For example, use flair() for the enclosing method to save to and load from "flair"
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T> PlayerData<T> data(T def) {
|
|
||||||
ThrowIfNoUser();
|
|
||||||
String mname = new Exception().getStackTrace()[1].getMethodName();
|
|
||||||
if (!datamap.containsKey(mname))
|
|
||||||
datamap.put(mname, new PlayerData<T>(mname, plugindata, def));
|
|
||||||
return datamap.get(mname);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private final HashMap<String, EnumPlayerData> dataenummap = new HashMap<>();
|
|
||||||
private ChannelPlayerData datachannel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use from a data() method, which is in a method with the name of the key. For example, use flair() for the enclosing method of the outer data() to save to and load from "flair"
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T extends Enum<T>> EnumPlayerData<T> dataEnum(String sectionname, Class<T> cl, T def) {
|
|
||||||
ThrowIfNoUser();
|
|
||||||
String mname = sectionname + "." + new Exception().getStackTrace()[2].getMethodName();
|
|
||||||
if (!dataenummap.containsKey(mname))
|
|
||||||
dataenummap.put(mname, new EnumPlayerData<T>(mname, plugindata, cl, def));
|
|
||||||
return dataenummap.get(mname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use from a method with the name of the key. For example, use flair() for the enclosing method to save to and load from "flair"
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T extends Enum<T>> EnumPlayerData<T> dataEnum(Class<T> cl, T def) {
|
|
||||||
ThrowIfNoUser();
|
|
||||||
String mname = new Exception().getStackTrace()[1].getMethodName();
|
|
||||||
if (!dataenummap.containsKey(mname))
|
|
||||||
dataenummap.put(mname, new EnumPlayerData<T>(mname, plugindata, cl, def));
|
|
||||||
return dataenummap.get(mname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Channel
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected ChannelPlayerData dataChannel(Channel def) { //TODO: Make interface with fromString() method and require use of that for player data types
|
|
||||||
ThrowIfNoUser();
|
|
||||||
if (datachannel == null)
|
|
||||||
datachannel = new ChannelPlayerData("channel", plugindata, def);
|
|
||||||
return datachannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get player information. This method calls the {@link TBMCPlayerGetInfoEvent} to get all the player information across the TBMC plugins.
|
* Get player information. This method calls the {@link TBMCPlayerGetInfoEvent} to get all the player information across the TBMC plugins.
|
||||||
*
|
*
|
||||||
* @param target
|
* @param target The {@link InfoTarget} to return the info for.
|
||||||
* The {@link InfoTarget} to return the info for.
|
|
||||||
* @return The player information.
|
* @return The player information.
|
||||||
*/
|
*/
|
||||||
public String getInfo(InfoTarget target) {
|
public final String getInfo(InfoTarget target) {
|
||||||
TBMCPlayerGetInfoEvent event = new TBMCPlayerGetInfoEvent(this, target);
|
TBMCPlayerGetInfoEvent event = new TBMCPlayerGetInfoEvent(this, target);
|
||||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||||
return event.getResult();
|
return event.getResult();
|
||||||
|
@ -323,7 +311,6 @@ public abstract class ChromaGamerBase implements AutoCloseable {
|
||||||
|
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
|
|
||||||
public ChannelPlayerData channel() {
|
public final ConfigData<Channel> channel = config.getData("channel", Channel.GlobalChat,
|
||||||
return dataChannel(Channel.GlobalChat);
|
id -> Channel.getChannels().filter(ch -> ch.ID.equalsIgnoreCase((String) id)).findAny().orElse(null), ch -> ch.ID);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package buttondevteam.lib.player;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per user, regardless of actual type
|
||||||
|
*
|
||||||
|
* @param <T> The user class, may be abstract
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CommonUserData<T extends ChromaGamerBase> {
|
||||||
|
private final HashMap<Class<? extends T>, ? extends T> userCache = new HashMap<>();
|
||||||
|
private final YamlConfiguration playerData;
|
||||||
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
package buttondevteam.lib.player;
|
|
||||||
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
|
|
||||||
public class EnumPlayerData<T extends Enum<T>> {
|
|
||||||
private final PlayerData<String> data;
|
|
||||||
private final Class<T> cl;
|
|
||||||
private final T def;
|
|
||||||
|
|
||||||
public EnumPlayerData(String name, YamlConfiguration yaml, Class<T> cl, T def) {
|
|
||||||
data = new PlayerData<String>(name, yaml, "");
|
|
||||||
this.cl = cl;
|
|
||||||
this.def = def;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T get() {
|
|
||||||
String str = data.get();
|
|
||||||
if (str.isEmpty())
|
|
||||||
return def;
|
|
||||||
return Enum.valueOf(cl, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(T value) {
|
|
||||||
data.set(value.toString());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package buttondevteam.lib.player;
|
|
||||||
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
|
|
||||||
public class PlayerData<T> {
|
|
||||||
private final String name;
|
|
||||||
private final YamlConfiguration yaml;
|
|
||||||
private final T def;
|
|
||||||
|
|
||||||
public PlayerData(String name, YamlConfiguration yaml, T def) {
|
|
||||||
this.name = name;
|
|
||||||
this.yaml = yaml;
|
|
||||||
this.def = def;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
// @Deprecated - What was once enforced (2 days ago from now) vanished now
|
|
||||||
public T get() {
|
|
||||||
Object value = yaml.get(name, def);
|
|
||||||
if (value instanceof Integer) {
|
|
||||||
if (def instanceof Short) // If the default is Short the value must be as well because both are T
|
|
||||||
return (T) (Short) ((Integer) value).shortValue();
|
|
||||||
if (def instanceof Long)
|
|
||||||
return (T) (Long) ((Integer) value).longValue();
|
|
||||||
}
|
|
||||||
return (T) value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(T value) {
|
|
||||||
yaml.set(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return get().toString();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package buttondevteam.lib.player;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per user class
|
||||||
|
*
|
||||||
|
* @param <T> The user class type, may be abstract
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class StaticUserData<T extends ChromaGamerBase> {
|
||||||
|
private final HashMap<Class<? extends T>, Supplier<T>> constructors = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* Key: User ID
|
||||||
|
*/
|
||||||
|
private final HashMap<String, CommonUserData<?>> userDataMap = new HashMap<>();
|
||||||
|
private final String folder;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package buttondevteam.lib.player;
|
package buttondevteam.lib.player;
|
||||||
|
|
||||||
@PlayerClass(pluginname = "ButtonCore") //TODO: Migrate
|
@PlayerClass(pluginname = "Chroma-Core")
|
||||||
public final class TBMCPlayer extends TBMCPlayerBase {
|
public final class TBMCPlayer extends TBMCPlayerBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,21 @@
|
||||||
package buttondevteam.lib.player;
|
package buttondevteam.lib.player;
|
||||||
|
|
||||||
import buttondevteam.core.component.towny.TownyComponent;
|
import buttondevteam.lib.architecture.ConfigData;
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.architecture.IHaveConfig;
|
||||||
|
import lombok.Getter;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
@AbstractUserClass(foldername = "minecraft", prototype = TBMCPlayer.class)
|
@AbstractUserClass(foldername = "minecraft", prototype = TBMCPlayer.class)
|
||||||
@TBMCPlayerEnforcer
|
@TBMCPlayerEnforcer
|
||||||
public abstract class TBMCPlayerBase extends ChromaGamerBase {
|
public abstract class TBMCPlayerBase extends ChromaGamerBase {
|
||||||
protected UUID uuid;
|
protected UUID uuid;
|
||||||
|
|
||||||
private String pluginname;
|
@Getter
|
||||||
|
private final IHaveConfig config = new IHaveConfig(this::save);
|
||||||
protected TBMCPlayerBase() {
|
|
||||||
if (getClass().isAnnotationPresent(PlayerClass.class))
|
|
||||||
pluginname = getClass().getAnnotation(PlayerClass.class).pluginname();
|
|
||||||
else
|
|
||||||
throw new RuntimeException("Class not defined as player class! Use @PlayerClass");
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getUUID() {
|
public UUID getUUID() {
|
||||||
if (uuid == null)
|
if (uuid == null)
|
||||||
|
@ -30,156 +23,61 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase {
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlayerData<String> PlayerName() {
|
public final ConfigData<String> PlayerName = super.config.getData("PlayerName", "");
|
||||||
return super.data(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use from a method with the name of the key. For example, use flair() for the enclosing method to save to and load from "flair"
|
* Get player as a plugin player.
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected <T> PlayerData<T> data(T def) {
|
|
||||||
return super.data(pluginname, def);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use from a method with the name of the key. For example, use flair() for the enclosing method to save to and load from "flair"
|
|
||||||
*
|
|
||||||
* @return A data object with methods to get and set
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected <T extends Enum<T>> EnumPlayerData<T> dataEnum(Class<T> cl, T def) {
|
|
||||||
return super.dataEnum(pluginname, cl, def);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get player as a plugin player
|
|
||||||
*
|
*
|
||||||
* @param uuid The UUID of the player to get
|
* @param uuid The UUID of the player to get
|
||||||
* @param cl The type of the player
|
* @param cl The type of the player
|
||||||
* @return The requested player object
|
* @return The requested player object
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T extends TBMCPlayerBase> T getPlayer(UUID uuid, Class<T> cl) {
|
public static <T extends TBMCPlayerBase> T getPlayer(UUID uuid, Class<T> cl) {
|
||||||
if (playermap.containsKey(uuid + "-" + cl.getSimpleName()))
|
var player = ChromaGamerBase.getUser(uuid.toString(), cl);
|
||||||
return (T) playermap.get(uuid + "-" + cl.getSimpleName());
|
if (!player.getUUID().equals(uuid)) //It will be set from the filename because we check it for scheduling the uncache.
|
||||||
try {
|
throw new IllegalStateException("Player UUID differs after converting from and to string...");
|
||||||
T player;
|
return player;
|
||||||
if (playermap.containsKey(uuid + "-" + TBMCPlayer.class.getSimpleName())) {
|
}
|
||||||
player = cl.newInstance();
|
|
||||||
player.plugindata = playermap.get(uuid + "-" + TBMCPlayer.class.getSimpleName()).plugindata;
|
@Override
|
||||||
playermap.put(uuid + "-" + cl.getSimpleName(), player); // It will get removed on player quit
|
public void init() {
|
||||||
} else
|
super.init();
|
||||||
player = ChromaGamerBase.getUser(uuid.toString(), cl);
|
|
||||||
player.uuid = uuid;
|
String pluginname;
|
||||||
return player;
|
if (getClass().isAnnotationPresent(PlayerClass.class))
|
||||||
} catch (Exception e) {
|
pluginname = getClass().getAnnotation(PlayerClass.class).pluginname();
|
||||||
TBMCCoreAPI.SendException(
|
else
|
||||||
"Failed to get player with UUID " + uuid + " and class " + cl.getSimpleName() + "!", e);
|
throw new RuntimeException("Class not defined as player class! Use @PlayerClass");
|
||||||
return null;
|
|
||||||
}
|
var playerData = commonUserData.getPlayerData();
|
||||||
|
var section = playerData.getConfigurationSection(pluginname);
|
||||||
|
if (section == null) section = playerData.createSection(pluginname);
|
||||||
|
config.reset(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void scheduleUncache() { //Don't schedule it, it will happen on quit - if the player is online
|
||||||
|
var p = Bukkit.getPlayer(getUUID());
|
||||||
|
if (p == null || !p.isOnline())
|
||||||
|
super.scheduleUncache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key: UUID-Class
|
* This method returns a TBMC player from their name. See {@link Bukkit#getOfflinePlayer(String)}.
|
||||||
*/
|
|
||||||
static final ConcurrentHashMap<String, TBMCPlayerBase> playermap = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the TBMCPlayer object as a specific plugin player, keeping it's data<br>
|
|
||||||
* Make sure to use try-with-resources with this to save the data, as it may need to load the file
|
|
||||||
*
|
|
||||||
* @param cl The TBMCPlayer subclass
|
|
||||||
*/
|
|
||||||
public <T extends TBMCPlayerBase> T asPluginPlayer(Class<T> cl) {
|
|
||||||
return getPlayer(uuid, cl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only intended to use from ButtonCore
|
|
||||||
*/
|
|
||||||
public static void savePlayer(TBMCPlayerBase player) {
|
|
||||||
Bukkit.getServer().getPluginManager().callEvent(new TBMCPlayerSaveEvent(player));
|
|
||||||
try {
|
|
||||||
player.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
new Exception("Failed to save player data for " + player.PlayerName().get(), e).printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only intended to use from ButtonCore
|
|
||||||
*/
|
|
||||||
public static void joinPlayer(Player p) {
|
|
||||||
TBMCPlayer player = TBMCPlayerBase.getPlayer(p.getUniqueId(), TBMCPlayer.class);
|
|
||||||
Bukkit.getLogger().info("Loaded player: " + player.PlayerName().get());
|
|
||||||
if (player.PlayerName().get() == null) {
|
|
||||||
player.PlayerName().set(p.getName());
|
|
||||||
Bukkit.getLogger().info("Player name saved: " + player.PlayerName().get());
|
|
||||||
} else if (!p.getName().equals(player.PlayerName().get())) {
|
|
||||||
TownyComponent.renameInTowny(player.PlayerName().get(), p.getName());
|
|
||||||
player.PlayerName().set(p.getName());
|
|
||||||
Bukkit.getLogger().info("Renamed to " + p.getName());
|
|
||||||
}
|
|
||||||
playermap.put(p.getUniqueId() + "-" + TBMCPlayer.class.getSimpleName(), player);
|
|
||||||
|
|
||||||
// Load in other plugins
|
|
||||||
Bukkit.getServer().getPluginManager().callEvent(new TBMCPlayerLoadEvent(player));
|
|
||||||
Bukkit.getServer().getPluginManager().callEvent(new TBMCPlayerJoinEvent(player, p));
|
|
||||||
player.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only intended to use from ButtonCore
|
|
||||||
*/
|
|
||||||
public static void quitPlayer(Player p) {
|
|
||||||
final TBMCPlayerBase player = playermap.get(p.getUniqueId() + "-" + TBMCPlayer.class.getSimpleName());
|
|
||||||
player.save();
|
|
||||||
Bukkit.getServer().getPluginManager().callEvent(new TBMCPlayerQuitEvent(player, p));
|
|
||||||
playermap.entrySet().removeIf(entry -> entry.getKey().startsWith(p.getUniqueId().toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void savePlayers() {
|
|
||||||
playermap.values().forEach(p -> {
|
|
||||||
try {
|
|
||||||
p.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
TBMCCoreAPI.SendException("Error while saving player " + p.PlayerName().get() + " (" + p.getFolder()
|
|
||||||
+ "/" + p.getFileName() + ")!", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns a TBMC player from their name. Calling this method may return an offline player which will load it, therefore it's highly recommended to use {@link #close()} to unload the
|
|
||||||
* player data. Using try-with-resources may be the easiest way to achieve this. Example:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* {@code
|
|
||||||
* try(TBMCPlayer player = getFromName(p))
|
|
||||||
* {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*
|
*
|
||||||
* @param name The player's name
|
* @param name The player's name
|
||||||
* @return The {@link TBMCPlayer} object for the player
|
* @return The {@link TBMCPlayer} object for the player
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public static <T extends TBMCPlayerBase> T getFromName(String name, Class<T> cl) {
|
public static <T extends TBMCPlayerBase> T getFromName(String name, Class<T> cl) {
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
OfflinePlayer p = Bukkit.getOfflinePlayer(name);
|
OfflinePlayer p = Bukkit.getOfflinePlayer(name);
|
||||||
if (p != null)
|
return getPlayer(p.getUniqueId(), cl);
|
||||||
return getPlayer(p.getUniqueId(), cl);
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
protected void save() {
|
||||||
Set<String> keys = plugindata.getKeys(false);
|
Set<String> keys = commonUserData.getPlayerData().getKeys(false);
|
||||||
if (keys.size() > 1) // PlayerName is always saved, but we don't need a file for just that
|
if (keys.size() > 1) // PlayerName is always saved, but we don't need a file for just that
|
||||||
super.close();
|
super.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
package buttondevteam.lib.player;
|
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
|
|
||||||
public class TBMCPlayerJoinEvent extends Event {
|
|
||||||
private static final HandlerList handlers = new HandlerList();
|
|
||||||
|
|
||||||
private final TBMCPlayerBase player;
|
|
||||||
private final Player player_;
|
|
||||||
|
|
||||||
public TBMCPlayerJoinEvent(TBMCPlayerBase player, Player player_) {
|
|
||||||
this.player = player;
|
|
||||||
this.player_ = player_;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TBMCPlayerBase GetPlayer() {
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player getPlayer() { // :P
|
|
||||||
return player_;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandlerList getHandlers() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package buttondevteam.lib.player;
|
|
||||||
|
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
|
|
||||||
public class TBMCPlayerLoadEvent extends Event {
|
|
||||||
private static final HandlerList handlers = new HandlerList();
|
|
||||||
|
|
||||||
private final TBMCPlayerBase player;
|
|
||||||
|
|
||||||
public TBMCPlayerLoadEvent(TBMCPlayerBase player) {
|
|
||||||
this.player = player;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TBMCPlayerBase GetPlayer() {
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandlerList getHandlers() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package buttondevteam.lib.player;
|
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
|
|
||||||
public class TBMCPlayerQuitEvent extends Event {
|
|
||||||
private static final HandlerList handlers = new HandlerList();
|
|
||||||
|
|
||||||
private final TBMCPlayerBase player;
|
|
||||||
private final Player player_;
|
|
||||||
|
|
||||||
public TBMCPlayerQuitEvent(TBMCPlayerBase player, Player player_) {
|
|
||||||
this.player = player;
|
|
||||||
this.player_ = player_;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TBMCPlayerBase GetPlayer() {
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player getPlayer() {
|
|
||||||
return player_;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandlerList getHandlers() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package buttondevteam.lib.player;
|
|
||||||
|
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
|
|
||||||
public class TBMCPlayerSaveEvent extends Event {
|
|
||||||
private static final HandlerList handlers = new HandlerList();
|
|
||||||
|
|
||||||
private final TBMCPlayerBase player;
|
|
||||||
|
|
||||||
public TBMCPlayerSaveEvent(TBMCPlayerBase player) {
|
|
||||||
this.player = player;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TBMCPlayerBase GetPlayer() {
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandlerList getHandlers() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: ChromaCore
|
name: Chroma-Core
|
||||||
main: buttondevteam.core.MainPlugin
|
main: buttondevteam.core.MainPlugin
|
||||||
version: 1.0
|
version: '${noprefix.version}'
|
||||||
author: NorbiPeti
|
author: NorbiPeti
|
||||||
commands:
|
commands:
|
||||||
updateplugin:
|
updateplugin:
|
||||||
|
@ -9,10 +9,6 @@ commands:
|
||||||
description: Schedules a restart for a given time.
|
description: Schedules a restart for a given time.
|
||||||
primerestart:
|
primerestart:
|
||||||
description: Restarts the server as soon as nobody is online.
|
description: Restarts the server as soon as nobody is online.
|
||||||
randomtp:
|
|
||||||
description: 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.
|
|
||||||
member:
|
|
||||||
description: Add or remove a member
|
|
||||||
component:
|
component:
|
||||||
description: Enable or disable or list components
|
description: Enable or disable or list components
|
||||||
dontrunthiscmd:
|
dontrunthiscmd:
|
||||||
|
@ -22,4 +18,5 @@ softdepend:
|
||||||
- Towny
|
- Towny
|
||||||
- Votifier
|
- Votifier
|
||||||
- Multiverse-Core
|
- Multiverse-Core
|
||||||
- Essentials
|
- Essentials
|
||||||
|
api-version: '1.13'
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
package buttondevteam.core;
|
|
||||||
|
|
||||||
import buttondevteam.core.TestPlayerClass.TestEnum;
|
|
||||||
import buttondevteam.lib.player.ChromaGamerBase;
|
|
||||||
import buttondevteam.lib.player.TBMCPlayerBase;
|
|
||||||
import junit.framework.Test;
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import junit.framework.TestSuite;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.FileVisitResult;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.SimpleFileVisitor;
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PlayerDataTest extends TestCase {
|
|
||||||
public PlayerDataTest() {
|
|
||||||
super("Player data test");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the suite of tests being tested
|
|
||||||
*/
|
|
||||||
public static Test suite() {
|
|
||||||
return new TestSuite(PlayerDataTest.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testConfig() throws Exception {
|
|
||||||
TestPrepare.PrepareServer();
|
|
||||||
//FileUtils.deleteDirectory(new File(ChromaGamerBase.TBMC_PLAYERS_DIR));
|
|
||||||
File file = new File(ChromaGamerBase.TBMC_PLAYERS_DIR);
|
|
||||||
if (file.exists()) {
|
|
||||||
Files.walkFileTree(file.toPath(), new SimpleFileVisitor<Path>() {
|
|
||||||
@Override
|
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
|
||||||
throws IOException {
|
|
||||||
Files.delete(file);
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileVisitResult postVisitDirectory(Path dir, IOException e)
|
|
||||||
throws IOException {
|
|
||||||
if (e == null) {
|
|
||||||
Files.delete(dir);
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
} else {
|
|
||||||
// directory iteration failed
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
UUID uuid = new UUID(0L, 0L);
|
|
||||||
try (TestPlayerClass p = TBMCPlayerBase.getPlayer(uuid, TestPlayerClass.class)) {
|
|
||||||
p.PlayerName().set("Test");
|
|
||||||
assertEquals("Test", p.PlayerName().get());
|
|
||||||
assertEquals(TestEnum.A, p.testenum().get());
|
|
||||||
assertEquals((short) 0, (short) p.TestShort().get());
|
|
||||||
assertFalse(p.TestBool().get());
|
|
||||||
p.testenum().set(TestEnum.B);
|
|
||||||
assertEquals(TestEnum.B, p.testenum().get());
|
|
||||||
p.TestShort().set((short) 5);
|
|
||||||
assertEquals((short) 5, (short) p.TestShort().get());
|
|
||||||
p.TestBool().set(true);
|
|
||||||
assertTrue(p.TestBool().get());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
try (TestPlayerClass p = TBMCPlayerBase.getPlayer(uuid, TestPlayerClass.class)) {
|
|
||||||
assertEquals("Test", p.PlayerName().get());
|
|
||||||
assertEquals(TestEnum.B, p.testenum().get());
|
|
||||||
assertEquals((short) 5, (short) p.TestShort().get());
|
|
||||||
assertTrue(p.TestBool().get());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package buttondevteam.core;
|
|
||||||
|
|
||||||
import buttondevteam.lib.player.EnumPlayerData;
|
|
||||||
import buttondevteam.lib.player.PlayerClass;
|
|
||||||
import buttondevteam.lib.player.PlayerData;
|
|
||||||
import buttondevteam.lib.player.TBMCPlayerBase;
|
|
||||||
|
|
||||||
@PlayerClass(pluginname = "TestPlugin")
|
|
||||||
public class TestPlayerClass extends TBMCPlayerBase {
|
|
||||||
public EnumPlayerData<TestEnum> testenum() {
|
|
||||||
return dataEnum(TestEnum.class, TestEnum.A);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum TestEnum {
|
|
||||||
A, B
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerData<Short> TestShort() {
|
|
||||||
return data((short) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerData<Boolean> TestBool() {
|
|
||||||
return data(false);
|
|
||||||
}
|
|
||||||
}
|
|
221
CorePOM/pom.xml
Executable file → Normal file
221
CorePOM/pom.xml
Executable file → Normal file
|
@ -1,110 +1,127 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||||
http://maven.apache.org/maven-v4_0_0.xsd">
|
http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
|
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
|
||||||
<artifactId>CorePOM</artifactId>
|
<artifactId>CorePOM</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>master-SNAPSHOT</version>
|
<version>master-SNAPSHOT</version>
|
||||||
<properties>
|
<properties>
|
||||||
<lombok.version>1.18.10</lombok.version>
|
<lombok.version>1.18.10</lombok.version>
|
||||||
</properties>
|
</properties>
|
||||||
<name>Core POM for Chroma</name>
|
<name>Core POM for Chroma</name>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<pluginManagement>
|
<pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>3.8.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<release>8</release>
|
<release>8</release>
|
||||||
<annotationProcessorPaths>
|
<annotationProcessorPaths>
|
||||||
<annotationProcessorPath>
|
<annotationProcessorPath>
|
||||||
<groupId>com.github.bsideup.jabel</groupId>
|
<groupId>com.github.bsideup.jabel</groupId>
|
||||||
<artifactId>jabel-javac-plugin</artifactId>
|
<artifactId>jabel-javac-plugin</artifactId>
|
||||||
<version>0.2.0</version>
|
<version>0.2.0</version>
|
||||||
</annotationProcessorPath>
|
</annotationProcessorPath>
|
||||||
<annotationProcessorPath>
|
<annotationProcessorPath>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>${lombok.version}</version>
|
<version>${lombok.version}</version>
|
||||||
</annotationProcessorPath>
|
</annotationProcessorPath>
|
||||||
<annotationProcessorPath>
|
<annotationProcessorPath>
|
||||||
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
|
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
|
||||||
<artifactId>ButtonProcessor</artifactId>
|
<artifactId>ButtonProcessor</artifactId>
|
||||||
<version>master-SNAPSHOT</version>
|
<version>master-SNAPSHOT</version>
|
||||||
</annotationProcessorPath>
|
</annotationProcessorPath>
|
||||||
</annotationProcessorPaths>
|
</annotationProcessorPaths>
|
||||||
<annotationProcessors> <!-- Order is important, so these lines are needed -->
|
<annotationProcessors> <!-- Order is important, so these lines are needed -->
|
||||||
<annotationProcessor>com.github.bsideup.jabel.JabelJavacProcessor</annotationProcessor>
|
<annotationProcessor>com.github.bsideup.jabel.JabelJavacProcessor</annotationProcessor>
|
||||||
<annotationProcessor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor
|
<annotationProcessor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor
|
||||||
</annotationProcessor>
|
</annotationProcessor>
|
||||||
<annotationProcessor>buttondevteam.buttonproc.ButtonProcessor</annotationProcessor>
|
<annotationProcessor>buttondevteam.buttonproc.ButtonProcessor</annotationProcessor>
|
||||||
</annotationProcessors>
|
</annotationProcessors>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
<useSystemClassLoader>false
|
<useSystemClassLoader>false
|
||||||
</useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 -->
|
</useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 -->
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
<!-- <plugin>
|
||||||
</pluginManagement>
|
<groupId>io.github.1tchy</groupId>
|
||||||
</build>
|
<artifactId>variable-search-replace-plugin</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>replace</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<text>${project.version}</text>
|
||||||
|
<search>^v</search>
|
||||||
|
<variableName>noprefix.version</variableName>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin> -->
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>jitpack.io</id>
|
<id>jitpack.io</id>
|
||||||
<url>https://jitpack.io/</url>
|
<url>https://jitpack.io/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>4.13.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>${lombok.version}</version>
|
<version>${lombok.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>intellij-idea-only</id>
|
<id>intellij-idea-only</id>
|
||||||
<activation>
|
<activation>
|
||||||
<property>
|
<property>
|
||||||
<name>idea.maven.embedder.version</name>
|
<name>idea.maven.embedder.version</name>
|
||||||
</property>
|
</property>
|
||||||
</activation>
|
</activation>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
<release>11</release>
|
<release>11</release>
|
||||||
<!--
|
<!--
|
||||||
<compilerArgs>
|
<compilerArgs>
|
||||||
<arg>HYPHENHYPHENenable-preview</arg>
|
<arg>HYPHENHYPHENenable-preview</arg>
|
||||||
</compilerArgs>
|
</compilerArgs>
|
||||||
-->
|
-->
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
|
|
2
pom.xml
Executable file → Normal file
2
pom.xml
Executable file → Normal file
|
@ -44,7 +44,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>4.13.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||||
|
|
Loading…
Reference in a new issue