1 Commits
Author SHA1 Message Date
oskar3123 d4da0a5f7e Release v1.4.1 2022-03-22 01:56:41 +01:00
36 changed files with 925 additions and 1470 deletions
+1
View File
@@ -0,0 +1 @@
repo_token: $COVERALLS_REPO_TOKEN
-36
View File
@@ -1,36 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: build
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 8 and 17
uses: actions/setup-java@v4
with:
java-version: |
8
17
distribution: 'corretto'
- name: Build with Gradle
uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
with:
arguments: shadowJar
-33
View File
@@ -1,33 +0,0 @@
name: "pre-release"
on:
push:
branches:
- "master"
jobs:
pre-release:
name: "Pre Release"
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v3
- name: Set up JDK 8 and 17
uses: actions/setup-java@v4
with:
java-version: |
8
17
distribution: 'corretto'
- name: Build with Gradle
uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
with:
arguments: shadowJar
- uses: "marvinpinto/action-automatic-releases@v1.2.1"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
prerelease: true
title: "Development Build"
files: |
build/libs/*.jar
-31
View File
@@ -1,31 +0,0 @@
name: "tagged-release"
on:
push:
tags:
- "v*"
jobs:
tagged-release:
name: "Tagged Release"
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v3
- name: Set up JDK 8 and 17
uses: actions/setup-java@v4
with:
java-version: |
8
17
distribution: 'corretto'
- name: Build with Gradle
uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
with:
arguments: shadowJar
- uses: "marvinpinto/action-automatic-releases@v1.2.1"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
files: |
build/libs/*.jar
+2 -2
View File
@@ -1,4 +1,4 @@
.idea/
.gradle/
build/
out/
target/
StaffChat.iml
+13
View File
@@ -0,0 +1,13 @@
language: java
jdk:
- openjdk8
deploy:
provider: releases
api_key: $GITHUB_KEY
file_glob: true
file: "*/staffchat*.jar"
skip_cleanup: true
on:
tags: true
after_success:
- mvn clean test jacoco:report coveralls:report
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 Oskar Nordling
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+22 -117
View File
@@ -1,3 +1,4 @@
[![Travis-CI](https://img.shields.io/travis/com/oskar3123/StaffChat.svg?style=flat)](https://app.travis-ci.com/github/oskar3123/StaffChat)
[![Downloads](https://img.shields.io/github/downloads/oskar3123/StaffChat/total.svg?style=flat)](https://github.com/oskar3123/StaffChat/releases/latest)
[![Latest Release](https://img.shields.io/github/v/release/oskar3123/StaffChat.svg?style=flat)](https://github.com/oskar3123/StaffChat/releases/latest)
@@ -12,155 +13,59 @@
# [StaffChat](https://oskar3123.github.io/StaffChat)
Simple and highly configurable staff chat.
Simple and highly configurable staffchat
## Download
You can download the plugin from
the [Spigot resource page](https://www.spigotmc.org/resources/37804/) or via
the [GitHub releases](https://github.com/oskar3123/StaffChat/releases).
## License
This plugin is licensed with the MIT License, for more information see
the [LICENSE file](https://github.com/oskar3123/StaffChat/blob/master/LICENSE).
## Building
To build this yourself just clone the repository and run the `shadowJar` task with the Gradle
Wrapper.
The plugin jar-file can be found inside the `build/libs` folder after building.
### Windows
```bat
git clone git@github.com:oskar3123/StaffChat.git
cd StaffChat
gradlew.bat shadowJar
```
### *nix (Linux, FreeBSD, macOS, ...)
```bash
git clone git@github.com:oskar3123/StaffChat.git
cd StaffChat
./gradlew shadowJar
```
You can download the plugin from the [Spigot resource page](https://www.spigotmc.org/resources/37804/) or via the [GitHub releases](https://github.com/oskar3123/StaffChat/releases)
## Event API (For developers)
### Bukkit/Spigot
Because the event API in 1.14+ is now strict between sync and async events you should check whether
this was called synchronously or asynchronously by using `event.isAsynchronous()`.
Because the event API in 1.14+ is now strict between sync and async events you should check whether this was called synchronously or asynchronously by using `event.isAsynchronous()`
```java
public class StaffChatListener implements Listener {
public class StaffChatListener implements Listener
{
@EventHandler
public void onStaffChat(StaffChatEvent event) {
// Player player = event.getPlayer();
public void onStaffChat(StaffChatEvent event)
{
// String format = event.getFormat();
// String message = event.getMessage();
// event.setFormat("&b{NAME} >> {MESSAGE}");
// event.setMessage(filterMessage(message));
// String message = event.getMessage();
// Player player = event.getPlayer();
// event.setCancelled(true);
}
private static String filterMessage(String message) {
// ...
return message;
}
}
```
Register the listener in the `onEnable` method of your plugin.
Register the listener with
```java
public class YourPlugin extends JavaPlugin {
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(new StaffChatListener(), this);
}
}
getServer().getPluginManager().registerEvents(new StaffChatListener(), this);
```
in your plugin onEnable.
### BungeeCord
```java
public class StaffChatListener implements Listener {
public class StaffChatListener implements Listener
{
@EventHandler
public void onStaffChat(BungeeStaffChatEvent event) {
// ProxiedPlayer player = event.getPlayer();
public void onStaffChat(BungeeStaffChatEvent event)
{
// String format = event.getFormat();
// String message = event.getMessage();
// event.setFormat("&b{NAME} >> {MESSAGE}");
// event.setMessage(filterMessage(message));
// String message = event.getMessage();
// Player player = event.getPlayer();
// event.setCancelled(true);
}
private static String filterMessage(String message) {
// ...
return message;
}
}
```
Register the listener in the `onEnable` method of your plugin.
Register the listener with
```java
public class YourPlugin extends Plugin {
@Override
public void onEnable() {
getProxy().getPluginManager().registerListener(this, new StaffChatListener());
}
}
```
### Velocity
```java
public class StaffChatListener {
@Subscribe
public void onStaffChat(VelocityStaffChatEvent event) {
// Player player = event.getPlayer();
// String format = event.getFormat();
// String message = event.getMessage();
// StaffChatResult result = StaffChatResult.denied();
// StaffChatResult result = StaffChatResult.format("&b{NAME} >> {MESSAGE}");
// StaffChatResult result = StaffChatResult.message(filterMessage(message));
// StaffChatResult result = StaffChatResult.formatAndMessage("&b{NAME} >> {MESSAGE}", filterMessage(message));
// event.setResult(result);
}
private static String filterMessage(String message) {
// ...
return message;
}
}
```
Register the listener on the `ProxyInitializeEvent` in your plugin.
```java
@Plugin
public class YourPlugin {
private final ProxyServer server;
@Inject
public YourPlugin(ProxyServer server) {
this.server = server;
}
@Subscribe
public void onProxyInitialization(ProxyInitializeEvent event) {
server.getEventManager().register(this, new StaffChatListener());
}
}
getProxy().getPluginManager().registerListener(this, new StaffChatListener());
```
in your plugin onEnable.
-84
View File
@@ -1,84 +0,0 @@
plugins {
id("com.github.johnrengelman.shadow").version("8.1.1")
id("java")
}
group = "me.oskar3123"
version = "SNAPSHOT"
// Main sourceSet remains on Java 8
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
// Create a sourceSet for Velocity code that uses Java 17
sourceSets {
create("velocity") {
java {
srcDir("src/velocity/java")
compileClasspath += sourceSets.main.get().output + sourceSets.main.get().compileClasspath
runtimeClasspath += sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath
}
}
}
// Configure the automatically created Velocity compilation task to use Java 17
tasks.named<JavaCompile>("compileVelocityJava") {
sourceCompatibility = "17"
targetCompatibility = "17"
}
repositories {
mavenCentral()
maven {
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}
maven {
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
}
maven {
url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/")
}
maven {
name = "m2-dv8tion"
url = uri("https://m2.dv8tion.net/releases")
}
maven {
url = uri("https://nexus.scarsz.me/content/groups/public/")
}
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
}
dependencies {
compileOnly("org.spigotmc:spigot-api:1.20.2-R0.1-SNAPSHOT")
compileOnly("net.md-5:bungeecord-api:1.20-R0.2-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.1")
compileOnly("com.discordsrv:discordsrv:1.25.1")
"velocityCompileOnly"("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
"velocityAnnotationProcessor"("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
implementation("org.bstats:bstats-bukkit:3.1.0")
implementation("org.bstats:bstats-bungeecord:3.1.0")
implementation("org.bstats:bstats-velocity:3.1.0")
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
}
tasks.shadowJar {
relocate("org.bstats", "me.oskar3123.staffchat.bstats")
dependsOn("compileVelocityJava")
from(sourceSets["velocity"].output)
}
tasks.test {
useJUnitPlatform()
}
Binary file not shown.
-6
View File
@@ -1,6 +0,0 @@
#Fri Sep 22 00:35:34 CEST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
-234
View File
@@ -1,234 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
-89
View File
@@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+91
View File
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>StaffChat</name>
<groupId>me.oskar3123</groupId>
<artifactId>staffchat</artifactId>
<version>1.4.1</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
<repository>
<id>placeholderapi</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
<repository>
<id>dv8tion</id>
<name>m2-dv8tion</name>
<url>https://m2.dv8tion.net/releases</url>
</repository>
<repository>
<id>Scarsz-Nexus</id>
<url>https://nexus.scarsz.me/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<type>javadoc</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.discordsrv</groupId>
<artifactId>discordsrv</artifactId>
<version>1.25.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.name}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
-1
View File
@@ -1 +0,0 @@
rootProject.name = "StaffChat"
@@ -10,17 +10,18 @@ import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import org.bstats.bungeecord.Metrics;
public class BungeeMain extends Plugin {
private static final int BSTATS_PLUGIN_ID = 836;
public final String usePerm = "staffchat.use";
public final String seePerm = "staffchat.see";
public final String commandPerm = "staffchat.command";
public final String reloadPerm = "staffchat.reload";
public final BungeeChatListener chatListener = new BungeeChatListener(this);
private Configuration config;
public void onEnable() {
new Metrics(this, BSTATS_PLUGIN_ID);
new MetricsLite(this);
saveDefaultConfig();
if (!reloadConfig()) {
getLogger().severe("Could not load config file, using defaults.");
@@ -0,0 +1,395 @@
package me.oskar3123.staffchat.bungee;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
import javax.net.ssl.HttpsURLConnection;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.scheduler.TaskScheduler;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
/**
* bStats collects some data for plugin authors.
*
* <p>Check out https://bStats.org/ to learn more about bStats!
*/
@SuppressWarnings("all")
public class MetricsLite {
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bungeecord";
// A list with all known metrics class objects including this one
private static final List<Object> knownMetricsInstances = new ArrayList<>();
static {
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick"
// ... :D
final String defaultPackage =
new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'});
final String examplePackage =
new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure nobody just copy & pastes the example and use the wrong package names
if (MetricsLite.class.getPackage().getName().equals(defaultPackage)
|| MetricsLite.class.getPackage().getName().equals(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
// The plugin
private final Plugin plugin;
// Is bStats enabled on this server?
private boolean enabled;
// The uuid of the server
private String serverUUID;
// Should failed requests be logged?
private boolean logFailedRequests = false;
public MetricsLite(Plugin plugin) {
this.plugin = plugin;
try {
loadConfig();
} catch (IOException e) {
// Failed to load configuration
plugin.getLogger().log(Level.WARNING, "Failed to load bStats config!", e);
return;
}
// We are not allowed to send data about this server :(
if (!enabled) {
return;
}
Class<?> usedMetricsClass = getFirstBStatsClass();
if (usedMetricsClass == null) {
// Failed to get first metrics class
return;
}
if (usedMetricsClass == getClass()) {
// We are the first! :)
linkMetrics(this);
startSubmitting();
} else {
// We aren't the first so we link to the first metrics class
try {
usedMetricsClass.getMethod("linkMetrics", Object.class).invoke(null, this);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
if (logFailedRequests) {
plugin
.getLogger()
.log(
Level.WARNING,
"Failed to link to first metrics class " + usedMetricsClass.getName() + "!",
e);
}
}
}
}
/**
* Links an other metrics class with this class. This method is called using Reflection.
*
* @param metrics An object of the metrics class to link.
*/
public static void linkMetrics(Object metrics) {
knownMetricsInstances.add(metrics);
}
/**
* Sends the data to the bStats server.
*
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null");
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty(
"Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
connection.getInputStream().close(); // We don't care about the response - Just send our data :)
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
gzip.write(str.getBytes("UTF-8"));
gzip.close();
return outputStream.toByteArray();
}
/**
* Gets the plugin specific data. This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
String pluginName = plugin.getDescription().getName();
String pluginVersion = plugin.getDescription().getVersion();
data.addProperty("pluginName", pluginName);
data.addProperty("pluginVersion", pluginVersion);
JsonArray customCharts = new JsonArray();
data.add("customCharts", customCharts);
return data;
}
private void startSubmitting() {
// We use a timer cause want to be independent from the server tps
final Timer timer = new Timer(true);
timer.scheduleAtFixedRate(
new TimerTask() {
@Override
public void run() {
// The data collection (e.g. for custom graphs) is done sync
// Don't be afraid! The connection to the bStats server is still async, only the stats
// collection is sync ;)
TaskScheduler scheduler = plugin.getProxy().getScheduler();
scheduler.schedule(
plugin,
new Runnable() {
@Override
public void run() {
submitData();
}
},
0L,
TimeUnit.SECONDS);
}
},
1000 * 60 * 2,
1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 2 minutes to give other plugins enough
// time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
// Minecraft specific data
int playerAmount = plugin.getProxy().getOnlineCount();
playerAmount = playerAmount > 500 ? 500 : playerAmount;
int onlineMode = plugin.getProxy().getConfig().isOnlineMode() ? 1 : 0;
String bungeecordVersion = plugin.getProxy().getVersion();
int managedServers = plugin.getProxy().getServers().size();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("managedServers", managedServers);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bungeecordVersion", bungeecordVersion);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/** Collects the data and sends it afterwards. */
private void submitData() {
final JsonObject data = getServerData();
final JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Object metrics : knownMetricsInstances) {
try {
Object plugin = metrics.getClass().getMethod("getPluginData").invoke(metrics);
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
}
}
data.add("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(
new Runnable() {
@Override
public void run() {
try {
// Send the data
sendData(data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats!", e);
}
}
}
})
.start();
}
/**
* Loads the bStats configuration.
*
* @throws IOException If something did not work :(
*/
private void loadConfig() throws IOException {
Path configPath = plugin.getDataFolder().toPath().getParent().resolve("bStats");
configPath.toFile().mkdirs();
File configFile = new File(configPath.toFile(), "config.yml");
if (!configFile.exists()) {
writeFile(
configFile,
"#bStats collects some data for plugin authors like how many servers are using their plugins.",
"#To honor their work, you should not disable it.",
"#This has nearly no effect on the server performance!",
"#Check out https://bStats.org/ to learn more :)",
"enabled: true",
"serverUuid: \"" + UUID.randomUUID().toString() + "\"",
"logFailedRequests: false");
}
Configuration configuration =
ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
// Load configuration
enabled = configuration.getBoolean("enabled", true);
serverUUID = configuration.getString("serverUuid");
logFailedRequests = configuration.getBoolean("logFailedRequests", false);
}
/**
* Gets the first bStat Metrics class.
*
* @return The first bStats metrics class.
*/
private Class<?> getFirstBStatsClass() {
Path configPath = plugin.getDataFolder().toPath().getParent().resolve("bStats");
configPath.toFile().mkdirs();
File tempFile = new File(configPath.toFile(), "temp.txt");
try {
String className = readFile(tempFile);
if (className != null) {
try {
// Let's check if a class with the given name exists.
return Class.forName(className);
} catch (ClassNotFoundException ignored) {
}
}
writeFile(tempFile, getClass().getName());
return getClass();
} catch (IOException e) {
if (logFailedRequests) {
plugin.getLogger().log(Level.WARNING, "Failed to get first bStats class!", e);
}
return null;
}
}
/**
* Reads the first line of the file.
*
* @param file The file to read. Cannot be null.
* @return The first line of the file or <code>null</code> if the file does not exist or is empty.
* @throws IOException If something did not work :(
*/
private String readFile(File file) throws IOException {
if (!file.exists()) {
return null;
}
try (FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader); ) {
return bufferedReader.readLine();
}
}
/**
* Writes a String to a file. It also adds a note for the user,
*
* @param file The file to write to. Cannot be null.
* @param lines The lines to write.
* @throws IOException If something did not work :(
*/
private void writeFile(File file, String... lines) throws IOException {
if (!file.exists()) {
file.createNewFile();
}
try (FileWriter fileWriter = new FileWriter(file);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) {
for (String line : lines) {
bufferedWriter.write(line);
bufferedWriter.newLine();
}
}
}
}
@@ -1,7 +1,6 @@
package me.oskar3123.staffchat.bungee.command;
import me.oskar3123.staffchat.bungee.BungeeMain;
import me.oskar3123.staffchat.util.Permissions;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
@@ -26,7 +25,7 @@ public class BungeeStaffChatCommand extends Command {
public void execute(@NotNull CommandSender sender, @NotNull String[] args) {
BaseComponent[] noPerm =
txt(config.getString("messages.prefix") + config.getString("messages.nopermission"));
if (!sender.hasPermission(Permissions.COMMAND.permission())) {
if (!sender.hasPermission(plugin.commandPerm)) {
sender.sendMessage(noPerm);
return;
}
@@ -35,14 +34,14 @@ public class BungeeStaffChatCommand extends Command {
return;
}
if (args[0].equalsIgnoreCase("reload")) {
if (sender.hasPermission(Permissions.RELOAD.permission())) {
if (sender.hasPermission(plugin.reloadPerm)) {
reload(sender);
} else {
sender.sendMessage(noPerm);
}
return;
} else if (args[0].equalsIgnoreCase("toggle")) {
if (sender.hasPermission(Permissions.USE.permission())) {
if (sender.hasPermission(plugin.usePerm)) {
toggle(sender);
} else {
sender.sendMessage(noPerm);
@@ -61,11 +60,11 @@ public class BungeeStaffChatCommand extends Command {
String prefix = config.getString("messages.prefix");
sender.sendMessage(
txt(prefix + "Version " + plugin.getDescription().getVersion() + ", made by oskar3123"));
if (sender.hasPermission(Permissions.USE.permission())) {
if (sender.hasPermission(plugin.usePerm)) {
sender.sendMessage(txt(prefix + "Message prefix: " + config.getString("settings.character")));
sender.sendMessage(txt(prefix + "/" + label + " toggle - Toggles auto staffchat"));
}
if (sender.hasPermission(Permissions.RELOAD.permission())) {
if (sender.hasPermission(plugin.reloadPerm)) {
sender.sendMessage(txt(prefix + "/" + label + " reload - Reloads the config file"));
}
}
@@ -91,8 +90,13 @@ public class BungeeStaffChatCommand extends Command {
+ String.format(config.getString("messages.toggled", ""), state)));
}
@Contract("_ -> new")
private @NotNull String clr(@NotNull String string) {
return ChatColor.translateAlternateColorCodes('&', string);
}
@Contract("_ -> new")
private @NotNull BaseComponent[] txt(@NotNull String text) {
return TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&', text));
return TextComponent.fromLegacyText(clr(text));
}
}
@@ -2,19 +2,15 @@ package me.oskar3123.staffchat.bungee.listener;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import me.oskar3123.staffchat.bungee.BungeeMain;
import me.oskar3123.staffchat.bungee.event.BungeeStaffChatEvent;
import me.oskar3123.staffchat.util.FormatUtils;
import me.oskar3123.staffchat.util.Permissions;
import me.oskar3123.staffchat.util.StringUtils;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.ChatEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.config.Configuration;
@@ -41,7 +37,7 @@ public class BungeeChatListener implements Listener {
}
ProxiedPlayer player = (ProxiedPlayer) event.getSender();
if (!player.hasPermission(Permissions.USE.permission())) {
if (!player.hasPermission(plugin.usePerm)) {
return;
}
@@ -52,8 +48,6 @@ public class BungeeChatListener implements Listener {
return;
}
event.setCancelled(true);
String format = Objects.requireNonNull(config.getString("settings.format", ""));
String message = event.getMessage().substring(isToggled ? 0 : character.length()).trim();
@@ -65,28 +59,26 @@ public class BungeeChatListener implements Listener {
format = chatEvent.getFormat();
message = chatEvent.getMessage();
String appliedFormat =
FormatUtils.replacePlaceholders(
format,
s -> ChatColor.translateAlternateColorCodes('&', s),
() ->
Optional.ofNullable(player.getServer())
.map(Server::getInfo)
.map(ServerInfo::getName)
.orElse(""),
player::getName,
message);
final BaseComponent[] messageComponents = txt(appliedFormat);
format = format.replaceAll("\\{NAME}", StringUtils.sanitize(player.getName()));
format = format.replaceAll("\\{MESSAGE}", StringUtils.sanitize(message));
final BaseComponent[] messageComponents = txt(format);
plugin.getProxy().getPlayers().stream()
.filter(p -> p.hasPermission(Permissions.SEE.permission()))
.filter(p -> p.hasPermission(plugin.seePerm))
.forEach(p -> p.sendMessage(messageComponents));
plugin.getLogger().info(ChatColor.stripColor(appliedFormat));
plugin.getLogger().info(ChatColor.stripColor(clr(format)));
event.setCancelled(true);
}
@Contract("_ -> new")
private @NotNull String clr(@NotNull String string) {
return ChatColor.translateAlternateColorCodes('&', string);
}
@Contract("_ -> new")
private @NotNull BaseComponent[] txt(@NotNull String text) {
return TextComponent.fromLegacyText(text);
return TextComponent.fromLegacyText(clr(text));
}
public boolean togglePlayer(@NotNull UUID player) {
@@ -8,7 +8,6 @@ import me.oskar3123.staffchat.spigot.handler.StaffChatHandler;
import me.oskar3123.staffchat.spigot.listener.ChatListener;
import me.oskar3123.staffchat.spigot.listener.DiscordSrvListener;
import me.oskar3123.staffchat.spigot.listener.StaffChatPml;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
@@ -16,8 +15,10 @@ import org.jetbrains.annotations.NotNull;
public class Main extends JavaPlugin {
private static final int BSTATS_PLUGIN_ID = 835;
public final String usePerm = "staffchat.use";
public final String seePerm = "staffchat.see";
public final String commandPerm = "staffchat.command";
public final String reloadPerm = "staffchat.reload";
public final StaffChatHandler staffChatHandler = new StaffChatHandler(this);
public final ChatListener chatListener = new ChatListener(staffChatHandler);
public final StaffChatPml staffChatPml = new StaffChatPml(staffChatHandler);
@@ -25,7 +26,7 @@ public class Main extends JavaPlugin {
public void onEnable() {
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", staffChatPml);
new Metrics(this, BSTATS_PLUGIN_ID);
new MetricsLite(this);
saveDefaultConfig();
registerCommands();
registerEvents();
@@ -0,0 +1,316 @@
package me.oskar3123.staffchat.spigot;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
import javax.net.ssl.HttpsURLConnection;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
/**
* bStats collects some data for plugin authors.
*
* <p>Check out https://bStats.org/ to learn more about bStats!
*/
@SuppressWarnings("all")
public class MetricsLite {
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Should failed requests be logged?
private static boolean logFailedRequests;
// The uuid of the server
private static String serverUUID;
static {
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick"
// ... :D
final String defaultPackage =
new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'});
final String examplePackage =
new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure nobody just copy & pastes the example and use the wrong package names
if (MetricsLite.class.getPackage().getName().equals(defaultPackage)
|| MetricsLite.class.getPackage().getName().equals(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
// The plugin
private final JavaPlugin plugin;
/**
* Class constructor.
*
* @param plugin The plugin which stats should be submitted.
*/
public MetricsLite(JavaPlugin plugin) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
this.plugin = plugin;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid")) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Inform the server owners about bStats
config
.options()
.header(
"bStats collects some data for plugin authors like how many servers are using their plugins.\n"
+ "To honor their work, you should not disable it.\n"
+ "This has nearly no effect on the server performance!\n"
+ "Check out https://bStats.org/ to learn more :)")
.copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) {
}
}
// Load the data
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
if (config.getBoolean("enabled", true)) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
} catch (NoSuchFieldException ignored) {
}
}
// Register our service
Bukkit.getServicesManager().register(MetricsLite.class, this, plugin, ServicePriority.Normal);
if (!found) {
// We are the first!
startSubmitting();
}
}
}
/**
* Sends the data to the bStats server.
*
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread()) {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty(
"Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
connection.getInputStream().close(); // We don't care about the response - Just send our data :)
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
gzip.write(str.getBytes("UTF-8"));
gzip.close();
return outputStream.toByteArray();
}
/** Starts the Scheduler which submits our data every 30 minutes. */
private void startSubmitting() {
final Timer timer =
new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(
new TimerTask() {
@Override
public void run() {
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the
// Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the stats
// collection is sync ;)
Bukkit.getScheduler()
.runTask(
plugin,
new Runnable() {
@Override
public void run() {
submitData();
}
});
}
},
1000 * 60 * 5,
1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough
// time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data. This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
String pluginName = plugin.getDescription().getName();
String pluginVersion = plugin.getDescription().getVersion();
data.addProperty("pluginName", pluginName); // Append the name of the plugin
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
JsonArray customCharts = new JsonArray();
data.add("customCharts", customCharts);
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
// Minecraft specific data
int playerAmount = Bukkit.getOnlinePlayers().size();
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
bukkitVersion =
bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1);
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bukkitVersion", bukkitVersion);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/** Collects the data and sends it afterwards. */
private void submitData() {
final JsonObject data = getServerData();
JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
} catch (NoSuchFieldException ignored) {
continue; // Continue "searching"
}
// Found one!
try {
pluginData.add(
(JsonObject)
service
.getMethod("getPluginData")
.invoke(Bukkit.getServicesManager().load(service)));
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
}
}
data.add("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(
new Runnable() {
@Override
public void run() {
try {
// Send the data
sendData(data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
plugin
.getLogger()
.log(
Level.WARNING,
"Could not submit plugin stats of " + plugin.getName(),
e);
}
}
}
})
.start();
}
}
@@ -2,7 +2,6 @@ package me.oskar3123.staffchat.spigot.command;
import java.util.Objects;
import me.oskar3123.staffchat.spigot.Main;
import me.oskar3123.staffchat.util.Permissions;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@@ -30,7 +29,7 @@ public class StaffChatCommand implements CommandExecutor {
@NotNull String[] args) {
String noPerm =
clr(config.getString("messages.prefix") + config.getString("messages.nopermission"));
if (!sender.hasPermission(Permissions.COMMAND.permission())) {
if (!sender.hasPermission(plugin.commandPerm)) {
sender.sendMessage(noPerm);
return true;
}
@@ -39,14 +38,14 @@ public class StaffChatCommand implements CommandExecutor {
return true;
}
if (args[0].equalsIgnoreCase("reload")) {
if (sender.hasPermission(Permissions.RELOAD.permission())) {
if (sender.hasPermission(plugin.reloadPerm)) {
reload(sender);
} else {
sender.sendMessage(noPerm);
}
return true;
} else if (args[0].equalsIgnoreCase("toggle")) {
if (sender.hasPermission(Permissions.USE.permission())) {
if (sender.hasPermission(plugin.usePerm)) {
toggle(sender);
} else {
sender.sendMessage(noPerm);
@@ -66,11 +65,11 @@ public class StaffChatCommand implements CommandExecutor {
String prefix = clr(Objects.requireNonNull(config.getString("messages.prefix", "")));
sender.sendMessage(
prefix + "Version " + plugin.getDescription().getVersion() + ", made by oskar3123");
if (sender.hasPermission(Permissions.USE.permission())) {
if (sender.hasPermission(plugin.usePerm)) {
sender.sendMessage(prefix + "Message prefix: " + config.getString("settings.character"));
sender.sendMessage(prefix + "/" + label + " toggle - Toggles auto staffchat");
}
if (sender.hasPermission(Permissions.RELOAD.permission())) {
if (sender.hasPermission(plugin.reloadPerm)) {
sender.sendMessage(prefix + "/" + label + " reload - Reloads the config file");
}
}
@@ -14,12 +14,9 @@ import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.logging.Level;
import me.oskar3123.staffchat.spigot.Main;
import me.oskar3123.staffchat.spigot.event.StaffChatEvent;
import me.oskar3123.staffchat.util.FormatUtils;
import me.oskar3123.staffchat.util.Permissions;
import me.oskar3123.staffchat.util.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
@@ -37,7 +34,7 @@ public class StaffChatHandler {
}
public void onChatEvent(@NotNull AsyncPlayerChatEvent event) {
if (!event.getPlayer().hasPermission(Permissions.USE.permission())) {
if (!event.getPlayer().hasPermission(plugin.usePerm)) {
return;
}
@@ -48,8 +45,6 @@ public class StaffChatHandler {
return;
}
event.setCancelled(true);
String format = Objects.requireNonNull(config.getString("settings.format", ""));
String message = event.getMessage().substring(isToggled ? 0 : character.length()).trim();
if (config.getBoolean("settings.replaceplaceholdersinmessage")) {
@@ -67,17 +62,20 @@ public class StaffChatHandler {
format = plugin.replacePlaceholders(event.getPlayer(), format);
sendStaffChatMessage(format, event.getPlayer()::getName, message);
sendStaffChatMessage(format, event.getPlayer().getName(), message);
if (plugin.getConfig().getBoolean("discordsrv.enable")) {
sendDiscordMessage(event.getPlayer(), message);
}
event.setCancelled(true);
}
public void sendStaffChatMessage(String format, Supplier<String> nameSupplier, String message) {
final String finalMessage =
FormatUtils.replacePlaceholders(
format, s -> ChatColor.translateAlternateColorCodes('&', s), nameSupplier, message);
public void sendStaffChatMessage(String format, String name, String message) {
format = format.replaceAll("\\{NAME}", StringUtils.sanitize(name));
format = ChatColor.translateAlternateColorCodes('&', format);
format = format.replaceAll("\\{MESSAGE}", StringUtils.sanitize(message));
final String finalMessage = format;
if (plugin.getConfig().getBoolean("settings.sendmessagestoallservers")) {
// Find any player and send using that object
@@ -107,7 +105,8 @@ public class StaffChatHandler {
try {
actualMessage = msgin.readUTF();
} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to read plugin message from bungeecord", e);
e.printStackTrace();
plugin.getLogger().severe("Failed to read plugin message from bungeecord");
return;
}
sendStaffChatMessage(actualMessage);
@@ -123,7 +122,8 @@ public class StaffChatHandler {
try {
dataStream.writeUTF(message);
} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to send plugin message to bungeecord", e);
e.printStackTrace();
plugin.getLogger().severe("Failed to send plugin message to bungeecord");
return;
}
byte[] data = byteStream.toByteArray();
@@ -134,7 +134,7 @@ public class StaffChatHandler {
private void sendStaffChatMessage(@NotNull String message) {
Bukkit.getOnlinePlayers().stream()
.filter(p -> p.hasPermission(Permissions.SEE.permission()))
.filter(p -> p.hasPermission(plugin.seePerm))
.forEach(p -> p.sendMessage(message));
plugin.getLogger().info(ChatColor.stripColor(message));
}
@@ -142,14 +142,12 @@ public class StaffChatHandler {
private void sendDiscordMessage(Player player, String message) {
if (Bukkit.getPluginManager().isPluginEnabled("DiscordSRV")) {
String format =
plugin.replacePlaceholders(
player, plugin.getConfig().getString("discordsrv.minecraft-to-discord-format", ""));
format =
FormatUtils.replacePlaceholders(
format,
s -> ChatColor.translateAlternateColorCodes('&', s),
player::getName,
message);
plugin
.replacePlaceholders(
player,
plugin.getConfig().getString("discordsrv.minecraft-to-discord-format", ""))
.replaceAll("\\{NAME}", StringUtils.sanitize(player.getName()))
.replaceAll("\\{MESSAGE}", StringUtils.sanitize(message));
DiscordUtil.sendMessage(
DiscordSRV.getPlugin()
.getOptionalTextChannel(
@@ -8,7 +8,7 @@ import github.scarsz.discordsrv.dependencies.jda.api.entities.Message;
import github.scarsz.discordsrv.dependencies.jda.api.entities.User;
import java.util.Optional;
import me.oskar3123.staffchat.spigot.Main;
import me.oskar3123.staffchat.spigot.util.FoliaUtils;
import org.bukkit.Bukkit;
import org.bukkit.configuration.Configuration;
public class DiscordSrvListener {
@@ -38,14 +38,15 @@ public class DiscordSrvListener {
Optional.ofNullable(event.getMessage())
.map(Message::getContentDisplay)
.orElse("");
FoliaUtils.execute(
Bukkit.getScheduler()
.runTask(
plugin,
() ->
plugin.staffChatHandler.sendStaffChatMessage(
plugin
.getConfig()
.getString("discordsrv.discord-to-minecraft-format", ""),
() -> name,
name,
message));
});
}
@@ -1,52 +0,0 @@
package me.oskar3123.staffchat.spigot.util;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
public class FoliaUtils {
private static final String FOLIA_PROBE_CLASS =
"io.papermc.paper.threadedregions.RegionizedServer";
private static final boolean IS_FOLIA;
static {
boolean isFolia;
try {
Class.forName(FOLIA_PROBE_CLASS);
isFolia = true;
} catch (ClassNotFoundException e) {
isFolia = false;
}
IS_FOLIA = isFolia;
}
private FoliaUtils() {}
public static boolean isFolia() {
return IS_FOLIA;
}
public static void execute(Plugin plugin, Runnable runnable) {
if (isFolia()) {
try {
Method getGlobalRegionSchedulerMethod =
Class.forName("org.bukkit.Bukkit").getDeclaredMethod("getGlobalRegionScheduler");
Method executeMethod =
Class.forName("io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler")
.getDeclaredMethod("execute", Plugin.class, Runnable.class);
Object globalRegionScheduler = getGlobalRegionSchedulerMethod.invoke(null);
executeMethod.invoke(globalRegionScheduler, plugin, runnable);
} catch (ClassNotFoundException
| IllegalAccessException
| NoSuchMethodException
| InvocationTargetException e) {
// Should not happen, rethrow as a runtime exception if it does
throw new RuntimeException(e);
}
} else {
Bukkit.getScheduler().runTask(plugin, runnable);
}
}
}
@@ -1,32 +0,0 @@
package me.oskar3123.staffchat.util;
import java.util.function.Function;
import java.util.function.Supplier;
public class FormatUtils {
private FormatUtils() {}
public static String replacePlaceholders(
String string,
Function<String, String> colorize,
Supplier<String> serverSupplier,
Supplier<String> nameSupplier,
String message) {
string =
string.replaceAll("\\{SERVER}", StringUtils.sanitize(colorize.apply(serverSupplier.get())));
return replacePlaceholders(string, colorize, nameSupplier, message);
}
public static String replacePlaceholders(
String string,
Function<String, String> colorize,
Supplier<String> nameSupplier,
String message) {
string = colorize.apply(string);
string =
string.replaceAll("\\{NAME}", StringUtils.sanitize(colorize.apply(nameSupplier.get())));
string = string.replaceAll("\\{MESSAGE}", StringUtils.sanitize(message));
return string;
}
}
@@ -1,21 +0,0 @@
package me.oskar3123.staffchat.util;
import org.jetbrains.annotations.NotNull;
public enum Permissions {
USE("staffchat.use"),
SEE("staffchat.see"),
COMMAND("staffchat.command"),
RELOAD("staffchat.reload");
private final String permission;
Permissions(@NotNull String permission) {
this.permission = permission;
}
@NotNull
public String permission() {
return permission;
}
}
@@ -2,8 +2,6 @@ package me.oskar3123.staffchat.util;
public class StringUtils {
private StringUtils() {}
public static String sanitize(String string) {
string = string.replaceAll("\\\\", "\\\\\\\\");
string = string.replaceAll("\\$", "\\\\\\$");
+1 -1
View File
@@ -1,4 +1,4 @@
name: StaffChat
author: oskar3123
main: me.oskar3123.staffchat.bungee.BungeeMain
version: SNAPSHOT
version: 1.4.1
-1
View File
@@ -8,7 +8,6 @@ messages:
nopermission: '&cYou don''t have permission to do that'
playeronly: '&cPlayer only command'
reloaded: 'Reloaded the config file'
reload-failed: '&cFailed to reload the config file'
toggled: 'You toggled auto staffchat %s'
onstring: 'on'
offstring: 'off'
+1 -2
View File
@@ -2,9 +2,8 @@ name: StaffChat
authors: [ oskar3123 ]
main: me.oskar3123.staffchat.spigot.Main
api-version: 1.13
version: SNAPSHOT
version: 1.4.1
softdepend: [ PlaceholderAPI, DiscordSRV ]
folia-supported: true
commands:
staffchat:
usage: /<command>
@@ -1,117 +0,0 @@
package me.oskar3123.staffchat.velocity;
import com.google.inject.Inject;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import me.oskar3123.staffchat.velocity.command.VelocityStaffChatCommand;
import me.oskar3123.staffchat.velocity.handler.VelocityStaffChatHandler;
import me.oskar3123.staffchat.velocity.listener.VelocityStaffChatListener;
import org.bstats.velocity.Metrics;
import org.slf4j.Logger;
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
@Plugin(
id = "staffchat",
name = "StaffChat",
version = VelocityStaffChat.PLUGIN_VERSION,
description = "A staff chat plugin",
authors = {"oskar3123"})
public class VelocityStaffChat {
public static final String PLUGIN_VERSION = "SNAPSHOT";
private static final int BSTATS_PLUGIN_ID = 24679;
private final ProxyServer server;
private final Logger logger;
private final Path dataDirectory;
private final Metrics.Factory metricsFactory;
private VelocityStaffChatHandler staffChatHandler;
private CommentedConfigurationNode config;
@Inject
public VelocityStaffChat(
ProxyServer server,
Logger logger,
@DataDirectory Path dataDirectory,
Metrics.Factory metricsFactory) {
this.server = server;
this.logger = logger;
this.dataDirectory = dataDirectory;
this.metricsFactory = metricsFactory;
}
@Subscribe
public void onProxyInitialization(ProxyInitializeEvent event) {
try {
config = loadConfig();
// Initialize metrics
metricsFactory.make(this, BSTATS_PLUGIN_ID);
// Initialize handler
staffChatHandler = new VelocityStaffChatHandler(this);
// Register commands
server
.getCommandManager()
.register(
server.getCommandManager().metaBuilder("staffchat").build(),
new VelocityStaffChatCommand(this));
// Register listeners
server.getEventManager().register(this, new VelocityStaffChatListener(this));
logger.info("StaffChat has been enabled!");
} catch (IOException e) {
logger.error("Failed to start StaffChat!", e);
}
}
@Subscribe
public void onProxyShutdown(ProxyShutdownEvent event) {
logger.info("StaffChat has been disabled!");
}
public VelocityStaffChatHandler getStaffChatHandler() {
return staffChatHandler;
}
public ProxyServer getServer() {
return server;
}
public void reloadConfig() throws IOException {
config = loadConfig();
}
public CommentedConfigurationNode getConfig() {
return config;
}
private CommentedConfigurationNode loadConfig() throws IOException {
if (Files.notExists(dataDirectory)) {
Files.createDirectory(dataDirectory);
}
Path config = dataDirectory.resolve("config.yml");
if (Files.notExists(config)) {
try (InputStream stream = getClass().getClassLoader().getResourceAsStream("config.yml")) {
if (stream == null) {
throw new IOException("no default config.yml");
}
Files.copy(stream, config);
}
}
YamlConfigurationLoader loader = YamlConfigurationLoader.builder().path(config).build();
return loader.load();
}
}
@@ -1,123 +0,0 @@
package me.oskar3123.staffchat.velocity.command;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.proxy.Player;
import java.io.IOException;
import me.oskar3123.staffchat.util.Permissions;
import me.oskar3123.staffchat.velocity.VelocityStaffChat;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.configurate.CommentedConfigurationNode;
public class VelocityStaffChatCommand implements SimpleCommand {
private final VelocityStaffChat plugin;
private CommentedConfigurationNode config;
public VelocityStaffChatCommand(VelocityStaffChat plugin) {
this.plugin = plugin;
config = plugin.getConfig();
}
@Override
public void execute(Invocation invocation) {
CommandSource sender = invocation.source();
String label = invocation.alias();
String[] args = invocation.arguments();
Component noPerm =
txt(
config.node("messages", "prefix").getString()
+ config.node("messages", "nopermission").getString());
if (!sender.hasPermission(Permissions.COMMAND.permission())) {
sender.sendMessage(noPerm);
return;
}
if (args.length < 1) {
help(sender, label);
return;
}
if (args[0].equalsIgnoreCase("reload")) {
if (sender.hasPermission(Permissions.RELOAD.permission())) {
reload(sender);
} else {
sender.sendMessage(noPerm);
}
return;
} else if (args[0].equalsIgnoreCase("toggle")) {
if (sender.hasPermission(Permissions.USE.permission())) {
toggle(sender);
} else {
sender.sendMessage(noPerm);
}
return;
}
help(sender, label);
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission(Permissions.COMMAND.permission());
}
private void playerOnly(@NotNull CommandSource sender) {
sender.sendMessage(
txt(
config.node("messages", "prefix").getString()
+ config.node("messages", "playeronly").getString()));
}
private void help(@NotNull CommandSource sender, String label) {
String prefix = config.node("messages", "prefix").getString();
sender.sendMessage(
txt(prefix + "Version " + VelocityStaffChat.PLUGIN_VERSION + ", made by oskar3123"));
if (sender.hasPermission(Permissions.USE.permission())) {
sender.sendMessage(
txt(prefix + "Message prefix: " + config.node("settings", "character").getString()));
sender.sendMessage(txt(prefix + "/" + label + " toggle - Toggles auto staffchat"));
}
if (sender.hasPermission(Permissions.RELOAD.permission())) {
sender.sendMessage(txt(prefix + "/" + label + " reload - Reloads the config file"));
}
}
private void reload(@NotNull CommandSource sender) {
try {
plugin.reloadConfig();
config = plugin.getConfig();
sender.sendMessage(
txt(
config.node("messages", "prefix").getString()
+ config.node("messages", "reloaded").getString()));
} catch (IOException e) {
sender.sendMessage(
txt(
config.node("messages", "prefix").getString()
+ config
.node("messages", "reload-failed")
.getString("Failed to reload the config file")));
}
}
private void toggle(@NotNull CommandSource sender) {
if (!(sender instanceof Player player)) {
playerOnly(sender);
return;
}
boolean toggled = plugin.getStaffChatHandler().togglePlayer(player.getUniqueId());
String state =
toggled
? config.node("messages", "onstring").getString()
: config.node("messages", "offstring").getString();
sender.sendMessage(
txt(
config.node("messages", "prefix").getString()
+ String.format(config.node("messages", "toggled").getString(""), state)));
}
private @NotNull Component txt(@NotNull String text) {
return LegacyComponentSerializer.legacyAmpersand().deserialize(text);
}
}
@@ -1,260 +0,0 @@
package me.oskar3123.staffchat.velocity.event;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;
import me.oskar3123.staffchat.velocity.event.VelocityStaffChatEvent.StaffChatResult;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
/**
* Event fired after deciding to broadcast a staff message.
*
* <p>Denying this event will still block the chat message.
*/
public class VelocityStaffChatEvent implements ResultedEvent<StaffChatResult> {
private final Player player;
private final String format;
private final String message;
private StaffChatResult result = StaffChatResult.allowed();
@Contract(pure = true)
public VelocityStaffChatEvent(
@NotNull Player player, @NotNull String format, @NotNull String message) {
this.player = Objects.requireNonNull(player, "player must not be null");
this.format = Objects.requireNonNull(format, "format must not be null");
this.message = Objects.requireNonNull(message, "message must not be null");
}
/**
* Gets the player that is sending the staff chat message.
*
* @return the player
*/
@NotNull
@Contract(pure = true)
public Player getPlayer() {
return player;
}
/**
* Gets the initial format decided by the plugin.
*
* <p>Use {@link #getResult()} to get the potentially modified format.
*
* @return the initial format
*/
@NotNull
@Contract(pure = true)
public String getFormat() {
return format;
}
/**
* Gets the initial message entered by the player.
*
* <p>Use {@link #getResult()} to get the potentially modified message.
*
* @return the initial message
*/
@NotNull
@Contract(pure = true)
public String getMessage() {
return message;
}
/**
* Gets the current result.
*
* <p>This will by default be an allowed result with no modifications.
*
* @return the current result
*/
@NotNull
@Contract(pure = true)
@Override
public StaffChatResult getResult() {
return result;
}
/**
* Sets the current result.
*
* <p>You can {@linkplain StaffChatResult#allowed() allow}, {@linkplain StaffChatResult#denied()
* deny}, {@linkplain StaffChatResult#format(String) modify the format}, {@linkplain
* StaffChatResult#message(String) modify the message}, or {@linkplain
* StaffChatResult#formatAndMessage(String, String) modify both the format and message}.
*
* @param result the new result
*/
@Override
public void setResult(@NotNull StaffChatResult result) {
this.result = Objects.requireNonNull(result, "result must not be null");
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
VelocityStaffChatEvent that = (VelocityStaffChatEvent) o;
return Objects.equals(player, that.player)
&& Objects.equals(format, that.format)
&& Objects.equals(message, that.message)
&& Objects.equals(result, that.result);
}
@Override
public int hashCode() {
return Objects.hash(player, format, message, result);
}
@Override
public String toString() {
return new StringJoiner(", ", VelocityStaffChatEvent.class.getSimpleName() + "[", "]")
.add("player=" + player)
.add("format='" + format + "'")
.add("message='" + message + "'")
.add("result=" + result)
.toString();
}
/** Result of the {@linkplain VelocityStaffChatEvent}. */
public static class StaffChatResult implements Result {
private static final StaffChatResult ALLOWED = new StaffChatResult(true, null, null);
private static final StaffChatResult DENIED = new StaffChatResult(false, null, null);
private final boolean allowed;
private final String format;
private final String message;
private StaffChatResult(boolean allowed, String format, String message) {
this.allowed = allowed;
this.format = format;
this.message = message;
}
/**
* Allows the message to be sent, without modifying anything.
*
* @return the allowed result
*/
@NotNull
@Contract(pure = true)
public static StaffChatResult allowed() {
return ALLOWED;
}
/**
* Prevents the message from being sent.
*
* @return the denied result
*/
@NotNull
@Contract(pure = true)
public static StaffChatResult denied() {
return DENIED;
}
/**
* Allows the message to be sent, modifying the format.
*
* @param format the format
* @return the modify format result
*/
@NotNull
@Contract(value = "_ -> new", pure = true)
public static StaffChatResult format(@NotNull String format) {
return new StaffChatResult(
true, Objects.requireNonNull(format, "format must not be null"), null);
}
/**
* Allows the message to be sent, modifying the message.
*
* @param message the message
* @return the modify message result
*/
@NotNull
@Contract(value = "_ -> new", pure = true)
public static StaffChatResult message(@NotNull String message) {
return new StaffChatResult(
true, null, Objects.requireNonNull(message, "message must not be null"));
}
/**
* Allows the message to be sent, modifying the format and message.
*
* @param format the format
* @param message the message
* @return the modify format and message result
*/
@NotNull
@Contract(value = "_, _ -> new", pure = true)
public static StaffChatResult formatAndMessage(
@NotNull String format, @NotNull String message) {
return new StaffChatResult(
true,
Objects.requireNonNull(format, "format must not be null"),
Objects.requireNonNull(message, "message must not be null"));
}
@Contract(pure = true)
@Override
public boolean isAllowed() {
return allowed;
}
/**
* Gets the potentially modified format.
*
* <p>An empty optional means that the format has not been modified.
*
* @return the potentially modified format
*/
@NotNull
@Contract(pure = true)
public Optional<String> getFormat() {
return Optional.ofNullable(format);
}
/**
* Gets the potentially modified message.
*
* <p>An empty optional means that the message has not been modified.
*
* @return the potentially modified message
*/
@NotNull
@Contract(pure = true)
public Optional<String> getMessage() {
return Optional.ofNullable(message);
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
StaffChatResult that = (StaffChatResult) o;
return allowed == that.allowed
&& Objects.equals(format, that.format)
&& Objects.equals(message, that.message);
}
@Override
public int hashCode() {
return Objects.hash(allowed, format, message);
}
@Override
public String toString() {
return new StringJoiner(", ", StaffChatResult.class.getSimpleName() + "[", "]")
.add("allowed=" + allowed)
.add("format='" + format + "'")
.add("message='" + message + "'")
.toString();
}
}
}
@@ -1,82 +0,0 @@
package me.oskar3123.staffchat.velocity.handler;
import com.velocitypowered.api.proxy.Player;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import me.oskar3123.staffchat.util.FormatUtils;
import me.oskar3123.staffchat.util.Permissions;
import me.oskar3123.staffchat.velocity.VelocityStaffChat;
import me.oskar3123.staffchat.velocity.event.VelocityStaffChatEvent;
import me.oskar3123.staffchat.velocity.event.VelocityStaffChatEvent.StaffChatResult;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
public class VelocityStaffChatHandler {
private final VelocityStaffChat plugin;
private final Set<UUID> toggledPlayers;
public VelocityStaffChatHandler(VelocityStaffChat plugin) {
this.plugin = plugin;
this.toggledPlayers = new HashSet<>();
}
public boolean isToggled(UUID uuid) {
return toggledPlayers.contains(uuid);
}
public boolean togglePlayer(UUID uuid) {
if (toggledPlayers.contains(uuid)) {
toggledPlayers.remove(uuid);
return false;
} else {
toggledPlayers.add(uuid);
return true;
}
}
public void broadcastStaffMessage(Player player, String message) {
String format = plugin.getConfig().node("settings", "format").getString("&b{NAME}: {MESSAGE}");
plugin
.getServer()
.getEventManager()
.fire(new VelocityStaffChatEvent(player, format, message))
.thenAccept(
staffChatEvent -> {
StaffChatResult result = staffChatEvent.getResult();
if (result.isAllowed()) {
plugin
.getStaffChatHandler()
.broadcastStaffMessage(
player,
result.getFormat().orElse(format),
result.getMessage().orElse(message));
}
});
}
private void broadcastStaffMessage(Player player, String format, String message) {
String formattedMessage =
FormatUtils.replacePlaceholders(
format,
Function.identity(),
() ->
player
.getCurrentServer()
.map(connection -> connection.getServerInfo().getName())
.orElse(""),
player::getUsername,
message);
TextComponent component =
LegacyComponentSerializer.legacyAmpersand().deserialize(formattedMessage);
plugin.getServer().getAllPlayers().stream()
.filter(p -> p.hasPermission(Permissions.SEE.permission()))
.forEach(p -> p.sendMessage(component));
plugin.getServer().getConsoleCommandSource().sendMessage(component);
}
}
@@ -1,36 +0,0 @@
package me.oskar3123.staffchat.velocity.listener;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.player.PlayerChatEvent;
import com.velocitypowered.api.proxy.Player;
import me.oskar3123.staffchat.util.Permissions;
import me.oskar3123.staffchat.velocity.VelocityStaffChat;
public class VelocityStaffChatListener {
private final VelocityStaffChat plugin;
public VelocityStaffChatListener(VelocityStaffChat plugin) {
this.plugin = plugin;
}
@Subscribe
public void onPlayerChat(PlayerChatEvent event) {
Player player = event.getPlayer();
if (!player.hasPermission(Permissions.USE.permission())) {
return;
}
String character = plugin.getConfig().node("settings", "character").getString("@");
String message = event.getMessage();
boolean toggled = plugin.getStaffChatHandler().isToggled(player.getUniqueId());
// Check if player has staff chat toggled on or message starts with @
if (toggled || message.startsWith(character)) {
String staffMessage = toggled ? message : message.substring(character.length()).trim();
plugin.getStaffChatHandler().broadcastStaffMessage(player, staffMessage);
event.setResult(PlayerChatEvent.ChatResult.denied());
}
}
}