43 Commits
Author SHA1 Message Date
oskar3123andGitHub 89e26a8958 Merge pull request #42 from oskar3123/38-cancelling-a-staff-chat-event-will-allow-the-chat-message
pre-release / Pre Release (push) Canceled after 6m24s
build / build (push) Successful in 15m22s
Fix cancelling staff chat events
2025-02-10 01:04:42 +01:00
oskar3123 e8b612ee9d #38: fix so that the chat event is cancelled regardless of staff chat event outcome 2025-02-10 00:58:04 +01:00
oskar3123andGitHub d398959533 Merge pull request #41 from oskar3123/39-clean-the-readme-a-bit
Clean the README a bit
2025-02-10 00:46:33 +01:00
oskar3123 428adde024 #39: update code-examples 2025-02-10 00:42:23 +01:00
oskar3123 02ef475267 #39: improve build instructions 2025-02-10 00:42:23 +01:00
oskar3123 d515f0138c #39: fix spelling, punctuation, and code-style 2025-02-10 00:37:13 +01:00
oskar3123andGitHub 7efea34519 Merge pull request #40 from oskar3123/34-create-velocity-staff-chat-event
Create a staff chat event for the velocity plugin
2025-02-10 00:13:16 +01:00
oskar3123 7c52e1076e #34: create a staff chat event for the velocity plugin 2025-02-10 00:06:44 +01:00
oskar3123andGitHub faae458f3f Merge pull request #37 from oskar3123/35-fix-configurable-format-in-velocity
Fix configurable format in velocity
2025-02-08 03:35:25 +01:00
oskar3123 e3ecac87c7 #35: actually use the configurable format for velocity 2025-02-08 03:33:18 +01:00
oskar3123andGitHub 1c9487d52d Merge pull request #36 from oskar3123/33-refactor-permissions-to-use-a-common-definition
Refactor permissions to common defintion
2025-02-08 03:20:01 +01:00
oskar3123 d57d45f220 #33: refactor permissions to common definition 2025-02-08 03:11:36 +01:00
oskar3123andGitHub 5ca17aa33b Merge pull request #32 from oskar3123/31-main-builds-as-java-17
Fix main source-set class versions
2025-02-07 23:35:54 +01:00
oskar3123 9b65a0365f #31: fix class version of main source-set 2025-02-07 23:31:53 +01:00
oskar3123andGitHub 655a3b119d Merge pull request #30 from oskar3123/29-remove-custom-folia-metrics
Remove custom Folia metrics
2025-02-07 23:06:14 +01:00
oskar3123 b3df263dc2 #29: remove custom Folia metrics 2025-02-07 23:02:38 +01:00
oskar3123 8b7374b0f7 #29: update bStats to 3.1.0 2025-02-07 23:02:08 +01:00
oskar3123andGitHub 082cc9b239 Merge pull request #28 from oskar3123/7-velocity-support
Support for Velocity
2025-02-07 22:29:19 +01:00
oskar3123 9dd3047ced #7: implement velocity version of staffchat 2025-02-07 22:23:47 +01:00
oskar3123 75874023a6 #7: add velocity dependencies 2025-02-07 00:01:43 +01:00
oskar3123 23527c65a8 #7: add velocity sourceset that builds with java 17 2025-02-07 00:01:43 +01:00
oskar3123 82559d10c6 #7: make github workflows setup correct jdk versions 2025-02-06 22:31:04 +01:00
oskar3123andGitHub 7eee2597c1 Merge pull request #27 from oskar3123/update-github-workflows
Update GitHub workflows
2023-09-26 23:04:25 +02:00
oskar3123 b89efb90ae Update GitHub workflows 2023-09-26 23:01:33 +02:00
oskar3123andGitHub 277c49c77e Merge pull request #26 from oskar3123/github-workflow-gradle
Create GitHub workflow for Gradle
2023-09-26 14:31:51 +02:00
oskar3123andGitHub 2a90d25f61 Create GitHub workflow for Gradle 2023-09-26 13:09:20 +02:00
oskar3123andGitHub f32cae5cd6 Merge pull request #25 from oskar3123/15-folia-support
#15: Add support for Folia
2023-09-25 22:24:54 +02:00
oskar3123 40ab41b6c5 #15: Add support for Folia 2023-09-25 22:24:06 +02:00
oskar3123andGitHub f248c4084d Merge pull request #24 from oskar3123/23-fix-format-colorization
#23: Fix format colorization
2023-09-25 22:22:12 +02:00
oskar3123 4efd666ee1 #23: Fix format colorization 2023-09-25 22:20:47 +02:00
oskar3123andGitHub 9d75ccf48b Merge pull request #22 from oskar3123/14-update-bstats-metrics
#14: Update bStats metrics
2023-09-25 20:41:15 +02:00
oskar3123 9d26fa3ce4 #14: Update bStats metrics 2023-09-25 20:40:22 +02:00
oskar3123andGitHub f582e05727 Merge pull request #20 from oskar3123/16-update-to-kotlin-gradle-dls
#16: Update gradle scripts to Kotlin
2023-09-25 19:58:22 +02:00
oskar3123 7c9e84ba4a #16: Update gradle scripts to Kotlin 2023-09-25 19:57:53 +02:00
oskar3123andGitHub 6903bac6a3 Merge pull request #19 from oskar3123/18-remove-stdout-and-stderr-logging
#18: Remove `printStackTrace` logging and use plugin logger
2023-09-25 19:41:20 +02:00
oskar3123 d0e017291d #18: Remove printStackTrace logging and use plugin logger 2023-09-25 19:40:12 +02:00
oskar3123andGitHub 8dedb9e706 Merge pull request #17 from oskar3123/6-server-placeholder
#6: Add {SERVER} placeholder to the BungeeCord plugin
2023-09-25 19:37:15 +02:00
oskar3123 ae6ef2b887 #6: Add {SERVER} placeholder to the BungeeCord plugin 2023-09-25 19:32:01 +02:00
oskar3123 c9ab8c98d8 Add license, update readme and add execute bit to gradlew 2023-09-22 01:30:41 +02:00
oskar3123 4da3ddaed7 Change from Maven to Gradle 2023-09-22 01:10:43 +02:00
oskar3123andGitHub d7be18f0cb Merge pull request #13 from oskar3123/5-discordsrv-support-2
#5: DiscordSRV support 2
2022-03-22 01:55:13 +01:00
oskar3123 5f078be227 #5: DiscordSRV support 2 2022-03-22 01:50:40 +01:00
oskar3123 ae800abe53 update readme shields 2022-03-21 03:12:53 +01:00
39 changed files with 1717 additions and 1046 deletions
-1
View File
@@ -1 +0,0 @@
repo_token: $COVERALLS_REPO_TOKEN
+36
View File
@@ -0,0 +1,36 @@
# 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
@@ -0,0 +1,33 @@
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
@@ -0,0 +1,31 @@
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/
out/
target/
.gradle/
build/
StaffChat.iml
-13
View File
@@ -1,13 +0,0 @@
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
@@ -0,0 +1,21 @@
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.
+137 -34
View File
@@ -1,63 +1,166 @@
[![Travis-CI](https://img.shields.io/travis/com/oskar3123/StaffChat.svg?style=flat)](https://app.travis-ci.com/github/oskar3123/StaffChat)
[![All Release](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/release/oskar3123/StaffChat.svg?style=flat)](https://github.com/oskar3123/StaffChat/releases/latest)
[![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)
[![Github commits (since latest release)](https://img.shields.io/github/commits-since/oskar3123/StaffChat/latest.svg?style=flat)](https://github.com/oskar3123/StaffChat/commits/master)
[![Latest Pre-release](https://img.shields.io/github/v/release/oskar3123/StaffChat.svg?style=flat&include_prereleases)](https://github.com/oskar3123/StaffChat/releases)
[![Github commits (since latest pre-release)](https://img.shields.io/github/commits-since/oskar3123/StaffChat/latest.svg?style=flat&include_prereleases)](https://github.com/oskar3123/StaffChat/commits/master)
[![Open Issues](https://img.shields.io/github/issues/oskar3123/StaffChat)](https://github.com/oskar3123/StaffChat/issues?q=is%3Aopen+is%3Aissue)
[![Closed Issues](https://img.shields.io/github/issues-closed/oskar3123/StaffChat)](https://github.com/oskar3123/StaffChat/issues?q=is%3Aissue+is%3Aclosed)
[![Open Pull requests](https://img.shields.io/github/issues-pr/oskar3123/StaffChat)](https://github.com/oskar3123/StaffChat/pulls?q=is%3Aopen+is%3Apr)
[![Closed Pull requests](https://img.shields.io/github/issues-pr-closed/oskar3123/StaffChat)](https://github.com/oskar3123/StaffChat/pulls?q=is%3Apr+is%3Aclosed)
# [StaffChat](https://oskar3123.github.io/StaffChat)
Simple and highly configurable staffchat
Simple and highly configurable staff chat.
## 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)
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
```
## 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)
{
// String format = event.getFormat();
// event.setFormat("&b{NAME} >> {MESSAGE}");
// String message = event.getMessage();
// Player player = event.getPlayer();
// event.setCancelled(true);
}
@EventHandler
public void onStaffChat(StaffChatEvent event) {
// Player player = event.getPlayer();
// String format = event.getFormat();
// String message = event.getMessage();
// event.setFormat("&b{NAME} >> {MESSAGE}");
// event.setMessage(filterMessage(message));
// event.setCancelled(true);
}
private static String filterMessage(String message) {
// ...
return message;
}
}
```
Register the listener with
Register the listener in the `onEnable` method of your plugin.
```java
getServer().getPluginManager().registerEvents(new StaffChatListener(), this);
public class YourPlugin extends JavaPlugin {
@Override
public void onEnable() {
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)
{
// String format = event.getFormat();
// event.setFormat("&b{NAME} >> {MESSAGE}");
// String message = event.getMessage();
// Player player = event.getPlayer();
// event.setCancelled(true);
}
@EventHandler
public void onStaffChat(BungeeStaffChatEvent event) {
// ProxiedPlayer player = event.getPlayer();
// String format = event.getFormat();
// String message = event.getMessage();
// event.setFormat("&b{NAME} >> {MESSAGE}");
// event.setMessage(filterMessage(message));
// event.setCancelled(true);
}
private static String filterMessage(String message) {
// ...
return message;
}
}
```
Register the listener with
Register the listener in the `onEnable` method of your plugin.
```java
getProxy().getPluginManager().registerListener(this, new StaffChatListener());
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());
}
}
```
in your plugin onEnable.
+84
View File
@@ -0,0 +1,84 @@
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
@@ -0,0 +1,6 @@
#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 Executable
+234
View File
@@ -0,0 +1,234 @@
#!/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
@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
-91
View File
@@ -1,91 +0,0 @@
<?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.0</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
@@ -0,0 +1 @@
rootProject.name = "StaffChat"
@@ -10,18 +10,17 @@ 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 {
public final String usePerm = "staffchat.use";
public final String seePerm = "staffchat.see";
public final String commandPerm = "staffchat.command";
public final String reloadPerm = "staffchat.reload";
private static final int BSTATS_PLUGIN_ID = 836;
public final BungeeChatListener chatListener = new BungeeChatListener(this);
private Configuration config;
public void onEnable() {
new MetricsLite(this);
new Metrics(this, BSTATS_PLUGIN_ID);
saveDefaultConfig();
if (!reloadConfig()) {
getLogger().severe("Could not load config file, using defaults.");
@@ -1,395 +0,0 @@
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,6 +1,7 @@
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;
@@ -25,7 +26,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(plugin.commandPerm)) {
if (!sender.hasPermission(Permissions.COMMAND.permission())) {
sender.sendMessage(noPerm);
return;
}
@@ -34,14 +35,14 @@ public class BungeeStaffChatCommand extends Command {
return;
}
if (args[0].equalsIgnoreCase("reload")) {
if (sender.hasPermission(plugin.reloadPerm)) {
if (sender.hasPermission(Permissions.RELOAD.permission())) {
reload(sender);
} else {
sender.sendMessage(noPerm);
}
return;
} else if (args[0].equalsIgnoreCase("toggle")) {
if (sender.hasPermission(plugin.usePerm)) {
if (sender.hasPermission(Permissions.USE.permission())) {
toggle(sender);
} else {
sender.sendMessage(noPerm);
@@ -60,11 +61,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(plugin.usePerm)) {
if (sender.hasPermission(Permissions.USE.permission())) {
sender.sendMessage(txt(prefix + "Message prefix: " + config.getString("settings.character")));
sender.sendMessage(txt(prefix + "/" + label + " toggle - Toggles auto staffchat"));
}
if (sender.hasPermission(plugin.reloadPerm)) {
if (sender.hasPermission(Permissions.RELOAD.permission())) {
sender.sendMessage(txt(prefix + "/" + label + " reload - Reloads the config file"));
}
}
@@ -90,13 +91,8 @@ 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(clr(text));
return TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&', text));
}
}
@@ -2,15 +2,19 @@ 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.StringUtils;
import me.oskar3123.staffchat.bungee.util.BungeeFormatUtils;
import me.oskar3123.staffchat.util.Permissions;
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;
@@ -37,7 +41,7 @@ public class BungeeChatListener implements Listener {
}
ProxiedPlayer player = (ProxiedPlayer) event.getSender();
if (!player.hasPermission(plugin.usePerm)) {
if (!player.hasPermission(Permissions.USE.permission())) {
return;
}
@@ -48,6 +52,8 @@ 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();
@@ -59,26 +65,28 @@ public class BungeeChatListener implements Listener {
format = chatEvent.getFormat();
message = chatEvent.getMessage();
format = format.replaceAll("\\{NAME}", StringUtils.sanitize(player.getName()));
format = format.replaceAll("\\{MESSAGE}", StringUtils.sanitize(message));
final BaseComponent[] messageComponents = txt(format);
String appliedFormat =
BungeeFormatUtils.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);
plugin.getProxy().getPlayers().stream()
.filter(p -> p.hasPermission(plugin.seePerm))
.filter(p -> p.hasPermission(Permissions.SEE.permission()))
.forEach(p -> p.sendMessage(messageComponents));
plugin.getLogger().info(ChatColor.stripColor(clr(format)));
event.setCancelled(true);
}
@Contract("_ -> new")
private @NotNull String clr(@NotNull String string) {
return ChatColor.translateAlternateColorCodes('&', string);
plugin.getLogger().info(ChatColor.stripColor(appliedFormat));
}
@Contract("_ -> new")
private @NotNull BaseComponent[] txt(@NotNull String text) {
return TextComponent.fromLegacyText(clr(text));
return TextComponent.fromLegacyText(text);
}
public boolean togglePlayer(@NotNull UUID player) {
@@ -0,0 +1,22 @@
package me.oskar3123.staffchat.bungee.util;
import java.util.function.Function;
import java.util.function.Supplier;
import me.oskar3123.staffchat.util.FormatUtils;
import me.oskar3123.staffchat.util.StringUtils;
public class BungeeFormatUtils {
private BungeeFormatUtils() {}
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 FormatUtils.replacePlaceholders(string, colorize, nameSupplier, message);
}
}
@@ -1,28 +1,37 @@
package me.oskar3123.staffchat.spigot;
import github.scarsz.discordsrv.DiscordSRV;
import java.util.Optional;
import me.clip.placeholderapi.PlaceholderAPI;
import me.oskar3123.staffchat.spigot.command.StaffChatCommand;
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;
import org.jetbrains.annotations.NotNull;
public class Main extends JavaPlugin {
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 ChatListener chatListener = new ChatListener(this);
private static final int BSTATS_PLUGIN_ID = 835;
public final StaffChatHandler staffChatHandler = new StaffChatHandler(this);
public final ChatListener chatListener = new ChatListener(staffChatHandler);
public final StaffChatPml staffChatPml = new StaffChatPml(staffChatHandler);
public void onEnable() {
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", chatListener);
new MetricsLite(this);
getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", staffChatPml);
new Metrics(this, BSTATS_PLUGIN_ID);
saveDefaultConfig();
registerCommands();
registerEvents();
if (Bukkit.getPluginManager().isPluginEnabled("DiscordSRV")) {
DiscordSRV.api.subscribe(new DiscordSrvListener(this));
}
}
@Override
@@ -45,7 +54,7 @@ public class Main extends JavaPlugin {
return Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
}
public String replacePlaceholders(Player player, String string) {
public @NotNull String replacePlaceholders(Player player, String string) {
if (!isPlaceholderApiEnabled()) {
return string;
}
@@ -1,316 +0,0 @@
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,6 +2,7 @@ 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;
@@ -29,7 +30,7 @@ public class StaffChatCommand implements CommandExecutor {
@NotNull String[] args) {
String noPerm =
clr(config.getString("messages.prefix") + config.getString("messages.nopermission"));
if (!sender.hasPermission(plugin.commandPerm)) {
if (!sender.hasPermission(Permissions.COMMAND.permission())) {
sender.sendMessage(noPerm);
return true;
}
@@ -38,14 +39,14 @@ public class StaffChatCommand implements CommandExecutor {
return true;
}
if (args[0].equalsIgnoreCase("reload")) {
if (sender.hasPermission(plugin.reloadPerm)) {
if (sender.hasPermission(Permissions.RELOAD.permission())) {
reload(sender);
} else {
sender.sendMessage(noPerm);
}
return true;
} else if (args[0].equalsIgnoreCase("toggle")) {
if (sender.hasPermission(plugin.usePerm)) {
if (sender.hasPermission(Permissions.USE.permission())) {
toggle(sender);
} else {
sender.sendMessage(noPerm);
@@ -65,11 +66,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(plugin.usePerm)) {
if (sender.hasPermission(Permissions.USE.permission())) {
sender.sendMessage(prefix + "Message prefix: " + config.getString("settings.character"));
sender.sendMessage(prefix + "/" + label + " toggle - Toggles auto staffchat");
}
if (sender.hasPermission(plugin.reloadPerm)) {
if (sender.hasPermission(Permissions.RELOAD.permission())) {
sender.sendMessage(prefix + "/" + label + " reload - Reloads the config file");
}
}
@@ -86,7 +87,7 @@ public class StaffChatCommand implements CommandExecutor {
playerOnly(sender);
return;
}
boolean toggled = plugin.chatListener.togglePlayer(((Player) sender).getUniqueId());
boolean toggled = plugin.staffChatHandler.togglePlayer(((Player) sender).getUniqueId());
String state =
toggled ? config.getString("messages.onstring") : config.getString("messages.offstring");
sender.sendMessage(
@@ -0,0 +1,169 @@
package me.oskar3123.staffchat.spigot.handler;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import github.scarsz.discordsrv.DiscordSRV;
import github.scarsz.discordsrv.util.DiscordUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
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 org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.jetbrains.annotations.NotNull;
public class StaffChatHandler {
private final Main plugin;
private final Set<UUID> toggledPlayers = new HashSet<>();
public StaffChatHandler(Main plugin) {
this.plugin = plugin;
}
public void onChatEvent(@NotNull AsyncPlayerChatEvent event) {
if (!event.getPlayer().hasPermission(Permissions.USE.permission())) {
return;
}
FileConfiguration config = plugin.getConfig();
String character = Objects.requireNonNull(config.getString("settings.character", "@"));
boolean isToggled = toggledPlayers.contains(event.getPlayer().getUniqueId());
if (!event.getMessage().startsWith(character) && !isToggled) {
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")) {
message = plugin.replacePlaceholders(event.getPlayer(), message);
}
StaffChatEvent chatEvent =
new StaffChatEvent(event.isAsynchronous(), event.getPlayer(), format, message);
Bukkit.getServer().getPluginManager().callEvent(chatEvent);
if (chatEvent.isCancelled()) {
return;
}
format = chatEvent.getFormat();
message = chatEvent.getMessage();
format = plugin.replacePlaceholders(event.getPlayer(), format);
sendStaffChatMessage(format, event.getPlayer()::getName, message);
if (plugin.getConfig().getBoolean("discordsrv.enable")) {
sendDiscordMessage(event.getPlayer(), message);
}
}
public void sendStaffChatMessage(String format, Supplier<String> nameSupplier, String message) {
final String finalMessage =
FormatUtils.replacePlaceholders(
format, s -> ChatColor.translateAlternateColorCodes('&', s), nameSupplier, message);
if (plugin.getConfig().getBoolean("settings.sendmessagestoallservers")) {
// Find any player and send using that object
// (which player sends the plugin message is not important)
Bukkit.getOnlinePlayers().stream()
.findAny()
.ifPresent(player -> sendForwardPluginMessage(player, finalMessage));
}
sendStaffChatMessage(finalMessage);
}
public void onPluginMessageReceived(@NotNull String channel, byte[] message) {
if (!channel.equals("BungeeCord")) {
return;
}
ByteArrayDataInput in = ByteStreams.newDataInput(message);
String subChannel = in.readUTF();
if (!subChannel.equals("StaffChat")) {
return;
}
short len = in.readShort();
byte[] msgbytes = new byte[len];
in.readFully(msgbytes);
DataInputStream msgin = new DataInputStream(new ByteArrayInputStream(msgbytes));
String actualMessage;
try {
actualMessage = msgin.readUTF();
} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to read plugin message from bungeecord", e);
return;
}
sendStaffChatMessage(actualMessage);
}
private void sendForwardPluginMessage(@NotNull Player player, @NotNull String message) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("Forward");
out.writeUTF("ONLINE");
out.writeUTF("StaffChat");
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
DataOutputStream dataStream = new DataOutputStream(byteStream);
try {
dataStream.writeUTF(message);
} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to send plugin message to bungeecord", e);
return;
}
byte[] data = byteStream.toByteArray();
out.writeShort(data.length);
out.write(data);
player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
}
private void sendStaffChatMessage(@NotNull String message) {
Bukkit.getOnlinePlayers().stream()
.filter(p -> p.hasPermission(Permissions.SEE.permission()))
.forEach(p -> p.sendMessage(message));
plugin.getLogger().info(ChatColor.stripColor(message));
}
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);
DiscordUtil.sendMessage(
DiscordSRV.getPlugin()
.getOptionalTextChannel(
plugin.getConfig().getString("discordsrv.channel", "staffchat")),
format);
}
}
public boolean togglePlayer(@NotNull UUID player) {
if (toggledPlayers.contains(player)) {
toggledPlayers.remove(player);
return false;
}
toggledPlayers.add(player);
return true;
}
}
@@ -1,161 +1,21 @@
package me.oskar3123.staffchat.spigot.listener;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import github.scarsz.discordsrv.DiscordSRV;
import github.scarsz.discordsrv.util.DiscordUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import me.oskar3123.staffchat.spigot.Main;
import me.oskar3123.staffchat.spigot.event.StaffChatEvent;
import me.oskar3123.staffchat.util.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import me.oskar3123.staffchat.spigot.handler.StaffChatHandler;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
public class ChatListener implements Listener, PluginMessageListener {
public class ChatListener implements Listener {
private final Main plugin;
private final Set<UUID> toggledPlayers = new HashSet<>();
private final StaffChatHandler staffChatHandler;
public ChatListener(@NotNull Main plugin) {
this.plugin = plugin;
public ChatListener(@NotNull StaffChatHandler staffChatHandler) {
this.staffChatHandler = staffChatHandler;
}
@EventHandler
public void chat(@NotNull AsyncPlayerChatEvent event) {
if (!event.getPlayer().hasPermission(plugin.usePerm)) {
return;
}
FileConfiguration config = plugin.getConfig();
String character = Objects.requireNonNull(config.getString("settings.character", "@"));
boolean isToggled = toggledPlayers.contains(event.getPlayer().getUniqueId());
if (!event.getMessage().startsWith(character) && !isToggled) {
return;
}
String format = Objects.requireNonNull(config.getString("settings.format", ""));
String message = event.getMessage().substring(isToggled ? 0 : character.length()).trim();
if (config.getBoolean("settings.replaceplaceholdersinmessage")) {
message = plugin.replacePlaceholders(event.getPlayer(), message);
}
StaffChatEvent chatEvent =
new StaffChatEvent(event.isAsynchronous(), event.getPlayer(), format, message);
Bukkit.getServer().getPluginManager().callEvent(chatEvent);
if (chatEvent.isCancelled()) {
return;
}
format = chatEvent.getFormat();
message = chatEvent.getMessage();
format = plugin.replacePlaceholders(event.getPlayer(), format);
format = format.replaceAll("\\{NAME}", StringUtils.sanitize(event.getPlayer().getName()));
format = ChatColor.translateAlternateColorCodes('&', format);
format = format.replaceAll("\\{MESSAGE}", StringUtils.sanitize(message));
final String finalMessage = format;
if (plugin.getConfig().getBoolean("discordsrv.enable")) {
sendDiscordMessage(event.getPlayer(), message);
}
if (plugin.getConfig().getBoolean("settings.sendmessagestoallservers")) {
sendForwardPluginMessage(event.getPlayer(), finalMessage);
}
sendStaffChatMessage(finalMessage);
event.setCancelled(true);
}
@Override
public void onPluginMessageReceived(
@NotNull String channel, @NotNull Player player, byte[] message) {
if (!channel.equals("BungeeCord")) {
return;
}
ByteArrayDataInput in = ByteStreams.newDataInput(message);
String subChannel = in.readUTF();
if (!subChannel.equals("StaffChat")) {
return;
}
short len = in.readShort();
byte[] msgbytes = new byte[len];
in.readFully(msgbytes);
DataInputStream msgin = new DataInputStream(new ByteArrayInputStream(msgbytes));
String actualMessage;
try {
actualMessage = msgin.readUTF();
} catch (IOException e) {
e.printStackTrace();
plugin.getLogger().severe("Failed to read plugin message from bungeecord");
return;
}
sendStaffChatMessage(actualMessage);
}
private void sendStaffChatMessage(@NotNull String message) {
Bukkit.getOnlinePlayers().stream()
.filter(p -> p.hasPermission(plugin.seePerm))
.forEach(p -> p.sendMessage(message));
plugin.getLogger().info(ChatColor.stripColor(message));
}
private void sendForwardPluginMessage(@NotNull Player player, @NotNull String message) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("Forward");
out.writeUTF("ONLINE");
out.writeUTF("StaffChat");
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
DataOutputStream dataStream = new DataOutputStream(byteStream);
try {
dataStream.writeUTF(message);
} catch (IOException e) {
e.printStackTrace();
plugin.getLogger().severe("Failed to send plugin message to bungeecord");
return;
}
byte[] data = byteStream.toByteArray();
out.writeShort(data.length);
out.write(data);
player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
}
private void sendDiscordMessage(Player player, String message) {
if (Bukkit.getPluginManager().isPluginEnabled("DiscordSRV")) {
String format =
plugin
.replacePlaceholders(player, plugin.getConfig().getString("discordsrv.format", ""))
.replaceAll("\\{NAME}", StringUtils.sanitize(player.getName()))
.replaceAll("\\{MESSAGE}", StringUtils.sanitize(message));
DiscordUtil.sendMessage(
DiscordSRV.getPlugin()
.getOptionalTextChannel(
plugin.getConfig().getString("discordsrv.channel", "staffchat")),
format);
}
}
public boolean togglePlayer(@NotNull UUID player) {
if (toggledPlayers.contains(player)) {
toggledPlayers.remove(player);
return false;
}
toggledPlayers.add(player);
return true;
staffChatHandler.onChatEvent(event);
}
}
@@ -0,0 +1,56 @@
package me.oskar3123.staffchat.spigot.listener;
import github.scarsz.discordsrv.DiscordSRV;
import github.scarsz.discordsrv.api.ListenerPriority;
import github.scarsz.discordsrv.api.Subscribe;
import github.scarsz.discordsrv.api.events.DiscordGuildMessageReceivedEvent;
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.configuration.Configuration;
public class DiscordSrvListener {
private final Main plugin;
private final DiscordSRV discordSrv;
public DiscordSrvListener(Main plugin) {
this.plugin = plugin;
this.discordSrv = DiscordSRV.getPlugin();
}
@Subscribe(priority = ListenerPriority.MONITOR)
public void onDiscordGuildMessageReceivedEvent(DiscordGuildMessageReceivedEvent event) {
Configuration config = plugin.getConfig();
if (!isDiscordSrvEnabled(config)) {
return;
}
String channel = config.getString("discordsrv.channel", "staffchat");
Optional.ofNullable(discordSrv.getDestinationGameChannelNameForTextChannel(event.getChannel()))
.filter(gameChannel -> gameChannel.equals(channel))
.ifPresent(
gameChannel -> {
String name =
Optional.ofNullable(event.getAuthor()).map(User::getName).orElse("Discord User");
String message =
Optional.ofNullable(event.getMessage())
.map(Message::getContentDisplay)
.orElse("");
FoliaUtils.execute(
plugin,
() ->
plugin.staffChatHandler.sendStaffChatMessage(
plugin
.getConfig()
.getString("discordsrv.discord-to-minecraft-format", ""),
() -> name,
message));
});
}
private boolean isDiscordSrvEnabled(Configuration config) {
return config.getBoolean("discordsrv.enable", false);
}
}
@@ -0,0 +1,21 @@
package me.oskar3123.staffchat.spigot.listener;
import me.oskar3123.staffchat.spigot.handler.StaffChatHandler;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
public class StaffChatPml implements PluginMessageListener {
private final StaffChatHandler staffChatHandler;
public StaffChatPml(StaffChatHandler staffChatHandler) {
this.staffChatHandler = staffChatHandler;
}
@Override
public void onPluginMessageReceived(
@NotNull String channel, @NotNull Player player, byte[] message) {
staffChatHandler.onPluginMessageReceived(channel, message);
}
}
@@ -0,0 +1,52 @@
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);
}
}
}
@@ -0,0 +1,21 @@
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> 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;
}
}
@@ -0,0 +1,21 @@
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,6 +2,8 @@ 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: 1.4.0
version: SNAPSHOT
+3 -1
View File
@@ -8,10 +8,12 @@ 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'
discordsrv:
enable: false
channel: 'staffchat'
format: '{NAME} » {MESSAGE}'
minecraft-to-discord-format: '{NAME} » {MESSAGE}'
discord-to-minecraft-format: '&b(Discord) {NAME}: {MESSAGE}'
+2 -1
View File
@@ -2,8 +2,9 @@ name: StaffChat
authors: [ oskar3123 ]
main: me.oskar3123.staffchat.spigot.Main
api-version: 1.13
version: 1.4.0
version: SNAPSHOT
softdepend: [ PlaceholderAPI, DiscordSRV ]
folia-supported: true
commands:
staffchat:
usage: /<command>
@@ -0,0 +1,117 @@
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();
}
}
@@ -0,0 +1,123 @@
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);
}
}
@@ -0,0 +1,260 @@
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();
}
}
}
@@ -0,0 +1,73 @@
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::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);
}
}
@@ -0,0 +1,36 @@
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());
}
}
}