This commit is contained in:
1 parent
bbccd4a815
commit
71396a86a0
581 files changed
+9084
-38713
No files matched your search
@@ -13,18 +13,20 @@ concurrency:
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
prepare-release:
|
||||
build-and-release:
|
||||
runs-on: docker-builder
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
tag: ${{ steps.version.outputs.tag }}
|
||||
release_id: ${{ steps.release.outputs.release_id }}
|
||||
steps:
|
||||
- name: Checkout repository and tags
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Java 25
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: '25'
|
||||
|
||||
- name: Calculate next version
|
||||
id: version
|
||||
shell: bash
|
||||
@@ -44,134 +46,36 @@ jobs:
|
||||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=v$version" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create Gitea release
|
||||
id: release
|
||||
- name: Build plugin and run crypto test
|
||||
shell: bash
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chmod +x ./gradlew
|
||||
./gradlew --no-daemon clean build -PbuildVersion="$VERSION"
|
||||
|
||||
- name: Create Gitea release and upload plugin
|
||||
shell: bash
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ -z "$GITEA_TOKEN" ]]; then
|
||||
echo "Add a repository write token as the GITEA_TOKEN secret."
|
||||
exit 1
|
||||
fi
|
||||
jar="build/libs/AsminVoiceChat-${VERSION}.jar"
|
||||
api="$GITEA_SERVER_URL/api/v1/repos/$GITEA_REPOSITORY"
|
||||
release="$(curl --fail-with-body -sS -X POST "$api/releases" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"tag_name\":\"$TAG\",\"name\":\"AsminVoiceChat $VERSION\",\"body\":\"Dual-protocol Simple Voice Chat server release.\",\"draft\":false,\"prerelease\":false}")"
|
||||
-d "{\"tag_name\":\"$TAG\",\"name\":\"AsminVoiceChat $VERSION\",\"body\":\"AsminVoiceChat $VERSION\",\"draft\":false,\"prerelease\":false}")"
|
||||
release_id="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<< "$release")"
|
||||
echo "release_id=$release_id" >> "$GITHUB_OUTPUT"
|
||||
|
||||
build-and-test:
|
||||
needs: prepare-release
|
||||
runs-on: docker-builder
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
java:
|
||||
- '16'
|
||||
- '17'
|
||||
- '18'
|
||||
- '19'
|
||||
- '20'
|
||||
- '21'
|
||||
- '22'
|
||||
- '23'
|
||||
- '24'
|
||||
- '25'
|
||||
- '26'
|
||||
- '27-ea'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Java ${{ matrix.java }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: ${{ matrix.java }}
|
||||
|
||||
- name: Download 2.6.25 base and compile dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p build/deps build/classes build/test-classes
|
||||
hangar="https://hangarcdn.papermc.io/plugins/henkelmax/SimpleVoiceChat/versions/bukkit-2.6.25/PAPER/voicechat-bukkit-2.6.25.jar"
|
||||
curl --fail --location --silent --show-error "$hangar" -o build/deps/voicechat-bukkit-2.6.25.jar
|
||||
|
||||
repo="https://repo.papermc.io/repository/maven-public"
|
||||
curl --fail --location --silent --show-error "$repo/io/papermc/paper/paper-api/1.17.1-R0.1-SNAPSHOT/paper-api-1.17.1-R0.1-20220414.034903-311.jar" -o build/deps/paper-api.jar
|
||||
curl --fail --location --silent --show-error "$repo/net/md-5/bungeecord-chat/1.16-R0.4/bungeecord-chat-1.16-R0.4.jar" -o build/deps/bungeecord-chat.jar
|
||||
curl --fail --location --silent --show-error "$repo/net/kyori/adventure-api/4.9.3/adventure-api-4.9.3.jar" -o build/deps/adventure-api.jar
|
||||
curl --fail --location --silent --show-error "$repo/net/kyori/adventure-key/4.9.3/adventure-key-4.9.3.jar" -o build/deps/adventure-key.jar
|
||||
curl --fail --location --silent --show-error "$repo/net/kyori/examination-api/1.3.0/examination-api-1.3.0.jar" -o build/deps/examination-api.jar
|
||||
curl --fail --location --silent --show-error "$repo/net/kyori/examination-string/1.3.0/examination-string-1.3.0.jar" -o build/deps/examination-string.jar
|
||||
curl --fail --location --silent --show-error "$repo/org/jetbrains/annotations/22.0.0/annotations-22.0.0.jar" -o build/deps/annotations.jar
|
||||
curl --fail --location --silent --show-error "$repo/io/netty/netty-buffer/4.1.63.Final/netty-buffer-4.1.63.Final.jar" -o build/deps/netty-buffer.jar
|
||||
curl --fail --location --silent --show-error "$repo/io/netty/netty-common/4.1.63.Final/netty-common-4.1.63.Final.jar" -o build/deps/netty-common.jar
|
||||
|
||||
- name: Compile Java 8-compatible patch classes
|
||||
env:
|
||||
VERSION: ${{ needs.prepare-release.outputs.version }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cp="build/deps/voicechat-bukkit-2.6.25.jar:$(printf '%s:' build/deps/*.jar)"
|
||||
javac --release 8 -cp "$cp" -d build/classes \
|
||||
source/patch/de/maxhenkel/voicechat/voice/common/Secret.java \
|
||||
source/patch/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java
|
||||
|
||||
sed "s/^version:.*/version: '$VERSION'/" plugin.yml > build/plugin.yml
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
from zipfile import ZIP_DEFLATED, ZipFile
|
||||
|
||||
base = Path("build/deps/voicechat-bukkit-2.6.25.jar")
|
||||
target = Path("build/AsminVoiceChat-${{ needs.prepare-release.outputs.version }}.jar")
|
||||
replacements = {
|
||||
"de/maxhenkel/voicechat/voice/common/Secret.class": Path("build/classes/de/maxhenkel/voicechat/voice/common/Secret.class"),
|
||||
"de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.class": Path("build/classes/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.class"),
|
||||
"plugin.yml": Path("build/plugin.yml"),
|
||||
}
|
||||
with ZipFile(base) as source, ZipFile(target, "w", ZIP_DEFLATED) as output:
|
||||
for item in source.infolist():
|
||||
replacement = replacements.get(item.filename)
|
||||
output.writestr(item, replacement.read_bytes() if replacement else source.read(item.filename))
|
||||
print(f"Built {target}")
|
||||
PY
|
||||
|
||||
- name: Run CBC/GCM interoperability test
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cp="build/AsminVoiceChat-${{ needs.prepare-release.outputs.version }}.jar:build/deps/netty-buffer.jar:build/deps/netty-common.jar"
|
||||
javac --release 8 -cp "$cp" -d build/test-classes tests/ProtocolCryptoSelfTest.java
|
||||
java -cp "build/test-classes:$cp" ProtocolCryptoSelfTest
|
||||
|
||||
- name: Upload JVM-specific build to release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||
RELEASE_ID: ${{ needs.prepare-release.outputs.release_id }}
|
||||
VERSION: ${{ needs.prepare-release.outputs.version }}
|
||||
JAVA_VERSION: ${{ matrix.java }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
api="$GITEA_SERVER_URL/api/v1/repos/$GITEA_REPOSITORY"
|
||||
file="build/AsminVoiceChat-${VERSION}.jar"
|
||||
asset="AsminVoiceChat-${VERSION}-Java${JAVA_VERSION}.jar"
|
||||
curl --fail-with-body -sS -X POST "$api/releases/$RELEASE_ID/assets?name=$asset" \
|
||||
curl --fail-with-body -sS -X POST "$api/releases/$release_id/assets?name=AsminVoiceChat-${VERSION}.jar" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-F "attachment=@$file;filename=$asset"
|
||||
if [[ "$JAVA_VERSION" == "21" ]]; then
|
||||
curl --fail-with-body -sS -X POST "$api/releases/$RELEASE_ID/assets?name=AsminVoiceChat-${VERSION}.jar" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-F "attachment=@$file;filename=AsminVoiceChat-${VERSION}.jar"
|
||||
fi
|
||||
-F "attachment=@$jar;filename=AsminVoiceChat-${VERSION}.jar"
|
||||
@@ -0,0 +1,4 @@
|
||||
.gradle/
|
||||
build/
|
||||
*.class
|
||||
*.log
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
Copyright (c) Max Henkel - All Rights Reserved
|
||||
@@ -3,6 +3,7 @@
|
||||
Simple Voice Chat server fork based on 2.6.25. It accepts protocol compatibility
|
||||
18 (2.5.x clients) and 20 (2.6.x clients) at the same time. Clients remain
|
||||
unmodified; each player still needs a mod release for their Minecraft version.
|
||||
The plugin is compiled to Java 8 bytecode and runs on newer JVMs.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -12,8 +13,9 @@ uses AES-CBC for 2.5 clients and AES-GCM for 2.6 clients. Shared packet layouts
|
||||
are passed through the existing 2.6 voice routing. The fork changes only the
|
||||
per-client cipher selection and compatibility-version acceptance.
|
||||
|
||||
The full source is in `source/decompiled/`. The two changed classes are also
|
||||
available separately in `source/patch/`.
|
||||
The Bukkit plugin source and API source are in `source/bukkit/` and
|
||||
`source/api/`. The cipher and compatibility changes are in the regular source
|
||||
files for those modules.
|
||||
|
||||
## Goxy
|
||||
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'com.gradleup.shadow' version '9.6.0'
|
||||
}
|
||||
|
||||
group = 'de.maxhenkel.voicechat'
|
||||
version = findProperty('buildVersion') ?: '1.0.0'
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url = 'https://hub.spigotmc.org/nexus/content/groups/public/' }
|
||||
maven { url = 'https://libraries.minecraft.net/' }
|
||||
maven { url = 'https://repo.extendedclip.com/content/repositories/placeholderapi/' }
|
||||
maven { url = 'https://maven.maxhenkel.de/repository/public' }
|
||||
maven { url = 'https://repo.viaversion.com/' }
|
||||
}
|
||||
|
||||
configurations {
|
||||
shadow
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'org.bukkit:bukkit:1.8.8-R0.1-SNAPSHOT'
|
||||
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
compileOnly 'me.clip:placeholderapi:2.12.2'
|
||||
compileOnly 'com.viaversion:viaversion-bukkit:5.7.2'
|
||||
compileOnly 'com.viaversion:viaversion-common:5.7.2'
|
||||
|
||||
implementation 'io.netty:netty-all:4.1.132.Final'
|
||||
|
||||
implementation 'org.json:json:20251224'
|
||||
shadow 'org.json:json:20251224'
|
||||
implementation 'me.lucko:commodore:2.2'
|
||||
shadow 'me.lucko:commodore:2.2'
|
||||
implementation 'org.concentus:Concentus:1.0.1'
|
||||
shadow 'org.concentus:Concentus:1.0.1'
|
||||
implementation 'de.maxhenkel.configbuilder:configbuilder:2.1.0'
|
||||
shadow 'de.maxhenkel.configbuilder:configbuilder:2.1.0'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = [
|
||||
'source/api/src/main/java',
|
||||
'source/bukkit/src/main/java',
|
||||
layout.buildDirectory.dir('generated/sources/java').get().asFile
|
||||
]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ['source/bukkit/src/main/resources']
|
||||
exclude 'plugin.yml'
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDirs = ['tests']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8'
|
||||
options.release = 8
|
||||
}
|
||||
|
||||
tasks.register('generateBuildConstants', Copy) {
|
||||
from 'source/bukkit/src/template/java'
|
||||
into layout.buildDirectory.dir('generated/sources/java')
|
||||
expand compatibility_version: '20',
|
||||
mod_compatible_version: '2.6.x',
|
||||
target_bukkit_version: '1.8.8-R0.1-SNAPSHOT',
|
||||
plugin_name: 'Simple Voice Chat'
|
||||
}
|
||||
compileJava.dependsOn tasks.named('generateBuildConstants')
|
||||
|
||||
tasks.register('generateFallbackTranslations') {
|
||||
def inputFile = file('source/common/src/main/resources/assets/voicechat/lang/en_us.json')
|
||||
def outputFile = layout.buildDirectory.file('generated/sources/java/de/maxhenkel/voicechat/FallbackTranslations.java')
|
||||
inputs.file inputFile
|
||||
outputs.file outputFile
|
||||
|
||||
doLast {
|
||||
def translations = new JsonSlurper().parse(inputFile)
|
||||
def output = new StringBuilder('''package de.maxhenkel.voicechat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FallbackTranslations {
|
||||
public static final Map<String, String> FALLBACK_TRANSLATIONS;
|
||||
static {
|
||||
Map<String, String> translations = new HashMap<>();
|
||||
''')
|
||||
translations.each { key, value ->
|
||||
def k = key.toString().replace('\\', '\\\\').replace('\n', '\\n').replace('"', '\\"')
|
||||
def v = value.toString().replace('\\', '\\\\').replace('\n', '\\n').replace('"', '\\"')
|
||||
output.append(" translations.put(\"${k}\", \"${v}\");\n")
|
||||
}
|
||||
output.append(''' FALLBACK_TRANSLATIONS = Collections.unmodifiableMap(translations);
|
||||
}
|
||||
}
|
||||
''')
|
||||
outputFile.get().asFile.parentFile.mkdirs()
|
||||
outputFile.get().asFile.text = output.toString()
|
||||
}
|
||||
}
|
||||
compileJava.dependsOn tasks.named('generateFallbackTranslations')
|
||||
|
||||
processResources {
|
||||
from('plugin.yml') {
|
||||
filter { String line ->
|
||||
line.startsWith('version:') ? "version: '${project.version}'" : line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('jar') {
|
||||
archiveClassifier = 'dev'
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
configurations = [project.configurations.shadow]
|
||||
archiveClassifier = ''
|
||||
archiveFileName = "AsminVoiceChat-${project.version}.jar"
|
||||
from('LICENSE-upstream.txt') {
|
||||
rename { 'LICENSE_SimpleVoiceChat' }
|
||||
}
|
||||
relocate 'org.json', 'de.maxhenkel.voicechat.org.json'
|
||||
relocate 'me.lucko.commodore', 'de.maxhenkel.voicechat.commodore'
|
||||
relocate 'org.concentus', 'de.maxhenkel.voicechat.concentus'
|
||||
relocate 'de.maxhenkel.configbuilder', 'de.maxhenkel.voicechat.configbuilder'
|
||||
dependencies {
|
||||
exclude(dependency('com.mojang:brigadier:.*'))
|
||||
exclude(dependency('com.google.code.findbugs:jsr305:.*'))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('build') {
|
||||
dependsOn 'shadowJar'
|
||||
dependsOn 'cryptoTest'
|
||||
}
|
||||
|
||||
tasks.register('cryptoTest', JavaExec) {
|
||||
dependsOn tasks.named('testClasses')
|
||||
classpath = sourceSets.test.runtimeClasspath + files(shadowJar.archiveFile)
|
||||
mainClass = 'ProtocolCryptoSelfTest'
|
||||
}
|
||||
|
||||
tasks.withType(Test).configureEach {
|
||||
failOnNoDiscoveredTests = false
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.gradle.jvmargs=-Xmx2G
|
||||
org.gradle.daemon=false
|
||||
Vendored
BIN
Binary file not shown.
+9
@@ -0,0 +1,9 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,248 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 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.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# 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/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/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
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
# 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
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
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
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
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" )
|
||||
|
||||
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
|
||||
|
||||
|
||||
# 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"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# 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
+82
@@ -0,0 +1,82 @@
|
||||
@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
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||
setlocal EnableExtensions
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
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% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||
@rem which allows us to clear the local environment before executing the java command
|
||||
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||
|
||||
:exitWithErrorLevel
|
||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||
@@ -0,0 +1,8 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven { url = 'https://repo.papermc.io/repository/maven-public/' }
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'AsminVoicechat'
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
public interface BukkitVoicechatService {
|
||||
|
||||
/**
|
||||
* Registers the voice chat plugin on bukkit based servers.
|
||||
*
|
||||
* @param plugin the voicechat plugin
|
||||
*/
|
||||
void registerPlugin(VoicechatPlugin plugin);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
/**
|
||||
* A socket used for client side voice chat traffic.
|
||||
* Can be set using {@link de.maxhenkel.voicechat.api.events.ClientVoicechatInitializationEvent#setSocketImplementation(ClientVoicechatSocket)}.
|
||||
*/
|
||||
public interface ClientVoicechatSocket {
|
||||
|
||||
void open() throws Exception;
|
||||
|
||||
RawUdpPacket read() throws Exception;
|
||||
|
||||
void send(byte[] data, SocketAddress address) throws Exception;
|
||||
|
||||
void close();
|
||||
|
||||
boolean isClosed();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface Entity {
|
||||
|
||||
/**
|
||||
* @return the UUID of the entity
|
||||
*/
|
||||
UUID getUuid();
|
||||
|
||||
/**
|
||||
* @return the actual entity object
|
||||
*/
|
||||
Object getEntity();
|
||||
|
||||
/**
|
||||
* @return the current position of the entity
|
||||
*/
|
||||
Position getPosition();
|
||||
|
||||
}
|
||||
+1
-5
@@ -1,12 +1,8 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(value=RetentionPolicy.RUNTIME)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ForgeVoicechatPlugin {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Groups can be created using {@link VoicechatServerApi#groupBuilder()}.
|
||||
*/
|
||||
public interface Group {
|
||||
|
||||
/**
|
||||
* @return the visual name of the group
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* @return if the group has a password
|
||||
*/
|
||||
boolean hasPassword();
|
||||
|
||||
/**
|
||||
* @return the ID of the group
|
||||
*/
|
||||
UUID getId();
|
||||
|
||||
/**
|
||||
* @return if the group is persistent
|
||||
*/
|
||||
boolean isPersistent();
|
||||
|
||||
/**
|
||||
* @return if the group is hidden in the group list UI
|
||||
*/
|
||||
boolean isHidden();
|
||||
|
||||
/**
|
||||
* @return the group type
|
||||
*/
|
||||
Type getType();
|
||||
|
||||
public interface Type {
|
||||
|
||||
/**
|
||||
* Players in a group can hear nearby players that are not in a group
|
||||
*/
|
||||
public static final Type NORMAL = new Type() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Players in a group can hear nearby players and nearby players can hear players in the group
|
||||
*/
|
||||
public static final Type OPEN = new Type() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Players in a group can only hear other players in the group
|
||||
*/
|
||||
public static final Type ISOLATED = new Type() {
|
||||
};
|
||||
}
|
||||
|
||||
public interface Builder {
|
||||
|
||||
/**
|
||||
* Sets the ID of the group.
|
||||
* If this is not set, the ID will be randomly generated.
|
||||
* <br/>
|
||||
* <b>NOTE</b>: If there is already a group with the same ID, the group will be overwritten.
|
||||
*
|
||||
* @param id the group ID or <code>null</code> if the ID should be randomly generated
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setId(@Nullable UUID id);
|
||||
|
||||
/**
|
||||
* <b>NOTE</b>: The name might be stripped of special characters and whitespace.
|
||||
*
|
||||
* @param name the name of the group
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setName(String name);
|
||||
|
||||
/**
|
||||
* @param password the group password
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setPassword(@Nullable String password);
|
||||
|
||||
/**
|
||||
* @param persistent if the group should be persistent
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setPersistent(boolean persistent);
|
||||
|
||||
/**
|
||||
* @param hidden if the group should be hidden in the group list UI
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setHidden(boolean hidden);
|
||||
|
||||
/**
|
||||
* @param type the group type
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setType(Type type);
|
||||
|
||||
/**
|
||||
* @return the built group
|
||||
* @throws IllegalStateException if the name is not set or invalid
|
||||
*/
|
||||
Group build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
public interface Player extends Entity {
|
||||
|
||||
/**
|
||||
* @return the actual player object
|
||||
*/
|
||||
Object getPlayer();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
public interface Position {
|
||||
|
||||
/**
|
||||
* @return the X position
|
||||
*/
|
||||
double getX();
|
||||
|
||||
/**
|
||||
* @return the Y position
|
||||
*/
|
||||
double getY();
|
||||
|
||||
/**
|
||||
* @return the Z position
|
||||
*/
|
||||
double getZ();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
public interface RawUdpPacket {
|
||||
|
||||
byte[] getData();
|
||||
|
||||
long getTimestamp();
|
||||
|
||||
SocketAddress getSocketAddress();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
public interface ServerLevel {
|
||||
|
||||
/**
|
||||
* @return the actual level object
|
||||
*/
|
||||
Object getServerLevel();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
public interface ServerPlayer extends Player {
|
||||
|
||||
/**
|
||||
* @return the level of the player
|
||||
*/
|
||||
ServerLevel getServerLevel();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import de.maxhenkel.voicechat.api.audio.AudioConverter;
|
||||
import de.maxhenkel.voicechat.api.mp3.Mp3Decoder;
|
||||
import de.maxhenkel.voicechat.api.mp3.Mp3Encoder;
|
||||
import de.maxhenkel.voicechat.api.opus.OpusDecoder;
|
||||
import de.maxhenkel.voicechat.api.opus.OpusEncoder;
|
||||
import de.maxhenkel.voicechat.api.opus.OpusEncoderMode;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public interface VoicechatApi {
|
||||
|
||||
/**
|
||||
* Creates a new opus encoder.
|
||||
* Note that the encoder needs to be closed after you are finished using it.
|
||||
*/
|
||||
OpusEncoder createEncoder();
|
||||
|
||||
/**
|
||||
* Creates a new opus encoder.
|
||||
* Note that the encoder needs to be closed after you are finished using it.
|
||||
*
|
||||
* @param mode the opus encoder mode
|
||||
* @return the opus encoder
|
||||
*/
|
||||
OpusEncoder createEncoder(OpusEncoderMode mode);
|
||||
|
||||
/**
|
||||
* Creates a new opus decoder.
|
||||
* Note that the decoder needs to be closed after you are finished using it.
|
||||
*
|
||||
* @return the opus decoder
|
||||
*/
|
||||
OpusDecoder createDecoder();
|
||||
|
||||
/**
|
||||
* <b>NOTE</b>: This is not available for Bukkit! It will always return null.
|
||||
*
|
||||
* @param audioFormat the audio format
|
||||
* @param bitrate the bitrate in kbps
|
||||
* @param quality the quality from 0 (highest) to 9 (lowest)
|
||||
* @param outputStream the output stream to write the mp3 to
|
||||
* @return the mp3 encoder or null if the encoder could not be initialized or is not available
|
||||
*/
|
||||
@Nullable
|
||||
Mp3Encoder createMp3Encoder(AudioFormat audioFormat, int bitrate, int quality, OutputStream outputStream);
|
||||
|
||||
/**
|
||||
* <b>NOTE</b>: This is not available for Bukkit! It will always return null.
|
||||
*
|
||||
* @param inputStream the input stream to read the mp3 from
|
||||
* @return the mp3 decoder or null if the decoder could not be initialized or is not available
|
||||
*/
|
||||
@Nullable
|
||||
Mp3Decoder createMp3Decoder(InputStream inputStream);
|
||||
|
||||
/**
|
||||
* @return the audio converter
|
||||
*/
|
||||
AudioConverter getAudioConverter();
|
||||
|
||||
/**
|
||||
* Creates an entity object from an actual entity.
|
||||
*
|
||||
* @param entity the entity implementation of your mod/plugin loader
|
||||
* @return the entity object
|
||||
*/
|
||||
Entity fromEntity(Object entity);
|
||||
|
||||
/**
|
||||
* Creates a level object from an actual level.
|
||||
*
|
||||
* @param serverLevel the level implementation of your mod/plugin loader
|
||||
* @return the level
|
||||
*/
|
||||
ServerLevel fromServerLevel(Object serverLevel);
|
||||
|
||||
/**
|
||||
* Creates a player object from an actual player.
|
||||
*
|
||||
* @param serverPlayer the player implementation of your mod/plugin loader
|
||||
* @return the player
|
||||
*/
|
||||
ServerPlayer fromServerPlayer(Object serverPlayer);
|
||||
|
||||
/**
|
||||
* Creates a new position object.
|
||||
*
|
||||
* @param x the X position
|
||||
* @param y the Y position
|
||||
* @param z the Z position
|
||||
* @return the position with the provided coordinates
|
||||
*/
|
||||
Position createPosition(double x, double y, double z);
|
||||
|
||||
/**
|
||||
* Don't forget to register your category with {@link VoicechatServerApi#registerVolumeCategory(VolumeCategory)} or {@link VoicechatClientApi#registerClientVolumeCategory(VolumeCategory)}
|
||||
*
|
||||
* @return a builder to build a category
|
||||
*/
|
||||
VolumeCategory.Builder volumeCategoryBuilder();
|
||||
|
||||
/**
|
||||
* <b>NOTE</b>: Voice chat plugins can change this.
|
||||
* This is only the default value.
|
||||
* This value might not be correct if you are not connected to a server.
|
||||
*
|
||||
* @return the default distance, audio from voice chat is heard from
|
||||
*/
|
||||
double getVoiceChatDistance();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import de.maxhenkel.voicechat.api.audiochannel.ClientEntityAudioChannel;
|
||||
import de.maxhenkel.voicechat.api.audiochannel.ClientLocationalAudioChannel;
|
||||
import de.maxhenkel.voicechat.api.audiochannel.ClientStaticAudioChannel;
|
||||
import de.maxhenkel.voicechat.api.config.ConfigAccessor;
|
||||
import de.maxhenkel.voicechat.api.config.VolumeConfigAccessor;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface VoicechatClientApi extends VoicechatApi {
|
||||
|
||||
/**
|
||||
* @return if the local voice chat is muted
|
||||
*/
|
||||
boolean isMuted();
|
||||
|
||||
/**
|
||||
* Does the same as {@link #isDisabled(UUID)} with <code>null</code> as the player UUID.
|
||||
*
|
||||
* @return if the local voice chat is disabled
|
||||
*/
|
||||
default boolean isDisabled() {
|
||||
return isDisabled(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param playerId the UUID of the player or <code>null</code> for the local player
|
||||
* @return if the player with the provided UUID has the voice chat disabled
|
||||
*/
|
||||
boolean isDisabled(@Nullable UUID playerId);
|
||||
|
||||
/**
|
||||
* Does the same as {@link #isDisconnected(UUID)} with <code>null</code> as the player UUID.
|
||||
*
|
||||
* @return if the voice chat is disconnected from the server
|
||||
*/
|
||||
default boolean isDisconnected() {
|
||||
return isDisconnected(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param playerId the UUID of the player or <code>null</code> for the local player
|
||||
* @return if the player with the provided UUID is disconnected from voice chat
|
||||
*/
|
||||
boolean isDisconnected(@Nullable UUID playerId);
|
||||
|
||||
/**
|
||||
* Does the same as {@link #isTalking(UUID)} with <code>null</code> as the player UUID.
|
||||
*
|
||||
* @return if the local player is talking
|
||||
*/
|
||||
default boolean isTalking() {
|
||||
return isTalking(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param playerId the UUID of the player or <code>null</code> for the local player
|
||||
* @return if the player with the provided UUID is currently talking
|
||||
*/
|
||||
boolean isTalking(@Nullable UUID playerId);
|
||||
|
||||
/**
|
||||
* Does the same as {@link #isWhispering(UUID)} with <code>null</code> as the player UUID.
|
||||
*
|
||||
* @return if the local player is whispering
|
||||
*/
|
||||
default boolean isWhispering() {
|
||||
return isWhispering(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param playerId the UUID of the player or <code>null</code> for the local player
|
||||
* @return if the player with the provided UUID is currently whispering
|
||||
*/
|
||||
boolean isWhispering(@Nullable UUID playerId);
|
||||
|
||||
/**
|
||||
* This method returns if the push to talk key is pressed, even when using voice activation.
|
||||
*
|
||||
* @return if the push to talk key is pressed
|
||||
*/
|
||||
boolean isPushToTalkKeyPressed();
|
||||
|
||||
/**
|
||||
* @return if the whisper key is pressed
|
||||
*/
|
||||
boolean isWhisperKeyPressed();
|
||||
|
||||
@Nullable
|
||||
Group getGroup();
|
||||
|
||||
/**
|
||||
* Creates a client side entity audio channel.
|
||||
*
|
||||
* @param uuid the UUID of the entity
|
||||
* @return the audio channel
|
||||
* @deprecated use {@link #createEntityAudioChannel(UUID, Entity)}
|
||||
*/
|
||||
@Deprecated
|
||||
ClientEntityAudioChannel createEntityAudioChannel(UUID uuid);
|
||||
|
||||
/**
|
||||
* Creates a client side entity audio channel.
|
||||
*
|
||||
* @param uuid the ID of the channel
|
||||
* @param entity the entity to attach the channel to
|
||||
* @return the audio channel
|
||||
*/
|
||||
ClientEntityAudioChannel createEntityAudioChannel(UUID uuid, Entity entity);
|
||||
|
||||
/**
|
||||
* Creates a client side locational audio channel.
|
||||
*
|
||||
* @param uuid the ID of the channel
|
||||
* @return the audio channel
|
||||
*/
|
||||
ClientLocationalAudioChannel createLocationalAudioChannel(UUID uuid, Position position);
|
||||
|
||||
/**
|
||||
* Creates a client side static audio channel.
|
||||
*
|
||||
* @param uuid the ID of the channel
|
||||
* @return the audio channel
|
||||
*/
|
||||
ClientStaticAudioChannel createStaticAudioChannel(UUID uuid);
|
||||
|
||||
/**
|
||||
* Registers a volume category just for this client.
|
||||
* A category can be created with {@link VoicechatApi#volumeCategoryBuilder()}.
|
||||
* The category can be unregistered with {@link #unregisterClientVolumeCategory}.
|
||||
*
|
||||
* @param category the category to register
|
||||
*/
|
||||
void registerClientVolumeCategory(VolumeCategory category);
|
||||
|
||||
/**
|
||||
* Unregisters a category on this client.
|
||||
* This will release the texture ID for the icon if one exists.
|
||||
*
|
||||
* @param category the category to remove
|
||||
*/
|
||||
default void unregisterClientVolumeCategory(VolumeCategory category) {
|
||||
unregisterClientVolumeCategory(category.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a category on this client.
|
||||
* This will release the texture ID for the icon if one exists.
|
||||
*
|
||||
* @param categoryId the category ID to remove
|
||||
*/
|
||||
void unregisterClientVolumeCategory(String categoryId);
|
||||
|
||||
/**
|
||||
* @return a read-only config accessor for the mods client config
|
||||
*/
|
||||
ConfigAccessor getClientConfig();
|
||||
|
||||
/**
|
||||
* @return a read-only accessor for player and category volumes
|
||||
*/
|
||||
VolumeConfigAccessor getVolumeConfig();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* <b>Note</b>: It is not guaranteed that the state of this connection object is up to date.
|
||||
* Please re-fetch the connection object to get the latest state.
|
||||
*/
|
||||
public interface VoicechatConnection {
|
||||
|
||||
/**
|
||||
* <b>Note</b>: This only returns the group the player was in when fetching this connection object.
|
||||
* Calling {@link #setGroup(Group)} won't update the return value of this method.
|
||||
*
|
||||
* @return the group of the player - <code>null</code> if the player is not in a group
|
||||
*/
|
||||
@Nullable
|
||||
Group getGroup();
|
||||
|
||||
/**
|
||||
* <b>Note</b>: This only returns if the player was in a group when fetching this connection object.
|
||||
* Calling {@link #setGroup(Group)} won't update the return value of this method.
|
||||
*
|
||||
* @return if the player is in a group
|
||||
*/
|
||||
boolean isInGroup();
|
||||
|
||||
/**
|
||||
* Joins this player to the provided group.
|
||||
*
|
||||
* @param group the group to join or <code>null</code> to leave the current group
|
||||
*/
|
||||
void setGroup(@Nullable Group group);
|
||||
|
||||
/**
|
||||
* This might not represent the actual state of the voice chat connection, since other voice chat plugins can fake having a player connected.
|
||||
* <br/>
|
||||
* <b>Note</b>: This only returns if the player was connected fetching this connection object.
|
||||
* Calling {@link #setConnected(boolean)} won't update the return value of this method.
|
||||
*
|
||||
* @return if the player is connected to voice chat
|
||||
*/
|
||||
boolean isConnected();
|
||||
|
||||
/**
|
||||
* The players disconnected state will reset if its actual disconnected state changes or if the player reconnects.
|
||||
* <br/>
|
||||
* <b>NOTE</b>: This method will only work for players that don't have the mod installed. See {@link #isInstalled()}.
|
||||
*
|
||||
* @param connected if the player should be shown as connected to voice chat
|
||||
*/
|
||||
void setConnected(boolean connected);
|
||||
|
||||
/**
|
||||
* <b>Note</b>: This only returns if the player has the voice chat disabled when fetching this connection object.
|
||||
* Calling {@link #setDisabled(boolean)} won't update the return value of this method.
|
||||
*
|
||||
* @return If the player muted the sound
|
||||
*/
|
||||
boolean isDisabled();
|
||||
|
||||
/**
|
||||
* Sets the players disabled state.
|
||||
* <b>NOTE</b>: This method will only work for players that don't have the mod installed. See {@link #isInstalled()}.
|
||||
*
|
||||
* @param disabled if the player should have the voice chat disabled icon
|
||||
*/
|
||||
void setDisabled(boolean disabled);
|
||||
|
||||
/**
|
||||
* @return if the player has a version of voice chat installed that is compatible with the server
|
||||
*/
|
||||
boolean isInstalled();
|
||||
|
||||
/**
|
||||
* @return the player
|
||||
*/
|
||||
ServerPlayer getPlayer();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import de.maxhenkel.voicechat.api.events.EventRegistration;
|
||||
|
||||
public interface VoicechatPlugin {
|
||||
|
||||
/**
|
||||
* @return the ID of this plugin - Has to be unique
|
||||
*/
|
||||
String getPluginId();
|
||||
|
||||
/**
|
||||
* Called after loading the plugin.
|
||||
*/
|
||||
default void initialize(VoicechatApi api) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register your events here - Only here!
|
||||
*
|
||||
* @param registration the event registration object, used to register events
|
||||
*/
|
||||
default void registerEvents(EventRegistration registration) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import de.maxhenkel.voicechat.api.audiochannel.*;
|
||||
import de.maxhenkel.voicechat.api.audiolistener.AudioListener;
|
||||
import de.maxhenkel.voicechat.api.audiolistener.PlayerAudioListener;
|
||||
import de.maxhenkel.voicechat.api.audiosender.AudioSender;
|
||||
import de.maxhenkel.voicechat.api.config.ConfigAccessor;
|
||||
import de.maxhenkel.voicechat.api.opus.OpusEncoder;
|
||||
import de.maxhenkel.voicechat.api.packets.ConvertablePacket;
|
||||
import de.maxhenkel.voicechat.api.packets.EntitySoundPacket;
|
||||
import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket;
|
||||
import de.maxhenkel.voicechat.api.packets.StaticSoundPacket;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface VoicechatServerApi extends VoicechatApi {
|
||||
|
||||
/**
|
||||
* Sends the sound packet to the provided connection.
|
||||
*
|
||||
* @param connection the connection to send the packet to
|
||||
* @param packet the packet to send
|
||||
*/
|
||||
void sendEntitySoundPacketTo(VoicechatConnection connection, EntitySoundPacket packet);
|
||||
|
||||
/**
|
||||
* Sends the sound packet to the provided connection.
|
||||
*
|
||||
* @param connection the connection to send the packet to
|
||||
* @param packet the packet to send
|
||||
*/
|
||||
void sendLocationalSoundPacketTo(VoicechatConnection connection, LocationalSoundPacket packet);
|
||||
|
||||
/**
|
||||
* Sends the sound packet to the provided connection.
|
||||
*
|
||||
* @param connection the connection to send the packet to
|
||||
* @param packet the packet to send
|
||||
*/
|
||||
void sendStaticSoundPacketTo(VoicechatConnection connection, StaticSoundPacket packet);
|
||||
|
||||
/**
|
||||
* Creates a sound packet that can be converted to any type.
|
||||
* <b>NOTE</b>: When creating a packet using this method, the sequence number must be set manually using {@link de.maxhenkel.voicechat.api.packets.SoundPacket.Builder#sequenceNumber(long)}.
|
||||
*
|
||||
* @return a convertable packet without any data
|
||||
*/
|
||||
ConvertablePacket createPacket();
|
||||
|
||||
/**
|
||||
* Creates a sound channel for the specified entity.
|
||||
*
|
||||
* @param channelId the ID of the channel - Has to be unique
|
||||
* @param entity the entity to attach the channel to
|
||||
* @return the channel
|
||||
*/
|
||||
@Nullable
|
||||
EntityAudioChannel createEntityAudioChannel(UUID channelId, Entity entity);
|
||||
|
||||
/**
|
||||
* Creates a sound channel at the provided location.
|
||||
*
|
||||
* @param channelId the ID of the channel - Has to be unique
|
||||
* @param level the world
|
||||
* @param initialPosition the position where the sound should be played
|
||||
* @return the channel
|
||||
*/
|
||||
@Nullable
|
||||
LocationalAudioChannel createLocationalAudioChannel(UUID channelId, ServerLevel level, Position initialPosition);
|
||||
|
||||
/**
|
||||
* Creates a static audio channel.
|
||||
*
|
||||
* @param channelId the ID of the channel - Has to be unique
|
||||
* @param level the level
|
||||
* @param connection the connection that should hear the audio
|
||||
* @return the channel
|
||||
* @deprecated use {@link #createStaticAudioChannel(UUID)} and add a target using {@link StaticAudioChannel#addTarget(VoicechatConnection)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
StaticAudioChannel createStaticAudioChannel(UUID channelId, ServerLevel level, VoicechatConnection connection);
|
||||
|
||||
/**
|
||||
* Creates a static audio channel.
|
||||
*
|
||||
* @param channelId the ID of the channel - Has to be unique
|
||||
* @return the channel
|
||||
*/
|
||||
@Nullable
|
||||
StaticAudioChannel createStaticAudioChannel(UUID channelId);
|
||||
|
||||
/**
|
||||
* Creates a new audio player.
|
||||
* <br/>
|
||||
* <br/>
|
||||
* <b>NOTE</b>: Never use more than one audio player for every audio channel.
|
||||
*
|
||||
* @param audioChannel the channel where the audio player should send the audio to
|
||||
* @param encoder the opus encoder used to encode the audio data
|
||||
* @param audioSupplier this gets called whenever a new audio frame needs to be sent. The size of the array always needs to be 960. To end the playback, return <code>null</code>
|
||||
* @return the audio player
|
||||
*/
|
||||
AudioPlayer createAudioPlayer(AudioChannel audioChannel, OpusEncoder encoder, Supplier<short[]> audioSupplier);
|
||||
|
||||
/**
|
||||
* Creates a new audio player.
|
||||
* <br/>
|
||||
* <br/>
|
||||
* <b>NOTE</b>: Never use more than one audio player for every audio channel.
|
||||
*
|
||||
* @param audioChannel the channel where the audio player should send the audio to
|
||||
* @param encoder the opus encoder used to encode the audio data
|
||||
* @param audio the audio data
|
||||
* @return the audio player
|
||||
*/
|
||||
AudioPlayer createAudioPlayer(AudioChannel audioChannel, OpusEncoder encoder, short[] audio);
|
||||
|
||||
/**
|
||||
* Creates a new audio sender.
|
||||
* <br/>
|
||||
* This can be used to simulate a player sending microphone packets.
|
||||
* This needs to be registered using {@link #registerAudioSender(AudioSender)} and unregistered using {@link #unregisterAudioSender(AudioSender)}.
|
||||
*
|
||||
* @param connection the connection of the player
|
||||
* @return the audio sender
|
||||
*/
|
||||
AudioSender createAudioSender(VoicechatConnection connection);
|
||||
|
||||
/**
|
||||
* <b>NOTE</b>: Only one instance of this can exist per player. This will return <code>false</code> if an audio sender for this player already exists.
|
||||
* <br/>
|
||||
* <b>NOTE</b>: The audio sender will only work for players that are connected to the server and don't have the mod installed. Otherwise, it will return <code>false</code>.
|
||||
*
|
||||
* @param sender the sender to register
|
||||
* @return if the sender was registered
|
||||
*/
|
||||
boolean registerAudioSender(AudioSender sender);
|
||||
|
||||
/**
|
||||
* Unregisters an audio sender.
|
||||
*
|
||||
* @param sender the sender to unregister
|
||||
* @return <code>false</code> if the audio sender was not registered.
|
||||
*/
|
||||
boolean unregisterAudioSender(AudioSender sender);
|
||||
|
||||
/**
|
||||
* @return a {@link PlayerAudioListener} builder
|
||||
*/
|
||||
PlayerAudioListener.Builder playerAudioListenerBuilder();
|
||||
|
||||
/**
|
||||
* Registers a new {@link AudioListener}.
|
||||
* <br/>
|
||||
* Returns false if the listener is already registered.
|
||||
*
|
||||
* @param listener the listener to register
|
||||
* @return if the listener was registered
|
||||
*/
|
||||
boolean registerAudioListener(AudioListener listener);
|
||||
|
||||
/**
|
||||
* Unregisters an {@link AudioListener}.
|
||||
* <br/>
|
||||
* Returns false if the listener is already unregistered.
|
||||
*
|
||||
* @param listener the listener to unregister
|
||||
* @return if the listener was unregistered
|
||||
*/
|
||||
boolean unregisterAudioListener(AudioListener listener);
|
||||
|
||||
/**
|
||||
* Unregisters an {@link AudioListener}.
|
||||
* <br/>
|
||||
* Returns false if the listener is already unregistered.
|
||||
*
|
||||
* @param listenerId the {@link AudioListener#getListenerId()} of the listener to unregister
|
||||
* @return if the listener was unregistered
|
||||
*/
|
||||
boolean unregisterAudioListener(UUID listenerId);
|
||||
|
||||
/**
|
||||
* Gets the connection of the player with this UUID.
|
||||
*
|
||||
* @param playerUuid the players UUID
|
||||
* @return the connection or <code>null</code> if the player is not connected
|
||||
*/
|
||||
@Nullable
|
||||
VoicechatConnection getConnectionOf(UUID playerUuid);
|
||||
|
||||
/**
|
||||
* Gets the connection of the player.
|
||||
*
|
||||
* @param player the player
|
||||
* @return the connection or <code>null</code> if the player is not connected
|
||||
*/
|
||||
@Nullable
|
||||
default VoicechatConnection getConnectionOf(ServerPlayer player) {
|
||||
return getConnectionOf(player.getUuid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new group.
|
||||
*
|
||||
* @param name the name of the group
|
||||
* @param password the password of the group - <code>null</code> for no password
|
||||
* @return the group
|
||||
* @deprecated use {@link #groupBuilder()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
Group createGroup(String name, @Nullable String password);
|
||||
|
||||
/**
|
||||
* Creates a new group.
|
||||
*
|
||||
* @param name the name of the group
|
||||
* @param password the password of the group - <code>null</code> for no password
|
||||
* @param persistent if the group should be persistent
|
||||
* @return the group
|
||||
* @deprecated use {@link #groupBuilder()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
Group createGroup(String name, @Nullable String password, boolean persistent);
|
||||
|
||||
/**
|
||||
* @return a new group builder
|
||||
*/
|
||||
Group.Builder groupBuilder();
|
||||
|
||||
/**
|
||||
* Removes a persistent group.
|
||||
*
|
||||
* <b>NOTE</b>: You can't remove a group that is not persistent or has players in it.
|
||||
*
|
||||
* @param groupId the ID of the group
|
||||
* @return if the group was removed
|
||||
*/
|
||||
boolean removeGroup(UUID groupId);
|
||||
|
||||
/**
|
||||
* Gets a group by its ID.
|
||||
*
|
||||
* @param groupId the ID of the group
|
||||
* @return the group or <code>null</code> if the group doesn't exist
|
||||
*/
|
||||
@Nullable
|
||||
Group getGroup(UUID groupId);
|
||||
|
||||
/**
|
||||
* @return all groups
|
||||
*/
|
||||
Collection<Group> getGroups();
|
||||
|
||||
/**
|
||||
* Gets the secret for the user with this <code>userId</code>.
|
||||
* Calling this function with a players UUID, that is already connected, this will return the secret of that player.
|
||||
* Calling it with a new random UUID this will return a new secret.
|
||||
* Use this with caution - Keep this secret confidential, as this allows connecting to the voice chat as this person.
|
||||
* Note that if this <code>userId</code> is a players UUID and this player disconnects, the secret will get removed and thus is no longer valid to connect with.
|
||||
*
|
||||
* @param userId the user ID
|
||||
* @return the secret or <code>null</code> if no server is running
|
||||
* @deprecated this will just return <code>null</code>
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
UUID getSecret(UUID userId);
|
||||
|
||||
/**
|
||||
* A convenience method to get all players in the range of a specific location.
|
||||
*
|
||||
* @param level the world
|
||||
* @param pos the location
|
||||
* @param range the range
|
||||
* @param filter the filter to exclude specific players
|
||||
* @return all players in the provided location
|
||||
*/
|
||||
Collection<ServerPlayer> getPlayersInRange(ServerLevel level, Position pos, double range, Predicate<ServerPlayer> filter);
|
||||
|
||||
/**
|
||||
* The distance at which audio packets are broadcast.
|
||||
* <b>NOTE</b>: This is not the distance it can be heard.
|
||||
*
|
||||
* @return the maximum distance, voice chat audio packets can be received
|
||||
*/
|
||||
double getBroadcastRange();
|
||||
|
||||
/**
|
||||
* A convenience method to get all players in the range of a specific location.
|
||||
*
|
||||
* @param level the world
|
||||
* @param pos the location
|
||||
* @param range the range
|
||||
* @return all players in the provided location
|
||||
*/
|
||||
default Collection<ServerPlayer> getPlayersInRange(ServerLevel level, Position pos, double range) {
|
||||
return getPlayersInRange(level, pos, range, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a volume category.
|
||||
* This will get synchronized to all connected clients and all future clients that will connect.
|
||||
* A category can be created with {@link VoicechatApi#volumeCategoryBuilder()}.
|
||||
* The category can be unregistered with {@link #unregisterVolumeCategory}.
|
||||
*
|
||||
* @param category the category to register
|
||||
*/
|
||||
void registerVolumeCategory(VolumeCategory category);
|
||||
|
||||
/**
|
||||
* Unregisters a category for all connected players.
|
||||
* This will release the texture ID for the icon if one exists on all clients.
|
||||
*
|
||||
* @param category the category to remove
|
||||
*/
|
||||
default void unregisterVolumeCategory(VolumeCategory category) {
|
||||
unregisterVolumeCategory(category.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a category for all connected players.
|
||||
* This will release the texture ID for the icon if one exists on all clients.
|
||||
*
|
||||
* @param categoryId the category ID to remove
|
||||
*/
|
||||
void unregisterVolumeCategory(String categoryId);
|
||||
|
||||
/**
|
||||
* @return all registered volume categories
|
||||
*/
|
||||
Collection<VolumeCategory> getVolumeCategories();
|
||||
|
||||
/**
|
||||
* @return a read-only config accessor for the mods server config
|
||||
*/
|
||||
ConfigAccessor getServerConfig();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
/**
|
||||
* A socket used for server side voice chat traffic.
|
||||
* Can be set using {@link de.maxhenkel.voicechat.api.events.VoicechatServerStartingEvent#setSocketImplementation(VoicechatSocket)}.
|
||||
*/
|
||||
public interface VoicechatSocket {
|
||||
|
||||
void open(int port, String bindAddress) throws Exception;
|
||||
|
||||
RawUdpPacket read() throws Exception;
|
||||
|
||||
void send(byte[] data, SocketAddress address) throws Exception;
|
||||
|
||||
int getLocalPort();
|
||||
|
||||
void close();
|
||||
|
||||
boolean isClosed();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package de.maxhenkel.voicechat.api;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A custom volume category for the "adjust volumes screen".
|
||||
* Create it with {@link VoicechatApi#volumeCategoryBuilder()}.
|
||||
* Register it with {@link VoicechatServerApi#registerVolumeCategory(VolumeCategory)} or {@link VoicechatClientApi#registerClientVolumeCategory(VolumeCategory)}.
|
||||
* Unregister it with {@link VoicechatServerApi#unregisterVolumeCategory} or {@link VoicechatClientApi#unregisterClientVolumeCategory}.
|
||||
*/
|
||||
public interface VolumeCategory {
|
||||
|
||||
/**
|
||||
* @return the unique ID of the category
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* @return the full name of the category, that is displayed for the end user
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* @return the translation key for the full name of the category
|
||||
*/
|
||||
@Nullable
|
||||
String getNameTranslationKey();
|
||||
|
||||
/**
|
||||
* @return the hover tooltip that is shown for this category
|
||||
*/
|
||||
@Nullable
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* @return the translation key for the hover tooltip of the category
|
||||
*/
|
||||
@Nullable
|
||||
String getDescriptionTranslationKey();
|
||||
|
||||
/**
|
||||
* @return the icon that is shown for this category
|
||||
*/
|
||||
@Nullable
|
||||
int[][] getIcon();
|
||||
|
||||
public interface Builder {
|
||||
|
||||
/**
|
||||
* This ID has to be between 1 and 16 characters and can only contain lowercase <code>a-z</code> and <code>_</code>.
|
||||
*
|
||||
* @param id the unique ID of the category
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setId(String id);
|
||||
|
||||
/**
|
||||
* @param name the full name of the category, that is displayed for the end user if no translation is present
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setName(String name);
|
||||
|
||||
/**
|
||||
* @param translationKey the translation key for the full name of the category
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setNameTranslationKey(@Nullable String translationKey);
|
||||
|
||||
/**
|
||||
* @param description the hover tooltip that is shown for this category if no translation is present
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setDescription(@Nullable String description);
|
||||
|
||||
/**
|
||||
* @param translationKey the translation key for the hover tooltip
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setDescriptionTranslationKey(@Nullable String translationKey);
|
||||
|
||||
/**
|
||||
* The array has to be 16x16.
|
||||
* Each integer represents a pixel in the RGBA format.
|
||||
*
|
||||
* @param icon the icon that is shown for this category
|
||||
* @return the builder
|
||||
*/
|
||||
Builder setIcon(@Nullable int[][] icon);
|
||||
|
||||
/**
|
||||
* @return the built category
|
||||
*/
|
||||
VolumeCategory build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.maxhenkel.voicechat.api.audio;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatApi;
|
||||
|
||||
/**
|
||||
* A utility class to convert audio between different representations.
|
||||
* <br/>
|
||||
* Can be obtained by calling {@link VoicechatApi#getAudioConverter()}.
|
||||
* <br/>
|
||||
* <br/>
|
||||
* <b>NOTE</b>: Everything is assumed to be 16 bit PCM audio.
|
||||
*/
|
||||
public interface AudioConverter {
|
||||
|
||||
short[] bytesToShorts(byte[] bytes);
|
||||
|
||||
byte[] shortsToBytes(short[] shorts);
|
||||
|
||||
short[] floatsToShorts(float[] floats);
|
||||
|
||||
float[] shortsToFloats(short[] shorts);
|
||||
|
||||
byte[] floatsToBytes(float[] floats);
|
||||
|
||||
float[] bytesToFloats(byte[] bytes);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Everything related to audio conversion and manipulation.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.audio;
|
||||
@@ -0,0 +1,65 @@
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
|
||||
import de.maxhenkel.voicechat.api.ServerPlayer;
|
||||
import de.maxhenkel.voicechat.api.VolumeCategory;
|
||||
import de.maxhenkel.voicechat.api.packets.MicrophonePacket;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface AudioChannel {
|
||||
|
||||
/**
|
||||
* Sends the audio data to this audio channel.
|
||||
*
|
||||
* @param opusData Opus encoded audio data
|
||||
*/
|
||||
void send(byte[] opusData);
|
||||
|
||||
/**
|
||||
* Forwards the provided microphone packet to this audio channel.
|
||||
*
|
||||
* @param packet the microphone packet containing the audio data
|
||||
*/
|
||||
void send(MicrophonePacket packet);
|
||||
|
||||
/**
|
||||
* Applies a filter to the audio channel.
|
||||
*
|
||||
* @param filter only sends the data to players where the filter applies
|
||||
*/
|
||||
void setFilter(Predicate<ServerPlayer> filter);
|
||||
|
||||
/**
|
||||
* Call this if you are finished sending data.
|
||||
* You can still use this audio channel after calling this function,
|
||||
* just don't call it after every packet, only after you are finished sending data.
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* @return if the audio channel is closed
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* @return the ID of the audio channel
|
||||
*/
|
||||
UUID getId();
|
||||
|
||||
/**
|
||||
* @return the category ID of the audio channel
|
||||
*/
|
||||
@Nullable
|
||||
String getCategory();
|
||||
|
||||
/**
|
||||
* Make sure you registered your category before using it.
|
||||
* See {@link de.maxhenkel.voicechat.api.VoicechatServerApi#registerVolumeCategory(VolumeCategory)}.
|
||||
*
|
||||
* @param category the category ID of the audio channel
|
||||
*/
|
||||
void setCategory(@Nullable String category);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
|
||||
/**
|
||||
* Streams audio data from the server to clients.
|
||||
* A player instance can be obtained by calling {@link de.maxhenkel.voicechat.api.VoicechatServerApi#createAudioPlayer}.
|
||||
*/
|
||||
public interface AudioPlayer {
|
||||
|
||||
/**
|
||||
* Starts playing/streaming the audio.
|
||||
*/
|
||||
void startPlaying();
|
||||
|
||||
/**
|
||||
* Stops playing/streaming the audio.
|
||||
*/
|
||||
void stopPlaying();
|
||||
|
||||
/**
|
||||
* @return if the player has been started
|
||||
*/
|
||||
boolean isStarted();
|
||||
|
||||
/**
|
||||
* @return if the audio is still playing
|
||||
*/
|
||||
boolean isPlaying();
|
||||
|
||||
/**
|
||||
* @return if the player stopped playing
|
||||
*/
|
||||
boolean isStopped();
|
||||
|
||||
/**
|
||||
* @param onStopped a runnable that's called when the player is finished playing
|
||||
*/
|
||||
void setOnStopped(Runnable onStopped);
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VolumeCategory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ClientAudioChannel {
|
||||
|
||||
/**
|
||||
* @return the ID of the channel
|
||||
*/
|
||||
UUID getId();
|
||||
|
||||
/**
|
||||
* Plays this audio data on this audio channel.
|
||||
* <br>
|
||||
* Providing audio that's not 960 samples will be ignored.
|
||||
*
|
||||
* @param rawAudio the raw 16 bit PCM audio frame
|
||||
*/
|
||||
void play(short[] rawAudio);
|
||||
|
||||
/**
|
||||
* @return the category ID of the audio channel
|
||||
*/
|
||||
@Nullable
|
||||
String getCategory();
|
||||
|
||||
/**
|
||||
* Make sure you registered your category before using it.
|
||||
* See {@link de.maxhenkel.voicechat.api.VoicechatServerApi#registerVolumeCategory(VolumeCategory)}.
|
||||
*
|
||||
* @param category the category of the audio channel
|
||||
*/
|
||||
void setCategory(@Nullable String category);
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ClientEntityAudioChannel extends ClientAudioChannel {
|
||||
|
||||
/**
|
||||
* @return the UUID of the entity
|
||||
*/
|
||||
UUID getEntityId();
|
||||
|
||||
/**
|
||||
* @param whispering if the entity should whisper
|
||||
*/
|
||||
void setWhispering(boolean whispering);
|
||||
|
||||
/**
|
||||
* @return if the entity is whispering
|
||||
*/
|
||||
boolean isWhispering();
|
||||
|
||||
/**
|
||||
* @return the distance, the audio can be heard
|
||||
*/
|
||||
float getDistance();
|
||||
|
||||
/**
|
||||
* @param distance the distance, the audio can be heard
|
||||
*/
|
||||
void setDistance(float distance);
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Position;
|
||||
|
||||
public interface ClientLocationalAudioChannel extends ClientAudioChannel {
|
||||
|
||||
/**
|
||||
* @return the current location of the audio channel
|
||||
*/
|
||||
Position getLocation();
|
||||
|
||||
/**
|
||||
* Updates the location of the audio channel.
|
||||
*
|
||||
* @param position the location of the audio channel
|
||||
*/
|
||||
void setLocation(Position position);
|
||||
|
||||
/**
|
||||
* @return the distance, the audio can be heard
|
||||
*/
|
||||
float getDistance();
|
||||
|
||||
/**
|
||||
* @param distance the distance, the audio can be heard
|
||||
*/
|
||||
void setDistance(float distance);
|
||||
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
|
||||
public interface ClientStaticAudioChannel extends ClientAudioChannel {
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Entity;
|
||||
|
||||
/**
|
||||
* An audio channel that is bound to an entity.
|
||||
* <b>NOTE</b>: If you are using this for a player, you need to use {@link #updateEntity(Entity)} to update the player after it died.
|
||||
*/
|
||||
public interface EntityAudioChannel extends AudioChannel {
|
||||
|
||||
/**
|
||||
* @return if the entity is whispering
|
||||
*/
|
||||
boolean isWhispering();
|
||||
|
||||
/**
|
||||
* @param whispering if the entity should whisper
|
||||
*/
|
||||
void setWhispering(boolean whispering);
|
||||
|
||||
/**
|
||||
* Sets a new entity where this channel is attached to.
|
||||
*
|
||||
* @param entity the entity to attach the channel to
|
||||
*/
|
||||
void updateEntity(Entity entity);
|
||||
|
||||
/**
|
||||
* @return the entity where the channel is attached to
|
||||
*/
|
||||
Entity getEntity();
|
||||
|
||||
/**
|
||||
* @return the distance, the audio can be heard
|
||||
*/
|
||||
float getDistance();
|
||||
|
||||
/**
|
||||
* @param distance the distance, the audio can be heard
|
||||
*/
|
||||
void setDistance(float distance);
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Position;
|
||||
|
||||
public interface LocationalAudioChannel extends AudioChannel {
|
||||
|
||||
/**
|
||||
* Updates the location of the audio.
|
||||
*
|
||||
* @param position the audio location
|
||||
*/
|
||||
void updateLocation(Position position);
|
||||
|
||||
/**
|
||||
* @return the current location of this channel
|
||||
*/
|
||||
Position getLocation();
|
||||
|
||||
/**
|
||||
* @return the distance, the audio can be heard
|
||||
*/
|
||||
float getDistance();
|
||||
|
||||
/**
|
||||
* @param distance the distance, the audio can be heard
|
||||
*/
|
||||
void setDistance(float distance);
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
|
||||
public interface StaticAudioChannel extends AudioChannel {
|
||||
|
||||
/**
|
||||
* Sets whether this audio channel should be received by targets in isolated groups.
|
||||
*
|
||||
* @param bypassGroupIsolation if group isolation should be bypassed
|
||||
*/
|
||||
void setBypassGroupIsolation(boolean bypassGroupIsolation);
|
||||
|
||||
/**
|
||||
* @return if this audio channel is received by targets in isolated groups
|
||||
*/
|
||||
boolean bypassesGroupIsolation();
|
||||
|
||||
/**
|
||||
* Adds a target to the audio channel.
|
||||
*
|
||||
* @param target the target to add
|
||||
*/
|
||||
void addTarget(VoicechatConnection target);
|
||||
|
||||
/**
|
||||
* Removes a target from the audio channel.
|
||||
*
|
||||
* @param target the target to remove
|
||||
*/
|
||||
void removeTarget(VoicechatConnection target);
|
||||
|
||||
/**
|
||||
* Removes all targets from the audio channel.
|
||||
*/
|
||||
void clearTargets();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Everything related to audio channels and serverside audio.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.audiochannel;
|
||||
+6
-5
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.audiolistener;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface AudioListener {
|
||||
public UUID getListenerId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the ID of the listener
|
||||
*/
|
||||
UUID getListenerId();
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package de.maxhenkel.voicechat.api.audiolistener;
|
||||
|
||||
import de.maxhenkel.voicechat.api.ServerPlayer;
|
||||
import de.maxhenkel.voicechat.api.packets.SoundPacket;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface PlayerAudioListener extends AudioListener {
|
||||
|
||||
/**
|
||||
* @return the {@link UUID} of the player that is listened to
|
||||
*/
|
||||
UUID getPlayerUuid();
|
||||
|
||||
public interface Builder {
|
||||
|
||||
/**
|
||||
* <b>Note</b>: It is required to either set a player with this method or with {@link #setPlayer(UUID)}
|
||||
*
|
||||
* @param player the player that should be listened to
|
||||
*/
|
||||
Builder setPlayer(ServerPlayer player);
|
||||
|
||||
/**
|
||||
* <b>Note</b>: It is required to either set a player with this method or with {@link #setPlayer(ServerPlayer)}
|
||||
*
|
||||
* @param playerUuid the player UUID that should be listened to
|
||||
*/
|
||||
Builder setPlayer(UUID playerUuid);
|
||||
|
||||
/**
|
||||
* <b>Note</b>: It is required to set a listener
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
Builder setPacketListener(Consumer<SoundPacket> listener);
|
||||
|
||||
/**
|
||||
* @return the built listener
|
||||
* @throws IllegalStateException if the player or the listener is not set
|
||||
*/
|
||||
PlayerAudioListener build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Utilities for listening to all audio a player would theoretically receive.
|
||||
* <p>
|
||||
* Can be obtained by calling {@link de.maxhenkel.voicechat.api.VoicechatServerApi#playerAudioListenerBuilder()}.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.audiolistener;
|
||||
@@ -0,0 +1,52 @@
|
||||
package de.maxhenkel.voicechat.api.audiosender;
|
||||
|
||||
public interface AudioSender {
|
||||
|
||||
/**
|
||||
* @param whispering if the player should whisper
|
||||
* @return the audio sender itself
|
||||
*/
|
||||
AudioSender whispering(boolean whispering);
|
||||
|
||||
/**
|
||||
* @return if the player is whispering
|
||||
*/
|
||||
boolean isWhispering();
|
||||
|
||||
/**
|
||||
* Sets the sequence number of the packet.
|
||||
* <br/>
|
||||
* Setting this will override the automatic sequence numbering.
|
||||
* <br/>
|
||||
* If you set this value, consequent calls to {@link AudioSender#send(byte[])} will increase this number by 1.
|
||||
* If you don't intend this, you need to set the sequence number every time before calling {@link AudioSender#send(byte[])}.
|
||||
* <br/>
|
||||
* Calling {@link AudioSender#reset()} will also reset the sequence number to start with <code>0</code> again.
|
||||
*
|
||||
* @param sequenceNumber the sequence number (Must be >= 0)
|
||||
* @return the audio sender itself
|
||||
*/
|
||||
AudioSender sequenceNumber(long sequenceNumber);
|
||||
|
||||
/**
|
||||
* @return if a microphone packet can be sent. This will return false if the player has the mod installed or the {@link AudioSender} is not registered.
|
||||
*/
|
||||
boolean canSend();
|
||||
|
||||
/**
|
||||
* Acts as if the player has sent a microphone packet.
|
||||
*
|
||||
* @param opusEncodedAudioData the opus encoded audio data
|
||||
* @return <code>true</code> if the packet was sent, <code>false</code> if the sender does have the mod installed or the {@link AudioSender} is not registered
|
||||
*/
|
||||
boolean send(byte[] opusEncodedAudioData);
|
||||
|
||||
/**
|
||||
* Resets the sequence number and indicates to clients that the current audio stream is paused/stopped.
|
||||
* <br/>
|
||||
* This method should be called when the sending of audio is paused or stopped.
|
||||
* You can still send audio after this method is called.
|
||||
*/
|
||||
boolean reset();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Utilities for simulating players sending audio without the mod.
|
||||
* <p>
|
||||
* Can be obtained by calling {@link de.maxhenkel.voicechat.api.VoicechatServerApi#createAudioSender(de.maxhenkel.voicechat.api.VoicechatConnection)}.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.audiosender;
|
||||
@@ -0,0 +1,100 @@
|
||||
package de.maxhenkel.voicechat.api.config;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatClientApi;
|
||||
import de.maxhenkel.voicechat.api.VoicechatServerApi;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An interface to access a config.
|
||||
* This is kept very generic, so it can be used for any config type.
|
||||
* <br/>
|
||||
* This contains a few default methods to get primitive values from the config.
|
||||
* <br/>
|
||||
* <b>NOTE</b>: You need to implement more complex values yourself (e.g. enums).
|
||||
* <br/>
|
||||
* Can be obtained by calling {@link VoicechatServerApi#getServerConfig()} or {@link VoicechatClientApi#getClientConfig()}.
|
||||
*/
|
||||
public interface ConfigAccessor {
|
||||
|
||||
/**
|
||||
* Checks if the config has the given key.
|
||||
*
|
||||
* @param key the key
|
||||
* @return <code>true</code> if the config has the key, <code>false</code> otherwise
|
||||
*/
|
||||
boolean hasKey(String key);
|
||||
|
||||
/**
|
||||
* Gets the raw value of the given key.
|
||||
* <b>NOTE</b>: This gets the <i>raw</i> value from the config, so it will be a {@link String} even if the value is a number, a boolean or an enum.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the value or <code>null</code> if the key doesn't exist
|
||||
*/
|
||||
@Nullable
|
||||
String getValue(String key);
|
||||
|
||||
/**
|
||||
* Gets the string value of the given key.
|
||||
*
|
||||
* @param key the key
|
||||
* @param def the default value
|
||||
* @return the value or the default value if the key doesn't exist
|
||||
*/
|
||||
default String getString(String key, String def) {
|
||||
String value = getValue(key);
|
||||
if (value == null) {
|
||||
return def;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the boolean value of the given key.
|
||||
*
|
||||
* @param key the key
|
||||
* @param def the default value
|
||||
* @return the boolean value, <code>false</code> if the value is not <code>"true"</code> or the default value if the key doesn't exist
|
||||
*/
|
||||
default boolean getBoolean(String key, boolean def) {
|
||||
String value = getValue(key);
|
||||
if (value == null) {
|
||||
return def;
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the integer value of the given key.
|
||||
*
|
||||
* @param key the key
|
||||
* @param def the default value
|
||||
* @return the value or the default value if the key doesn't exist
|
||||
* @throws NumberFormatException if the value is not an integer
|
||||
*/
|
||||
default int getInt(String key, int def) {
|
||||
String value = getValue(key);
|
||||
if (value == null) {
|
||||
return def;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the double value of the given key.
|
||||
*
|
||||
* @param key the key
|
||||
* @param def the default value
|
||||
* @return the value or the default value if the key doesn't exist
|
||||
* @throws NumberFormatException if the value is not a double
|
||||
*/
|
||||
default double getDouble(String key, double def) {
|
||||
String value = getValue(key);
|
||||
if (value == null) {
|
||||
return def;
|
||||
}
|
||||
return Double.parseDouble(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package de.maxhenkel.voicechat.api.config;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatClientApi;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* An interface to access client side adjusted volumes.
|
||||
* <br/>
|
||||
* Can be obtained by calling {@link VoicechatClientApi#getVolumeConfig()}.
|
||||
*/
|
||||
public interface VolumeConfigAccessor {
|
||||
|
||||
/**
|
||||
* Gets the volume of a player by its UUID.
|
||||
* <br/>
|
||||
* <br/>
|
||||
* This will always return a value, even if the player is not in the volume config.
|
||||
* <br/>
|
||||
* By default, the volume will be <code>1.0</code>.
|
||||
* A volume of <code>0.0</code> means this player is muted.
|
||||
* Can be larger than <code>1.0</code>.
|
||||
* Also works for players that are not online.
|
||||
* <br/>
|
||||
* <br/>
|
||||
* If you want the <code>Other</code> volume (All volumes that are not covered by categories or players), use the UUID <code>00000000-0000-0000-0000-000000000000</code>.
|
||||
*
|
||||
* @param uuid the UUID of the player
|
||||
* @return the volume of the player
|
||||
* @throws NullPointerException if the UUID is null
|
||||
*/
|
||||
double getVolume(UUID uuid);
|
||||
|
||||
/**
|
||||
* Gets the volume of a category by its UUID.
|
||||
* <br/>
|
||||
* <br/>
|
||||
* This will always return a value, even if the category doesn't exist.
|
||||
* <br/>
|
||||
* By default, the volume will be <code>1.0</code>.
|
||||
* A volume of <code>0.0</code> means this category is muted.
|
||||
* Can be larger than <code>1.0</code>.
|
||||
* Also works for categories that are currently not registered.
|
||||
*
|
||||
* @param category the category
|
||||
* @return the volume of the category
|
||||
* @throws NullPointerException if the category is null
|
||||
*/
|
||||
double getCategoryVolume(String category);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Everything configuration related.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.config;
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatClientApi;
|
||||
|
||||
public interface ClientEvent extends Event {
|
||||
|
||||
/**
|
||||
* @return the voice chat client API
|
||||
*/
|
||||
VoicechatClientApi getVoicechat();
|
||||
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Position;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This event is emitted before the sound is played on the client.
|
||||
*/
|
||||
public interface ClientReceiveSoundEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* @return the ID of the channel
|
||||
*/
|
||||
UUID getId();
|
||||
|
||||
/**
|
||||
* The unencoded audio data.
|
||||
* <br/>
|
||||
* Returns an empty array in case of the end of transmission
|
||||
*
|
||||
* @return the raw 16 bit PCM audio frame
|
||||
*/
|
||||
short[] getRawAudio();
|
||||
|
||||
/**
|
||||
* Overrides the actual audio data that is played.
|
||||
* <p>
|
||||
* This is ignored, if the {@link #getRawAudio} array is empty
|
||||
* <br>
|
||||
* Providing audio that's not 960 samples will be ignored.
|
||||
* An empty array skips this frame, which adds latency. To mute the audio, use an array filled with zeros instead.
|
||||
*
|
||||
* @param rawAudio the raw 16 bit PCM audio frame
|
||||
*/
|
||||
void setRawAudio(@Nullable short[] rawAudio);
|
||||
|
||||
public static interface EntitySound extends ClientReceiveSoundEvent {
|
||||
|
||||
/**
|
||||
* @return the UUID of the entity
|
||||
*/
|
||||
UUID getEntityId();
|
||||
|
||||
/**
|
||||
* @return if the player is whispering
|
||||
*/
|
||||
boolean isWhispering();
|
||||
|
||||
/**
|
||||
* @return the distance, the audio can be heard
|
||||
*/
|
||||
float getDistance();
|
||||
}
|
||||
|
||||
public static interface LocationalSound extends ClientReceiveSoundEvent {
|
||||
/**
|
||||
* @return the position of the sound
|
||||
*/
|
||||
Position getPosition();
|
||||
|
||||
/**
|
||||
* @return the distance, the audio can be heard
|
||||
*/
|
||||
float getDistance();
|
||||
}
|
||||
|
||||
public static interface StaticSound extends ClientReceiveSoundEvent {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
/**
|
||||
* This event is emitted before the client encodes the audio and sends it to the server.
|
||||
*/
|
||||
public interface ClientSoundEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* The unencoded audio data.
|
||||
* <br/>
|
||||
* <s>Returns an empty array in case of the end of transmission</s>
|
||||
*
|
||||
* @return the raw 16 bit PCM audio frame
|
||||
*/
|
||||
short[] getRawAudio();
|
||||
|
||||
/**
|
||||
* Overrides the actual audio data that's sent to the server.
|
||||
*
|
||||
* @param rawAudio the raw 16 bit PCM audio frame
|
||||
*/
|
||||
void setRawAudio(short[] rawAudio);
|
||||
|
||||
/**
|
||||
* @return if the player is whispering
|
||||
*/
|
||||
boolean isWhispering();
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
/**
|
||||
* This event is emitted on the client when the voice chat connects/disconnects.
|
||||
*/
|
||||
public interface ClientVoicechatConnectionEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* @return if the voice chat is connected to the server
|
||||
*/
|
||||
boolean isConnected();
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.ClientVoicechatSocket;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface ClientVoicechatInitializationEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* Sets a custom implementation of the socket used for client side voice chat traffic.
|
||||
*
|
||||
* @param socket the custom socket implementation
|
||||
*/
|
||||
void setSocketImplementation(ClientVoicechatSocket socket);
|
||||
|
||||
/**
|
||||
* @return the custom socket implementation or <code>null</code> to use voice chats default one
|
||||
*/
|
||||
@Nullable
|
||||
ClientVoicechatSocket getSocketImplementation();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Group;
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface CreateGroupEvent extends GroupEvent {
|
||||
|
||||
/**
|
||||
* @return the group that was created
|
||||
*/
|
||||
Group getGroup();
|
||||
|
||||
/**
|
||||
* @return the connection of the player that created the group or <code>null</code> if the group was not created by a player
|
||||
*/
|
||||
@Nullable
|
||||
VoicechatConnection getConnection();
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
/**
|
||||
* This event is emitted when the voice chats OpenAL context is created.
|
||||
*/
|
||||
public interface CreateOpenALContextEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* @return the OpenAL context
|
||||
*/
|
||||
long getContext();
|
||||
|
||||
/**
|
||||
* @return the OpenAL device
|
||||
*/
|
||||
long getDevice();
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
/**
|
||||
* This event is emitted when the voice chats OpenAL context is destroyed.
|
||||
*/
|
||||
public interface DestroyOpenALContextEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* @return the OpenAL context - Might be <code>0L</code> in some cases
|
||||
*/
|
||||
long getContext();
|
||||
|
||||
/**
|
||||
* @return the OpenAL device - Might be <code>0L</code> in some cases
|
||||
*/
|
||||
long getDevice();
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.packets.EntitySoundPacket;
|
||||
|
||||
/**
|
||||
* This event is emitted when an entity sound packet is about to get sent to a client.
|
||||
*/
|
||||
public interface EntitySoundPacketEvent extends SoundPacketEvent<EntitySoundPacket> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
public interface Event {
|
||||
|
||||
/**
|
||||
* @return if this event can be cancelled
|
||||
*/
|
||||
boolean isCancellable();
|
||||
|
||||
/**
|
||||
* Cancels this event.
|
||||
* Does nothing if the event isn't cancellable.
|
||||
*
|
||||
* @return if the event was actually cancelled
|
||||
*/
|
||||
boolean cancel();
|
||||
|
||||
/**
|
||||
* @return if the event was cancelled
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface EventRegistration {
|
||||
|
||||
/**
|
||||
* Registers an event.
|
||||
*
|
||||
* @param eventClass the class of the event you want to receive
|
||||
* @param onPacket the consumer that is called when the event was dispatched
|
||||
* @param priority the event priority - Higher values mean a higher priority
|
||||
* @param <T> the event type
|
||||
*/
|
||||
<T extends Event> void registerEvent(Class<T> eventClass, Consumer<T> onPacket, int priority);
|
||||
|
||||
/**
|
||||
* Registers an event.
|
||||
*
|
||||
* @param eventClass the class of the event you want to receive
|
||||
* @param onPacket the consumer that is called when the event was dispatched
|
||||
* @param <T> the event type
|
||||
*/
|
||||
default <T extends Event> void registerEvent(Class<T> eventClass, Consumer<T> onPacket) {
|
||||
registerEvent(eventClass, onPacket, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Group;
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface GroupEvent extends ServerEvent {
|
||||
|
||||
/**
|
||||
* @return the group - <code>null</code> if there is no group
|
||||
*/
|
||||
@Nullable
|
||||
Group getGroup();
|
||||
|
||||
/**
|
||||
* @return the connection of the player
|
||||
*/
|
||||
@Nullable
|
||||
VoicechatConnection getConnection();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Group;
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
|
||||
public interface JoinGroupEvent extends GroupEvent {
|
||||
|
||||
/**
|
||||
* @return the group that was joined
|
||||
*/
|
||||
Group getGroup();
|
||||
|
||||
/**
|
||||
* @return the connection of the player
|
||||
*/
|
||||
VoicechatConnection getConnection();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Group;
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface LeaveGroupEvent extends GroupEvent {
|
||||
|
||||
/**
|
||||
* @return the group that was left or <code>null</code> if the player was not in a group
|
||||
*/
|
||||
@Nullable
|
||||
Group getGroup();
|
||||
|
||||
/**
|
||||
* @return the connection of the player
|
||||
*/
|
||||
VoicechatConnection getConnection();
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket;
|
||||
|
||||
/**
|
||||
* This event is emitted when a locational sound packet is about to get sent to a client.
|
||||
*/
|
||||
public interface LocationalSoundPacketEvent extends SoundPacketEvent<LocationalSoundPacket> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
/**
|
||||
* This event is emitted before the {@link ClientSoundEvent} is getting called.
|
||||
* It allows you to merge audio into the audio that is captured from the microphone.
|
||||
* <br/>
|
||||
* This is called even when the player is muted or not talking.
|
||||
* <br/>
|
||||
* When the voice chat it disabled, this event is not called.
|
||||
*/
|
||||
public interface MergeClientSoundEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* Merges the audio into the audio that is captured from the microphone.
|
||||
* <br/>
|
||||
* Calling this multiple times will merge the audio multiple times into the same audio frame.
|
||||
*
|
||||
* @param audio the audio that should get merged
|
||||
*/
|
||||
void mergeAudio(short[] audio);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
/**
|
||||
* This event is emitted on the client when the state of the microphone changes.
|
||||
*/
|
||||
public interface MicrophoneMuteEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* @return if the microphone is muted
|
||||
*/
|
||||
boolean isDisabled();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.packets.MicrophonePacket;
|
||||
|
||||
/**
|
||||
* This event is emitted when a microphone packet arrives at the server.
|
||||
*/
|
||||
public interface MicrophonePacketEvent extends PacketEvent<MicrophonePacket> {
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatClientApi;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This event is emitted when an icon is getting rendered.
|
||||
* <br/>
|
||||
* This event should not be used to do any actual rendering.
|
||||
* Just use this event to cancel the rendering of the icon.
|
||||
* <br/>
|
||||
* <b>Warning: </b> Don't do heavy operations in this event, as it will block the render thread!
|
||||
* <br/>
|
||||
* <br/>
|
||||
* <b>Note: </b> Don't keep references to this event. It will be reused for every icon render to avoid allocations.
|
||||
* <br/>
|
||||
* <br/>
|
||||
* Use {@link VoicechatClientApi#isTalking(UUID)}, {@link VoicechatClientApi#isWhispering(UUID)},
|
||||
* {@link VoicechatClientApi#isDisabled(UUID)} and {@link VoicechatClientApi#isDisconnected(UUID)}
|
||||
* to get the status of the player and render it using your mod loaders render event system.
|
||||
*/
|
||||
public interface NameTagIconRenderEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* @return the UUID of the entity the icon is being rendered for
|
||||
*/
|
||||
UUID getEntityId();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Position;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This event is emitted for every audio chunk for every audio channel.
|
||||
* These events are meant to be used to modify OpenAL effects.
|
||||
* <br/>
|
||||
* This specific event is emitted after the position of the sound is set, but before the any other OpenAL calls.
|
||||
* Use {@link Pre} or {@link Post} get earlier or later events respectively.
|
||||
*/
|
||||
public interface OpenALSoundEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* Returns the position of the sound.
|
||||
* This returns <code>null</code> for static sounds.
|
||||
*
|
||||
* @return the position of the sound
|
||||
*/
|
||||
@Nullable
|
||||
Position getPosition();
|
||||
|
||||
/**
|
||||
* This returns <code>null</code> for non audio channel sounds, like microphone testing.
|
||||
*
|
||||
* @return the unique ID of the audio channel
|
||||
*/
|
||||
@Nullable
|
||||
UUID getChannelId();
|
||||
|
||||
/**
|
||||
* @return the OpenAL source
|
||||
*/
|
||||
int getSource();
|
||||
|
||||
/**
|
||||
* @return the category of the sound
|
||||
*/
|
||||
@Nullable
|
||||
String getCategory();
|
||||
|
||||
/**
|
||||
* This event is emitted before any OpenAL calls were made by the voice chat.
|
||||
*/
|
||||
public interface Pre extends OpenALSoundEvent {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is emitted after all OpenAL calls by the voice chat and the audio is added to the buffer queue.
|
||||
*/
|
||||
public interface Post extends OpenALSoundEvent {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
import de.maxhenkel.voicechat.api.packets.Packet;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface PacketEvent<T extends Packet> extends ServerEvent {
|
||||
|
||||
/**
|
||||
* @return the packet
|
||||
*/
|
||||
T getPacket();
|
||||
|
||||
/**
|
||||
* @return the connection of the player that should receive this packet
|
||||
*/
|
||||
@Nullable
|
||||
VoicechatConnection getReceiverConnection();
|
||||
|
||||
/**
|
||||
* @return the connection of the player that sent this packet or <code>null</code> if it wasn't sent by a player
|
||||
*/
|
||||
@Nullable
|
||||
VoicechatConnection getSenderConnection();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
|
||||
public interface PlayerConnectedEvent extends ServerEvent {
|
||||
|
||||
/**
|
||||
* @return the connection of the player
|
||||
*/
|
||||
VoicechatConnection getConnection();
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PlayerDisconnectedEvent extends ServerEvent {
|
||||
|
||||
/**
|
||||
* @return the UUID of the player
|
||||
*/
|
||||
UUID getPlayerUuid();
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Called when a player changes its state.
|
||||
* For example if voice chat connects/disconnects, the player joins/leaves a group or disables voice chat.
|
||||
*/
|
||||
public interface PlayerStateChangedEvent extends ServerEvent {
|
||||
|
||||
/**
|
||||
* @return if the player disabled voice chat
|
||||
*/
|
||||
boolean isDisabled();
|
||||
|
||||
/**
|
||||
* @return if the player is disconnected from voice chat
|
||||
*/
|
||||
boolean isDisconnected();
|
||||
|
||||
/**
|
||||
* @return the uuid of the player
|
||||
*/
|
||||
UUID getPlayerUuid();
|
||||
|
||||
/**
|
||||
* @return the connection of the player
|
||||
*/
|
||||
@Nullable
|
||||
VoicechatConnection getConnection();
|
||||
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
public interface RegisterVolumeCategoryEvent extends VolumeCategoryEvent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Group;
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This event is only cancellable if the group is persistent
|
||||
*/
|
||||
public interface RemoveGroupEvent extends GroupEvent {
|
||||
|
||||
/**
|
||||
* @return the group that was removed
|
||||
*/
|
||||
Group getGroup();
|
||||
|
||||
/**
|
||||
* @return <code>null</code>
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
VoicechatConnection getConnection();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatServerApi;
|
||||
|
||||
public interface ServerEvent extends Event {
|
||||
|
||||
/**
|
||||
* @return the voice chat server API
|
||||
*/
|
||||
VoicechatServerApi getVoicechat();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.packets.Packet;
|
||||
|
||||
public interface SoundPacketEvent<T extends Packet> extends PacketEvent<T> {
|
||||
|
||||
String SOURCE_GROUP = "group";
|
||||
String SOURCE_PROXIMITY = "proximity";
|
||||
String SOURCE_SPECTATOR = "spectator";
|
||||
String SOURCE_PLUGIN = "plugin";
|
||||
|
||||
/**
|
||||
* Where the packet originated from.
|
||||
* It can be either {@value SOURCE_GROUP} for group chats,
|
||||
* {@value SOURCE_PROXIMITY} for proximity sound,
|
||||
* {@value SOURCE_SPECTATOR} for spectator sound
|
||||
* or {@value SOURCE_PLUGIN} if the packet was sent from a plugin.
|
||||
*
|
||||
* @return the source of the packet
|
||||
*/
|
||||
String getSource();
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.packets.StaticSoundPacket;
|
||||
|
||||
/**
|
||||
* This event is emitted when a static sound packet is about to get sent to a client.
|
||||
*/
|
||||
public interface StaticSoundPacketEvent extends SoundPacketEvent<StaticSoundPacket> {
|
||||
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
public interface UnregisterVolumeCategoryEvent extends VolumeCategoryEvent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatConnection;
|
||||
import de.maxhenkel.voicechat.api.packets.MicrophonePacket;
|
||||
|
||||
/**
|
||||
* This event is emitted after a microphone packet arrives at the server and the distance is processed.
|
||||
* This can be used to modify the distance at which other players can hear this packet.
|
||||
*/
|
||||
public interface VoiceDistanceEvent extends ServerEvent {
|
||||
|
||||
/**
|
||||
* @return the microphone packet
|
||||
*/
|
||||
MicrophonePacket getPacket();
|
||||
|
||||
/**
|
||||
* @return the connection of the player
|
||||
*/
|
||||
VoicechatConnection getSenderConnection();
|
||||
|
||||
/**
|
||||
* This returns either the default distance that's set on the server or the modified distance if another subscriber changed it already.
|
||||
* In case a player is whispering, this will return the whisper distance.
|
||||
* The whisper state can be obtained by calling {@link #getPacket()} and getting {@link MicrophonePacket#isWhispering()}.
|
||||
*
|
||||
* @return the distance in blocks
|
||||
*/
|
||||
float getDistance();
|
||||
|
||||
/**
|
||||
* Sets the distance this player sends audio at.
|
||||
* Note that this also includes the whisper distance, if the player is whispering.
|
||||
*
|
||||
* @param distance the distance in blocks
|
||||
*/
|
||||
void setDistance(float distance);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.ServerPlayer;
|
||||
|
||||
public interface VoiceHostEvent extends ServerEvent {
|
||||
|
||||
/**
|
||||
* @return the voice host string
|
||||
*/
|
||||
String getVoiceHost();
|
||||
|
||||
/**
|
||||
* Overwrites voice chats voice host - This is sent to the client and used by it to connect to the server.
|
||||
*
|
||||
* @param voiceHost the voice host string
|
||||
*/
|
||||
void setVoiceHost(String voiceHost);
|
||||
|
||||
/**
|
||||
* @return the player that is connecting
|
||||
*/
|
||||
ServerPlayer getPlayer();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
/**
|
||||
* This event is emitted on the client when the voice chat is getting enabled/disabled.
|
||||
*/
|
||||
public interface VoicechatDisableEvent extends ClientEvent {
|
||||
|
||||
/**
|
||||
* @return if the voice chat is disabled
|
||||
*/
|
||||
boolean isDisabled();
|
||||
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
public interface VoicechatServerStartedEvent extends ServerEvent {
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatSocket;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface VoicechatServerStartingEvent extends ServerEvent {
|
||||
|
||||
/**
|
||||
* Sets a custom implementation of the socket used for voice chat traffic.
|
||||
*
|
||||
* @param socket the custom socket implementation
|
||||
*/
|
||||
void setSocketImplementation(VoicechatSocket socket);
|
||||
|
||||
/**
|
||||
* @return the custom socket implementation or <code>null</code> to use voice chats default one
|
||||
*/
|
||||
@Nullable
|
||||
VoicechatSocket getSocketImplementation();
|
||||
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
public interface VoicechatServerStoppedEvent extends ServerEvent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VolumeCategory;
|
||||
|
||||
public interface VolumeCategoryEvent extends ServerEvent {
|
||||
|
||||
/**
|
||||
* @return the volume category
|
||||
*/
|
||||
VolumeCategory getVolumeCategory();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* All registerable events.
|
||||
* <p>
|
||||
* Events can be registered by calling {@link de.maxhenkel.voicechat.api.events.EventRegistration#registerEvent}.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.events;
|
||||
@@ -0,0 +1,37 @@
|
||||
package de.maxhenkel.voicechat.api.mp3;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatApi;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* You can obtain an instance of this class by calling {@link VoicechatApi#createMp3Decoder}.
|
||||
*/
|
||||
public interface Mp3Decoder {
|
||||
|
||||
/**
|
||||
* Decodes the MP3 file and returns the decoded audio data as PCM samples.
|
||||
*
|
||||
* @return the decoded audio data as PCM samples
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
short[] decode() throws IOException;
|
||||
|
||||
/**
|
||||
* Decodes the MP3 file if {@link #decode()} has not been called before.
|
||||
*
|
||||
* @return the audio format of the decoded audio data
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
AudioFormat getAudioFormat() throws IOException;
|
||||
|
||||
/**
|
||||
* Decodes the MP3 file if {@link #decode()} has not been called before.
|
||||
*
|
||||
* @return the bitrate of the mp3 file
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
int getBitrate() throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package de.maxhenkel.voicechat.api.mp3;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatApi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* You can obtain an instance of this class by calling {@link VoicechatApi#createMp3Encoder}.
|
||||
*/
|
||||
public interface Mp3Encoder {
|
||||
|
||||
/**
|
||||
* Encodes the given PCM samples and writes it to the provided output stream.
|
||||
*
|
||||
* @param samples the PCM samples to encode
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
void encode(short[] samples) throws IOException;
|
||||
|
||||
/**
|
||||
* Closes the encoder and flushes the output stream.
|
||||
* Also writes leftover mp3 data to the output stream.
|
||||
*
|
||||
* <b>NOTE</b>: Not closing encoders will cause a memory leak in voice chat versions <code><2.6.3</code>!
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
void close() throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Wrappers for mp3 encoding and decoding.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.mp3;
|
||||
@@ -0,0 +1,45 @@
|
||||
package de.maxhenkel.voicechat.api.opus;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatApi;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Instances can be obtained by calling {@link VoicechatApi#createDecoder()}.
|
||||
*/
|
||||
public interface OpusDecoder {
|
||||
|
||||
/**
|
||||
* Decodes opus encoded audio data to 16 bit PCM audio.
|
||||
*
|
||||
* @param data the opus encoded data or <code>null</code> to do PLC
|
||||
* @return the 16 bit PCM audio data
|
||||
*/
|
||||
short[] decode(@Nullable byte[] data);
|
||||
|
||||
/**
|
||||
* Decodes the provided packet and recovers previous lost frames using FEC.
|
||||
*
|
||||
* @param input the input packet
|
||||
* @param frames the number of frames to return (min 1 for just the current frame)
|
||||
* @return an array containing the decoded frames - the length of the array is equal to {@param frames}
|
||||
*/
|
||||
short[][] decode(byte[] input, int frames);
|
||||
|
||||
/**
|
||||
* Resets the decoders state.
|
||||
*/
|
||||
void resetState();
|
||||
|
||||
/**
|
||||
* @return if the decoder is closed
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Closes the decoder.
|
||||
* <b>NOTE</b>: Not closing encoders will cause a memory leak in voice chat versions <code><2.6.3</code>!
|
||||
*/
|
||||
void close();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.maxhenkel.voicechat.api.opus;
|
||||
|
||||
import de.maxhenkel.voicechat.api.VoicechatApi;
|
||||
|
||||
/**
|
||||
* Instances can be obtained by calling {@link VoicechatApi#createEncoder()}.
|
||||
*/
|
||||
public interface OpusEncoder {
|
||||
|
||||
/**
|
||||
* Encodes 16 bit PCM audio with opus.
|
||||
*
|
||||
* @param rawAudio the raw 16 bit PCM audio
|
||||
* @return the opus encoded data
|
||||
*/
|
||||
byte[] encode(short[] rawAudio);
|
||||
|
||||
/**
|
||||
* Resets the encoders state.
|
||||
*/
|
||||
void resetState();
|
||||
|
||||
/**
|
||||
* @return if the encoder is closed
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Closes the encoder.
|
||||
* <b>NOTE</b>: Not closing encoders will cause a memory leak in voice chat versions <code><2.6.3</code>!
|
||||
*/
|
||||
void close();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.maxhenkel.voicechat.api.opus;
|
||||
|
||||
/**
|
||||
* The different Opus encoder modes
|
||||
*/
|
||||
public enum OpusEncoderMode {
|
||||
VOIP, AUDIO, RESTRICTED_LOWDELAY;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Wrappers for the Opus codec.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.opus;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* The main Simple Voice Chat API package.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api;
|
||||
@@ -0,0 +1,64 @@
|
||||
package de.maxhenkel.voicechat.api.packets;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Position;
|
||||
import de.maxhenkel.voicechat.api.VoicechatServerApi;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ConvertablePacket {
|
||||
|
||||
/**
|
||||
* <b>NOTE</b>: Make sure to set {@link SoundPacket.Builder#channelId(UUID)} to avoid conflicts with other channels.
|
||||
*
|
||||
* @return a builder to build an entity sound packet based on this packet
|
||||
*/
|
||||
EntitySoundPacket.Builder<?> entitySoundPacketBuilder();
|
||||
|
||||
/**
|
||||
* <b>NOTE</b>: Make sure to set {@link SoundPacket.Builder#channelId(UUID)} to avoid conflicts with other channels.
|
||||
*
|
||||
* @return a builder to build a locational sound packet based on this packet
|
||||
*/
|
||||
LocationalSoundPacket.Builder<?> locationalSoundPacketBuilder();
|
||||
|
||||
/**
|
||||
* <b>NOTE</b>: Make sure to set {@link SoundPacket.Builder#channelId(UUID)} to avoid conflicts with other channels.
|
||||
*
|
||||
* @return a builder to build a static sound packet based on this packet
|
||||
*/
|
||||
StaticSoundPacket.Builder<?> staticSoundPacketBuilder();
|
||||
|
||||
/**
|
||||
* Converts this packet to an entity sound packet.
|
||||
*
|
||||
* @param entityUuid the UUID of the entity
|
||||
* @param whispering if the entity is whispering
|
||||
* @return the entity sound packet
|
||||
* @throws UnsupportedOperationException if this is called on a packet created by {@link VoicechatServerApi#createPacket()}
|
||||
* @deprecated use {@link #entitySoundPacketBuilder()}
|
||||
*/
|
||||
@Deprecated
|
||||
EntitySoundPacket toEntitySoundPacket(UUID entityUuid, boolean whispering);
|
||||
|
||||
/**
|
||||
* Converts this packet to a locational sound packet.
|
||||
*
|
||||
* @param position the position of the audio
|
||||
* @return the locational sound packet
|
||||
* @throws UnsupportedOperationException if this is called on a packet created by {@link VoicechatServerApi#createPacket()}
|
||||
* @deprecated use {@link #locationalSoundPacketBuilder()}
|
||||
*/
|
||||
@Deprecated
|
||||
LocationalSoundPacket toLocationalSoundPacket(Position position);
|
||||
|
||||
/**
|
||||
* Converts this packet to a static sound packet.
|
||||
*
|
||||
* @return the static sound packet
|
||||
* @throws UnsupportedOperationException if this is called on a packet created by {@link VoicechatServerApi#createPacket()}
|
||||
* @deprecated use {@link #staticSoundPacketBuilder()}
|
||||
*/
|
||||
@Deprecated
|
||||
StaticSoundPacket toStaticSoundPacket();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package de.maxhenkel.voicechat.api.packets;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The receiver of this event will hear the sound from the specified entity.
|
||||
* This can be done for all entities, but only players will show the speaker icon.
|
||||
* If the entity is further away than the maximum voice distance, the receiving player won't actually hear the sound.
|
||||
*/
|
||||
public interface EntitySoundPacket extends SoundPacket {
|
||||
|
||||
/**
|
||||
* @return the UUID of the entity
|
||||
*/
|
||||
UUID getEntityUuid();
|
||||
|
||||
/**
|
||||
* @return if the entity is whispering
|
||||
*/
|
||||
boolean isWhispering();
|
||||
|
||||
/**
|
||||
* @return the distance, the audio can be heard
|
||||
*/
|
||||
float getDistance();
|
||||
|
||||
/**
|
||||
* A builder to build an entity sound packet.
|
||||
*
|
||||
* <b>NOTE</b>: Some values are required to be set.
|
||||
*
|
||||
* @param <T> the builder itself
|
||||
*/
|
||||
public interface Builder<T extends Builder<T>> extends SoundPacket.Builder<T, EntitySoundPacket> {
|
||||
|
||||
/**
|
||||
* This is required to be set!
|
||||
* <br>
|
||||
* The entity UUID is also used as the sender of the packet.
|
||||
*
|
||||
* @param entityUuid the UUID of the entity
|
||||
* @return the builder
|
||||
*/
|
||||
T entityUuid(UUID entityUuid);
|
||||
|
||||
/**
|
||||
* @param whispering if the entity should be whispering
|
||||
* @return the builder
|
||||
*/
|
||||
T whispering(boolean whispering);
|
||||
|
||||
/**
|
||||
* @param distance the distance, this packet can be heard
|
||||
* @return the builder
|
||||
*/
|
||||
T distance(float distance);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package de.maxhenkel.voicechat.api.packets;
|
||||
|
||||
import de.maxhenkel.voicechat.api.Position;
|
||||
|
||||
/**
|
||||
* The receiver of this event will hear the sound from the specified location.
|
||||
* If the location is further away than the maximum voice distance, the receiving player won't actually hear the sound.
|
||||
*/
|
||||
public interface LocationalSoundPacket extends SoundPacket {
|
||||
|
||||
/**
|
||||
* @return the audio location
|
||||
*/
|
||||
Position getPosition();
|
||||
|
||||
/**
|
||||
* @return the distance, the audio can be heard
|
||||
*/
|
||||
float getDistance();
|
||||
|
||||
/**
|
||||
* A builder to build a locational sound packet.
|
||||
*
|
||||
* <b>NOTE</b>: Some values are required to be set.
|
||||
*
|
||||
* @param <T> the builder itself
|
||||
*/
|
||||
public interface Builder<T extends Builder<T>> extends SoundPacket.Builder<T, LocationalSoundPacket> {
|
||||
|
||||
/**
|
||||
* This is required to be set!
|
||||
*
|
||||
* @param position the position of the sound
|
||||
* @return the builder
|
||||
*/
|
||||
T position(Position position);
|
||||
|
||||
/**
|
||||
* @param distance the distance, this packet can be heard
|
||||
* @return the builder
|
||||
*/
|
||||
T distance(float distance);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.maxhenkel.voicechat.api.packets;
|
||||
|
||||
public interface MicrophonePacket extends Packet, ConvertablePacket {
|
||||
|
||||
/**
|
||||
* @return if the player is whispering
|
||||
*/
|
||||
boolean isWhispering();
|
||||
|
||||
/**
|
||||
* @return the opus encoded audio data from the player
|
||||
*/
|
||||
byte[] getOpusEncodedData();
|
||||
|
||||
/**
|
||||
* Allows you to modify or replace the opus encoded audio data.
|
||||
*
|
||||
* @param data the opus encoded audio data to replace
|
||||
*/
|
||||
void setOpusEncodedData(byte[] data);
|
||||
|
||||
}
|
||||
-4
@@ -1,8 +1,4 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package de.maxhenkel.voicechat.api.packets;
|
||||
|
||||
public interface Packet {
|
||||
}
|
||||
|
||||
Loaded 100 of 581 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user