mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge remote-tracking branch 'upstream/master' into extension-api-check
# Conflicts: # api/src/main/java/org/geysermc/geyser/api/GeyserApi.java # core/src/main/java/org/geysermc/geyser/extension/GeyserExtensionLoader.java # gradle/libs.versions.toml
This commit is contained in:
commit
507819d66f
881 changed files with 48630 additions and 28598 deletions
14
.editorconfig
Normal file
14
.editorconfig
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
tab_width = 4
|
||||||
|
max_line_length = off
|
||||||
|
|
||||||
|
[*.java]
|
||||||
|
ij_java_class_count_to_use_import_on_demand = 9999
|
||||||
|
ij_java_doc_align_exception_comments = false
|
||||||
|
ij_java_doc_align_param_comments = false
|
||||||
47
.github/workflows/build-remote.yml
vendored
Normal file
47
.github/workflows/build-remote.yml
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
name: Build Remote
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
repository:
|
||||||
|
required: true
|
||||||
|
description: 'The repo of the remote'
|
||||||
|
type: string
|
||||||
|
ref:
|
||||||
|
required: true
|
||||||
|
description: 'The ref of the remote'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set Build Number
|
||||||
|
run: |
|
||||||
|
echo "BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: GeyserMC/actions/setup-gradle-composite@master
|
||||||
|
with:
|
||||||
|
checkout_repository: ${{ inputs.repository }}
|
||||||
|
checkout_ref: ${{ inputs.ref }}
|
||||||
|
setup-java_java-version: 21
|
||||||
|
setup-gradle_cache-read-only: true
|
||||||
|
|
||||||
|
- name: Build Geyser
|
||||||
|
run: ./gradlew build
|
||||||
|
|
||||||
|
- name: Archive Artifacts
|
||||||
|
uses: GeyserMC/actions/upload-multi-artifact@master
|
||||||
|
if: success()
|
||||||
|
with:
|
||||||
|
artifacts: |
|
||||||
|
bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||||
|
bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||||
|
bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||||
|
bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||||
|
bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||||
|
bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||||
|
bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||||
162
.github/workflows/build.yml
vendored
162
.github/workflows/build.yml
vendored
|
|
@ -3,128 +3,118 @@ name: Build
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'gh-readonly-queue/**'
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.github/ISSUE_TEMPLATE/*.yml'
|
- '.github/ISSUE_TEMPLATE/*.yml'
|
||||||
- '.github/actions/pullrequest.yml'
|
- '.github/workflows/build-remote.yml'
|
||||||
|
- '.github/workflows/preview.yml'
|
||||||
|
- '.github/workflows/pull-request.yml'
|
||||||
- '.idea/copyright/*.xml'
|
- '.idea/copyright/*.xml'
|
||||||
- '.gitignore'
|
- '.gitignore'
|
||||||
- 'CONTRIBUTING.md'
|
- 'CONTRIBUTING.md'
|
||||||
- 'LICENSE'
|
- 'LICENSE'
|
||||||
- 'Jenkinsfile '
|
- 'Jenkinsfile '
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
- 'licenseheader.txt'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository and submodules
|
- name: Get Release Info
|
||||||
uses: actions/checkout@v3
|
id: release-info
|
||||||
|
uses: GeyserMC/actions/previous-release@master
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
data: ${{ vars.RELEASEACTION_PREVRELEASE }}
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Setup Gradle
|
||||||
uses: gradle/wrapper-validation-action@v1
|
uses: GeyserMC/actions/setup-gradle-composite@master
|
||||||
|
with:
|
||||||
|
setup-java_java-version: 21
|
||||||
|
|
||||||
- uses: actions/setup-java@v3
|
- name: Build Geyser
|
||||||
with:
|
run: ./gradlew build
|
||||||
java-version: 17
|
env:
|
||||||
distribution: temurin
|
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||||
|
|
||||||
- name: Build
|
- name: Archive Artifacts
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: GeyserMC/actions/upload-multi-artifact@master
|
||||||
with:
|
|
||||||
arguments: build
|
|
||||||
gradle-home-cache-cleanup: true
|
|
||||||
|
|
||||||
- name: Archive artifacts (Geyser Fabric)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Geyser Fabric
|
artifacts: |
|
||||||
path: bootstrap/fabric/build/libs/Geyser-Fabric.jar
|
bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||||
if-no-files-found: error
|
bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||||
- name: Archive artifacts (Geyser Standalone)
|
bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||||
uses: actions/upload-artifact@v3
|
bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||||
if: success()
|
bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||||
with:
|
bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||||
name: Geyser Standalone
|
bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||||
path: bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser Spigot)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser Spigot
|
|
||||||
path: bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser BungeeCord)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser BungeeCord
|
|
||||||
path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser Sponge)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser Sponge
|
|
||||||
path: bootstrap/sponge/build/libs/Geyser-Sponge.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser Velocity)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser Velocity
|
|
||||||
path: bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Publish to Maven Repository
|
- name: Publish to Maven Repository
|
||||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||||
uses: gradle/gradle-build-action@v2
|
run: ./gradlew publish
|
||||||
env:
|
env:
|
||||||
|
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||||
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
||||||
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
|
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
|
||||||
|
|
||||||
|
- name: Get Version
|
||||||
|
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||||
|
id: get-version
|
||||||
|
run: |
|
||||||
|
version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
|
||||||
|
echo "VERSION=${version}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Get Release Metadata
|
||||||
|
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||||
|
uses: GeyserMC/actions/release@master
|
||||||
|
id: metadata
|
||||||
with:
|
with:
|
||||||
arguments: publish
|
appID: ${{ secrets.RELEASE_APP_ID }}
|
||||||
|
appPrivateKey: ${{ secrets.RELEASE_APP_PK }}
|
||||||
|
files: |
|
||||||
|
bungeecord:bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||||
|
fabric:bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||||
|
neoforge:bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||||
|
spigot:bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||||
|
standalone:bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||||
|
velocity:bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||||
|
viaproxy:bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||||
|
releaseEnabled: false
|
||||||
|
saveMetadata: true
|
||||||
|
releaseProject: 'geyser'
|
||||||
|
releaseVersion: ${{ steps.get-version.outputs.VERSION }}
|
||||||
|
|
||||||
- name: Publish to Downloads API
|
- name: Publish to Downloads API
|
||||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||||
shell: bash
|
uses: GeyserMC/actions/upload-release@master
|
||||||
env:
|
with:
|
||||||
DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }}
|
username: ${{ vars.DOWNLOADS_USERNAME }}
|
||||||
DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
|
privateKey: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
|
||||||
DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }}
|
host: ${{ secrets.DOWNLOADS_SERVER_IP }}
|
||||||
run: |
|
files: |
|
||||||
# Save the private key to a file
|
bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
||||||
echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa
|
bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||||
chmod 600 id_ecdsa
|
bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||||
# Set the project
|
bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
||||||
project=geyser
|
bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||||
# Get the version from gradle.properties
|
bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||||
version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
|
bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||||
# Create the build folder
|
changelog: ${{ steps.metadata.outputs.body }}
|
||||||
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP mkdir -p "~/uploads/$project/$GITHUB_RUN_NUMBER/"
|
|
||||||
# Copy over artifacts
|
|
||||||
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/**/build/libs/Geyser-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
|
|
||||||
# Run the build script
|
|
||||||
# Push the metadata
|
|
||||||
echo "{\"project\": \"$project\", \"version\": \"$version\", \"id\": $GITHUB_RUN_NUMBER, \"commit\": \"$GITHUB_SHA\"}" > metadata.json
|
|
||||||
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
|
|
||||||
|
|
||||||
- name: Publish to Modrinth
|
- name: Publish to Modrinth
|
||||||
uses: gradle/gradle-build-action@v2
|
|
||||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||||
env:
|
env:
|
||||||
|
CHANGELOG: ${{ steps.metadata.outputs.body }}
|
||||||
|
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||||
with:
|
run: ./gradlew modrinth
|
||||||
arguments: fabric:modrinth
|
|
||||||
gradle-home-cache-cleanup: true
|
|
||||||
|
|
||||||
- name: Notify Discord
|
- name: Notify Discord
|
||||||
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||||
uses: Tim203/actions-git-discord-webhook@main
|
uses: GeyserMC/actions/notify-discord@master
|
||||||
with:
|
with:
|
||||||
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
|
discordWebhook: ${{ secrets.DISCORD_WEBHOOK }}
|
||||||
status: ${{ job.status }}
|
status: ${{ job.status }}
|
||||||
|
body: ${{ steps.metadata.outputs.body }}
|
||||||
|
includeDownloads: ${{ github.ref_name == 'master' }}
|
||||||
|
|
|
||||||
33
.github/workflows/dispatch-preview.yml
vendored
Normal file
33
.github/workflows/dispatch-preview.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
name: Dispatch Preview
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
runId:
|
||||||
|
required: true
|
||||||
|
description: 'ID of the action to pull artifacts from'
|
||||||
|
build:
|
||||||
|
required: true
|
||||||
|
description: 'Build number for the release'
|
||||||
|
version:
|
||||||
|
required: true
|
||||||
|
description: 'Version under which to upload to the Downloads API'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dispatch-preview:
|
||||||
|
# Allow access to secrets if we are uploading a preview
|
||||||
|
secrets: inherit
|
||||||
|
uses: GeyserMC/actions/.github/workflows/upload-preview.yml@master
|
||||||
|
with:
|
||||||
|
build: ${{ inputs.build }}
|
||||||
|
version: ${{ inputs.version }}
|
||||||
|
files: |
|
||||||
|
bungeecord:Geyser-BungeeCord.jar
|
||||||
|
fabric:Geyser-Fabric.jar
|
||||||
|
neoforge:Geyser-NeoForge.jar
|
||||||
|
spigot:Geyser-Spigot.jar
|
||||||
|
standalone:Geyser-Standalone.jar
|
||||||
|
velocity:Geyser-Velocity.jar
|
||||||
|
viaproxy:Geyser-ViaProxy.jar
|
||||||
|
project: geyserpreview
|
||||||
|
runId: ${{ inputs.runId }}
|
||||||
34
.github/workflows/pull-request.yml
vendored
Normal file
34
.github/workflows/pull-request.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: Process Pull Request
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
# Forbid access to secrets nor GH Token perms while building the PR
|
||||||
|
permissions: {}
|
||||||
|
secrets: {}
|
||||||
|
uses: GeyserMC/Geyser/.github/workflows/build-remote.yml@master
|
||||||
|
with:
|
||||||
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
preview:
|
||||||
|
needs: [build]
|
||||||
|
if: >-
|
||||||
|
contains(github.event.pull_request.labels.*.name, 'PR: Needs Testing')
|
||||||
|
# Allow access to secrets if we are uploading a preview
|
||||||
|
secrets: inherit
|
||||||
|
uses: GeyserMC/actions/.github/workflows/upload-preview.yml@master
|
||||||
|
with:
|
||||||
|
build: ${{ github.run_number }}
|
||||||
|
version: pr.${{ github.event.pull_request.number }}
|
||||||
|
files: |
|
||||||
|
bungeecord:Geyser-BungeeCord.jar
|
||||||
|
fabric:Geyser-Fabric.jar
|
||||||
|
neoforge:Geyser-NeoForge.jar
|
||||||
|
spigot:Geyser-Spigot.jar
|
||||||
|
standalone:Geyser-Standalone.jar
|
||||||
|
velocity:Geyser-Velocity.jar
|
||||||
|
viaproxy:Geyser-ViaProxy.jar
|
||||||
|
project: geyserpreview
|
||||||
|
runId: ${{ github.run_id }}
|
||||||
96
.github/workflows/pullrequest.yml
vendored
96
.github/workflows/pullrequest.yml
vendored
|
|
@ -1,96 +0,0 @@
|
||||||
name: Build Pull Request
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths-ignore:
|
|
||||||
- '.github/ISSUE_TEMPLATE/*.yml'
|
|
||||||
- '.idea/copyright/*.xml'
|
|
||||||
- '.gitignore'
|
|
||||||
- 'CONTRIBUTING.md'
|
|
||||||
- 'LICENSE'
|
|
||||||
- 'Jenkinsfile '
|
|
||||||
- 'README.md'
|
|
||||||
- 'licenseheader.txt'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up JDK 17
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
java-version: 17
|
|
||||||
distribution: temurin
|
|
||||||
|
|
||||||
- name: Check if the author has forked the API repo
|
|
||||||
uses: Kas-tle/find-forks-action@v1.0.1
|
|
||||||
id: find_forks
|
|
||||||
with:
|
|
||||||
owner: GeyserMC
|
|
||||||
repo: api
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Use author's API repo if it exists
|
|
||||||
if: ${{ steps.find_forks.outputs.target_branch_found == 'true' }}
|
|
||||||
env:
|
|
||||||
API_FORK_URL: ${{ steps.find_forks.outputs.user_fork_url }}
|
|
||||||
API_FORK_BRANCH: ${{ github.event.pull_request.head.ref }}
|
|
||||||
run: |
|
|
||||||
git clone "${API_FORK_URL}" --single-branch --branch "${API_FORK_BRANCH}" api
|
|
||||||
cd api
|
|
||||||
./gradlew publishToMavenLocal
|
|
||||||
|
|
||||||
- name: Checkout repository and submodules
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
path: geyser
|
|
||||||
|
|
||||||
- name: Build Geyser
|
|
||||||
uses: gradle/gradle-build-action@v2
|
|
||||||
with:
|
|
||||||
arguments: build
|
|
||||||
build-root-directory: geyser
|
|
||||||
|
|
||||||
- name: Archive artifacts (Geyser Fabric)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser Fabric
|
|
||||||
path: geyser/bootstrap/fabric/build/libs/Geyser-Fabric.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser Standalone)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser Standalone
|
|
||||||
path: geyser/bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser Spigot)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser Spigot
|
|
||||||
path: geyser/bootstrap/spigot/build/libs/Geyser-Spigot.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser BungeeCord)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser BungeeCord
|
|
||||||
path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser Sponge)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser Sponge
|
|
||||||
path: geyser/bootstrap/sponge/build/libs/Geyser-Sponge.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser Velocity)
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser Velocity
|
|
||||||
path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -251,3 +251,4 @@ locales/
|
||||||
/saved-refresh-tokens.json
|
/saved-refresh-tokens.json
|
||||||
/custom_mappings/
|
/custom_mappings/
|
||||||
/languages/
|
/languages/
|
||||||
|
/custom-skulls.yml
|
||||||
3
.idea/copyright/Geyser.xml
generated
3
.idea/copyright/Geyser.xml
generated
|
|
@ -1,6 +1,7 @@
|
||||||
<component name="CopyrightManager">
|
<component name="CopyrightManager">
|
||||||
<copyright>
|
<copyright>
|
||||||
<option name="notice" value="Copyright (c) 2019-&#36;today.year GeyserMC. http://geysermc.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @author GeyserMC @link https://github.com/GeyserMC/Geyser" />
|
<option name="allowReplaceRegexp" value="Copyright" />
|
||||||
|
<option name="notice" value="Copyright (c) &#36;originalComment.match("Copyright \(c\) (\d+)", 1, "-")&#36;today.year GeyserMC. http://geysermc.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @author GeyserMC @link https://github.com/GeyserMC/Geyser" />
|
||||||
<option name="myName" value="Geyser" />
|
<option name="myName" value="Geyser" />
|
||||||
</copyright>
|
</copyright>
|
||||||
</component>
|
</component>
|
||||||
|
|
@ -33,18 +33,14 @@ public class LongClassName {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 0:
|
case 0 -> stuff();
|
||||||
stuff();
|
case 1 -> differentStuff();
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
differentStuff();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Make sure to comment your code where possible.
|
Make sure to comment your code where possible. To mark nullable methods, use `@Nullable` (and subsequently, `@NonNull`) from the `org.checkerframework.checker.nullness.qual` package.
|
||||||
|
|
||||||
The nature of our software requires a lot of arrays and maps to be stored - where possible, use Fastutil's specialized maps. For example, if you're storing block state translations, use an `Int2IntMap`.
|
The nature of our software requires a lot of arrays and maps to be stored - where possible, use Fastutil's specialized maps. For example, if you're storing block state translations, use an `Int2IntMap`.
|
||||||
|
|
||||||
|
|
|
||||||
28
Jenkinsfile
vendored
28
Jenkinsfile
vendored
|
|
@ -1,28 +0,0 @@
|
||||||
pipeline {
|
|
||||||
agent any
|
|
||||||
tools {
|
|
||||||
gradle 'Gradle 7'
|
|
||||||
jdk 'Java 17'
|
|
||||||
}
|
|
||||||
options {
|
|
||||||
buildDiscarder(logRotator(artifactNumToKeepStr: '20'))
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage ('Build') {
|
|
||||||
steps {
|
|
||||||
sh 'git submodule update --init --recursive'
|
|
||||||
rtGradleRun(
|
|
||||||
usesPlugin: true,
|
|
||||||
tool: 'Gradle 7',
|
|
||||||
buildFile: 'build.gradle.kts',
|
|
||||||
tasks: 'clean build',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
success {
|
|
||||||
archiveArtifacts artifacts: 'bootstrap/**/build/libs/Geyser-*.jar', fingerprint: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
The MIT License
|
The MIT License
|
||||||
|
|
||||||
Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://discord.gg/geysermc)
|
[](https://discord.gg/geysermc)
|
||||||
[](https://translate.geysermc.org/)
|
[](https://translate.geysermc.org/)
|
||||||
|
|
||||||
Geyser is a bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition, closing the gap from those wanting to play true cross-platform.
|
Geyser is a bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition, closing the gap from those wanting to play true cross-platform.
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
|
||||||
|
|
||||||
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
|
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
|
||||||
|
|
||||||
### Currently supporting Minecraft Bedrock 1.19.80 - 1.20 and Minecraft Java 1.20.
|
### Currently supporting Minecraft Bedrock 1.20.80 - 1.21.2 and Minecraft Java 1.21
|
||||||
|
|
||||||
## Setting Up
|
## Setting Up
|
||||||
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
||||||
|
|
@ -32,7 +32,6 @@ Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Ge
|
||||||
## What's Left to be Added/Fixed
|
## What's Left to be Added/Fixed
|
||||||
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
|
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
|
||||||
- Some Entity Flags
|
- Some Entity Flags
|
||||||
- Structure block UI
|
|
||||||
|
|
||||||
## What can't be fixed
|
## What can't be fixed
|
||||||
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://wiki.geysermc.org/geyser/current-limitations/) page.
|
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://wiki.geysermc.org/geyser/current-limitations/) page.
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
|
||||||
import javax.lang.model.SourceVersion;
|
import javax.lang.model.SourceVersion;
|
||||||
|
|
||||||
@SupportedAnnotationTypes("*")
|
@SupportedAnnotationTypes("*")
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_16)
|
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||||
public class BlockEntityProcessor extends ClassProcessor {
|
public class BlockEntityProcessor extends ClassProcessor {
|
||||||
public BlockEntityProcessor() {
|
public BlockEntityProcessor() {
|
||||||
super("org.geysermc.geyser.translator.level.block.entity.BlockEntity");
|
super("org.geysermc.geyser.translator.level.block.entity.BlockEntity");
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.processor;
|
package org.geysermc.geyser.processor;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import javax.annotation.processing.AbstractProcessor;
|
import javax.annotation.processing.AbstractProcessor;
|
||||||
import javax.annotation.processing.ProcessingEnvironment;
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
import javax.annotation.processing.RoundEnvironment;
|
import javax.annotation.processing.RoundEnvironment;
|
||||||
|
|
@ -159,7 +161,7 @@ public class ClassProcessor extends AbstractProcessor {
|
||||||
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Completed processing for " + this.annotationClassName);
|
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Completed processing for " + this.annotationClassName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedReader createReader() throws IOException {
|
private @Nullable BufferedReader createReader() throws IOException {
|
||||||
if (this.outputPath != null) {
|
if (this.outputPath != null) {
|
||||||
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Reading existing " + this.annotationClassName + " list from " + this.outputPath);
|
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Reading existing " + this.annotationClassName + " list from " + this.outputPath);
|
||||||
return Files.newBufferedReader(this.outputPath);
|
return Files.newBufferedReader(this.outputPath);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
|
||||||
import javax.lang.model.SourceVersion;
|
import javax.lang.model.SourceVersion;
|
||||||
|
|
||||||
@SupportedAnnotationTypes("*")
|
@SupportedAnnotationTypes("*")
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_16)
|
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||||
public class CollisionRemapperProcessor extends ClassProcessor {
|
public class CollisionRemapperProcessor extends ClassProcessor {
|
||||||
public CollisionRemapperProcessor() {
|
public CollisionRemapperProcessor() {
|
||||||
super("org.geysermc.geyser.translator.collision.CollisionRemapper");
|
super("org.geysermc.geyser.translator.collision.CollisionRemapper");
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
|
||||||
import javax.lang.model.SourceVersion;
|
import javax.lang.model.SourceVersion;
|
||||||
|
|
||||||
@SupportedAnnotationTypes("*")
|
@SupportedAnnotationTypes("*")
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_16)
|
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||||
public class PacketTranslatorProcessor extends ClassProcessor {
|
public class PacketTranslatorProcessor extends ClassProcessor {
|
||||||
public PacketTranslatorProcessor() {
|
public PacketTranslatorProcessor() {
|
||||||
super("org.geysermc.geyser.translator.protocol.Translator");
|
super("org.geysermc.geyser.translator.protocol.Translator");
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
|
||||||
import javax.lang.model.SourceVersion;
|
import javax.lang.model.SourceVersion;
|
||||||
|
|
||||||
@SupportedAnnotationTypes("*")
|
@SupportedAnnotationTypes("*")
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_16)
|
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||||
public class SoundHandlerProcessor extends ClassProcessor {
|
public class SoundHandlerProcessor extends ClassProcessor {
|
||||||
public SoundHandlerProcessor() {
|
public SoundHandlerProcessor() {
|
||||||
super("org.geysermc.geyser.translator.sound.SoundTranslator");
|
super("org.geysermc.geyser.translator.sound.SoundTranslator");
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,5 @@ plugins {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(libs.base.api)
|
api(libs.base.api)
|
||||||
|
api(libs.math)
|
||||||
}
|
}
|
||||||
|
|
@ -30,12 +30,14 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.api.Geyser;
|
import org.geysermc.api.Geyser;
|
||||||
import org.geysermc.api.GeyserApiBase;
|
import org.geysermc.api.GeyserApiBase;
|
||||||
import org.geysermc.api.util.ApiVersion;
|
import org.geysermc.api.util.ApiVersion;
|
||||||
|
import org.geysermc.geyser.api.command.CommandSource;
|
||||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
import org.geysermc.geyser.api.event.EventBus;
|
import org.geysermc.geyser.api.event.EventBus;
|
||||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||||
import org.geysermc.geyser.api.extension.ExtensionManager;
|
import org.geysermc.geyser.api.extension.ExtensionManager;
|
||||||
import org.geysermc.geyser.api.network.BedrockListener;
|
import org.geysermc.geyser.api.network.BedrockListener;
|
||||||
import org.geysermc.geyser.api.network.RemoteServer;
|
import org.geysermc.geyser.api.network.RemoteServer;
|
||||||
|
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
@ -78,6 +80,7 @@ public interface GeyserApi extends GeyserApiBase {
|
||||||
* @param apiClass the builder class
|
* @param apiClass the builder class
|
||||||
* @param <R> the implementation type
|
* @param <R> the implementation type
|
||||||
* @param <T> the API type
|
* @param <T> the API type
|
||||||
|
* @throws IllegalArgumentException if there is no provider for the specified API class
|
||||||
* @return the builder instance
|
* @return the builder instance
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
@ -134,6 +137,30 @@ public interface GeyserApi extends GeyserApiBase {
|
||||||
@NonNull
|
@NonNull
|
||||||
PlatformType platformType();
|
PlatformType platformType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the version of Java Minecraft that is supported.
|
||||||
|
*
|
||||||
|
* @return the supported version of Java Minecraft
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
MinecraftVersion supportedJavaVersion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of Bedrock Minecraft versions that are supported.
|
||||||
|
*
|
||||||
|
* @return the list of supported Bedrock Minecraft versions
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
List<MinecraftVersion> supportedBedrockVersions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link CommandSource} for the console.
|
||||||
|
*
|
||||||
|
* @return the console command source
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
CommandSource consoleCommandSource();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current {@link GeyserApiBase} instance.
|
* Gets the current {@link GeyserApiBase} instance.
|
||||||
*
|
*
|
||||||
|
|
@ -150,6 +177,6 @@ public interface GeyserApi extends GeyserApiBase {
|
||||||
* @return the current geyser api version
|
* @return the current geyser api version
|
||||||
*/
|
*/
|
||||||
default ApiVersion geyserApiVersion() {
|
default ApiVersion geyserApiVersion() {
|
||||||
return new ApiVersion(2, 1, 1);
|
return new ApiVersion(2, 4, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.bedrock.camera;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface holds all the methods that relate to a client's camera.
|
||||||
|
* Can be accessed through {@link GeyserConnection#camera()}.
|
||||||
|
*/
|
||||||
|
public interface CameraData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a camera fade instruction to the client.
|
||||||
|
* If an existing camera fade is already in progress, the current fade will be prolonged.
|
||||||
|
* Can be built using {@link CameraFade.Builder}.
|
||||||
|
* To stop a fade early, use {@link #clearCameraInstructions()}.
|
||||||
|
*
|
||||||
|
* @param fade the camera fade instruction to send
|
||||||
|
*/
|
||||||
|
void sendCameraFade(@NonNull CameraFade fade);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a camera position instruction to the client.
|
||||||
|
* If an existing camera movement is already in progress,
|
||||||
|
* the final camera position will be the one of the latest instruction, and
|
||||||
|
* the (optional) camera fade will be added on top of the existing fade.
|
||||||
|
* Can be built using {@link CameraPosition.Builder}.
|
||||||
|
* To stop reset the camera position/stop ongoing instructions, use {@link #clearCameraInstructions()}.
|
||||||
|
*
|
||||||
|
* @param position the camera position instruction to send
|
||||||
|
*/
|
||||||
|
void sendCameraPosition(@NonNull CameraPosition position);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops all sent camera instructions (fades, movements, and perspective locks).
|
||||||
|
* This will not stop any camera shakes/input locks/fog effects, use the respective methods for those.
|
||||||
|
*/
|
||||||
|
void clearCameraInstructions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces a {@link CameraPerspective} on the client. This will prevent the client
|
||||||
|
* from changing their camera perspective until it is unlocked via {@link #clearCameraInstructions()}.
|
||||||
|
* <p>
|
||||||
|
* Note: You cannot force a client into a free camera perspective with this method.
|
||||||
|
* To do that, send a {@link CameraPosition} via {@link #sendCameraPosition(CameraPosition)} - it requires a set position
|
||||||
|
* instead of being relative to the player.
|
||||||
|
*
|
||||||
|
* @param perspective the {@link CameraPerspective} to force
|
||||||
|
*/
|
||||||
|
void forceCameraPerspective(@NonNull CameraPerspective perspective);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the client's current {@link CameraPerspective}, if one is currently forced.
|
||||||
|
* This will return {@code null} if the client is not currently forced into a perspective.
|
||||||
|
* If a perspective is forced, the client will not be able to change their camera perspective until it is unlocked.
|
||||||
|
*
|
||||||
|
* @return the forced perspective, or {@code null} if none is forced
|
||||||
|
*/
|
||||||
|
@Nullable CameraPerspective forcedCameraPerspective();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shakes the client's camera.
|
||||||
|
* <p>
|
||||||
|
* If the camera is already shaking with the same {@link CameraShake} type, then the additional intensity
|
||||||
|
* will be layered on top of the existing intensity, with their own distinct durations.<br>
|
||||||
|
* If the existing shake type is different and the new intensity/duration are not positive, the existing shake only
|
||||||
|
* switches to the new type. Otherwise, the existing shake is completely overridden.
|
||||||
|
*
|
||||||
|
* @param intensity the intensity of the shake. The client has a maximum total intensity of 4.
|
||||||
|
* @param duration the time in seconds that the shake will occur for
|
||||||
|
* @param type the type of shake
|
||||||
|
*/
|
||||||
|
void shakeCamera(float intensity, float duration, @NonNull CameraShake type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops all camera shakes of any type.
|
||||||
|
*/
|
||||||
|
void stopCameraShake();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given fog IDs to the fog cache, then sends all fog IDs in the cache to the client.
|
||||||
|
* <p>
|
||||||
|
* Fog IDs can be found <a href="https://wiki.bedrock.dev/documentation/fog-ids.html">here</a>
|
||||||
|
*
|
||||||
|
* @param fogNameSpaces the fog IDs to add. If empty, the existing cached IDs will still be sent.
|
||||||
|
*/
|
||||||
|
void sendFog(String... fogNameSpaces);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given fog IDs from the fog cache, then sends all fog IDs in the cache to the client.
|
||||||
|
*
|
||||||
|
* @param fogNameSpaces the fog IDs to remove. If empty, all fog IDs will be removed.
|
||||||
|
*/
|
||||||
|
void removeFog(String... fogNameSpaces);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an immutable copy of all fog affects currently applied to this client.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Set<String> fogEffects();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Un)locks the client's camera, so that they cannot look around.
|
||||||
|
* To ensure the camera is only unlocked when all locks are released, you must supply
|
||||||
|
* a UUID when using method, and use the same UUID to unlock the camera.
|
||||||
|
*
|
||||||
|
* @param lock whether to lock the camera
|
||||||
|
* @param owner the owner of the lock, represented with a UUID
|
||||||
|
* @return if the camera is locked after this method call
|
||||||
|
*/
|
||||||
|
boolean lockCamera(boolean lock, @NonNull UUID owner);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the client's camera is locked.
|
||||||
|
*
|
||||||
|
* @return whether the camera is currently locked
|
||||||
|
*/
|
||||||
|
boolean isCameraLocked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides a {@link GuiElement} on the client's side.
|
||||||
|
*
|
||||||
|
* @param element the {@link GuiElement} to hide
|
||||||
|
*/
|
||||||
|
void hideElement(@NonNull GuiElement... element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets a {@link GuiElement} on the client's side.
|
||||||
|
* This makes the client decide on its own - e.g. based on client settings -
|
||||||
|
* whether to show or hide the gui element.
|
||||||
|
* <p>
|
||||||
|
* If no elements are specified, this will reset all currently hidden elements
|
||||||
|
*
|
||||||
|
* @param element the {@link GuiElement} to reset
|
||||||
|
*/
|
||||||
|
void resetElement(@NonNull GuiElement @Nullable... element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a {@link GuiElement} is currently hidden.
|
||||||
|
*
|
||||||
|
* @param element the {@link GuiElement} to check
|
||||||
|
*/
|
||||||
|
boolean isHudElementHidden(@NonNull GuiElement element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently hidden {@link GuiElement}s.
|
||||||
|
*
|
||||||
|
* @return an unmodifiable view of all currently hidden {@link GuiElement}s
|
||||||
|
*/
|
||||||
|
@NonNull Set<GuiElement> hiddenElements();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.bedrock.camera;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These are all the easing types that can be used when sending a {@link CameraPosition} instruction.
|
||||||
|
* When using these, the client won't teleport to the new camera position, but instead transition to it.
|
||||||
|
* <p>
|
||||||
|
* See <a href="https://easings.net/">https://easings.net/</a> for more information.
|
||||||
|
*/
|
||||||
|
public enum CameraEaseType {
|
||||||
|
LINEAR("linear"),
|
||||||
|
SPRING("spring"),
|
||||||
|
EASE_IN_SINE("in_sine"),
|
||||||
|
EASE_OUT_SINE("out_sine"),
|
||||||
|
EASE_IN_OUT_SINE("in_out_sine"),
|
||||||
|
EASE_IN_QUAD("in_quad"),
|
||||||
|
EASE_OUT_QUAD("out_quad"),
|
||||||
|
EASE_IN_OUT_QUAD("in_out_quad"),
|
||||||
|
EASE_IN_CUBIC("in_cubic"),
|
||||||
|
EASE_OUT_CUBIC("out_cubic"),
|
||||||
|
EASE_IN_OUT_CUBIC("in_out_cubic"),
|
||||||
|
EASE_IN_QUART("in_quart"),
|
||||||
|
EASE_OUT_QUART("out_quart"),
|
||||||
|
EASE_IN_OUT_QUART("in_out_quart"),
|
||||||
|
EASE_IN_QUINT("in_quint"),
|
||||||
|
EASE_OUT_QUINT("out_quint"),
|
||||||
|
EASE_IN_OUT_QUINT("in_out_quint"),
|
||||||
|
EASE_IN_EXPO("in_expo"),
|
||||||
|
EASE_OUT_EXPO("out_expo"),
|
||||||
|
EASE_IN_OUT_EXPO("in_out_expo"),
|
||||||
|
EASE_IN_CIRC("in_circ"),
|
||||||
|
EASE_OUT_CIRC("out_circ"),
|
||||||
|
EASE_IN_OUT_CIRC("in_out_circ"),
|
||||||
|
EASE_IN_BACK("in_back"),
|
||||||
|
EASE_OUT_BACK("out_back"),
|
||||||
|
EASE_IN_OUT_BACK("in_out_back"),
|
||||||
|
EASE_IN_ELASTIC("in_elastic"),
|
||||||
|
EASE_OUT_ELASTIC("out_elastic"),
|
||||||
|
EASE_IN_OUT_ELASTIC("in_out_elastic"),
|
||||||
|
EASE_IN_BOUNCE("in_bounce"),
|
||||||
|
EASE_OUT_BOUNCE("out_bounce"),
|
||||||
|
EASE_IN_OUT_BOUNCE("in_out_bounce");
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
CameraEaseType(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.bedrock.camera;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.common.value.qual.IntRange;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a coloured fade overlay on the camera.
|
||||||
|
* <p>
|
||||||
|
* Can be sent with {@link CameraData#sendCameraFade(CameraFade)}, or with a {@link CameraPosition} instruction.
|
||||||
|
*/
|
||||||
|
public interface CameraFade {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the color overlay of the camera.
|
||||||
|
* Bedrock uses an RGB color system.
|
||||||
|
*
|
||||||
|
* @return the color of the fade
|
||||||
|
*/
|
||||||
|
@NonNull Color color();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the seconds it takes to fade in.
|
||||||
|
* All fade times combined must take at least 0.5 seconds, and at most 30 seconds.
|
||||||
|
*
|
||||||
|
* @return the seconds it takes to fade in
|
||||||
|
*/
|
||||||
|
float fadeInSeconds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the seconds the overlay is held.
|
||||||
|
* All fade times combined must take at least 0.5 seconds, and at most 30 seconds.
|
||||||
|
*
|
||||||
|
* @return the seconds the overlay is held
|
||||||
|
*/
|
||||||
|
float fadeHoldSeconds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the seconds it takes to fade out.
|
||||||
|
* All fade times combined must take at least 0.5 seconds, and at most 30 seconds.
|
||||||
|
*
|
||||||
|
* @return the seconds it takes to fade out
|
||||||
|
*/
|
||||||
|
float fadeOutSeconds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Builder for CameraFade
|
||||||
|
*
|
||||||
|
* @return a CameraFade Builder
|
||||||
|
*/
|
||||||
|
static CameraFade.Builder builder() {
|
||||||
|
return GeyserApi.api().provider(CameraFade.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
|
||||||
|
Builder color(@NonNull Color color);
|
||||||
|
|
||||||
|
Builder fadeInSeconds(@IntRange(from = 0, to = 10) float fadeInSeconds);
|
||||||
|
|
||||||
|
Builder fadeHoldSeconds(@IntRange(from = 0, to = 10) float fadeHoldSeconds);
|
||||||
|
|
||||||
|
Builder fadeOutSeconds(@IntRange(from = 0, to = 10) float fadeOutSeconds);
|
||||||
|
|
||||||
|
CameraFade build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.bedrock.camera;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a camera perspective that a player's camera can take.
|
||||||
|
* All perspectives except for {@link #FREE} are locked to the player's head,
|
||||||
|
* and are therefore relative to the player's position and rotation.
|
||||||
|
*/
|
||||||
|
public enum CameraPerspective {
|
||||||
|
FIRST_PERSON("minecraft:first_person"),
|
||||||
|
FREE("minecraft:free"),
|
||||||
|
THIRD_PERSON("minecraft:third_person"),
|
||||||
|
THIRD_PERSON_FRONT("minecraft:third_person_front");
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
CameraPerspective(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.bedrock.camera;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.checkerframework.common.value.qual.IntRange;
|
||||||
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface represents a camera position instruction. Can be built with the {@link #builder()}.
|
||||||
|
* <p>
|
||||||
|
* Any camera position instruction pins the client camera to a specific position and rotation.
|
||||||
|
* You can set {@link CameraEaseType} to ensure a smooth transition that will last {@link #easeSeconds()} seconds.
|
||||||
|
* A {@link CameraFade} can also be sent, which will transition the player to a coloured transition during the transition.
|
||||||
|
* <p>
|
||||||
|
* Use {@link CameraData#sendCameraPosition(CameraPosition)} to send such an instruction to any connection.
|
||||||
|
*/
|
||||||
|
public interface CameraPosition {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the camera's position.
|
||||||
|
*
|
||||||
|
* @return camera position vector
|
||||||
|
*/
|
||||||
|
@NonNull Vector3f position();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link CameraEaseType} of the camera.
|
||||||
|
* If not set, there is no easing.
|
||||||
|
*
|
||||||
|
* @return camera ease type
|
||||||
|
*/
|
||||||
|
@Nullable CameraEaseType easeType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link CameraFade} to be sent along the camera position instruction.
|
||||||
|
* If set, they will run at once.
|
||||||
|
*
|
||||||
|
* @return camera fade, or null if not present
|
||||||
|
*/
|
||||||
|
@Nullable CameraFade cameraFade();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the easing duration of the camera, in seconds.
|
||||||
|
* Is only used if a {@link CameraEaseType} is set.
|
||||||
|
*
|
||||||
|
* @return camera easing duration in seconds
|
||||||
|
*/
|
||||||
|
float easeSeconds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the x-axis rotation of the camera.
|
||||||
|
* To prevent the camera from being upside down, Bedrock limits the range to -90 to 90.
|
||||||
|
* Will be overridden if {@link #facingPosition()} is set.
|
||||||
|
*
|
||||||
|
* @return camera x-axis rotation
|
||||||
|
*/
|
||||||
|
@IntRange(from = -90, to = 90) int rotationX();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the y-axis rotation of the camera.
|
||||||
|
* Will be overridden if {@link #facingPosition()} is set.
|
||||||
|
*
|
||||||
|
* @return camera y-axis rotation
|
||||||
|
*/
|
||||||
|
int rotationY();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position that the camera is facing.
|
||||||
|
* Can be used instead of manually setting rotation values.
|
||||||
|
* <p>
|
||||||
|
* If set, the rotation values set via {@link #rotationX()} and {@link #rotationY()} will be ignored.
|
||||||
|
*
|
||||||
|
* @return Camera's facing position
|
||||||
|
*/
|
||||||
|
@Nullable Vector3f facingPosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls whether player effects, such as night vision or blindness, should be rendered on the camera.
|
||||||
|
* Defaults to false.
|
||||||
|
*
|
||||||
|
* @return whether player effects should be rendered
|
||||||
|
*/
|
||||||
|
boolean renderPlayerEffects();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls whether the player position should be used for directional audio.
|
||||||
|
* If false, the camera position will be used instead.
|
||||||
|
*
|
||||||
|
* @return whether the players position should be used for directional audio
|
||||||
|
*/
|
||||||
|
boolean playerPositionForAudio();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Builder for CameraPosition
|
||||||
|
*
|
||||||
|
* @return a CameraPosition Builder
|
||||||
|
*/
|
||||||
|
static CameraPosition.Builder builder() {
|
||||||
|
return GeyserApi.api().provider(CameraPosition.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
|
||||||
|
Builder cameraFade(@Nullable CameraFade cameraFade);
|
||||||
|
|
||||||
|
Builder renderPlayerEffects(boolean renderPlayerEffects);
|
||||||
|
|
||||||
|
Builder playerPositionForAudio(boolean playerPositionForAudio);
|
||||||
|
|
||||||
|
Builder easeType(@Nullable CameraEaseType easeType);
|
||||||
|
|
||||||
|
Builder easeSeconds(float easeSeconds);
|
||||||
|
|
||||||
|
Builder position(@NonNull Vector3f position);
|
||||||
|
|
||||||
|
Builder rotationX(@IntRange(from = -90, to = 90) int rotationX);
|
||||||
|
|
||||||
|
Builder rotationY(int rotationY);
|
||||||
|
|
||||||
|
Builder facingPosition(@Nullable Vector3f facingPosition);
|
||||||
|
|
||||||
|
CameraPosition build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,11 +23,12 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.item.type;
|
package org.geysermc.geyser.api.bedrock.camera;
|
||||||
|
|
||||||
// If blocks are implemented, then this class is not needed.
|
/**
|
||||||
public class FlowerItem extends BlockItem {
|
* Represents a camera shake instruction. Can be sent in {@link CameraData#shakeCamera(float, float, CameraShake)}
|
||||||
public FlowerItem(String javaIdentifier, Builder builder) {
|
*/
|
||||||
super(javaIdentifier, builder);
|
public enum CameraShake {
|
||||||
}
|
POSITIONAL,
|
||||||
|
ROTATIONAL
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.bedrock.camera;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent GUI elements on the players HUD display.
|
||||||
|
* These can be hidden using {@link CameraData#hideElement(GuiElement...)},
|
||||||
|
* and one can reset their visibility using {@link CameraData#resetElement(GuiElement...)}.
|
||||||
|
*/
|
||||||
|
public class GuiElement {
|
||||||
|
public static final GuiElement PAPER_DOLL = new GuiElement(0);
|
||||||
|
public static final GuiElement ARMOR = new GuiElement(1);
|
||||||
|
public static final GuiElement TOOL_TIPS = new GuiElement(2);
|
||||||
|
public static final GuiElement TOUCH_CONTROLS = new GuiElement(3);
|
||||||
|
public static final GuiElement CROSSHAIR = new GuiElement(4);
|
||||||
|
public static final GuiElement HOTBAR = new GuiElement(5);
|
||||||
|
public static final GuiElement HEALTH = new GuiElement(6);
|
||||||
|
public static final GuiElement PROGRESS_BAR = new GuiElement(7);
|
||||||
|
public static final GuiElement FOOD_BAR = new GuiElement(8);
|
||||||
|
public static final GuiElement AIR_BUBBLES_BAR = new GuiElement(9);
|
||||||
|
public static final GuiElement VEHICLE_HEALTH = new GuiElement(10);
|
||||||
|
public static final GuiElement EFFECTS_BAR = new GuiElement(11);
|
||||||
|
public static final GuiElement ITEM_TEXT_POPUP = new GuiElement(12);
|
||||||
|
|
||||||
|
private GuiElement(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal use only; don't depend on these values being consistent.
|
||||||
|
*/
|
||||||
|
public int id() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
|
||||||
|
import org.geysermc.geyser.api.block.custom.property.CustomBlockProperty;
|
||||||
|
import org.geysermc.geyser.api.util.CreativeCategory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to store data for a custom block.
|
||||||
|
*/
|
||||||
|
public interface CustomBlockData {
|
||||||
|
/**
|
||||||
|
* Gets the name of the custom block
|
||||||
|
*
|
||||||
|
* @return The name of the custom block.
|
||||||
|
*/
|
||||||
|
@NonNull String name();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the identifier of the custom block
|
||||||
|
*
|
||||||
|
* @return The identifier of the custom block.
|
||||||
|
*/
|
||||||
|
@NonNull String identifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if the custom block is included in the creative inventory
|
||||||
|
*
|
||||||
|
* @return If the custom block is included in the creative inventory.
|
||||||
|
*/
|
||||||
|
boolean includedInCreativeInventory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the block's creative category, or tab id.
|
||||||
|
*
|
||||||
|
* @return the block's creative category
|
||||||
|
*/
|
||||||
|
@Nullable CreativeCategory creativeCategory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the block's creative group.
|
||||||
|
*
|
||||||
|
* @return the block's creative group
|
||||||
|
*/
|
||||||
|
@Nullable String creativeGroup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the components of the custom block
|
||||||
|
*
|
||||||
|
* @return The components of the custom block.
|
||||||
|
*/
|
||||||
|
@Nullable CustomBlockComponents components();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the custom block's map of block property names to CustomBlockProperty
|
||||||
|
* objects
|
||||||
|
*
|
||||||
|
* @return The custom block's map of block property names to CustomBlockProperty objects.
|
||||||
|
*/
|
||||||
|
@NonNull Map<String, CustomBlockProperty<?>> properties();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of the custom block's permutations
|
||||||
|
*
|
||||||
|
* @return The permutations of the custom block.
|
||||||
|
*/
|
||||||
|
@NonNull List<CustomBlockPermutation> permutations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the custom block's default block state
|
||||||
|
*
|
||||||
|
* @return The default block state of the custom block.
|
||||||
|
*/
|
||||||
|
@NonNull CustomBlockState defaultBlockState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a builder for a custom block state
|
||||||
|
*
|
||||||
|
* @return The builder for a custom block state.
|
||||||
|
*/
|
||||||
|
CustomBlockState.@NonNull Builder blockStateBuilder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Builder for CustomBlockData
|
||||||
|
*
|
||||||
|
* @return A CustomBlockData Builder
|
||||||
|
*/
|
||||||
|
static CustomBlockData.Builder builder() {
|
||||||
|
return GeyserApi.api().provider(CustomBlockData.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder name(@NonNull String name);
|
||||||
|
|
||||||
|
Builder includedInCreativeInventory(boolean includedInCreativeInventory);
|
||||||
|
|
||||||
|
Builder creativeCategory(@Nullable CreativeCategory creativeCategory);
|
||||||
|
|
||||||
|
Builder creativeGroup(@Nullable String creativeGroup);
|
||||||
|
|
||||||
|
Builder components(@NonNull CustomBlockComponents components);
|
||||||
|
|
||||||
|
Builder booleanProperty(@NonNull String propertyName);
|
||||||
|
|
||||||
|
Builder intProperty(@NonNull String propertyName, List<Integer> values);
|
||||||
|
|
||||||
|
Builder stringProperty(@NonNull String propertyName, List<String> values);
|
||||||
|
|
||||||
|
Builder permutations(@NonNull List<CustomBlockPermutation> permutations);
|
||||||
|
|
||||||
|
CustomBlockData build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,12 +23,17 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains useful collections for use in Geyser.
|
* This class is used to store a custom block permutations, which contain custom
|
||||||
|
* block components mapped to a Molang query that should return true or false
|
||||||
*
|
*
|
||||||
* Of note are the fixed int maps. Designed for use with block states that are positive and sequential, they do not allow keys to be
|
* @param components The components of the block
|
||||||
* added that are not greater by one versus the previous key. Because of this, speedy operations of {@link java.util.Map#get(java.lang.Object)}
|
* @param condition The Molang query that should return true or false
|
||||||
* and {@link java.util.Map#containsKey(java.lang.Object)} can be performed by simply checking the bounds of the map
|
|
||||||
* size and its "start" integer.
|
|
||||||
*/
|
*/
|
||||||
package org.geysermc.geyser.util.collection;
|
public record CustomBlockPermutation(@NonNull CustomBlockComponents components, @NonNull String condition) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to store a custom block state, which contains CustomBlockData
|
||||||
|
* tied to defined properties and values
|
||||||
|
*/
|
||||||
|
public interface CustomBlockState {
|
||||||
|
/**
|
||||||
|
* Gets the custom block data associated with the state
|
||||||
|
*
|
||||||
|
* @return The custom block data for the state.
|
||||||
|
*/
|
||||||
|
@NonNull CustomBlockData block();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of the state
|
||||||
|
*
|
||||||
|
* @return The name of the state.
|
||||||
|
*/
|
||||||
|
@NonNull String name();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the given property for the state
|
||||||
|
*
|
||||||
|
* @param propertyName the property name
|
||||||
|
* @return the boolean, int, or string property.
|
||||||
|
*/
|
||||||
|
@NonNull <T> T property(@NonNull String propertyName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a map of the properties for the state
|
||||||
|
*
|
||||||
|
* @return The properties for the state.
|
||||||
|
*/
|
||||||
|
@NonNull Map<String, Object> properties();
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder booleanProperty(@NonNull String propertyName, boolean value);
|
||||||
|
|
||||||
|
Builder intProperty(@NonNull String propertyName, int value);
|
||||||
|
|
||||||
|
Builder stringProperty(@NonNull String propertyName, @NonNull String value);
|
||||||
|
|
||||||
|
CustomBlockState build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
|
||||||
|
import org.geysermc.geyser.api.util.CreativeCategory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a completely custom block that is not based on an existing vanilla Minecraft block.
|
||||||
|
*/
|
||||||
|
public interface NonVanillaCustomBlockData extends CustomBlockData {
|
||||||
|
/**
|
||||||
|
* Gets the namespace of the custom block
|
||||||
|
*
|
||||||
|
* @return The namespace of the custom block.
|
||||||
|
*/
|
||||||
|
@NonNull String namespace();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Builder for NonVanillaCustomBlockData
|
||||||
|
*
|
||||||
|
* @return A NonVanillaCustomBlockData Builder
|
||||||
|
*/
|
||||||
|
static NonVanillaCustomBlockData.Builder builder() {
|
||||||
|
return GeyserApi.api().provider(NonVanillaCustomBlockData.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder extends CustomBlockData.Builder {
|
||||||
|
|
||||||
|
Builder namespace(@NonNull String namespace);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder name(@NonNull String name);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder includedInCreativeInventory(boolean includedInCreativeInventory);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder creativeCategory(@Nullable CreativeCategory creativeCategory);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder creativeGroup(@Nullable String creativeGroup);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder components(@NonNull CustomBlockComponents components);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder booleanProperty(@NonNull String propertyName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder intProperty(@NonNull String propertyName, List<Integer> values);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder stringProperty(@NonNull String propertyName, List<String> values);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder permutations(@NonNull List<CustomBlockPermutation> permutations);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
NonVanillaCustomBlockData build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,42 +23,47 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.util;
|
package org.geysermc.geyser.api.block.custom.component;
|
||||||
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
public final class JavaCodecUtil {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate over a Java Edition codec and return each entry as a CompoundTag
|
* This class is used to store a box component for the selection and
|
||||||
|
* collision boxes of a custom block.
|
||||||
|
*
|
||||||
|
* @param originX The origin X of the box
|
||||||
|
* @param originY The origin Y of the box
|
||||||
|
* @param originZ The origin Z of the box
|
||||||
|
* @param sizeX The size X of the box
|
||||||
|
* @param sizeY The size Y of the box
|
||||||
|
* @param sizeZ The size Z of the box
|
||||||
*/
|
*/
|
||||||
public static Iterable<CompoundTag> iterateAsTag(CompoundTag tag) {
|
public record BoxComponent(float originX, float originY, float originZ, float sizeX, float sizeY, float sizeZ) {
|
||||||
ListTag value = tag.get("value");
|
private static final BoxComponent FULL_BOX = new BoxComponent(-8, 0, -8, 16, 16, 16);
|
||||||
Iterator<Tag> originalIterator = value.iterator();
|
private static final BoxComponent EMPTY_BOX = new BoxComponent(0, 0, 0, 0, 0, 0);
|
||||||
return new Iterable<>() {
|
|
||||||
@Nonnull
|
/**
|
||||||
@Override
|
* Gets a full box component
|
||||||
public Iterator<CompoundTag> iterator() {
|
*
|
||||||
return new Iterator<>() {
|
* @return A full box component
|
||||||
@Override
|
*/
|
||||||
public boolean hasNext() {
|
public static BoxComponent fullBox() {
|
||||||
return originalIterator.hasNext();
|
return FULL_BOX;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public CompoundTag next() {
|
* Gets an empty box component
|
||||||
return (CompoundTag) originalIterator.next();
|
*
|
||||||
}
|
* @return An empty box component
|
||||||
};
|
*/
|
||||||
}
|
public static BoxComponent emptyBox() {
|
||||||
};
|
return EMPTY_BOX;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JavaCodecUtil() {
|
/**
|
||||||
|
* Gets if the box component is empty
|
||||||
|
*
|
||||||
|
* @return If the box component is empty.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return sizeX == 0 && sizeY == 0 && sizeZ == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom.component;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to store components for a custom block or custom block permutation.
|
||||||
|
*/
|
||||||
|
public interface CustomBlockComponents {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the selection box component
|
||||||
|
* Equivalent to "minecraft:selection_box"
|
||||||
|
*
|
||||||
|
* @return The selection box.
|
||||||
|
*/
|
||||||
|
@Nullable BoxComponent selectionBox();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the collision box component
|
||||||
|
* Equivalent to "minecraft:collision_box"
|
||||||
|
* @return The collision box.
|
||||||
|
*/
|
||||||
|
@Nullable BoxComponent collisionBox();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the display name component
|
||||||
|
* Equivalent to "minecraft:display_name"
|
||||||
|
*
|
||||||
|
* @return The display name.
|
||||||
|
*/
|
||||||
|
@Nullable String displayName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the geometry component
|
||||||
|
* Equivalent to "minecraft:geometry"
|
||||||
|
*
|
||||||
|
* @return The geometry.
|
||||||
|
*/
|
||||||
|
@Nullable GeometryComponent geometry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the material instances component
|
||||||
|
* Equivalent to "minecraft:material_instances"
|
||||||
|
*
|
||||||
|
* @return The material instances.
|
||||||
|
*/
|
||||||
|
@NonNull Map<String, MaterialInstance> materialInstances();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the placement filter component
|
||||||
|
* Equivalent to "minecraft:placement_filter"
|
||||||
|
*
|
||||||
|
* @return The placement filter.
|
||||||
|
*/
|
||||||
|
@Nullable List<PlacementConditions> placementFilter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the destructible by mining component
|
||||||
|
* Equivalent to "minecraft:destructible_by_mining"
|
||||||
|
*
|
||||||
|
* @return The destructible by mining value.
|
||||||
|
*/
|
||||||
|
@Nullable Float destructibleByMining();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the friction component
|
||||||
|
* Equivalent to "minecraft:friction"
|
||||||
|
*
|
||||||
|
* @return The friction value.
|
||||||
|
*/
|
||||||
|
@Nullable Float friction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the light emission component
|
||||||
|
* Equivalent to "minecraft:light_emission"
|
||||||
|
*
|
||||||
|
* @return The light emission value.
|
||||||
|
*/
|
||||||
|
@Nullable Integer lightEmission();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the light dampening component
|
||||||
|
* Equivalent to "minecraft:light_dampening"
|
||||||
|
*
|
||||||
|
* @return The light dampening value.
|
||||||
|
*/
|
||||||
|
@Nullable Integer lightDampening();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the transformation component
|
||||||
|
* Equivalent to "minecraft:transformation"
|
||||||
|
*
|
||||||
|
* @return The transformation.
|
||||||
|
*/
|
||||||
|
@Nullable TransformationComponent transformation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the unit cube component
|
||||||
|
* Equivalent to "minecraft:unit_cube"
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #geometry()} and compare with `minecraft:geometry.full_block` instead.
|
||||||
|
*
|
||||||
|
* @return The rotation.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
boolean unitCube();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if the block should place only air
|
||||||
|
* Equivalent to setting a dummy event to run on "minecraft:on_player_placing"
|
||||||
|
*
|
||||||
|
* @return If the block should place only air.
|
||||||
|
*/
|
||||||
|
boolean placeAir();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the set of tags
|
||||||
|
* Equivalent to "tag:some_tag"
|
||||||
|
*
|
||||||
|
* @return The set of tags.
|
||||||
|
*/
|
||||||
|
@NonNull Set<String> tags();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Builder for CustomBlockComponents
|
||||||
|
*
|
||||||
|
* @return A CustomBlockComponents Builder
|
||||||
|
*/
|
||||||
|
static CustomBlockComponents.Builder builder() {
|
||||||
|
return GeyserApi.api().provider(CustomBlockComponents.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder selectionBox(BoxComponent selectionBox);
|
||||||
|
|
||||||
|
Builder collisionBox(BoxComponent collisionBox);
|
||||||
|
|
||||||
|
Builder displayName(String displayName);
|
||||||
|
|
||||||
|
Builder geometry(GeometryComponent geometry);
|
||||||
|
|
||||||
|
Builder materialInstance(@NonNull String name, @NonNull MaterialInstance materialInstance);
|
||||||
|
|
||||||
|
Builder placementFilter(List<PlacementConditions> placementConditions);
|
||||||
|
|
||||||
|
Builder destructibleByMining(Float destructibleByMining);
|
||||||
|
|
||||||
|
Builder friction(Float friction);
|
||||||
|
|
||||||
|
Builder lightEmission(Integer lightEmission);
|
||||||
|
|
||||||
|
Builder lightDampening(Integer lightDampening);
|
||||||
|
|
||||||
|
Builder transformation(TransformationComponent transformation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #geometry(GeometryComponent)} with `minecraft:geometry.full_block` instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
Builder unitCube(boolean unitCube);
|
||||||
|
|
||||||
|
Builder placeAir(boolean placeAir);
|
||||||
|
|
||||||
|
Builder tags(@Nullable Set<String> tags);
|
||||||
|
|
||||||
|
CustomBlockComponents build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
@ -23,37 +23,47 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.sponge.command;
|
package org.geysermc.geyser.api.block.custom.component;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.spongepowered.api.command.CommandCause;
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
import java.util.Map;
|
||||||
public class SpongeCommandSource implements GeyserCommandSource {
|
|
||||||
|
|
||||||
private final CommandCause handle;
|
/**
|
||||||
|
* This class is used to store data for a geometry component.
|
||||||
|
*/
|
||||||
|
public interface GeometryComponent {
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public String name() {
|
* Gets the identifier of the geometry
|
||||||
return handle.friendlyIdentifier().orElse(handle.identifier());
|
*
|
||||||
|
* @return The identifier of the geometry.
|
||||||
|
*/
|
||||||
|
@NonNull String identifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the bone visibility of the geometry
|
||||||
|
*
|
||||||
|
* @return The bone visibility of the geometry.
|
||||||
|
*/
|
||||||
|
@Nullable Map<String, String> boneVisibility();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a builder for GeometryComponent
|
||||||
|
*
|
||||||
|
* @return a builder for GeometryComponent.
|
||||||
|
*/
|
||||||
|
static GeometryComponent.Builder builder() {
|
||||||
|
return GeyserApi.api().provider(GeometryComponent.Builder.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
interface Builder {
|
||||||
public void sendMessage(@NonNull String message) {
|
Builder identifier(@NonNull String identifier);
|
||||||
handle.audience().sendMessage(LegacyComponentSerializer.legacySection().deserialize(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
Builder boneVisibility(@Nullable Map<String, String> boneVisibility);
|
||||||
public boolean isConsole() {
|
|
||||||
return !(handle.cause().root() instanceof ServerPlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
GeometryComponent build();
|
||||||
public boolean hasPermission(String permission) {
|
|
||||||
return handle.hasPermission(permission);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom.component;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to store data for a material instance.
|
||||||
|
*/
|
||||||
|
public interface MaterialInstance {
|
||||||
|
/**
|
||||||
|
* Gets the texture of the block
|
||||||
|
*
|
||||||
|
* @return The texture of the block.
|
||||||
|
*/
|
||||||
|
@Nullable String texture();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the render method of the block
|
||||||
|
*
|
||||||
|
* @return The render method of the block.
|
||||||
|
*/
|
||||||
|
@Nullable String renderMethod();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if the block should be dimmed on certain faces
|
||||||
|
*
|
||||||
|
* @return If the block should be dimmed on certain faces.
|
||||||
|
*/
|
||||||
|
boolean faceDimming();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if the block should have ambient occlusion
|
||||||
|
*
|
||||||
|
* @return If the block should have ambient occlusion.
|
||||||
|
*/
|
||||||
|
boolean ambientOcclusion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a builder for MaterialInstance.
|
||||||
|
*
|
||||||
|
* @return a builder for MaterialInstance
|
||||||
|
*/
|
||||||
|
static MaterialInstance.Builder builder() {
|
||||||
|
return GeyserApi.api().provider(MaterialInstance.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder texture(@Nullable String texture);
|
||||||
|
|
||||||
|
Builder renderMethod(@Nullable String renderMethod);
|
||||||
|
|
||||||
|
Builder faceDimming(boolean faceDimming);
|
||||||
|
|
||||||
|
Builder ambientOcclusion(boolean ambientOcclusion);
|
||||||
|
|
||||||
|
MaterialInstance build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom.component;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to store conditions for a placement filter for a custom block.
|
||||||
|
*
|
||||||
|
* @param allowedFaces The faces that the block can be placed on
|
||||||
|
* @param blockFilters The block filters that control what blocks the block can be placed on
|
||||||
|
*/
|
||||||
|
public record PlacementConditions(@NonNull Set<Face> allowedFaces, @NonNull LinkedHashMap<String, BlockFilterType> blockFilters) {
|
||||||
|
public enum Face {
|
||||||
|
DOWN,
|
||||||
|
UP,
|
||||||
|
NORTH,
|
||||||
|
SOUTH,
|
||||||
|
WEST,
|
||||||
|
EAST
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BlockFilterType {
|
||||||
|
BLOCK,
|
||||||
|
TAG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom.component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to store the transformation component of a block
|
||||||
|
*
|
||||||
|
* @param rx The rotation on the x axis
|
||||||
|
* @param ry The rotation on the y axis
|
||||||
|
* @param rz The rotation on the z axis
|
||||||
|
* @param sx The scale on the x axis
|
||||||
|
* @param sy The scale on the y axis
|
||||||
|
* @param sz The scale on the z axis
|
||||||
|
* @param tx The translation on the x axis
|
||||||
|
* @param ty The translation on the y axis
|
||||||
|
* @param tz The translation on the z axis
|
||||||
|
*/
|
||||||
|
public record TransformationComponent(int rx, int ry, int rz, float sx, float sy, float sz, float tx, float ty, float tz) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new TransformationComponent with the rotation values and assumes default scale and translation
|
||||||
|
*
|
||||||
|
* @param rx The rotation on the x axis
|
||||||
|
* @param ry The rotation on the y axis
|
||||||
|
* @param rz The rotation on the z axis
|
||||||
|
*/
|
||||||
|
public TransformationComponent(int rx, int ry, int rz) {
|
||||||
|
this(rx, ry, rz, 1, 1, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new TransformationComponent with the rotation and scale values and assumes default translation
|
||||||
|
*
|
||||||
|
* @param rx The rotation on the x axis
|
||||||
|
* @param ry The rotation on the y axis
|
||||||
|
* @param rz The rotation on the z axis
|
||||||
|
* @param sx The scale on the x axis
|
||||||
|
* @param sy The scale on the y axis
|
||||||
|
* @param sz The scale on the z axis
|
||||||
|
*/
|
||||||
|
public TransformationComponent(int rx, int ry, int rz, float sx, float sy, float sz) {
|
||||||
|
this(rx, ry, rz, sx, sy, sz, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.geysermc.geyser.api.block.custom.nonvanilla;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.index.qual.NonNegative;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
public record JavaBlockItem(@NonNull String identifier, @NonNegative int javaId, @NonNegative int stackSize) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
package org.geysermc.geyser.api.block.custom.nonvanilla;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.index.qual.NonNegative;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
public interface JavaBlockState {
|
||||||
|
/**
|
||||||
|
* Gets the identifier of the block state
|
||||||
|
*
|
||||||
|
* @return the identifier of the block state
|
||||||
|
*/
|
||||||
|
@NonNull String identifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Java ID of the block state
|
||||||
|
*
|
||||||
|
* @return the Java ID of the block state
|
||||||
|
*/
|
||||||
|
@NonNegative int javaId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the state group ID of the block state
|
||||||
|
*
|
||||||
|
* @return the state group ID of the block state
|
||||||
|
*/
|
||||||
|
@NonNegative int stateGroupId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the block hardness of the block state
|
||||||
|
*
|
||||||
|
* @return the block hardness of the block state
|
||||||
|
*/
|
||||||
|
@NonNegative float blockHardness();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the block state is waterlogged
|
||||||
|
*
|
||||||
|
* @return whether the block state is waterlogged
|
||||||
|
*/
|
||||||
|
boolean waterlogged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the collision of the block state
|
||||||
|
*
|
||||||
|
* @return the collision of the block state
|
||||||
|
*/
|
||||||
|
@NonNull JavaBoundingBox[] collision();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the block state can be broken with hand
|
||||||
|
*
|
||||||
|
* @return whether the block state can be broken with hand
|
||||||
|
*/
|
||||||
|
boolean canBreakWithHand();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the pick item of the block state
|
||||||
|
*
|
||||||
|
* @return the pick item of the block state
|
||||||
|
*/
|
||||||
|
@Nullable String pickItem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the piston behavior of the block state
|
||||||
|
*
|
||||||
|
* @return the piston behavior of the block state
|
||||||
|
*/
|
||||||
|
@Nullable String pistonBehavior();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the block state has block entity
|
||||||
|
*
|
||||||
|
* @return whether the block state has block entity
|
||||||
|
* @deprecated Does not have an effect. If you were using this to
|
||||||
|
* set piston behavior, use {@link #pistonBehavior()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
boolean hasBlockEntity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link JavaBlockState.Builder} instance
|
||||||
|
*
|
||||||
|
* @return a new {@link JavaBlockState.Builder} instance
|
||||||
|
*/
|
||||||
|
static JavaBlockState.Builder builder() {
|
||||||
|
return GeyserApi.api().provider(JavaBlockState.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder identifier(@NonNull String identifier);
|
||||||
|
|
||||||
|
Builder javaId(@NonNegative int javaId);
|
||||||
|
|
||||||
|
Builder stateGroupId(@NonNegative int stateGroupId);
|
||||||
|
|
||||||
|
Builder blockHardness(@NonNegative float blockHardness);
|
||||||
|
|
||||||
|
Builder waterlogged(boolean waterlogged);
|
||||||
|
|
||||||
|
Builder collision(@NonNull JavaBoundingBox[] collision);
|
||||||
|
|
||||||
|
Builder canBreakWithHand(boolean canBreakWithHand);
|
||||||
|
|
||||||
|
Builder pickItem(@Nullable String pickItem);
|
||||||
|
|
||||||
|
Builder pistonBehavior(@Nullable String pistonBehavior);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Does not have an effect. If you were using this to
|
||||||
|
* * set piston behavior, use {@link #pistonBehavior(String)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
Builder hasBlockEntity(boolean hasBlockEntity);
|
||||||
|
|
||||||
|
JavaBlockState build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
package org.geysermc.geyser.api.block.custom.nonvanilla;
|
||||||
|
|
||||||
|
public record JavaBoundingBox(double middleX, double middleY, double middleZ, double sizeX, double sizeY, double sizeZ) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom.property;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to store a property of a custom block of a generic type.
|
||||||
|
*/
|
||||||
|
public interface CustomBlockProperty<T> {
|
||||||
|
/**
|
||||||
|
* Gets the name of the property
|
||||||
|
*
|
||||||
|
* @return The name of the property.
|
||||||
|
*/
|
||||||
|
@NonNull String name();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the values of the property
|
||||||
|
*
|
||||||
|
* @return The values of the property.
|
||||||
|
*/
|
||||||
|
@NonNull List<T> values();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of the property
|
||||||
|
*
|
||||||
|
* @return The type of the property.
|
||||||
|
*/
|
||||||
|
@NonNull PropertyType type();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.block.custom.property;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to define a custom block property's type.
|
||||||
|
*/
|
||||||
|
public class PropertyType {
|
||||||
|
private static final PropertyType BOOLEAN = new PropertyType(Boolean.class);
|
||||||
|
private static final PropertyType INTEGER = new PropertyType(Integer.class);
|
||||||
|
private static final PropertyType STRING = new PropertyType(String.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the property type for a boolean.
|
||||||
|
*
|
||||||
|
* @return The property type for a boolean.
|
||||||
|
*/
|
||||||
|
@NonNull public static PropertyType booleanProp() {
|
||||||
|
return BOOLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the property type for an integer.
|
||||||
|
*
|
||||||
|
* @return The property type for an integer.
|
||||||
|
*/
|
||||||
|
@NonNull public static PropertyType integerProp() {
|
||||||
|
return INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the property type for a string.
|
||||||
|
*
|
||||||
|
* @return The property type for a string.
|
||||||
|
*/
|
||||||
|
@NonNull public static PropertyType stringProp() {
|
||||||
|
return STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Class<?> typeClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the class of the property type
|
||||||
|
*
|
||||||
|
* @return The class of the property type.
|
||||||
|
*/
|
||||||
|
@NonNull public Class<?> typeClass() {
|
||||||
|
return typeClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PropertyType(Class<?> typeClass) {
|
||||||
|
this.typeClass = typeClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,27 +29,107 @@ import org.checkerframework.checker.index.qual.NonNegative;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.api.connection.Connection;
|
import org.geysermc.api.connection.Connection;
|
||||||
|
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||||
|
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||||
import org.geysermc.geyser.api.command.CommandSource;
|
import org.geysermc.geyser.api.command.CommandSource;
|
||||||
|
import org.geysermc.geyser.api.entity.EntityData;
|
||||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a player connection used in Geyser.
|
* Represents a player connection used in Geyser.
|
||||||
*/
|
*/
|
||||||
public interface GeyserConnection extends Connection, CommandSource {
|
public interface GeyserConnection extends Connection, CommandSource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes the {@link CameraData} for this connection.
|
||||||
|
* It allows you to send fogs, camera shakes, force camera perspectives, and more.
|
||||||
|
*
|
||||||
|
* @return the CameraData for this connection.
|
||||||
|
*/
|
||||||
|
@NonNull CameraData camera();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes the {@link EntityData} for this connection.
|
||||||
|
* It allows you to get entities by their Java entity ID, show emotes, and get the player entity.
|
||||||
|
*
|
||||||
|
* @return the EntityData for this connection.
|
||||||
|
*/
|
||||||
|
@NonNull EntityData entities();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param javaId the Java entity ID to look up.
|
* @param javaId the Java entity ID to look up.
|
||||||
* @return a {@link GeyserEntity} if present in this connection's entity tracker.
|
* @return a {@link GeyserEntity} if present in this connection's entity tracker.
|
||||||
|
* @deprecated Use {@link EntityData#entityByJavaId(int)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@NonNull
|
@NonNull
|
||||||
CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
|
CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Displays a player entity as emoting to this client.
|
||||||
*
|
*
|
||||||
* @param emoter the player entity emoting.
|
* @param emoter the player entity emoting.
|
||||||
* @param emoteId the emote ID to send to the client.
|
* @param emoteId the emote ID to send to this client.
|
||||||
|
* @deprecated use {@link EntityData#showEmote(GeyserPlayerEntity, String)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId);
|
void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shakes the client's camera.
|
||||||
|
* <p>
|
||||||
|
* If the camera is already shaking with the same {@link CameraShake} type, then the additional intensity
|
||||||
|
* will be layered on top of the existing intensity, with their own distinct durations.<br>
|
||||||
|
* If the existing shake type is different and the new intensity/duration are not positive, the existing shake only
|
||||||
|
* switches to the new type. Otherwise, the existing shake is completely overridden.
|
||||||
|
*
|
||||||
|
* @param intensity the intensity of the shake. The client has a maximum total intensity of 4.
|
||||||
|
* @param duration the time in seconds that the shake will occur for
|
||||||
|
* @param type the type of shake
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link CameraData#shakeCamera(float, float, CameraShake)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
void shakeCamera(float intensity, float duration, @NonNull CameraShake type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops all camera shake of any type.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link CameraData#stopCameraShake()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
void stopCameraShake();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given fog IDs to the fog cache, then sends all fog IDs in the cache to the client.
|
||||||
|
* <p>
|
||||||
|
* Fog IDs can be found <a href="https://wiki.bedrock.dev/documentation/fog-ids.html">here</a>
|
||||||
|
*
|
||||||
|
* @param fogNameSpaces the fog IDs to add. If empty, the existing cached IDs will still be sent.
|
||||||
|
* @deprecated Use {@link CameraData#sendFog(String...)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
void sendFog(String... fogNameSpaces);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given fog IDs from the fog cache, then sends all fog IDs in the cache to the client.
|
||||||
|
*
|
||||||
|
* @param fogNameSpaces the fog IDs to remove. If empty, all fog IDs will be removed.
|
||||||
|
* @deprecated Use {@link CameraData#removeFog(String...)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
void removeFog(String... fogNameSpaces);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an immutable copy of all fog affects currently applied to this client.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link CameraData#fogEffects()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@NonNull
|
||||||
|
Set<String> fogEffects();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.entity;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.index.qual.NonNegative;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
|
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||||
|
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds all the methods that relate to entities.
|
||||||
|
* Can be accessed through {@link GeyserConnection#entities()}.
|
||||||
|
*/
|
||||||
|
public interface EntityData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link GeyserEntity} to e.g. make them play an emote.
|
||||||
|
*
|
||||||
|
* @param javaId the Java entity ID to look up
|
||||||
|
* @return a {@link GeyserEntity} if present in this connection's entity tracker
|
||||||
|
*/
|
||||||
|
@NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a player entity as emoting to this client.
|
||||||
|
*
|
||||||
|
* @param emoter the player entity emoting
|
||||||
|
* @param emoteId the emote ID to send to this client
|
||||||
|
*/
|
||||||
|
void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link GeyserPlayerEntity} of this connection.
|
||||||
|
*
|
||||||
|
* @return the {@link GeyserPlayerEntity} of this connection
|
||||||
|
*/
|
||||||
|
@NonNull GeyserPlayerEntity playerEntity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Un)locks the client's movement inputs, so that they cannot move.
|
||||||
|
* To ensure that movement is only unlocked when all locks are released, you must supply
|
||||||
|
* a UUID with this method, and use the same UUID to unlock the camera.
|
||||||
|
*
|
||||||
|
* @param lock whether to lock the movement
|
||||||
|
* @param owner the owner of the lock
|
||||||
|
* @return if the movement is locked after this method call
|
||||||
|
*/
|
||||||
|
boolean lockMovement(boolean lock, @NonNull UUID owner);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the client's movement is currently locked.
|
||||||
|
*
|
||||||
|
* @return whether the movement is locked
|
||||||
|
*/
|
||||||
|
boolean isMovementLocked();
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,15 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.api.entity.type.player;
|
package org.geysermc.geyser.api.entity.type.player;
|
||||||
|
|
||||||
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||||
|
|
||||||
public interface GeyserPlayerEntity extends GeyserEntity {
|
public interface GeyserPlayerEntity extends GeyserEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position of the player.
|
||||||
|
*
|
||||||
|
* @return the position of the player.
|
||||||
|
*/
|
||||||
|
Vector3f position();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ package org.geysermc.geyser.api.event;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.event.Event;
|
import org.geysermc.event.Event;
|
||||||
import org.geysermc.event.bus.OwnedEventBus;
|
import org.geysermc.event.bus.OwnedEventBus;
|
||||||
import org.geysermc.geyser.api.extension.Extension;
|
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ package org.geysermc.geyser.api.event;
|
||||||
|
|
||||||
import org.geysermc.event.Event;
|
import org.geysermc.event.Event;
|
||||||
import org.geysermc.event.subscribe.OwnedSubscriber;
|
import org.geysermc.event.subscribe.OwnedSubscriber;
|
||||||
import org.geysermc.geyser.api.extension.Extension;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a subscribed listener to a {@link Event}. Wraps around
|
* Represents a subscribed listener to a {@link Event}. Wraps around
|
||||||
|
|
|
||||||
|
|
@ -23,31 +23,39 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.item.type;
|
package org.geysermc.geyser.api.event.bedrock;
|
||||||
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
|
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||||
|
|
||||||
public class ChestItem extends BlockItem {
|
/**
|
||||||
|
* Called when a Geyser session disconnects.
|
||||||
|
*/
|
||||||
|
public class SessionDisconnectEvent extends ConnectionEvent {
|
||||||
|
private String disconnectReason;
|
||||||
|
|
||||||
public ChestItem(String javaIdentifier, Builder builder) {
|
public SessionDisconnectEvent(@NonNull GeyserConnection connection, @NonNull String reason) {
|
||||||
super(javaIdentifier, builder);
|
super(connection);
|
||||||
|
this.disconnectReason = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void translateNbtToBedrock(@NonNull GeyserSession session, @NonNull CompoundTag tag) {
|
* Gets the disconnect reason.
|
||||||
super.translateNbtToBedrock(session, tag);
|
*
|
||||||
|
* @return the reason for the disconnect
|
||||||
|
*/
|
||||||
|
public @NonNull String disconnectReason() {
|
||||||
|
return disconnectReason;
|
||||||
|
}
|
||||||
|
|
||||||
// Strip the BlockEntityTag from the chests contents
|
/**
|
||||||
// sent to the client. The client does not parse this
|
* Sets the disconnect reason, thereby overriding th original reason.
|
||||||
// or use it for anything, as this tag is fully
|
*
|
||||||
// server-side, so we remove it to reduce bandwidth and
|
* @param disconnectReason the reason for the disconnect
|
||||||
// solve potential issues with very large tags.
|
*/
|
||||||
|
public void disconnectReason(@NonNull String disconnectReason) {
|
||||||
// There was a problem in the past where this would strip
|
this.disconnectReason = disconnectReason;
|
||||||
// NBT data in creative mode, however with the new server
|
|
||||||
// authoritative inventories, this is no longer a concern.
|
|
||||||
tag.remove("BlockEntityTag");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,18 +32,27 @@ import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||||
import org.geysermc.geyser.api.network.RemoteServer;
|
import org.geysermc.geyser.api.network.RemoteServer;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a session has logged in, and is about to connect to a remote java server.
|
* Called when a session has logged in, and is about to connect to a remote Java server.
|
||||||
* This event is cancellable, and can be used to prevent the player from connecting to the remote server.
|
* This event is cancellable, and can be used to prevent the player from connecting to the remote server.
|
||||||
*/
|
*/
|
||||||
public final class SessionLoginEvent extends ConnectionEvent implements Cancellable {
|
public final class SessionLoginEvent extends ConnectionEvent implements Cancellable {
|
||||||
private RemoteServer remoteServer;
|
private RemoteServer remoteServer;
|
||||||
private boolean cancelled;
|
private boolean cancelled;
|
||||||
private String disconnectReason;
|
private String disconnectReason;
|
||||||
|
private Map<String, byte[]> cookies;
|
||||||
|
private boolean transferring;
|
||||||
|
|
||||||
public SessionLoginEvent(@NonNull GeyserConnection connection, @NonNull RemoteServer remoteServer) {
|
public SessionLoginEvent(@NonNull GeyserConnection connection,
|
||||||
|
@NonNull RemoteServer remoteServer,
|
||||||
|
@NonNull Map<String, byte[]> cookies) {
|
||||||
super(connection);
|
super(connection);
|
||||||
this.remoteServer = remoteServer;
|
this.remoteServer = remoteServer;
|
||||||
|
this.cookies = cookies;
|
||||||
|
this.transferring = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -90,9 +99,9 @@ public final class SessionLoginEvent extends ConnectionEvent implements Cancella
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link RemoteServer} the section will attempt to connect to.
|
* Gets the {@link RemoteServer} the session will attempt to connect to.
|
||||||
*
|
*
|
||||||
* @return the {@link RemoteServer} the section will attempt to connect to.
|
* @return the {@link RemoteServer} the session will attempt to connect to.
|
||||||
*/
|
*/
|
||||||
public @NonNull RemoteServer remoteServer() {
|
public @NonNull RemoteServer remoteServer() {
|
||||||
return this.remoteServer;
|
return this.remoteServer;
|
||||||
|
|
@ -106,4 +115,36 @@ public final class SessionLoginEvent extends ConnectionEvent implements Cancella
|
||||||
public void remoteServer(@NonNull RemoteServer remoteServer) {
|
public void remoteServer(@NonNull RemoteServer remoteServer) {
|
||||||
this.remoteServer = remoteServer;
|
this.remoteServer = remoteServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a map of cookies from a possible previous session. The Java server can send and request these
|
||||||
|
* to store information on the client across server transfers.
|
||||||
|
*/
|
||||||
|
public void cookies(@NonNull Map<String, byte[]> cookies) {
|
||||||
|
Objects.requireNonNull(cookies);
|
||||||
|
this.cookies = cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a map of the sessions cookies, if set.
|
||||||
|
* @return the connections cookies
|
||||||
|
*/
|
||||||
|
public @NonNull Map<String, byte[]> cookies() {
|
||||||
|
return cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the connection intent of the connection
|
||||||
|
*/
|
||||||
|
public void transferring(boolean transferring) {
|
||||||
|
this.transferring = transferring;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether this login attempt to the Java server
|
||||||
|
* has the transfer intent
|
||||||
|
*/
|
||||||
|
public boolean transferring() {
|
||||||
|
return this.transferring;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.event.bedrock;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
|
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||||
|
import org.geysermc.geyser.api.skin.Cape;
|
||||||
|
import org.geysermc.geyser.api.skin.Skin;
|
||||||
|
import org.geysermc.geyser.api.skin.SkinData;
|
||||||
|
import org.geysermc.geyser.api.skin.SkinGeometry;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a skin is applied to a player.
|
||||||
|
* <p>
|
||||||
|
* Won't be called when a fake player is spawned for a player skull.
|
||||||
|
*/
|
||||||
|
public abstract class SessionSkinApplyEvent extends ConnectionEvent {
|
||||||
|
|
||||||
|
private final String username;
|
||||||
|
private final UUID uuid;
|
||||||
|
private final boolean slim;
|
||||||
|
private final boolean bedrock;
|
||||||
|
private final SkinData originalSkinData;
|
||||||
|
|
||||||
|
public SessionSkinApplyEvent(@NonNull GeyserConnection connection, String username, UUID uuid, boolean slim, boolean bedrock, SkinData skinData) {
|
||||||
|
super(connection);
|
||||||
|
this.username = username;
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.slim = slim;
|
||||||
|
this.bedrock = bedrock;
|
||||||
|
this.originalSkinData = skinData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The username of the player.
|
||||||
|
*
|
||||||
|
* @return the username of the player
|
||||||
|
*/
|
||||||
|
public @NonNull String username() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The UUID of the player.
|
||||||
|
*
|
||||||
|
* @return the UUID of the player
|
||||||
|
*/
|
||||||
|
public @NonNull UUID uuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the player is using a slim model.
|
||||||
|
*
|
||||||
|
* @return if the player is using a slim model
|
||||||
|
*/
|
||||||
|
public boolean slim() {
|
||||||
|
return slim;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the player is a Bedrock player.
|
||||||
|
*
|
||||||
|
* @return if the player is a Bedrock player
|
||||||
|
*/
|
||||||
|
public boolean bedrock() {
|
||||||
|
return bedrock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The original skin data of the player.
|
||||||
|
*
|
||||||
|
* @return the original skin data of the player
|
||||||
|
*/
|
||||||
|
public @NonNull SkinData originalSkin() {
|
||||||
|
return originalSkinData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The skin data of the player.
|
||||||
|
*
|
||||||
|
* @return the current skin data of the player
|
||||||
|
*/
|
||||||
|
public abstract @NonNull SkinData skinData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the skin of the player.
|
||||||
|
*
|
||||||
|
* @param newSkin the new skin
|
||||||
|
*/
|
||||||
|
public abstract void skin(@NonNull Skin newSkin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the cape of the player.
|
||||||
|
*
|
||||||
|
* @param newCape the new cape
|
||||||
|
*/
|
||||||
|
public abstract void cape(@NonNull Cape newCape);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the geometry of the player.
|
||||||
|
*
|
||||||
|
* @param newGeometry the new geometry
|
||||||
|
*/
|
||||||
|
public abstract void geometry(@NonNull SkinGeometry newGeometry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the geometry of the player.
|
||||||
|
* <p>
|
||||||
|
* Constructs a generic {@link SkinGeometry} object with the given data.
|
||||||
|
*
|
||||||
|
* @param geometryName the name of the geometry
|
||||||
|
* @param geometryData the data of the geometry
|
||||||
|
*/
|
||||||
|
public void geometry(@NonNull String geometryName, @NonNull String geometryData) {
|
||||||
|
geometry(new SkinGeometry("{\"geometry\" :{\"default\" :\"" + geometryName + "\"}}", geometryData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.event.connection;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.event.Cancellable;
|
||||||
|
import org.geysermc.event.Event;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever a client attempts to connect to the server, before the connection is accepted.
|
||||||
|
*/
|
||||||
|
public final class ConnectionRequestEvent implements Event, Cancellable {
|
||||||
|
|
||||||
|
private boolean cancelled;
|
||||||
|
private final InetSocketAddress ip;
|
||||||
|
private final InetSocketAddress proxyIp;
|
||||||
|
|
||||||
|
public ConnectionRequestEvent(@NonNull InetSocketAddress ip, @Nullable InetSocketAddress proxyIp) {
|
||||||
|
this.ip = ip;
|
||||||
|
this.proxyIp = proxyIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IP address of the client attempting to connect
|
||||||
|
*
|
||||||
|
* @return the IP address of the client attempting to connect
|
||||||
|
* @deprecated Use {@link #inetSocketAddress()} instead
|
||||||
|
*/
|
||||||
|
@NonNull @Deprecated(forRemoval = true)
|
||||||
|
public InetSocketAddress getInetSocketAddress() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IP address of the proxy handling the connection. It will return null if there is no proxy.
|
||||||
|
*
|
||||||
|
* @return the IP address of the proxy handling the connection
|
||||||
|
* @deprecated Use {@link #proxyIp()} instead
|
||||||
|
*/
|
||||||
|
@Nullable @Deprecated(forRemoval = true)
|
||||||
|
public InetSocketAddress getProxyIp() {
|
||||||
|
return proxyIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IP address of the client attempting to connect
|
||||||
|
*
|
||||||
|
* @return the IP address of the client attempting to connect
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public InetSocketAddress inetSocketAddress() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IP address of the proxy handling the connection. It will return null if there is no proxy.
|
||||||
|
*
|
||||||
|
* @return the IP address of the proxy handling the connection
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public InetSocketAddress proxyIp() {
|
||||||
|
return proxyIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cancel status of this event. If this event is cancelled, the connection will be rejected.
|
||||||
|
*
|
||||||
|
* @return the cancel status of this event
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the cancel status of this event. If this event is canceled, the connection will be rejected.
|
||||||
|
*
|
||||||
|
* @param cancelled the cancel status of this event.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setCancelled(boolean cancelled) {
|
||||||
|
this.cancelled = cancelled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,7 +34,7 @@ import java.net.InetSocketAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called whenever Geyser gets pinged
|
* Called whenever Geyser gets pinged
|
||||||
*
|
* <p>
|
||||||
* This event allows you to modify/obtain the MOTD, maximum player count, and current number of players online,
|
* This event allows you to modify/obtain the MOTD, maximum player count, and current number of players online,
|
||||||
* Geyser will reply to the client with what was given.
|
* Geyser will reply to the client with what was given.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.event.java;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.checkerframework.common.value.qual.IntRange;
|
||||||
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
|
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the Java server sends a transfer request to a different Java server.
|
||||||
|
* Geyser Extensions can listen to this event and set a target server ip/port for Bedrock players to be transferred to.
|
||||||
|
*/
|
||||||
|
public class ServerTransferEvent extends ConnectionEvent {
|
||||||
|
|
||||||
|
private final String host;
|
||||||
|
private final int port;
|
||||||
|
private String bedrockHost;
|
||||||
|
private int bedrockPort;
|
||||||
|
private final Map<String, byte[]> cookies;
|
||||||
|
|
||||||
|
public ServerTransferEvent(@NonNull GeyserConnection connection,
|
||||||
|
@NonNull String host, int port, @NonNull Map<String, byte[]> cookies) {
|
||||||
|
super(connection);
|
||||||
|
this.host = host;
|
||||||
|
this.port = port;
|
||||||
|
this.cookies = cookies;
|
||||||
|
this.bedrockHost = null;
|
||||||
|
this.bedrockPort = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The host that the Java server requests a transfer to.
|
||||||
|
*
|
||||||
|
* @return the host
|
||||||
|
*/
|
||||||
|
public @NonNull String host() {
|
||||||
|
return this.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The port that the Java server requests a transfer to.
|
||||||
|
*
|
||||||
|
* @return the port
|
||||||
|
*/
|
||||||
|
public int port() {
|
||||||
|
return this.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The host that the Bedrock player should try and connect to.
|
||||||
|
* If this is not set, the Bedrock player will just be disconnected.
|
||||||
|
*
|
||||||
|
* @return the host where the Bedrock client will be transferred to, or null if not set.
|
||||||
|
*/
|
||||||
|
public @Nullable String bedrockHost() {
|
||||||
|
return this.bedrockHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The port that the Bedrock player should try and connect to.
|
||||||
|
* If this is not set, the Bedrock player will just be disconnected.
|
||||||
|
*
|
||||||
|
* @return the port where the Bedrock client will be transferred to, or -1 if not set.
|
||||||
|
*/
|
||||||
|
public int bedrockPort() {
|
||||||
|
return this.bedrockPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the host for the Bedrock player to be transferred to
|
||||||
|
*/
|
||||||
|
public void bedrockHost(@NonNull String host) {
|
||||||
|
if (host == null || host.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Server address cannot be null or blank");
|
||||||
|
}
|
||||||
|
this.bedrockHost = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the port for the Bedrock player to be transferred to
|
||||||
|
*/
|
||||||
|
public void bedrockPort(@IntRange(from = 0, to = 65535) int port) {
|
||||||
|
if (port < 0 || port > 65535) {
|
||||||
|
throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port);
|
||||||
|
}
|
||||||
|
this.bedrockPort = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a map of the sessions current cookies.
|
||||||
|
*
|
||||||
|
* @return the connections cookies
|
||||||
|
*/
|
||||||
|
public @NonNull Map<String, byte[]> cookies() {
|
||||||
|
return cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when commands are defined within Geyser.
|
* Called when commands are defined within Geyser.
|
||||||
*
|
* <p>
|
||||||
* This event allows you to register new commands using the {@link #register(Command)}
|
* This event allows you to register new commands using the {@link #register(Command)}
|
||||||
* method and retrieve the default commands defined.
|
* method and retrieve the default commands defined.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.event.lifecycle;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||||
|
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
|
||||||
|
import org.geysermc.event.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on Geyser's startup when looking for custom blocks. Custom blocks must be registered through this event.
|
||||||
|
* <p>
|
||||||
|
* This event will not be called if the "add-non-bedrock-items" setting is disabled in the Geyser config.
|
||||||
|
*/
|
||||||
|
public abstract class GeyserDefineCustomBlocksEvent implements Event {
|
||||||
|
/**
|
||||||
|
* Registers the given {@link CustomBlockData} as a custom block
|
||||||
|
*
|
||||||
|
* @param customBlockData the custom block to register
|
||||||
|
*/
|
||||||
|
public abstract void register(@NonNull CustomBlockData customBlockData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given {@link CustomBlockState} as an override for the
|
||||||
|
* given java state identifier
|
||||||
|
* Java state identifiers are listed
|
||||||
|
* <a href="https://raw.githubusercontent.com/GeyserMC/mappings/master/blocks.json">here</a>
|
||||||
|
*
|
||||||
|
* @param javaIdentifier the java state identifier to override
|
||||||
|
* @param customBlockState the custom block state with which to override java state identifier
|
||||||
|
*/
|
||||||
|
public abstract void registerOverride(@NonNull String javaIdentifier, @NonNull CustomBlockState customBlockState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given {@link CustomBlockData} as an override for the
|
||||||
|
* given java item identifier
|
||||||
|
*
|
||||||
|
* @param javaIdentifier the java item identifier to override
|
||||||
|
* @param customBlockData the custom block data with which to override java item identifier
|
||||||
|
*/
|
||||||
|
public abstract void registerItemOverride(@NonNull String javaIdentifier, @NonNull CustomBlockData customBlockData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given {@link CustomBlockState} as an override for the
|
||||||
|
* given {@link JavaBlockState}
|
||||||
|
*
|
||||||
|
* @param javaBlockState the java block state for the non-vanilla block
|
||||||
|
* @param customBlockState the custom block state with which to override java state identifier
|
||||||
|
*/
|
||||||
|
public abstract void registerOverride(@NonNull JavaBlockState javaBlockState, @NonNull CustomBlockState customBlockState);
|
||||||
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called on Geyser's startup when looking for custom items. Custom items must be registered through this event.
|
* Called on Geyser's startup when looking for custom items. Custom items must be registered through this event.
|
||||||
*
|
* <p>
|
||||||
* This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config.
|
* This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config.
|
||||||
*/
|
*/
|
||||||
public interface GeyserDefineCustomItemsEvent extends Event {
|
public interface GeyserDefineCustomItemsEvent extends Event {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package org.geysermc.geyser.api.event.lifecycle;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.event.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on Geyser's startup when looking for custom skulls. Custom skulls must be registered through this event.
|
||||||
|
* <p>
|
||||||
|
* This event will not be called if the "add-non-bedrock-items" setting is disabled in the Geyser config.
|
||||||
|
*/
|
||||||
|
public abstract class GeyserDefineCustomSkullsEvent implements Event {
|
||||||
|
/**
|
||||||
|
* The type of texture provided
|
||||||
|
*/
|
||||||
|
public enum SkullTextureType {
|
||||||
|
USERNAME,
|
||||||
|
UUID,
|
||||||
|
PROFILE,
|
||||||
|
SKIN_HASH
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given username, UUID, base64 encoded profile, or skin hash as a custom skull blocks
|
||||||
|
* @param texture the username, UUID, base64 encoded profile, or skin hash
|
||||||
|
* @param type the type of texture provided
|
||||||
|
*/
|
||||||
|
public abstract void register(@NonNull String texture, @NonNull SkullTextureType type);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.event.lifecycle;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.event.Event;
|
||||||
|
import org.geysermc.geyser.api.event.EventBus;
|
||||||
|
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||||
|
import org.geysermc.geyser.api.extension.ExtensionManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when Geyser finished reloading and is accepting Bedrock connections again.
|
||||||
|
* Equivalent to the {@link GeyserPostInitializeEvent}
|
||||||
|
*
|
||||||
|
* @param extensionManager the extension manager
|
||||||
|
* @param eventBus the event bus
|
||||||
|
*/
|
||||||
|
public record GeyserPostReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.event.lifecycle;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.event.Event;
|
||||||
|
import org.geysermc.geyser.api.event.EventBus;
|
||||||
|
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||||
|
import org.geysermc.geyser.api.extension.ExtensionManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when Geyser is about to reload. Primarily aimed at extensions, so they can decide on their own what to reload.
|
||||||
|
* After this event is fired, some lifecycle events can be fired again - such as the {@link GeyserLoadResourcePacksEvent}.
|
||||||
|
*
|
||||||
|
* @param extensionManager the extension manager
|
||||||
|
* @param eventBus the event bus
|
||||||
|
*/
|
||||||
|
public record GeyserPreReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
|
||||||
|
}
|
||||||
|
|
@ -36,13 +36,13 @@ import java.util.Collection;
|
||||||
public abstract class ExtensionManager {
|
public abstract class ExtensionManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets an extension with the given name.
|
* Gets an extension by the given ID.
|
||||||
*
|
*
|
||||||
* @param name the name of the extension
|
* @param id the ID of the extension
|
||||||
* @return an extension with the given name
|
* @return an extension with the given ID
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public abstract Extension extension(@NonNull String name);
|
public abstract Extension extension(@NonNull String id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables the given {@link Extension}.
|
* Enables the given {@link Extension}.
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,16 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.api.extension.exception;
|
package org.geysermc.geyser.api.extension.exception;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when an extension's description is invalid.
|
* Thrown when an extension's description is invalid.
|
||||||
*/
|
*/
|
||||||
public class InvalidDescriptionException extends Exception {
|
public class InvalidDescriptionException extends Exception {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public InvalidDescriptionException(Throwable cause) {
|
public InvalidDescriptionException(Throwable cause) {
|
||||||
super(cause);
|
super(cause);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,16 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.api.extension.exception;
|
package org.geysermc.geyser.api.extension.exception;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when an extension is invalid.
|
* Thrown when an extension is invalid.
|
||||||
*/
|
*/
|
||||||
public class InvalidExtensionException extends Exception {
|
public class InvalidExtensionException extends Exception {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public InvalidExtensionException(Throwable cause) {
|
public InvalidExtensionException(Throwable cause) {
|
||||||
super(cause);
|
super(cause);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.api.GeyserApi;
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used to store data for a custom item.
|
* This is used to store data for a custom item.
|
||||||
*/
|
*/
|
||||||
|
|
@ -75,6 +78,20 @@ public interface CustomItemData {
|
||||||
*/
|
*/
|
||||||
boolean displayHandheld();
|
boolean displayHandheld();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's creative category, or tab id.
|
||||||
|
*
|
||||||
|
* @return the item's creative category
|
||||||
|
*/
|
||||||
|
@NonNull OptionalInt creativeCategory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's creative group.
|
||||||
|
*
|
||||||
|
* @return the item's creative group
|
||||||
|
*/
|
||||||
|
@Nullable String creativeGroup();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the item's texture size. This is to resize the item if the texture is not 16x16.
|
* Gets the item's texture size. This is to resize the item if the texture is not 16x16.
|
||||||
*
|
*
|
||||||
|
|
@ -89,6 +106,14 @@ public interface CustomItemData {
|
||||||
*/
|
*/
|
||||||
@Nullable CustomRenderOffsets renderOffsets();
|
@Nullable CustomRenderOffsets renderOffsets();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's set of tags that can be used in Molang.
|
||||||
|
* Equivalent to "tag:some_tag"
|
||||||
|
*
|
||||||
|
* @return the item's tags, if they exist
|
||||||
|
*/
|
||||||
|
@NonNull Set<String> tags();
|
||||||
|
|
||||||
static CustomItemData.Builder builder() {
|
static CustomItemData.Builder builder() {
|
||||||
return GeyserApi.api().provider(CustomItemData.Builder.class);
|
return GeyserApi.api().provider(CustomItemData.Builder.class);
|
||||||
}
|
}
|
||||||
|
|
@ -109,10 +134,16 @@ public interface CustomItemData {
|
||||||
|
|
||||||
Builder displayHandheld(boolean displayHandheld);
|
Builder displayHandheld(boolean displayHandheld);
|
||||||
|
|
||||||
|
Builder creativeCategory(int creativeCategory);
|
||||||
|
|
||||||
|
Builder creativeGroup(@Nullable String creativeGroup);
|
||||||
|
|
||||||
Builder textureSize(int textureSize);
|
Builder textureSize(int textureSize);
|
||||||
|
|
||||||
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
||||||
|
|
||||||
|
Builder tags(@Nullable Set<String> tags);
|
||||||
|
|
||||||
CustomItemData build();
|
CustomItemData build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
@ -30,7 +30,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.api.GeyserApi;
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
import java.util.OptionalInt;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -65,6 +64,14 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
*/
|
*/
|
||||||
int maxDamage();
|
int maxDamage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the attack damage of the item.
|
||||||
|
* This is purely visual, and only applied to tools
|
||||||
|
*
|
||||||
|
* @return the attack damage of the item
|
||||||
|
*/
|
||||||
|
int attackDamage();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the tool type of the item.
|
* Gets the tool type of the item.
|
||||||
*
|
*
|
||||||
|
|
@ -107,20 +114,6 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
*/
|
*/
|
||||||
@Nullable Set<String> repairMaterials();
|
@Nullable Set<String> repairMaterials();
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the item's creative category, or tab id.
|
|
||||||
*
|
|
||||||
* @return the item's creative category
|
|
||||||
*/
|
|
||||||
@NonNull OptionalInt creativeCategory();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the item's creative group.
|
|
||||||
*
|
|
||||||
* @return the item's creative group
|
|
||||||
*/
|
|
||||||
@Nullable String creativeGroup();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets if the item is a hat. This is used to determine if the item should be rendered on the player's head, and
|
* Gets if the item is a hat. This is used to determine if the item should be rendered on the player's head, and
|
||||||
* normally allow the player to equip it. This is not meant for armor.
|
* normally allow the player to equip it. This is not meant for armor.
|
||||||
|
|
@ -168,6 +161,13 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
return displayHandheld();
|
return displayHandheld();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the block the item places.
|
||||||
|
*
|
||||||
|
* @return the block the item places
|
||||||
|
*/
|
||||||
|
String block();
|
||||||
|
|
||||||
static NonVanillaCustomItemData.Builder builder() {
|
static NonVanillaCustomItemData.Builder builder() {
|
||||||
return GeyserApi.api().provider(NonVanillaCustomItemData.Builder.class);
|
return GeyserApi.api().provider(NonVanillaCustomItemData.Builder.class);
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +184,8 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
|
|
||||||
Builder maxDamage(int maxDamage);
|
Builder maxDamage(int maxDamage);
|
||||||
|
|
||||||
|
Builder attackDamage(int attackDamage);
|
||||||
|
|
||||||
Builder toolType(@Nullable String toolType);
|
Builder toolType(@Nullable String toolType);
|
||||||
|
|
||||||
Builder toolTier(@Nullable String toolTier);
|
Builder toolTier(@Nullable String toolTier);
|
||||||
|
|
@ -196,10 +198,6 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
|
|
||||||
Builder repairMaterials(@Nullable Set<String> repairMaterials);
|
Builder repairMaterials(@Nullable Set<String> repairMaterials);
|
||||||
|
|
||||||
Builder creativeCategory(int creativeCategory);
|
|
||||||
|
|
||||||
Builder creativeGroup(@Nullable String creativeGroup);
|
|
||||||
|
|
||||||
Builder hat(boolean isHat);
|
Builder hat(boolean isHat);
|
||||||
|
|
||||||
Builder foil(boolean isFoil);
|
Builder foil(boolean isFoil);
|
||||||
|
|
@ -210,6 +208,8 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
|
|
||||||
Builder chargeable(boolean isChargeable);
|
Builder chargeable(boolean isChargeable);
|
||||||
|
|
||||||
|
Builder block(String block);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #displayHandheld(boolean)} instead.
|
* @deprecated Use {@link #displayHandheld(boolean)} instead.
|
||||||
*/
|
*/
|
||||||
|
|
@ -218,6 +218,12 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
return displayHandheld(isTool);
|
return displayHandheld(isTool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder creativeCategory(int creativeCategory);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder creativeGroup(@Nullable String creativeGroup);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Builder customItemOptions(@NonNull CustomItemOptions customItemOptions);
|
Builder customItemOptions(@NonNull CustomItemOptions customItemOptions);
|
||||||
|
|
||||||
|
|
@ -239,6 +245,9 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
@Override
|
@Override
|
||||||
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder tags(@Nullable Set<String> tags);
|
||||||
|
|
||||||
NonVanillaCustomItemData build();
|
NonVanillaCustomItemData build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,14 @@ public interface BedrockListener {
|
||||||
*/
|
*/
|
||||||
int port();
|
int port();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the broadcast port that's sent to Bedrock clients with the motd.
|
||||||
|
* This is the port that Bedrock clients will connect with. It usually does not differ from the listening port.
|
||||||
|
*
|
||||||
|
* @return the broadcast port
|
||||||
|
*/
|
||||||
|
int broadcastPort();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the primary MOTD shown to Bedrock players if a ping passthrough setting is not enabled.
|
* Gets the primary MOTD shown to Bedrock players if a ping passthrough setting is not enabled.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
||||||
|
|
@ -67,4 +67,11 @@ public interface RemoteServer {
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
AuthType authType();
|
AuthType authType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if we should attempt to resolve the SRV record for this server.
|
||||||
|
*
|
||||||
|
* @return if we should attempt to resolve the SRV record for this server
|
||||||
|
*/
|
||||||
|
boolean resolveSrv();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
40
api/src/main/java/org/geysermc/geyser/api/skin/Cape.java
Normal file
40
api/src/main/java/org/geysermc/geyser/api/skin/Cape.java
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.skin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a cape.
|
||||||
|
*
|
||||||
|
* @param textureUrl The URL of the cape texture
|
||||||
|
* @param capeId The ID of the cape
|
||||||
|
* @param capeData The raw cape image data in ARGB format
|
||||||
|
* @param failed If the cape failed to load, this is for things like fallback capes
|
||||||
|
*/
|
||||||
|
public record Cape(String textureUrl, String capeId, byte[] capeData, boolean failed) {
|
||||||
|
public Cape(String textureUrl, String capeId, byte[] capeData) {
|
||||||
|
this(textureUrl, capeId, capeData, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
@ -23,16 +23,17 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.translator.collision;
|
package org.geysermc.geyser.api.skin;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
/**
|
||||||
|
* Represents a skin.
|
||||||
@EqualsAndHashCode(callSuper = true)
|
*
|
||||||
@CollisionRemapper(regex = "^spawner$")
|
* @param textureUrl The URL/ID of the skin texture
|
||||||
public class SpawnerCollision extends SolidCollision {
|
* @param skinData The raw skin image data in ARGB
|
||||||
public SpawnerCollision(String params) {
|
* @param failed If the skin failed to load, this is for things like fallback skins
|
||||||
super(params);
|
*/
|
||||||
// Increase pushAwayTolerance to work around https://bugs.mojang.com/browse/MCPE-41996
|
public record Skin(String textureUrl, byte[] skinData, boolean failed) {
|
||||||
pushAwayTolerance = 0.0002;
|
public Skin(String textureUrl, byte[] skinData) {
|
||||||
|
this(textureUrl, skinData, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
32
api/src/main/java/org/geysermc/geyser/api/skin/SkinData.java
Normal file
32
api/src/main/java/org/geysermc/geyser/api/skin/SkinData.java
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.skin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a full package of {@link Skin}, {@link Cape}, and {@link SkinGeometry}.
|
||||||
|
*/
|
||||||
|
public record SkinData(Skin skin, Cape cape, SkinGeometry geometry) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.skin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents geometry of a skin.
|
||||||
|
*
|
||||||
|
* @param geometryName The name of the geometry (JSON)
|
||||||
|
* @param geometryData The geometry data (JSON)
|
||||||
|
*/
|
||||||
|
public record SkinGeometry(String geometryName, String geometryData) {
|
||||||
|
|
||||||
|
public static SkinGeometry WIDE = getLegacy(false);
|
||||||
|
public static SkinGeometry SLIM = getLegacy(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate generic geometry
|
||||||
|
*
|
||||||
|
* @param isSlim if true, it will be the slimmer alex model
|
||||||
|
* @return The generic geometry object
|
||||||
|
*/
|
||||||
|
private static SkinGeometry getLegacy(boolean isSlim) {
|
||||||
|
return new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.custom" + (isSlim ? "Slim" : "") + "\"}}", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.util;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the creative menu categories or tabs.
|
||||||
|
*/
|
||||||
|
public enum CreativeCategory {
|
||||||
|
CONSTRUCTION("construction", 1),
|
||||||
|
NATURE("nature", 2),
|
||||||
|
EQUIPMENT("equipment", 3),
|
||||||
|
ITEMS("items", 4),
|
||||||
|
NONE("none", 6);
|
||||||
|
|
||||||
|
private final String internalName;
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
CreativeCategory(String internalName, int id) {
|
||||||
|
this.internalName = internalName;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the internal name of the category.
|
||||||
|
*
|
||||||
|
* @return the name of the category
|
||||||
|
*/
|
||||||
|
public @NonNull String internalName() {
|
||||||
|
return internalName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the internal ID of the category.
|
||||||
|
*
|
||||||
|
* @return the ID of the category
|
||||||
|
*/
|
||||||
|
public int id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.api.util;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Minecraft version.
|
||||||
|
*/
|
||||||
|
public interface MinecraftVersion {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Minecraft version as a String.
|
||||||
|
* Example: "1.20.2", or "1.20.40/1.20.41"
|
||||||
|
*
|
||||||
|
* @return the version string
|
||||||
|
*/
|
||||||
|
@NonNull String versionString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the protocol version of this Minecraft version.
|
||||||
|
*
|
||||||
|
* @return the protocol version
|
||||||
|
*/
|
||||||
|
int protocolVersion();
|
||||||
|
}
|
||||||
|
|
@ -29,11 +29,17 @@ package org.geysermc.geyser.api.util;
|
||||||
* Represents the platform Geyser is running on.
|
* Represents the platform Geyser is running on.
|
||||||
*/
|
*/
|
||||||
public record PlatformType(String platformName) {
|
public record PlatformType(String platformName) {
|
||||||
public static PlatformType ANDROID = new PlatformType("Android");
|
|
||||||
public static PlatformType BUNGEECORD = new PlatformType("BungeeCord");
|
@Deprecated
|
||||||
public static PlatformType FABRIC = new PlatformType("Fabric");
|
public static final PlatformType ANDROID = new PlatformType("Android");
|
||||||
public static PlatformType SPIGOT = new PlatformType("Spigot");
|
public static final PlatformType BUNGEECORD = new PlatformType("BungeeCord");
|
||||||
public static PlatformType SPONGE = new PlatformType("Sponge");
|
public static final PlatformType FABRIC = new PlatformType("Fabric");
|
||||||
public static PlatformType STANDALONE = new PlatformType("Standalone");
|
public static final PlatformType NEOFORGE = new PlatformType("NeoForge");
|
||||||
public static PlatformType VELOCITY = new PlatformType("Velocity");
|
public static final PlatformType SPIGOT = new PlatformType("Spigot");
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static final PlatformType SPONGE = new PlatformType("Sponge");
|
||||||
|
public static final PlatformType STANDALONE = new PlatformType("Standalone");
|
||||||
|
public static final PlatformType VELOCITY = new PlatformType("Velocity");
|
||||||
|
public static final PlatformType VIAPROXY = new PlatformType("ViaProxy");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a way to represent a boolean, but with a non set value added.
|
* This is a way to represent a boolean, but with a non set value added.
|
||||||
* This class was inspired by adventure's version https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/TriState.java
|
* This class was inspired by adventure's <a href="https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/TriState.java">TriState</a>
|
||||||
*/
|
*/
|
||||||
public enum TriState {
|
public enum TriState {
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
|
|
||||||
implementation(libs.adventure.text.serializer.bungeecord)
|
implementation(libs.adventure.text.serializer.bungeecord)
|
||||||
|
compileOnlyApi(libs.bungeecord.proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
platformRelocate("net.md_5.bungee.jni")
|
platformRelocate("net.md_5.bungee.jni")
|
||||||
|
|
@ -22,6 +22,7 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
exclude(dependency("com.google.*:.*"))
|
exclude(dependency("com.google.*:.*"))
|
||||||
|
exclude(dependency("io.netty.incubator:.*"))
|
||||||
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
|
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
|
||||||
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
|
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
|
||||||
exclude(dependency("io.netty:netty-handler:.*"))
|
exclude(dependency("io.netty:netty-handler:.*"))
|
||||||
|
|
@ -33,3 +34,8 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
exclude(dependency("io.netty:netty-resolver-dns:.*"))
|
exclude(dependency("io.netty:netty-resolver-dns:.*"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modrinth {
|
||||||
|
uploadFile.set(tasks.getByPath("shadowJar"))
|
||||||
|
loaders.add("bungeecord")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,18 +32,26 @@ import net.md_5.bungee.protocol.packet.LoginSuccess;
|
||||||
import net.md_5.bungee.protocol.packet.SetCompression;
|
import net.md_5.bungee.protocol.packet.SetCompression;
|
||||||
|
|
||||||
public class GeyserBungeeCompressionDisabler extends ChannelOutboundHandlerAdapter {
|
public class GeyserBungeeCompressionDisabler extends ChannelOutboundHandlerAdapter {
|
||||||
|
private boolean compressionDisabled = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
if (!(msg instanceof SetCompression)) {
|
if (!(msg instanceof SetCompression)) {
|
||||||
if (msg instanceof LoginSuccess) {
|
// Fixes https://github.com/GeyserMC/Geyser/issues/4281
|
||||||
// We're past the point that compression can be enabled
|
// The server may send a LoginDisconnect packet after compression is set.
|
||||||
|
if (!compressionDisabled) {
|
||||||
if (ctx.pipeline().get("compress") != null) {
|
if (ctx.pipeline().get("compress") != null) {
|
||||||
ctx.pipeline().remove("compress");
|
ctx.pipeline().remove("compress");
|
||||||
|
compressionDisabled = true;
|
||||||
}
|
}
|
||||||
if (ctx.pipeline().get("decompress") != null) {
|
if (ctx.pipeline().get("decompress") != null) {
|
||||||
ctx.pipeline().remove("decompress");
|
ctx.pipeline().remove("decompress");
|
||||||
|
compressionDisabled = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg instanceof LoginSuccess) {
|
||||||
|
// We're past the point that compression can be enabled
|
||||||
ctx.pipeline().remove(this);
|
ctx.pipeline().remove(this);
|
||||||
}
|
}
|
||||||
super.write(ctx, msg, promise);
|
super.write(ctx, msg, promise);
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -51,7 +52,8 @@ public class GeyserBungeeDumpInfo extends BootstrapDumpInfo {
|
||||||
this.plugins = new ArrayList<>();
|
this.plugins = new ArrayList<>();
|
||||||
|
|
||||||
for (net.md_5.bungee.api.config.ListenerInfo listener : proxy.getConfig().getListeners()) {
|
for (net.md_5.bungee.api.config.ListenerInfo listener : proxy.getConfig().getListeners()) {
|
||||||
this.listeners.add(new ListenerInfo(listener.getHost().getHostString(), listener.getHost().getPort()));
|
InetSocketAddress address = (InetSocketAddress) listener.getSocketAddress();
|
||||||
|
this.listeners.add(new ListenerInfo(address.getHostString(), address.getPort()));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Plugin plugin : proxy.getPluginManager().getPlugins()) {
|
for (Plugin plugin : proxy.getPluginManager().getPlugins()) {
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import net.md_5.bungee.api.plugin.Listener;
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
import net.md_5.bungee.event.EventHandler;
|
import net.md_5.bungee.event.EventHandler;
|
||||||
import net.md_5.bungee.netty.PipelineUtils;
|
import net.md_5.bungee.netty.PipelineUtils;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.network.netty.GeyserInjector;
|
import org.geysermc.geyser.network.netty.GeyserInjector;
|
||||||
|
|
@ -125,7 +126,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
||||||
.channel(LocalServerChannelWrapper.class)
|
.channel(LocalServerChannelWrapper.class)
|
||||||
.childHandler(new ChannelInitializer<>() {
|
.childHandler(new ChannelInitializer<>() {
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(Channel ch) throws Exception {
|
protected void initChannel(@NonNull Channel ch) throws Exception {
|
||||||
if (proxy.getConfig().getServers() == null) {
|
if (proxy.getConfig().getServers() == null) {
|
||||||
// Proxy hasn't finished loading all plugins - it loads the config after all plugins
|
// Proxy hasn't finished loading all plugins - it loads the config after all plugins
|
||||||
// Probably doesn't need to be translatable?
|
// Probably doesn't need to be translatable?
|
||||||
|
|
|
||||||
|
|
@ -26,22 +26,19 @@
|
||||||
package org.geysermc.geyser.platform.bungeecord;
|
package org.geysermc.geyser.platform.bungeecord;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class GeyserBungeeLogger implements GeyserLogger {
|
public class GeyserBungeeLogger implements GeyserLogger {
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
@Getter @Setter
|
@Getter @Setter
|
||||||
private boolean debug;
|
private boolean debug;
|
||||||
|
|
||||||
public GeyserBungeeLogger(Logger logger, boolean debug) {
|
|
||||||
this.logger = logger;
|
|
||||||
this.debug = debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void severe(String message) {
|
public void severe(String message) {
|
||||||
logger.severe(message);
|
logger.severe(message);
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,12 @@ import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||||
import net.md_5.bungee.api.plugin.Listener;
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.ping.GeyserPingInfo;
|
import org.geysermc.geyser.ping.GeyserPingInfo;
|
||||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
|
@ -61,16 +61,11 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
||||||
}));
|
}));
|
||||||
ProxyPingEvent event = future.join();
|
ProxyPingEvent event = future.join();
|
||||||
ServerPing response = event.getResponse();
|
ServerPing response = event.getResponse();
|
||||||
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(
|
return new GeyserPingInfo(
|
||||||
response.getDescriptionComponent().toLegacyText(),
|
response.getDescriptionComponent().toLegacyText(),
|
||||||
new GeyserPingInfo.Players(response.getPlayers().getMax(), response.getPlayers().getOnline()),
|
response.getPlayers().getMax(),
|
||||||
new GeyserPingInfo.Version(response.getVersion().getName(), response.getVersion().getProtocol())
|
response.getPlayers().getOnline()
|
||||||
);
|
);
|
||||||
if (event.getResponse().getPlayers().getSample() != null) {
|
|
||||||
Arrays.stream(event.getResponse().getPlayers().getSample()).forEach(proxiedPlayer ->
|
|
||||||
geyserPingInfo.getPlayerList().add(proxiedPlayer.getName()));
|
|
||||||
}
|
|
||||||
return geyserPingInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is static so pending connection can use it
|
// This is static so pending connection can use it
|
||||||
|
|
@ -110,7 +105,7 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InetSocketAddress getVirtualHost() {
|
public @Nullable InetSocketAddress getVirtualHost() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,13 @@ import net.md_5.bungee.BungeeCord;
|
||||||
import net.md_5.bungee.api.config.ListenerInfo;
|
import net.md_5.bungee.api.config.ListenerInfo;
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.command.Command;
|
import org.geysermc.geyser.api.command.Command;
|
||||||
import org.geysermc.geyser.api.extension.Extension;
|
import org.geysermc.geyser.api.extension.Extension;
|
||||||
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
|
|
@ -44,7 +45,6 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||||
import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor;
|
import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
import org.geysermc.geyser.util.FileUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -58,65 +58,97 @@ import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
|
|
||||||
private GeyserCommandManager geyserCommandManager;
|
private GeyserCommandManager geyserCommandManager;
|
||||||
private GeyserBungeeConfiguration geyserConfig;
|
private GeyserBungeeConfiguration geyserConfig;
|
||||||
private GeyserBungeeInjector geyserInjector;
|
private GeyserBungeeInjector geyserInjector;
|
||||||
private GeyserBungeeLogger geyserLogger;
|
private final GeyserBungeeLogger geyserLogger = new GeyserBungeeLogger(getLogger());
|
||||||
private IGeyserPingPassthrough geyserBungeePingPassthrough;
|
private IGeyserPingPassthrough geyserBungeePingPassthrough;
|
||||||
|
|
||||||
private GeyserImpl geyser;
|
private GeyserImpl geyser;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
|
onGeyserInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserInitialize() {
|
||||||
GeyserLocale.init(this);
|
GeyserLocale.init(this);
|
||||||
|
|
||||||
// Copied from ViaVersion.
|
// Copied from ViaVersion.
|
||||||
// https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43
|
// https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43
|
||||||
try {
|
try {
|
||||||
ProtocolConstants.class.getField("MINECRAFT_1_19_3");
|
ProtocolConstants.class.getField("MINECRAFT_1_21");
|
||||||
} catch (NoSuchFieldException e) {
|
} catch (NoSuchFieldException e) {
|
||||||
getLogger().warning(" / \\");
|
geyserLogger.error(" / \\");
|
||||||
getLogger().warning(" / \\");
|
geyserLogger.error(" / \\");
|
||||||
getLogger().warning(" / | \\");
|
geyserLogger.error(" / | \\");
|
||||||
getLogger().warning(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
|
geyserLogger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
|
||||||
getLogger().warning(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
geyserLogger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||||
getLogger().warning(" / o \\");
|
geyserLogger.error(" / o \\");
|
||||||
getLogger().warning("/_____________\\");
|
geyserLogger.error("/_____________\\");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!getDataFolder().exists())
|
if (!this.loadConfig()) {
|
||||||
getDataFolder().mkdir();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!getDataFolder().exists())
|
|
||||||
getDataFolder().mkdir();
|
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"),
|
|
||||||
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
|
||||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||||
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
|
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
|
||||||
|
this.geyserInjector = new GeyserBungeeInjector(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
// Remove this in like a year
|
// Big hack - Bungee does not provide us an event to listen to, so schedule a repeating
|
||||||
if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) {
|
// task that waits for a field to be filled which is set after the plugin enable
|
||||||
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/"));
|
// process is complete
|
||||||
|
this.awaitStartupCompletion(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void awaitStartupCompletion(int tries) {
|
||||||
|
// After 20 tries give up waiting. This will happen just after 3 minutes approximately
|
||||||
|
if (tries >= 20) {
|
||||||
|
this.geyserLogger.warning("BungeeCord plugin startup is taking abnormally long, so Geyser is starting now. " +
|
||||||
|
"If all your plugins are loaded properly, this is a bug! " +
|
||||||
|
"If not, consider cutting down the amount of plugins on your proxy as it is causing abnormally slow starting times.");
|
||||||
|
this.onGeyserEnable();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners");
|
||||||
|
listenersField.setAccessible(true);
|
||||||
|
|
||||||
|
Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
|
||||||
|
if (listeners.isEmpty()) {
|
||||||
|
this.getProxy().getScheduler().schedule(this, this::onGeyserEnable, tries, TimeUnit.SECONDS);
|
||||||
|
} else {
|
||||||
|
this.awaitStartupCompletion(++tries);
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onGeyserEnable() {
|
||||||
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
|
if (!loadConfig()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||||
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
} else {
|
||||||
|
// For consistency with other platforms - create command manager before GeyserImpl#start()
|
||||||
|
// This ensures the command events are called before the item/block ones are
|
||||||
|
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||||
|
this.geyserCommandManager.init();
|
||||||
|
}
|
||||||
|
|
||||||
// Force-disable query if enabled, or else Geyser won't enable
|
// Force-disable query if enabled, or else Geyser won't enable
|
||||||
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
|
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
|
||||||
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
|
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
|
||||||
|
|
@ -135,48 +167,21 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Big hack - Bungee does not provide us an event to listen to, so schedule a repeating
|
GeyserImpl.start();
|
||||||
// task that waits for a field to be filled which is set after the plugin enable
|
|
||||||
// process is complete
|
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||||
this.awaitStartupCompletion(0);
|
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
|
} else {
|
||||||
|
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
// No need to re-register commands or re-init injector when reloading
|
||||||
private void awaitStartupCompletion(int tries) {
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
// After 20 tries give up waiting. This will happen
|
|
||||||
// just after 3 minutes approximately
|
|
||||||
if (tries >= 20) {
|
|
||||||
this.geyserLogger.warning("BungeeCord plugin startup is taking abnormally long, so Geyser is starting now. " +
|
|
||||||
"If all your plugins are loaded properly, this is a bug! " +
|
|
||||||
"If not, consider cutting down the amount of plugins on your proxy as it is causing abnormally slow starting times.");
|
|
||||||
this.postStartup();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners");
|
|
||||||
listenersField.setAccessible(true);
|
|
||||||
|
|
||||||
Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
|
|
||||||
if (listeners.isEmpty()) {
|
|
||||||
this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS);
|
|
||||||
} else {
|
|
||||||
this.awaitStartupCompletion(++tries);
|
|
||||||
}
|
|
||||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void postStartup() {
|
|
||||||
GeyserImpl.start();
|
|
||||||
|
|
||||||
this.geyserInjector = new GeyserBungeeInjector(this);
|
|
||||||
this.geyserInjector.initializeLocalChannel(this);
|
this.geyserInjector.initializeLocalChannel(this);
|
||||||
|
|
||||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
|
||||||
this.geyserCommandManager.init();
|
|
||||||
|
|
||||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands()));
|
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands()));
|
||||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||||
Map<String, Command> commands = entry.getValue();
|
Map<String, Command> commands = entry.getValue();
|
||||||
|
|
@ -186,16 +191,17 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
|
|
||||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands));
|
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
@Override
|
||||||
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
public void onGeyserDisable() {
|
||||||
} else {
|
if (geyser != null) {
|
||||||
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
geyser.disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserShutdown() {
|
||||||
if (geyser != null) {
|
if (geyser != null) {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
}
|
}
|
||||||
|
|
@ -204,6 +210,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
this.onGeyserShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserBungeeConfiguration getGeyserConfig() {
|
public GeyserBungeeConfiguration getGeyserConfig() {
|
||||||
return geyserConfig;
|
return geyserConfig;
|
||||||
|
|
@ -245,7 +256,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
return this.geyserInjector.getServerSocketAddress();
|
return this.geyserInjector.getServerSocketAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String getServerBindAddress() {
|
public String getServerBindAddress() {
|
||||||
return findCompatibleListener().map(InetSocketAddress::getHostString).orElse("");
|
return findCompatibleListener().map(InetSocketAddress::getHostString).orElse("");
|
||||||
|
|
@ -271,4 +282,20 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
.map(info -> (InetSocketAddress) info.getSocketAddress())
|
.map(info -> (InetSocketAddress) info.getSocketAddress())
|
||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
private boolean loadConfig() {
|
||||||
|
try {
|
||||||
|
if (!getDataFolder().exists()) //noinspection ResultOfMethodCallIgnored
|
||||||
|
getDataFolder().mkdir();
|
||||||
|
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"),
|
||||||
|
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||||
|
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
|
||||||
|
|
@ -50,7 +51,7 @@ public class BungeeCommandSource implements GeyserCommandSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(String message) {
|
public void sendMessage(@NonNull String message) {
|
||||||
handle.sendMessage(TextComponent.fromLegacyText(message));
|
handle.sendMessage(TextComponent.fromLegacyText(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
import net.fabricmc.loom.task.RemapJarTask
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id("fabric-loom") version "1.0-SNAPSHOT"
|
|
||||||
id("com.modrinth.minotaur") version "2.+"
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
//to change the versions see the gradle.properties file
|
|
||||||
minecraft(libs.fabric.minecraft)
|
|
||||||
mappings(loom.officialMojangMappings())
|
|
||||||
modImplementation(libs.fabric.loader)
|
|
||||||
|
|
||||||
// Fabric API. This is technically optional, but you probably want it anyway.
|
|
||||||
modImplementation(libs.fabric.api)
|
|
||||||
|
|
||||||
// This should be in the libs TOML, but something about modImplementation AND include just doesn't work
|
|
||||||
include(modImplementation("me.lucko", "fabric-permissions-api", "0.2-SNAPSHOT"))
|
|
||||||
|
|
||||||
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
|
|
||||||
// You may need to force-disable transitiveness on them.
|
|
||||||
|
|
||||||
api(projects.core)
|
|
||||||
shadow(projects.core) {
|
|
||||||
exclude(group = "com.google.guava", module = "guava")
|
|
||||||
exclude(group = "com.google.code.gson", module = "gson")
|
|
||||||
exclude(group = "org.slf4j")
|
|
||||||
exclude(group = "com.nukkitx.fastutil")
|
|
||||||
exclude(group = "io.netty.incubator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
maven("https://repo.opencollab.dev/maven-releases/")
|
|
||||||
maven("https://repo.opencollab.dev/maven-snapshots/")
|
|
||||||
maven("https://jitpack.io")
|
|
||||||
maven("https://oss.sonatype.org/content/repositories/snapshots/")
|
|
||||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
|
||||||
}
|
|
||||||
|
|
||||||
application {
|
|
||||||
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
|
|
||||||
// if it is present.
|
|
||||||
// If you remove this task, sources will not be generated.
|
|
||||||
sourcesJar {
|
|
||||||
archiveClassifier.set("sources")
|
|
||||||
from(sourceSets.main.get().allSource)
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
// Mirrors the example fabric project, otherwise tons of dependencies are shaded that shouldn't be
|
|
||||||
configurations = listOf(project.configurations.shadow.get())
|
|
||||||
// The remapped shadowJar is the final desired Geyser-Fabric.jar
|
|
||||||
archiveVersion.set(project.version.toString())
|
|
||||||
archiveClassifier.set("shaded")
|
|
||||||
|
|
||||||
relocate("org.objectweb.asm", "org.geysermc.relocate.asm")
|
|
||||||
relocate("org.yaml", "org.geysermc.relocate.yaml") // https://github.com/CardboardPowered/cardboard/issues/139
|
|
||||||
relocate("com.fasterxml.jackson", "org.geysermc.relocate.jackson")
|
|
||||||
relocate("net.kyori", "org.geysermc.relocate.kyori")
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// Exclude everything EXCEPT some DNS stuff required for HAProxy
|
|
||||||
exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-transport-classes-kqueue:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-transport-native-kqueue:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-handler:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-common:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-buffer:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-resolver:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-transport:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-codec:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-resolver-dns:.*"))
|
|
||||||
exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remapJar {
|
|
||||||
dependsOn(shadowJar)
|
|
||||||
inputFile.set(shadowJar.get().archiveFile)
|
|
||||||
archiveBaseName.set("Geyser-Fabric")
|
|
||||||
archiveVersion.set("")
|
|
||||||
archiveClassifier.set("")
|
|
||||||
}
|
|
||||||
|
|
||||||
register("remapModrinthJar", RemapJarTask::class) {
|
|
||||||
dependsOn(shadowJar)
|
|
||||||
inputFile.set(shadowJar.get().archiveFile)
|
|
||||||
archiveBaseName.set("geyser-fabric")
|
|
||||||
archiveVersion.set(project.version.toString() + "+build." + System.getenv("GITHUB_RUN_NUMBER"))
|
|
||||||
archiveClassifier.set("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modrinth {
|
|
||||||
token.set(System.getenv("MODRINTH_TOKEN")) // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
|
|
||||||
projectId.set("wKkoqHrH")
|
|
||||||
versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER"))
|
|
||||||
versionType.set("beta")
|
|
||||||
changelog.set("A changelog can be found at https://github.com/GeyserMC/Geyser/commits")
|
|
||||||
|
|
||||||
syncBodyFrom.set(rootProject.file("README.md").readText())
|
|
||||||
|
|
||||||
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
|
||||||
gameVersions.addAll("1.20")
|
|
||||||
|
|
||||||
loaders.add("fabric")
|
|
||||||
failSilently.set(true)
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
required.project("fabric-api")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,268 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
* @author GeyserMC
|
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import net.fabricmc.api.EnvType;
|
|
||||||
import net.fabricmc.api.ModInitializer;
|
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
|
||||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
import net.fabricmc.loader.api.ModContainer;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import net.minecraft.commands.Commands;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
|
||||||
import org.geysermc.geyser.api.command.Command;
|
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
|
||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
|
||||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
|
||||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
|
||||||
import org.geysermc.geyser.platform.fabric.command.GeyserFabricCommandExecutor;
|
|
||||||
import org.geysermc.geyser.platform.fabric.world.GeyserFabricWorldManager;
|
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
|
||||||
private static GeyserFabricMod instance;
|
|
||||||
|
|
||||||
private boolean reloading;
|
|
||||||
|
|
||||||
private GeyserImpl geyser;
|
|
||||||
private ModContainer mod;
|
|
||||||
private Path dataFolder;
|
|
||||||
private MinecraftServer server;
|
|
||||||
|
|
||||||
private GeyserCommandManager geyserCommandManager;
|
|
||||||
private GeyserFabricConfiguration geyserConfig;
|
|
||||||
private GeyserFabricLogger geyserLogger;
|
|
||||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
|
||||||
private WorldManager geyserWorldManager;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onInitialize() {
|
|
||||||
instance = this;
|
|
||||||
mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
|
|
||||||
|
|
||||||
this.onEnable();
|
|
||||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
|
|
||||||
// Set as an event so we can get the proper IP and port if needed
|
|
||||||
ServerLifecycleEvents.SERVER_STARTED.register(this::startGeyser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnable() {
|
|
||||||
dataFolder = FabricLoader.getInstance().getConfigDir().resolve("Geyser-Fabric");
|
|
||||||
if (!dataFolder.toFile().exists()) {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
dataFolder.toFile().mkdir();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init dataFolder first as local language overrides call getConfigFolder()
|
|
||||||
GeyserLocale.init(this);
|
|
||||||
|
|
||||||
try {
|
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
|
|
||||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
|
||||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserFabricConfiguration.class);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
LogManager.getLogger("geyser-fabric").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.geyserLogger = new GeyserFabricLogger(geyserConfig.isDebugMode());
|
|
||||||
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this);
|
|
||||||
|
|
||||||
if (server == null) {
|
|
||||||
// Server has yet to start
|
|
||||||
// Register onDisable so players are properly kicked
|
|
||||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onDisable());
|
|
||||||
|
|
||||||
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserFabricUpdateListener.onPlayReady(handler));
|
|
||||||
} else {
|
|
||||||
// Server has started and this is a reload
|
|
||||||
startGeyser(this.server);
|
|
||||||
reloading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize core Geyser.
|
|
||||||
* A function, as it needs to be called in different places depending on if Geyser is being reloaded or not.
|
|
||||||
*
|
|
||||||
* @param server The minecraft server.
|
|
||||||
*/
|
|
||||||
public void startGeyser(MinecraftServer server) {
|
|
||||||
this.server = server;
|
|
||||||
|
|
||||||
GeyserImpl.start();
|
|
||||||
|
|
||||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
|
||||||
|
|
||||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
|
||||||
this.geyserCommandManager.init();
|
|
||||||
|
|
||||||
this.geyserWorldManager = new GeyserFabricWorldManager(server);
|
|
||||||
|
|
||||||
// Start command building
|
|
||||||
// Set just "geyser" as the help command
|
|
||||||
GeyserFabricCommandExecutor helpExecutor = new GeyserFabricCommandExecutor(geyser,
|
|
||||||
(GeyserCommand) geyser.commandManager().getCommands().get("help"));
|
|
||||||
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("geyser").executes(helpExecutor);
|
|
||||||
|
|
||||||
// Register all subcommands as valid
|
|
||||||
for (Map.Entry<String, Command> command : geyser.commandManager().getCommands().entrySet()) {
|
|
||||||
GeyserFabricCommandExecutor executor = new GeyserFabricCommandExecutor(geyser, (GeyserCommand) command.getValue());
|
|
||||||
builder.then(Commands.literal(command.getKey())
|
|
||||||
.executes(executor)
|
|
||||||
// Could also test for Bedrock but depending on when this is called it may backfire
|
|
||||||
.requires(executor::testPermission));
|
|
||||||
}
|
|
||||||
server.getCommands().getDispatcher().register(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisable() {
|
|
||||||
if (geyser != null) {
|
|
||||||
geyser.shutdown();
|
|
||||||
geyser = null;
|
|
||||||
}
|
|
||||||
if (!reloading) {
|
|
||||||
this.server = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GeyserConfiguration getGeyserConfig() {
|
|
||||||
return geyserConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GeyserLogger getGeyserLogger() {
|
|
||||||
return geyserLogger;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GeyserCommandManager getGeyserCommandManager() {
|
|
||||||
return geyserCommandManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IGeyserPingPassthrough getGeyserPingPassthrough() {
|
|
||||||
return geyserPingPassthrough;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WorldManager getWorldManager() {
|
|
||||||
return geyserWorldManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Path getConfigFolder() {
|
|
||||||
return dataFolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BootstrapDumpInfo getDumpInfo() {
|
|
||||||
return new GeyserFabricDumpInfo(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMinecraftServerVersion() {
|
|
||||||
return this.server.getServerVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions") // IDEA thinks that ip cannot be null
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getServerBindAddress() {
|
|
||||||
String ip = this.server.getLocalIp();
|
|
||||||
return ip != null ? ip : ""; // See issue #3812
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getServerPort() {
|
|
||||||
return ((GeyserServerPortGetter) server).geyser$getServerPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean testFloodgatePluginPresent() {
|
|
||||||
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
|
|
||||||
if (floodgate.isPresent()) {
|
|
||||||
geyserConfig.loadFloodgate(this, floodgate.orElse(null));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public InputStream getResourceOrNull(String resource) {
|
|
||||||
// We need to handle this differently, because Fabric shares the classloader across multiple mods
|
|
||||||
Path path = this.mod.findPath(resource).orElse(null);
|
|
||||||
if (path == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return path.getFileSystem()
|
|
||||||
.provider()
|
|
||||||
.newInputStream(path);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReloading(boolean reloading) {
|
|
||||||
this.reloading = reloading;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GeyserFabricMod getInstance() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,279 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
* @author GeyserMC
|
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric.world;
|
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
|
|
||||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.nbt.*;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.item.WritableBookItem;
|
|
||||||
import net.minecraft.world.item.WrittenBookItem;
|
|
||||||
import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
|
||||||
import org.cloudburstmc.nbt.NbtType;
|
|
||||||
import org.geysermc.erosion.util.LecternUtils;
|
|
||||||
import org.geysermc.geyser.level.GeyserWorldManager;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
|
||||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class GeyserFabricWorldManager extends GeyserWorldManager {
|
|
||||||
private final MinecraftServer server;
|
|
||||||
|
|
||||||
public GeyserFabricWorldManager(MinecraftServer server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
|
||||||
server.execute(() -> {
|
|
||||||
ServerPlayer player = getPlayer(session);
|
|
||||||
if (player == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LevelChunk chunk = player.level().getChunk(x, z);
|
|
||||||
final int chunkBlockX = x << 4;
|
|
||||||
final int chunkBlockZ = z << 4;
|
|
||||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
|
||||||
BlockEntityInfo blockEntityInfo = blockEntityInfos.get(i);
|
|
||||||
BlockEntity blockEntity = chunk.getBlockEntity(new BlockPos(chunkBlockX + blockEntityInfo.getX(),
|
|
||||||
blockEntityInfo.getY(), chunkBlockZ + blockEntityInfo.getZ()));
|
|
||||||
sendLecternData(session, blockEntity, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
|
||||||
server.execute(() -> {
|
|
||||||
ServerPlayer player = getPlayer(session);
|
|
||||||
if (player == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockEntity blockEntity = player.level().getBlockEntity(new BlockPos(x, y, z));
|
|
||||||
sendLecternData(session, blockEntity, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendLecternData(GeyserSession session, BlockEntity blockEntity, boolean isChunkLoad) {
|
|
||||||
if (!(blockEntity instanceof LecternBlockEntity lectern)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = blockEntity.getBlockPos().getX();
|
|
||||||
int y = blockEntity.getBlockPos().getY();
|
|
||||||
int z = blockEntity.getBlockPos().getZ();
|
|
||||||
|
|
||||||
if (!lectern.hasBook()) {
|
|
||||||
if (!isChunkLoad) {
|
|
||||||
BlockEntityUtils.updateBlockEntity(session, LecternUtils.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemStack book = lectern.getBook();
|
|
||||||
int pageCount = WrittenBookItem.getPageCount(book);
|
|
||||||
boolean hasBookPages = pageCount > 0;
|
|
||||||
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1);
|
|
||||||
lecternTag.putInt("page", lectern.getPage() / 2);
|
|
||||||
NbtMapBuilder bookTag = NbtMap.builder()
|
|
||||||
.putByte("Count", (byte) book.getCount())
|
|
||||||
.putShort("Damage", (short) 0)
|
|
||||||
.putString("Name", "minecraft:writable_book");
|
|
||||||
List<NbtMap> pages = new ArrayList<>(hasBookPages ? pageCount : 1);
|
|
||||||
if (hasBookPages && WritableBookItem.makeSureTagIsValid(book.getTag())) {
|
|
||||||
ListTag listTag = book.getTag().getList("pages", 8);
|
|
||||||
|
|
||||||
for (int i = 0; i < listTag.size(); i++) {
|
|
||||||
String page = listTag.getString(i);
|
|
||||||
NbtMapBuilder pageBuilder = NbtMap.builder()
|
|
||||||
.putString("photoname", "")
|
|
||||||
.putString("text", page);
|
|
||||||
pages.add(pageBuilder.build());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Empty page
|
|
||||||
NbtMapBuilder pageBuilder = NbtMap.builder()
|
|
||||||
.putString("photoname", "")
|
|
||||||
.putString("text", "");
|
|
||||||
pages.add(pageBuilder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
|
|
||||||
lecternTag.putCompound("book", bookTag.build());
|
|
||||||
NbtMap blockEntityTag = lecternTag.build();
|
|
||||||
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(GeyserSession session, String permission) {
|
|
||||||
ServerPlayer player = getPlayer(session);
|
|
||||||
return Permissions.check(player, permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
|
||||||
CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> future = new CompletableFuture<>();
|
|
||||||
server.execute(() -> {
|
|
||||||
ServerPlayer player = getPlayer(session);
|
|
||||||
if (player == null) {
|
|
||||||
future.complete(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockPos pos = new BlockPos(x, y, z);
|
|
||||||
// Don't create a new block entity if invalid
|
|
||||||
BlockEntity blockEntity = player.level().getChunkAt(pos).getBlockEntity(pos);
|
|
||||||
if (blockEntity instanceof BannerBlockEntity banner) {
|
|
||||||
// Potentially exposes other NBT data? But we need to get the NBT data for the banner patterns *and*
|
|
||||||
// the banner might have a custom name, both of which a Java client knows and caches
|
|
||||||
ItemStack itemStack = banner.getItem();
|
|
||||||
var tag = OpenNbtTagVisitor.convert("", itemStack.getOrCreateTag());
|
|
||||||
|
|
||||||
future.complete(tag);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
future.complete(null);
|
|
||||||
});
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ServerPlayer getPlayer(GeyserSession session) {
|
|
||||||
return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Future considerations: option to clone; would affect arrays
|
|
||||||
private static class OpenNbtTagVisitor implements TagVisitor {
|
|
||||||
private String currentKey;
|
|
||||||
private final com.github.steveice10.opennbt.tag.builtin.CompoundTag root;
|
|
||||||
private com.github.steveice10.opennbt.tag.builtin.Tag currentTag;
|
|
||||||
|
|
||||||
OpenNbtTagVisitor(String key) {
|
|
||||||
root = new com.github.steveice10.opennbt.tag.builtin.CompoundTag(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitString(StringTag stringTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.StringTag(currentKey, stringTag.getAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitByte(ByteTag byteTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.ByteTag(currentKey, byteTag.getAsByte());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitShort(ShortTag shortTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.ShortTag(currentKey, shortTag.getAsShort());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitInt(IntTag intTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.IntTag(currentKey, intTag.getAsInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitLong(LongTag longTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.LongTag(currentKey, longTag.getAsLong());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitFloat(FloatTag floatTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.FloatTag(currentKey, floatTag.getAsFloat());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitDouble(DoubleTag doubleTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.DoubleTag(currentKey, doubleTag.getAsDouble());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitByteArray(ByteArrayTag byteArrayTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.ByteArrayTag(currentKey, byteArrayTag.getAsByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitIntArray(IntArrayTag intArrayTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.IntArrayTag(currentKey, intArrayTag.getAsIntArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitLongArray(LongArrayTag longArrayTag) {
|
|
||||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.LongArrayTag(currentKey, longArrayTag.getAsLongArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitList(ListTag listTag) {
|
|
||||||
var newList = new com.github.steveice10.opennbt.tag.builtin.ListTag(currentKey);
|
|
||||||
for (Tag tag : listTag) {
|
|
||||||
currentKey = "";
|
|
||||||
tag.accept(this);
|
|
||||||
newList.add(currentTag);
|
|
||||||
}
|
|
||||||
currentTag = newList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitCompound(CompoundTag compoundTag) {
|
|
||||||
currentTag = convert(currentKey, compoundTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static com.github.steveice10.opennbt.tag.builtin.CompoundTag convert(String name, CompoundTag compoundTag) {
|
|
||||||
OpenNbtTagVisitor visitor = new OpenNbtTagVisitor(name);
|
|
||||||
for (String key : compoundTag.getAllKeys()) {
|
|
||||||
visitor.currentKey = key;
|
|
||||||
Tag tag = compoundTag.get(key);
|
|
||||||
tag.accept(visitor);
|
|
||||||
visitor.root.put(visitor.currentTag);
|
|
||||||
}
|
|
||||||
return visitor.root;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitEnd(EndTag endTag) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"required": true,
|
|
||||||
"package": "org.geysermc.geyser.platform.fabric.mixin",
|
|
||||||
"compatibilityLevel": "JAVA_16",
|
|
||||||
"client": [
|
|
||||||
"client.IntegratedServerMixin"
|
|
||||||
],
|
|
||||||
"server": [
|
|
||||||
"server.MinecraftDedicatedServerMixin"
|
|
||||||
],
|
|
||||||
"injectors": {
|
|
||||||
"defaultRequire": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
22
bootstrap/mod/build.gradle.kts
Normal file
22
bootstrap/mod/build.gradle.kts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
architectury {
|
||||||
|
common("neoforge", "fabric")
|
||||||
|
}
|
||||||
|
|
||||||
|
loom {
|
||||||
|
mixin.defaultRefmapName.set("geyser-refmap.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
// We don't need these
|
||||||
|
tasks.named("remapModrinthJar").configure {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(projects.core)
|
||||||
|
compileOnly(libs.mixin)
|
||||||
|
|
||||||
|
// Only here to suppress "unknown enum constant EnvType.CLIENT" warnings. DO NOT USE!
|
||||||
|
compileOnly(libs.fabric.loader)
|
||||||
|
}
|
||||||
70
bootstrap/mod/fabric/build.gradle.kts
Normal file
70
bootstrap/mod/fabric/build.gradle.kts
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
plugins {
|
||||||
|
application
|
||||||
|
}
|
||||||
|
|
||||||
|
architectury {
|
||||||
|
platformSetupLoomIde()
|
||||||
|
fabric()
|
||||||
|
}
|
||||||
|
|
||||||
|
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
modImplementation(libs.fabric.loader)
|
||||||
|
modApi(libs.fabric.api)
|
||||||
|
|
||||||
|
api(project(":mod", configuration = "namedElements"))
|
||||||
|
shadow(project(path = ":mod", configuration = "transformProductionFabric")) {
|
||||||
|
isTransitive = false
|
||||||
|
}
|
||||||
|
shadow(projects.core) { isTransitive = false }
|
||||||
|
includeTransitive(projects.core)
|
||||||
|
|
||||||
|
// These are NOT transitively included, and instead shadowed + relocated.
|
||||||
|
// Avoids fabric complaining about non-SemVer versioning
|
||||||
|
shadow(libs.protocol.connection) { isTransitive = false }
|
||||||
|
shadow(libs.protocol.common) { isTransitive = false }
|
||||||
|
shadow(libs.protocol.codec) { isTransitive = false }
|
||||||
|
shadow(libs.mcauthlib) { isTransitive = false }
|
||||||
|
shadow(libs.raknet) { isTransitive = false }
|
||||||
|
|
||||||
|
// Consequences of shading + relocating mcauthlib: shadow/relocate mcpl!
|
||||||
|
shadow(libs.mcprotocollib) { isTransitive = false }
|
||||||
|
|
||||||
|
// Since we also relocate cloudburst protocol: shade erosion common
|
||||||
|
shadow(libs.erosion.common) { isTransitive = false }
|
||||||
|
|
||||||
|
// Let's shade in our own api/common module
|
||||||
|
shadow(projects.api) { isTransitive = false }
|
||||||
|
shadow(projects.common) { isTransitive = false }
|
||||||
|
|
||||||
|
// Permissions
|
||||||
|
modImplementation(libs.fabric.permissions)
|
||||||
|
include(libs.fabric.permissions)
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
|
||||||
|
}
|
||||||
|
|
||||||
|
relocate("org.cloudburstmc.netty")
|
||||||
|
relocate("org.cloudburstmc.protocol")
|
||||||
|
relocate("com.github.steveice10.mc.auth")
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
remapJar {
|
||||||
|
archiveBaseName.set("Geyser-Fabric")
|
||||||
|
}
|
||||||
|
|
||||||
|
remapModrinthJar {
|
||||||
|
archiveBaseName.set("geyser-fabric")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modrinth {
|
||||||
|
loaders.add("fabric")
|
||||||
|
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
||||||
|
dependencies {
|
||||||
|
required.project("fabric-api")
|
||||||
|
}
|
||||||
|
}
|
||||||
1
bootstrap/mod/fabric/gradle.properties
Normal file
1
bootstrap/mod/fabric/gradle.properties
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
loom.platform=fabric
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.platform.fabric;
|
||||||
|
|
||||||
|
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
|
||||||
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||||
|
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
|
||||||
|
|
||||||
|
public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInitializer {
|
||||||
|
|
||||||
|
public GeyserFabricBootstrap() {
|
||||||
|
super(new GeyserFabricPlatform());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitialize() {
|
||||||
|
if (isServer()) {
|
||||||
|
// Set as an event, so we can get the proper IP and port if needed
|
||||||
|
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
|
||||||
|
this.setServer(server);
|
||||||
|
onGeyserEnable();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ClientLifecycleEvents.CLIENT_STOPPING.register(($)-> {
|
||||||
|
onGeyserShutdown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are only registered once
|
||||||
|
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> {
|
||||||
|
if (isServer()) {
|
||||||
|
onGeyserShutdown();
|
||||||
|
} else {
|
||||||
|
onGeyserDisable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserModUpdateListener.onPlayReady(handler.getPlayer()));
|
||||||
|
|
||||||
|
this.onGeyserInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isServer() {
|
||||||
|
return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
|
||||||
|
return Permissions.check(source, permissionNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
|
||||||
|
return Permissions.check(source, permissionNode, permissionLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -43,21 +43,27 @@ import java.util.stream.Collectors;
|
||||||
@Getter
|
@Getter
|
||||||
public class GeyserFabricDumpInfo extends BootstrapDumpInfo {
|
public class GeyserFabricDumpInfo extends BootstrapDumpInfo {
|
||||||
|
|
||||||
private String platformVersion = null;
|
private final String platformName;
|
||||||
|
private String platformVersion;
|
||||||
|
private final String minecraftVersion;
|
||||||
private final EnvType environmentType;
|
private final EnvType environmentType;
|
||||||
|
|
||||||
@AsteriskSerializer.Asterisk(isIp = true)
|
@AsteriskSerializer.Asterisk(isIp = true)
|
||||||
private final String serverIP;
|
private final String serverIP;
|
||||||
private final int serverPort;
|
private final int serverPort;
|
||||||
|
private final boolean onlineMode;
|
||||||
private final List<ModInfo> mods;
|
private final List<ModInfo> mods;
|
||||||
|
|
||||||
public GeyserFabricDumpInfo(MinecraftServer server) {
|
public GeyserFabricDumpInfo(MinecraftServer server) {
|
||||||
|
this.platformName = server.getServerModName();
|
||||||
FabricLoader.getInstance().getModContainer("fabricloader").ifPresent(mod ->
|
FabricLoader.getInstance().getModContainer("fabricloader").ifPresent(mod ->
|
||||||
this.platformVersion = mod.getMetadata().getVersion().getFriendlyString());
|
this.platformVersion = mod.getMetadata().getVersion().getFriendlyString());
|
||||||
|
|
||||||
|
this.minecraftVersion = server.getServerVersion();
|
||||||
this.environmentType = FabricLoader.getInstance().getEnvironmentType();
|
this.environmentType = FabricLoader.getInstance().getEnvironmentType();
|
||||||
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
|
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
|
||||||
this.serverPort = server.getPort();
|
this.serverPort = server.getPort();
|
||||||
|
this.onlineMode = server.usesAuthentication();
|
||||||
this.mods = new ArrayList<>();
|
this.mods = new ArrayList<>();
|
||||||
|
|
||||||
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {
|
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.platform.fabric;
|
||||||
|
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.fabricmc.loader.api.ModContainer;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
|
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||||
|
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class GeyserFabricPlatform implements GeyserModPlatform {
|
||||||
|
|
||||||
|
private final ModContainer mod;
|
||||||
|
|
||||||
|
public GeyserFabricPlatform() {
|
||||||
|
this.mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull PlatformType platformType() {
|
||||||
|
return PlatformType.FABRIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String configPath() {
|
||||||
|
return "Geyser-Fabric";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Path dataFolder(@NonNull String modId) {
|
||||||
|
return FabricLoader.getInstance().getConfigDir().resolve(modId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull BootstrapDumpInfo dumpInfo(@NonNull MinecraftServer server) {
|
||||||
|
return new GeyserFabricDumpInfo(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
|
||||||
|
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
|
||||||
|
if (floodgate.isPresent()) {
|
||||||
|
Path floodgateDataFolder = FabricLoader.getInstance().getConfigDir().resolve("floodgate");
|
||||||
|
bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable InputStream resolveResource(@NonNull String resource) {
|
||||||
|
// We need to handle this differently, because Fabric shares the classloader across multiple mods
|
||||||
|
Path path = this.mod.findPath(resource).orElse(null);
|
||||||
|
if (path == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return path.getFileSystem()
|
||||||
|
.provider()
|
||||||
|
.newInputStream(path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,23 +9,22 @@
|
||||||
],
|
],
|
||||||
"contact": {
|
"contact": {
|
||||||
"website": "${url}",
|
"website": "${url}",
|
||||||
"repo": "https://github.com/GeyserMC/Geyser-Fabric"
|
"repo": "https://github.com/GeyserMC/Geyser"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "assets/geyser-fabric/icon.png",
|
"icon": "assets/geyser/icon.png",
|
||||||
"environment": "*",
|
"environment": "*",
|
||||||
"entrypoints": {
|
"entrypoints": {
|
||||||
"main": [
|
"main": [
|
||||||
"org.geysermc.geyser.platform.fabric.GeyserFabricMod"
|
"org.geysermc.geyser.platform.fabric.GeyserFabricBootstrap"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"geyser-fabric.mixins.json"
|
"geyser.mixins.json"
|
||||||
],
|
],
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.14.21",
|
"fabricloader": ">=0.15.11",
|
||||||
"fabric": "*",
|
"fabric": "*",
|
||||||
"minecraft": ">=1.20",
|
"minecraft": ">=1.21"
|
||||||
"fabric-permissions-api-v0": "*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
59
bootstrap/mod/neoforge/build.gradle.kts
Normal file
59
bootstrap/mod/neoforge/build.gradle.kts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
plugins {
|
||||||
|
application
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is provided by "org.cloudburstmc.math.mutable" too, so yeet.
|
||||||
|
// NeoForge's class loader is *really* annoying.
|
||||||
|
provided("org.cloudburstmc.math", "api")
|
||||||
|
|
||||||
|
architectury {
|
||||||
|
platformSetupLoomIde()
|
||||||
|
neoForge()
|
||||||
|
}
|
||||||
|
|
||||||
|
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// See https://github.com/google/guava/issues/6618
|
||||||
|
modules {
|
||||||
|
module("com.google.guava:listenablefuture") {
|
||||||
|
replacedBy("com.google.guava:guava", "listenablefuture is part of guava")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
neoForge(libs.neoforge.minecraft)
|
||||||
|
|
||||||
|
api(project(":mod", configuration = "namedElements"))
|
||||||
|
shadow(project(path = ":mod", configuration = "transformProductionNeoForge")) {
|
||||||
|
isTransitive = false
|
||||||
|
}
|
||||||
|
shadow(projects.core) { isTransitive = false }
|
||||||
|
|
||||||
|
// Let's shade in our own api
|
||||||
|
shadow(projects.api) { isTransitive = false }
|
||||||
|
|
||||||
|
// cannot be shaded, since neoforge will complain if floodgate-neoforge tries to provide this
|
||||||
|
include(projects.common)
|
||||||
|
|
||||||
|
// Include all transitive deps of core via JiJ
|
||||||
|
includeTransitive(projects.core)
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass.set("org.geysermc.geyser.platform.forge.GeyserNeoForgeMain")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
remapJar {
|
||||||
|
archiveBaseName.set("Geyser-NeoForge")
|
||||||
|
}
|
||||||
|
|
||||||
|
remapModrinthJar {
|
||||||
|
archiveBaseName.set("geyser-neoforge")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modrinth {
|
||||||
|
loaders.add("neoforge")
|
||||||
|
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
||||||
|
}
|
||||||
1
bootstrap/mod/neoforge/gradle.properties
Normal file
1
bootstrap/mod/neoforge/gradle.properties
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
loom.platform=neoforge
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.platform.neoforge;
|
||||||
|
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.neoforged.fml.ModContainer;
|
||||||
|
import net.neoforged.fml.common.Mod;
|
||||||
|
import net.neoforged.fml.loading.FMLLoader;
|
||||||
|
import net.neoforged.neoforge.common.NeoForge;
|
||||||
|
import net.neoforged.neoforge.event.GameShuttingDownEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||||
|
import net.neoforged.neoforge.event.server.ServerStartedEvent;
|
||||||
|
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||||
|
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
|
||||||
|
|
||||||
|
@Mod(ModConstants.MOD_ID)
|
||||||
|
public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
|
||||||
|
|
||||||
|
private final GeyserNeoForgePermissionHandler permissionHandler = new GeyserNeoForgePermissionHandler();
|
||||||
|
|
||||||
|
public GeyserNeoForgeBootstrap(ModContainer container) {
|
||||||
|
super(new GeyserNeoForgePlatform(container));
|
||||||
|
|
||||||
|
if (isServer()) {
|
||||||
|
// Set as an event so we can get the proper IP and port if needed
|
||||||
|
NeoForge.EVENT_BUS.addListener(this::onServerStarted);
|
||||||
|
} else {
|
||||||
|
NeoForge.EVENT_BUS.addListener(this::onClientStopping);
|
||||||
|
}
|
||||||
|
|
||||||
|
NeoForge.EVENT_BUS.addListener(this::onServerStopping);
|
||||||
|
NeoForge.EVENT_BUS.addListener(this::onPlayerJoin);
|
||||||
|
NeoForge.EVENT_BUS.addListener(this.permissionHandler::onPermissionGather);
|
||||||
|
|
||||||
|
this.onGeyserInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onServerStarted(ServerStartedEvent event) {
|
||||||
|
this.setServer(event.getServer());
|
||||||
|
this.onGeyserEnable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onServerStopping(ServerStoppingEvent event) {
|
||||||
|
if (isServer()) {
|
||||||
|
this.onGeyserShutdown();
|
||||||
|
} else {
|
||||||
|
this.onGeyserDisable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onClientStopping(GameShuttingDownEvent ignored) {
|
||||||
|
this.onGeyserShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||||
|
GeyserModUpdateListener.onPlayReady(event.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isServer() {
|
||||||
|
return FMLLoader.getDist().isDedicatedServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
|
||||||
|
return this.permissionHandler.hasPermission(source, permissionNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
|
||||||
|
return this.permissionHandler.hasPermission(source, permissionNode, permissionLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.platform.neoforge;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.neoforged.api.distmarker.Dist;
|
||||||
|
import net.neoforged.fml.ModList;
|
||||||
|
import net.neoforged.fml.loading.FMLLoader;
|
||||||
|
import net.neoforged.neoforgespi.language.IModInfo;
|
||||||
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
|
import org.geysermc.geyser.text.AsteriskSerializer;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class GeyserNeoForgeDumpInfo extends BootstrapDumpInfo {
|
||||||
|
|
||||||
|
private final String platformName;
|
||||||
|
private final String platformVersion;
|
||||||
|
private final String minecraftVersion;
|
||||||
|
private final Dist dist;
|
||||||
|
|
||||||
|
@AsteriskSerializer.Asterisk(isIp = true)
|
||||||
|
private final String serverIP;
|
||||||
|
private final int serverPort;
|
||||||
|
private final boolean onlineMode;
|
||||||
|
private final List<ModInfo> mods;
|
||||||
|
|
||||||
|
public GeyserNeoForgeDumpInfo(MinecraftServer server) {
|
||||||
|
this.platformName = FMLLoader.launcherHandlerName();
|
||||||
|
this.platformVersion = FMLLoader.versionInfo().neoForgeVersion();
|
||||||
|
this.minecraftVersion = FMLLoader.versionInfo().mcVersion();
|
||||||
|
this.dist = FMLLoader.getDist();
|
||||||
|
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
|
||||||
|
this.serverPort = server.getPort();
|
||||||
|
this.onlineMode = server.usesAuthentication();
|
||||||
|
this.mods = new ArrayList<>();
|
||||||
|
|
||||||
|
for (IModInfo mod : ModList.get().getMods()) {
|
||||||
|
this.mods.add(new ModInfo(
|
||||||
|
ModList.get().isLoaded(mod.getModId()),
|
||||||
|
mod.getModId(),
|
||||||
|
mod.getVersion().toString(),
|
||||||
|
mod.getModURL().map(URL::toString).orElse("")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class ModInfo {
|
||||||
|
public boolean enabled;
|
||||||
|
public String name;
|
||||||
|
public String version;
|
||||||
|
public String url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
@ -23,20 +23,22 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.sponge;
|
package org.geysermc.geyser.platform.neoforge;
|
||||||
|
|
||||||
import org.geysermc.geyser.GeyserMain;
|
import org.geysermc.geyser.GeyserMain;
|
||||||
|
|
||||||
public class GeyserSpongeMain extends GeyserMain {
|
public class GeyserNeoForgeMain extends GeyserMain {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
new GeyserSpongeMain().displayMessage();
|
new GeyserNeoForgeMain().displayMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getPluginType() {
|
public String getPluginType() {
|
||||||
return "Sponge";
|
return "NeoForge";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getPluginFolder() {
|
public String getPluginFolder() {
|
||||||
return "mods";
|
return "mods";
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.platform.neoforge;
|
||||||
|
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.neoforged.neoforge.server.permission.PermissionAPI;
|
||||||
|
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
|
||||||
|
import net.neoforged.neoforge.server.permission.nodes.PermissionDynamicContextKey;
|
||||||
|
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
|
||||||
|
import net.neoforged.neoforge.server.permission.nodes.PermissionType;
|
||||||
|
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.Constants;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.command.Command;
|
||||||
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class GeyserNeoForgePermissionHandler {
|
||||||
|
|
||||||
|
private static final Constructor<?> PERMISSION_NODE_CONSTRUCTOR;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
Constructor<PermissionNode> constructor = PermissionNode.class.getDeclaredConstructor(
|
||||||
|
String.class,
|
||||||
|
PermissionType.class,
|
||||||
|
PermissionNode.PermissionResolver.class,
|
||||||
|
PermissionDynamicContextKey[].class
|
||||||
|
);
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
PERMISSION_NODE_CONSTRUCTOR = constructor;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException("Unable to construct PermissionNode!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<String, PermissionNode<Boolean>> permissionNodes = new HashMap<>();
|
||||||
|
|
||||||
|
public void onPermissionGather(PermissionGatherEvent.Nodes event) {
|
||||||
|
this.registerNode(Constants.UPDATE_PERMISSION, event);
|
||||||
|
|
||||||
|
GeyserCommandManager commandManager = GeyserImpl.getInstance().commandManager();
|
||||||
|
for (Map.Entry<String, Command> entry : commandManager.commands().entrySet()) {
|
||||||
|
Command command = entry.getValue();
|
||||||
|
|
||||||
|
// Don't register aliases
|
||||||
|
if (!command.name().equals(entry.getKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.registerNode(command.permission(), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map<String, Command> commands : commandManager.extensionCommands().values()) {
|
||||||
|
for (Map.Entry<String, Command> entry : commands.entrySet()) {
|
||||||
|
Command command = entry.getValue();
|
||||||
|
|
||||||
|
// Don't register aliases
|
||||||
|
if (!command.name().equals(entry.getKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.registerNode(command.permission(), event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
|
||||||
|
PermissionNode<Boolean> node = this.permissionNodes.get(permissionNode);
|
||||||
|
if (node == null) {
|
||||||
|
GeyserImpl.getInstance().getLogger().warning("Unable to find permission node " + permissionNode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PermissionAPI.getPermission((ServerPlayer) source, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
|
||||||
|
if (!source.isPlayer()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
assert source.getPlayer() != null;
|
||||||
|
boolean permission = this.hasPermission(source.getPlayer(), permissionNode);
|
||||||
|
if (!permission) {
|
||||||
|
return source.getPlayer().hasPermissions(permissionLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerNode(String node, PermissionGatherEvent.Nodes event) {
|
||||||
|
PermissionNode<Boolean> permissionNode = this.createNode(node);
|
||||||
|
|
||||||
|
// NeoForge likes to crash if you try and register a duplicate node
|
||||||
|
if (!event.getNodes().contains(permissionNode)) {
|
||||||
|
event.addNodes(permissionNode);
|
||||||
|
this.permissionNodes.put(node, permissionNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private PermissionNode<Boolean> createNode(String node) {
|
||||||
|
// The typical constructors in PermissionNode require a
|
||||||
|
// mod id, which means our permission nodes end up becoming
|
||||||
|
// geyser_neoforge.<node> instead of just <node>. We work around
|
||||||
|
// this by using reflection to access the constructor that
|
||||||
|
// doesn't require a mod id or ResourceLocation.
|
||||||
|
try {
|
||||||
|
return (PermissionNode<Boolean>) PERMISSION_NODE_CONSTRUCTOR.newInstance(
|
||||||
|
node,
|
||||||
|
PermissionTypes.BOOLEAN,
|
||||||
|
(PermissionNode.PermissionResolver<Boolean>) (player, playerUUID, context) -> false,
|
||||||
|
new PermissionDynamicContextKey[0]
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Unable to create permission node " + node, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.platform.neoforge;
|
||||||
|
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.neoforged.fml.ModContainer;
|
||||||
|
import net.neoforged.fml.ModList;
|
||||||
|
import net.neoforged.fml.loading.FMLPaths;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
|
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||||
|
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class GeyserNeoForgePlatform implements GeyserModPlatform {
|
||||||
|
|
||||||
|
private final ModContainer container;
|
||||||
|
|
||||||
|
public GeyserNeoForgePlatform(ModContainer container) {
|
||||||
|
this.container = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull PlatformType platformType() {
|
||||||
|
return PlatformType.NEOFORGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String configPath() {
|
||||||
|
return "Geyser-NeoForge";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Path dataFolder(@NonNull String modId) {
|
||||||
|
return FMLPaths.CONFIGDIR.get().resolve(modId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull BootstrapDumpInfo dumpInfo(@NonNull MinecraftServer server) {
|
||||||
|
return new GeyserNeoForgeDumpInfo(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
|
||||||
|
if (ModList.get().isLoaded("floodgate")) {
|
||||||
|
Path floodgateDataFolder = FMLPaths.CONFIGDIR.get().resolve("floodgate");
|
||||||
|
bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable InputStream resolveResource(@NonNull String resource) {
|
||||||
|
try {
|
||||||
|
Path path = container.getModInfo().getOwningFile().getFile().findResource(resource);
|
||||||
|
return Files.newInputStream(path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue