mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge remote-tracking branch 'refs/remotes/upstream/master' into build-number
# Conflicts: # core/build.gradle.kts
This commit is contained in:
commit
40c67ca23c
710 changed files with 37123 additions and 24378 deletions
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
|
||||||
164
.github/workflows/build.yml
vendored
164
.github/workflows/build.yml
vendored
|
|
@ -3,10 +3,14 @@ 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'
|
||||||
- '.idea/copyright/*.xml'
|
- '.github/workflows/preview.yml'
|
||||||
|
- '.github/workflows/pull-request.yml'
|
||||||
|
- '.idea/copyright/*.xml'
|
||||||
- '.gitignore'
|
- '.gitignore'
|
||||||
- 'CONTRIBUTING.md'
|
- 'CONTRIBUTING.md'
|
||||||
- 'LICENSE'
|
- 'LICENSE'
|
||||||
|
|
@ -18,112 +22,100 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository and submodules
|
- name: Get Release Info
|
||||||
# See https://github.com/actions/checkout/commits
|
id: release-info
|
||||||
uses: actions/checkout@72f2cec99f417b1a1c5e2e88945068983b7965f9
|
uses: GeyserMC/actions/previous-release@master
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
data: ${{ vars.RELEASEACTION_PREVRELEASE }}
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Setup Gradle
|
||||||
# See https://github.com/gradle/wrapper-validation-action/commits
|
uses: GeyserMC/actions/setup-gradle-composite@master
|
||||||
uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4
|
with:
|
||||||
|
setup-java_java-version: 21
|
||||||
|
|
||||||
# See https://github.com/actions/setup-java/commits
|
- name: Build Geyser
|
||||||
- uses: actions/setup-java@4075bfc1b51bf22876335ae1cd589602d60d8758
|
run: ./gradlew build
|
||||||
with:
|
env:
|
||||||
java-version: 17
|
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
|
||||||
distribution: temurin
|
|
||||||
|
|
||||||
- name: Build
|
- name: Archive Artifacts
|
||||||
# See https://github.com/gradle/gradle-build-action/commits
|
uses: GeyserMC/actions/upload-multi-artifact@master
|
||||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
|
||||||
with:
|
|
||||||
arguments: build
|
|
||||||
gradle-home-cache-cleanup: true
|
|
||||||
|
|
||||||
- name: Archive artifacts (Geyser Fabric)
|
|
||||||
# See https://github.com/actions/upload-artifact/commits
|
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
|
||||||
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@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
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@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
|
||||||
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@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser BungeeCord
|
|
||||||
path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser Velocity)
|
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
|
||||||
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@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
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@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
|
||||||
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' }}
|
||||||
# See https://github.com/Tim203/actions-git-discord-webhook/commits
|
uses: GeyserMC/actions/notify-discord@master
|
||||||
uses: Tim203/actions-git-discord-webhook@70f38ded3aca51635ec978ab4e1a58cd4cd0c2ff
|
|
||||||
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 }}
|
||||||
89
.github/workflows/pullrequest.yml
vendored
89
.github/workflows/pullrequest.yml
vendored
|
|
@ -1,89 +0,0 @@
|
||||||
name: Build Pull Request
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up JDK 17
|
|
||||||
# See https://github.com/actions/setup-java/commits
|
|
||||||
uses: actions/setup-java@4075bfc1b51bf22876335ae1cd589602d60d8758
|
|
||||||
with:
|
|
||||||
java-version: 17
|
|
||||||
distribution: temurin
|
|
||||||
|
|
||||||
- name: Check if the author has forked the API repo
|
|
||||||
# See https://github.com/Kas-tle/find-forks-action/commits
|
|
||||||
uses: Kas-tle/find-forks-action@1b5447d1e3c7a8ed79583dd817cc5399686eed3a
|
|
||||||
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
|
|
||||||
# See https://github.com/actions/checkout/commits
|
|
||||||
uses: actions/checkout@72f2cec99f417b1a1c5e2e88945068983b7965f9
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
path: geyser
|
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
|
||||||
# See https://github.com/gradle/wrapper-validation-action/commits
|
|
||||||
uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4
|
|
||||||
|
|
||||||
- name: Build Geyser
|
|
||||||
# See https://github.com/gradle/gradle-build-action/commits
|
|
||||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
|
||||||
with:
|
|
||||||
arguments: build
|
|
||||||
build-root-directory: geyser
|
|
||||||
|
|
||||||
- name: Archive artifacts (Geyser Fabric)
|
|
||||||
# See https://github.com/actions/upload-artifact/commits
|
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
|
||||||
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@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
|
||||||
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@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
|
||||||
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@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser BungeeCord
|
|
||||||
path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
- name: Archive artifacts (Geyser Velocity)
|
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
|
||||||
if: success()
|
|
||||||
with:
|
|
||||||
name: Geyser Velocity
|
|
||||||
path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
|
||||||
if-no-files-found: error
|
|
||||||
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>
|
||||||
|
|
@ -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.20.40 - 1.20.51 and Minecraft Java 1.20.4
|
### Currently supporting Minecraft Bedrock 1.20.80 - 1.21.1 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");
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
@ -29,12 +29,14 @@ 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.Geyser;
|
import org.geysermc.api.Geyser;
|
||||||
import org.geysermc.api.GeyserApiBase;
|
import org.geysermc.api.GeyserApiBase;
|
||||||
|
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;
|
||||||
|
|
@ -134,6 +136,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.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,9 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.api.bedrock.camera;
|
package org.geysermc.geyser.api.bedrock.camera;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a camera shake instruction. Can be sent in {@link CameraData#shakeCamera(float, float, CameraShake)}
|
||||||
|
*/
|
||||||
public enum CameraShake {
|
public enum CameraShake {
|
||||||
POSITIONAL,
|
POSITIONAL,
|
||||||
ROTATIONAL
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -61,16 +61,16 @@ public interface CustomBlockData {
|
||||||
boolean includedInCreativeInventory();
|
boolean includedInCreativeInventory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the item's creative category, or tab id.
|
* Gets the block's creative category, or tab id.
|
||||||
*
|
*
|
||||||
* @return the item's creative category
|
* @return the block's creative category
|
||||||
*/
|
*/
|
||||||
@Nullable CreativeCategory creativeCategory();
|
@Nullable CreativeCategory creativeCategory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the item's creative group.
|
* Gets the block's creative group.
|
||||||
*
|
*
|
||||||
* @return the item's creative group
|
* @return the block's creative group
|
||||||
*/
|
*/
|
||||||
@Nullable String creativeGroup();
|
@Nullable String creativeGroup();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,8 +129,11 @@ public interface CustomBlockComponents {
|
||||||
* Gets the unit cube component
|
* Gets the unit cube component
|
||||||
* Equivalent to "minecraft:unit_cube"
|
* Equivalent to "minecraft:unit_cube"
|
||||||
*
|
*
|
||||||
|
* @deprecated Use {@link #geometry()} and compare with `minecraft:geometry.full_block` instead.
|
||||||
|
*
|
||||||
* @return The rotation.
|
* @return The rotation.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
boolean unitCube();
|
boolean unitCube();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -181,6 +184,10 @@ public interface CustomBlockComponents {
|
||||||
|
|
||||||
Builder transformation(TransformationComponent transformation);
|
Builder transformation(TransformationComponent transformation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #geometry(GeometryComponent)} with `minecraft:geometry.full_block` instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
Builder unitCube(boolean unitCube);
|
Builder unitCube(boolean unitCube);
|
||||||
|
|
||||||
Builder placeAir(boolean placeAir);
|
Builder placeAir(boolean placeAir);
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,10 @@ public interface JavaBlockState {
|
||||||
* Gets whether the block state has block entity
|
* Gets whether the block state has block entity
|
||||||
*
|
*
|
||||||
* @return 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();
|
boolean hasBlockEntity();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -104,6 +107,11 @@ public interface JavaBlockState {
|
||||||
|
|
||||||
Builder pistonBehavior(@Nullable String pistonBehavior);
|
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);
|
Builder hasBlockEntity(boolean hasBlockEntity);
|
||||||
|
|
||||||
JavaBlockState build();
|
JavaBlockState build();
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,10 @@ 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.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;
|
||||||
|
|
||||||
|
|
@ -41,10 +43,29 @@ 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);
|
||||||
|
|
||||||
|
|
@ -53,11 +74,14 @@ public interface GeyserConnection extends Connection, CommandSource {
|
||||||
*
|
*
|
||||||
* @param emoter the player entity emoting.
|
* @param emoter the player entity emoting.
|
||||||
* @param emoteId the emote ID to send to this 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.<br><br>
|
* Shakes the client's camera.
|
||||||
|
* <p>
|
||||||
* If the camera is already shaking with the same {@link CameraShake} type, then the additional intensity
|
* 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>
|
* 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
|
* If the existing shake type is different and the new intensity/duration are not positive, the existing shake only
|
||||||
|
|
@ -66,12 +90,18 @@ public interface GeyserConnection extends Connection, CommandSource {
|
||||||
* @param intensity the intensity of the shake. The client has a maximum total intensity of 4.
|
* @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 duration the time in seconds that the shake will occur for
|
||||||
* @param type the type of shake
|
* @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);
|
void shakeCamera(float intensity, float duration, @NonNull CameraShake type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops all camera shake of any type.
|
* Stops all camera shake of any type.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link CameraData#stopCameraShake()} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
void stopCameraShake();
|
void stopCameraShake();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -80,19 +110,26 @@ public interface GeyserConnection extends Connection, CommandSource {
|
||||||
* Fog IDs can be found <a href="https://wiki.bedrock.dev/documentation/fog-ids.html">here</a>
|
* 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.
|
* @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);
|
void sendFog(String... fogNameSpaces);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the given fog IDs from the fog cache, then sends all fog IDs in the cache to the client.
|
* 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.
|
* @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);
|
void removeFog(String... fogNameSpaces);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an immutable copy of all fog affects currently applied to this client.
|
* Returns an immutable copy of all fog affects currently applied to this client.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link CameraData#fogEffects()} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@NonNull
|
@NonNull
|
||||||
Set<String> fogEffects();
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 {
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,7 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -77,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.
|
||||||
*
|
*
|
||||||
|
|
@ -119,6 +134,10 @@ 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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -42,4 +42,4 @@ public abstract class PathPackCodec extends PackCodec {
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public abstract Path path();
|
public abstract Path path();
|
||||||
}
|
}
|
||||||
|
|
@ -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,18 +23,18 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.level.block;
|
package org.geysermc.geyser.api.skin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This stores all values of double chests that are part of the Java block state.
|
* Represents a cape.
|
||||||
*
|
*
|
||||||
* @param isFacingEast If true, then chest is facing east/west; if false, south/north
|
* @param textureUrl The URL of the cape texture
|
||||||
* @param isDirectionPositive If true, direction is positive (east/south); if false, direction is negative (west/north)
|
* @param capeId The ID of the cape
|
||||||
* @param isLeft If true, chest is the left of a pair; if false, chest is the right of a pair.
|
* @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 DoubleChestValue(
|
public record Cape(String textureUrl, String capeId, byte[] capeData, boolean failed) {
|
||||||
boolean isFacingEast,
|
public Cape(String textureUrl, String capeId, byte[] capeData) {
|
||||||
boolean isDirectionPositive,
|
this(textureUrl, capeId, capeData, false);
|
||||||
boolean isLeft) {
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
39
api/src/main/java/org/geysermc/geyser/api/skin/Skin.java
Normal file
39
api/src/main/java/org/geysermc/geyser/api/skin/Skin.java
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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 skin.
|
||||||
|
*
|
||||||
|
* @param textureUrl The URL/ID of the skin texture
|
||||||
|
* @param skinData The raw skin image data in ARGB
|
||||||
|
* @param failed If the skin failed to load, this is for things like fallback skins
|
||||||
|
*/
|
||||||
|
public record Skin(String textureUrl, byte[] skinData, boolean failed) {
|
||||||
|
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" : "") + "\"}}", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
|
|
@ -31,11 +31,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
* Represents the creative menu categories or tabs.
|
* Represents the creative menu categories or tabs.
|
||||||
*/
|
*/
|
||||||
public enum CreativeCategory {
|
public enum CreativeCategory {
|
||||||
COMMANDS("commands", 1),
|
CONSTRUCTION("construction", 1),
|
||||||
CONSTRUCTION("construction", 2),
|
NATURE("nature", 2),
|
||||||
EQUIPMENT("equipment", 3),
|
EQUIPMENT("equipment", 3),
|
||||||
ITEMS("items", 4),
|
ITEMS("items", 4),
|
||||||
NATURE("nature", 5),
|
|
||||||
NONE("none", 6);
|
NONE("none", 6);
|
||||||
|
|
||||||
private final String internalName;
|
private final String internalName;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
@ -34,10 +34,12 @@ public record PlatformType(String platformName) {
|
||||||
public static final PlatformType ANDROID = new PlatformType("Android");
|
public static final PlatformType ANDROID = new PlatformType("Android");
|
||||||
public static final PlatformType BUNGEECORD = new PlatformType("BungeeCord");
|
public static final PlatformType BUNGEECORD = new PlatformType("BungeeCord");
|
||||||
public static final PlatformType FABRIC = new PlatformType("Fabric");
|
public static final PlatformType FABRIC = new PlatformType("Fabric");
|
||||||
|
public static final PlatformType NEOFORGE = new PlatformType("NeoForge");
|
||||||
public static final PlatformType SPIGOT = new PlatformType("Spigot");
|
public static final PlatformType SPIGOT = new PlatformType("Spigot");
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static final PlatformType SPONGE = new PlatformType("Sponge");
|
public static final PlatformType SPONGE = new PlatformType("Sponge");
|
||||||
public static final PlatformType STANDALONE = new PlatformType("Standalone");
|
public static final PlatformType STANDALONE = new PlatformType("Standalone");
|
||||||
public static final PlatformType VELOCITY = new PlatformType("Velocity");
|
public static final PlatformType VELOCITY = new PlatformType("Velocity");
|
||||||
|
public static final PlatformType VIAPROXY = new PlatformType("ViaProxy");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,11 @@ 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.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;
|
||||||
|
|
@ -58,62 +58,96 @@ 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;
|
||||||
|
|
||||||
private static boolean INITIALIZED = false;
|
|
||||||
|
|
||||||
@SuppressWarnings({"JavaReflectionMemberAccess", "ResultOfMethodCallIgnored"})
|
|
||||||
@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_20_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() {
|
||||||
|
// Big hack - Bungee does not provide us an event to listen to, so schedule a repeating
|
||||||
|
// task that waits for a field to be filled which is set after the plugin enable
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
|
@ -133,54 +167,20 @@ 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 (!INITIALIZED) {
|
|
||||||
this.awaitStartupCompletion(0);
|
|
||||||
} else {
|
|
||||||
// No need to "wait" for startup completion, just start Geyser - we're reloading.
|
|
||||||
this.postStartup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||||
private void awaitStartupCompletion(int tries) {
|
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
// After 20 tries give up waiting. This will happen
|
} else {
|
||||||
// just after 3 minutes approximately
|
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
||||||
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! " +
|
// No need to re-register commands or re-init injector when reloading
|
||||||
"If not, consider cutting down the amount of plugins on your proxy as it is causing abnormally slow starting times.");
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
this.postStartup();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
this.geyserInjector.initializeLocalChannel(this);
|
||||||
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();
|
|
||||||
|
|
||||||
if (!INITIALIZED) {
|
|
||||||
this.geyserInjector = new GeyserBungeeInjector(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()) {
|
||||||
|
|
@ -191,18 +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()) {
|
|
||||||
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
|
||||||
} else {
|
|
||||||
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
|
||||||
}
|
|
||||||
|
|
||||||
INITIALIZED = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserDisable() {
|
||||||
|
if (geyser != null) {
|
||||||
|
geyser.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserShutdown() {
|
||||||
if (geyser != null) {
|
if (geyser != null) {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
}
|
}
|
||||||
|
|
@ -211,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;
|
||||||
|
|
@ -278,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
import net.fabricmc.loom.task.RemapJarTask
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id("fabric-loom") version "1.0-SNAPSHOT"
|
|
||||||
id("com.modrinth.minotaur") version "2.+"
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loom {
|
|
||||||
mixin.defaultRefmapName.set("geyser-fabric-refmap.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
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.4")
|
|
||||||
|
|
||||||
loaders.add("fabric")
|
|
||||||
failSilently.set(true)
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
required.project("fabric-api")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,289 +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.entity.player.GameMode;
|
|
||||||
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.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
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 java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection resource - level() is just a getter
|
|
||||||
LevelChunk chunk = player.level().getChunk(x, z);
|
|
||||||
final int chunkBlockX = x << 4;
|
|
||||||
final int chunkBlockZ = z << 4;
|
|
||||||
//noinspection ForLoopReplaceableByForEach - avoid constructing iterator
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
//noinspection resource - level() is just a getter
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GameMode getDefaultGameMode(GeyserSession session) {
|
|
||||||
return GameMode.byId(server.getDefaultGameType().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
|
||||||
//noinspection resource - level() is just a getter
|
|
||||||
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(@NonNull 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 = Objects.requireNonNull(compoundTag.get(key));
|
|
||||||
tag.accept(visitor);
|
|
||||||
visitor.root.put(visitor.currentTag);
|
|
||||||
}
|
|
||||||
return visitor.root;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitEnd(@NonNull EndTag endTag) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
{
|
|
||||||
"required": true,
|
|
||||||
"package": "org.geysermc.geyser.platform.fabric.mixin",
|
|
||||||
"compatibilityLevel": "JAVA_16",
|
|
||||||
"refmap": "geyser-fabric-refmap.json",
|
|
||||||
"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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.15.2",
|
"fabricloader": ">=0.15.11",
|
||||||
"fabric": "*",
|
"fabric": "*",
|
||||||
"minecraft": ">=1.20.4",
|
"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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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 org.geysermc.geyser.GeyserMain;
|
||||||
|
|
||||||
|
public class GeyserNeoForgeMain extends GeyserMain {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new GeyserNeoForgeMain().displayMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPluginType() {
|
||||||
|
return "NeoForge";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPluginFolder() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,11 +23,8 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.item.type;
|
package org.geysermc.geyser.platform.neoforge;
|
||||||
|
|
||||||
// If blocks are implemented, then this class is not needed.
|
public class ModConstants {
|
||||||
public class FlowerItem extends BlockItem {
|
public static final String MOD_ID = "geyser_neoforge";
|
||||||
public FlowerItem(String javaIdentifier, Builder builder) {
|
|
||||||
super(javaIdentifier, builder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
modLoader="javafml"
|
||||||
|
loaderVersion="[1,)"
|
||||||
|
license="MIT"
|
||||||
|
[[mods]]
|
||||||
|
modId="geyser_neoforge"
|
||||||
|
version="${version}"
|
||||||
|
displayName="Geyser"
|
||||||
|
displayURL="https://geysermc.org/"
|
||||||
|
logoFile= "../assets/geyser/icon.png"
|
||||||
|
authors="GeyserMC"
|
||||||
|
description="${description}"
|
||||||
|
[[mixins]]
|
||||||
|
config = "geyser.mixins.json"
|
||||||
|
[[dependencies.geyser_neoforge]]
|
||||||
|
modId="neoforge"
|
||||||
|
type="required"
|
||||||
|
versionRange="[21.0.0-beta,)"
|
||||||
|
ordering="NONE"
|
||||||
|
side="BOTH"
|
||||||
|
[[dependencies.geyser_neoforge]]
|
||||||
|
modId="minecraft"
|
||||||
|
type="required"
|
||||||
|
versionRange="[1.21,)"
|
||||||
|
ordering="NONE"
|
||||||
|
side="BOTH"
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.mod;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a getter to the server channels in the connection listener class.
|
||||||
|
*/
|
||||||
|
public interface GeyserChannelGetter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the channels.
|
||||||
|
*
|
||||||
|
* @return The channels.
|
||||||
|
*/
|
||||||
|
List<ChannelFuture> geyser$getChannels();
|
||||||
|
}
|
||||||
|
|
@ -23,20 +23,17 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric;
|
package org.geysermc.geyser.platform.mod;
|
||||||
|
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import net.fabricmc.api.EnvType;
|
import lombok.Getter;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
import lombok.Setter;
|
||||||
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.CommandSourceStack;
|
||||||
import net.minecraft.commands.Commands;
|
import net.minecraft.commands.Commands;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import net.minecraft.world.entity.player.Player;
|
||||||
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.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
|
|
@ -44,7 +41,6 @@ import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
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.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||||
|
|
@ -52,95 +48,66 @@ import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||||
import org.geysermc.geyser.platform.fabric.command.GeyserFabricCommandExecutor;
|
import org.geysermc.geyser.platform.mod.command.GeyserModCommandExecutor;
|
||||||
import org.geysermc.geyser.platform.fabric.world.GeyserFabricWorldManager;
|
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
|
||||||
|
import org.geysermc.geyser.platform.mod.world.GeyserModWorldManager;
|
||||||
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 java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.SocketAddress;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
@RequiredArgsConstructor
|
||||||
private static GeyserFabricMod instance;
|
public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||||
|
|
||||||
private boolean reloading;
|
@Getter
|
||||||
|
private static GeyserModBootstrap instance;
|
||||||
|
|
||||||
|
private final GeyserModPlatform platform;
|
||||||
|
|
||||||
private GeyserImpl geyser;
|
private GeyserImpl geyser;
|
||||||
private ModContainer mod;
|
|
||||||
private Path dataFolder;
|
private Path dataFolder;
|
||||||
|
|
||||||
|
@Setter
|
||||||
private MinecraftServer server;
|
private MinecraftServer server;
|
||||||
|
|
||||||
private GeyserCommandManager geyserCommandManager;
|
private GeyserCommandManager geyserCommandManager;
|
||||||
private GeyserFabricConfiguration geyserConfig;
|
private GeyserModConfiguration geyserConfig;
|
||||||
private GeyserFabricLogger geyserLogger;
|
private GeyserModInjector geyserInjector;
|
||||||
|
private final GeyserModLogger geyserLogger = new GeyserModLogger();
|
||||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||||
private WorldManager geyserWorldManager;
|
private WorldManager geyserWorldManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onGeyserInitialize() {
|
||||||
instance = this;
|
instance = this;
|
||||||
mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
|
dataFolder = this.platform.dataFolder(this.platform.configPath());
|
||||||
|
|
||||||
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);
|
GeyserLocale.init(this);
|
||||||
|
if (!loadConfig()) {
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||||
this.geyserLogger = new GeyserFabricLogger(geyserConfig.isDebugMode());
|
|
||||||
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
this.geyser = GeyserImpl.load(this.platform.platformType(), this);
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this);
|
// Create command manager here, since the permission handler on neo needs it
|
||||||
|
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||||
if (server == null) {
|
this.geyserCommandManager.init();
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void onGeyserEnable() {
|
||||||
* Initialize core Geyser.
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
* A function, as it needs to be called in different places depending on if Geyser is being reloaded or not.
|
if (!loadConfig()) {
|
||||||
*
|
return;
|
||||||
* @param server The minecraft server.
|
}
|
||||||
*/
|
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||||
public void startGeyser(MinecraftServer server) {
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
this.server = server;
|
}
|
||||||
|
|
||||||
GeyserImpl.start();
|
GeyserImpl.start();
|
||||||
|
|
||||||
|
|
@ -150,20 +117,29 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
|
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
// No need to re-register commands, or try to re-inject
|
||||||
this.geyserCommandManager.init();
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.geyserWorldManager = new GeyserFabricWorldManager(server);
|
this.geyserWorldManager = new GeyserModWorldManager(server);
|
||||||
|
|
||||||
|
// We want to do this late in the server startup process to allow other mods
|
||||||
|
// To do their job injecting, then connect into *that*
|
||||||
|
this.geyserInjector = new GeyserModInjector(server, this.platform);
|
||||||
|
if (isServer()) {
|
||||||
|
this.geyserInjector.initializeLocalChannel(this);
|
||||||
|
}
|
||||||
|
|
||||||
// Start command building
|
// Start command building
|
||||||
// Set just "geyser" as the help command
|
// Set just "geyser" as the help command
|
||||||
GeyserFabricCommandExecutor helpExecutor = new GeyserFabricCommandExecutor(geyser,
|
GeyserModCommandExecutor helpExecutor = new GeyserModCommandExecutor(geyser,
|
||||||
(GeyserCommand) geyser.commandManager().getCommands().get("help"));
|
(GeyserCommand) geyser.commandManager().getCommands().get("help"));
|
||||||
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("geyser").executes(helpExecutor);
|
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("geyser").executes(helpExecutor);
|
||||||
|
|
||||||
// Register all subcommands as valid
|
// Register all subcommands as valid
|
||||||
for (Map.Entry<String, Command> command : geyser.commandManager().getCommands().entrySet()) {
|
for (Map.Entry<String, Command> command : geyser.commandManager().getCommands().entrySet()) {
|
||||||
GeyserFabricCommandExecutor executor = new GeyserFabricCommandExecutor(geyser, (GeyserCommand) command.getValue());
|
GeyserModCommandExecutor executor = new GeyserModCommandExecutor(geyser, (GeyserCommand) command.getValue());
|
||||||
builder.then(Commands.literal(command.getKey())
|
builder.then(Commands.literal(command.getKey())
|
||||||
.executes(executor)
|
.executes(executor)
|
||||||
// Could also test for Bedrock but depending on when this is called it may backfire
|
// Could also test for Bedrock but depending on when this is called it may backfire
|
||||||
|
|
@ -183,12 +159,12 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register help command for just "/<extensionId>"
|
// Register help command for just "/<extensionId>"
|
||||||
GeyserFabricCommandExecutor extensionHelpExecutor = new GeyserFabricCommandExecutor(geyser,
|
GeyserModCommandExecutor extensionHelpExecutor = new GeyserModCommandExecutor(geyser,
|
||||||
(GeyserCommand) extensionCommands.get("help"));
|
(GeyserCommand) extensionCommands.get("help"));
|
||||||
LiteralArgumentBuilder<CommandSourceStack> extCmdBuilder = Commands.literal(extensionMapEntry.getKey().description().id()).executes(extensionHelpExecutor);
|
LiteralArgumentBuilder<CommandSourceStack> extCmdBuilder = Commands.literal(extensionMapEntry.getKey().description().id()).executes(extensionHelpExecutor);
|
||||||
|
|
||||||
for (Map.Entry<String, Command> command : extensionCommands.entrySet()) {
|
for (Map.Entry<String, Command> command : extensionCommands.entrySet()) {
|
||||||
GeyserFabricCommandExecutor executor = new GeyserFabricCommandExecutor(geyser, (GeyserCommand) command.getValue());
|
GeyserModCommandExecutor executor = new GeyserModCommandExecutor(geyser, (GeyserCommand) command.getValue());
|
||||||
extCmdBuilder.then(Commands.literal(command.getKey())
|
extCmdBuilder.then(Commands.literal(command.getKey())
|
||||||
.executes(executor)
|
.executes(executor)
|
||||||
.requires(executor::testPermission)
|
.requires(executor::testPermission)
|
||||||
|
|
@ -201,18 +177,26 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserDisable() {
|
||||||
|
if (geyser != null) {
|
||||||
|
geyser.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserShutdown() {
|
||||||
if (geyser != null) {
|
if (geyser != null) {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
geyser = null;
|
geyser = null;
|
||||||
}
|
}
|
||||||
if (!reloading) {
|
if (geyserInjector != null) {
|
||||||
|
geyserInjector.shutdown();
|
||||||
this.server = null;
|
this.server = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserConfiguration getGeyserConfig() {
|
public GeyserModConfiguration getGeyserConfig() {
|
||||||
return geyserConfig;
|
return geyserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,7 +227,7 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BootstrapDumpInfo getDumpInfo() {
|
public BootstrapDumpInfo getDumpInfo() {
|
||||||
return new GeyserFabricDumpInfo(server);
|
return this.platform.dumpInfo(this.server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -259,43 +243,53 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getServerPort() {
|
public SocketAddress getSocketAddress() {
|
||||||
return ((GeyserServerPortGetter) server).geyser$getServerPort();
|
return this.geyserInjector.getServerSocketAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean testFloodgatePluginPresent() {
|
public int getServerPort() {
|
||||||
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
|
if (isServer()) {
|
||||||
if (floodgate.isPresent()) {
|
return ((GeyserServerPortGetter) server).geyser$getServerPort();
|
||||||
geyserConfig.loadFloodgate(this, floodgate.orElse(null));
|
} else {
|
||||||
return true;
|
// Set in the IntegratedServerMixin
|
||||||
|
return geyserConfig.getRemote().port();
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
public abstract boolean isServer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean testFloodgatePluginPresent() {
|
||||||
|
return this.platform.testFloodgatePluginPresent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public InputStream getResourceOrNull(String resource) {
|
public InputStream getResourceOrNull(String resource) {
|
||||||
// We need to handle this differently, because Fabric shares the classloader across multiple mods
|
return this.platform.resolveResource(resource);
|
||||||
Path path = this.mod.findPath(resource).orElse(null);
|
}
|
||||||
if (path == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public abstract boolean hasPermission(@NonNull Player source, @NonNull String permissionNode);
|
||||||
|
|
||||||
|
public abstract boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel);
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
private boolean loadConfig() {
|
||||||
try {
|
try {
|
||||||
return path.getFileSystem()
|
if (!dataFolder.toFile().exists()) {
|
||||||
.provider()
|
//noinspection ResultOfMethodCallIgnored
|
||||||
.newInputStream(path);
|
dataFolder.toFile().mkdir();
|
||||||
} catch (IOException e) {
|
}
|
||||||
return null;
|
|
||||||
|
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
|
||||||
|
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||||
|
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserModConfiguration.class);
|
||||||
|
return true;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReloading(boolean reloading) {
|
|
||||||
this.reloading = reloading;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GeyserFabricMod getInstance() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 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.mod;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||||
|
import io.netty.channel.ChannelPromise;
|
||||||
|
import net.minecraft.network.protocol.login.ClientboundGameProfilePacket;
|
||||||
|
import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients
|
||||||
|
* that won't be receiving the data over the network.
|
||||||
|
* <p>
|
||||||
|
* As of 1.8 - 1.17.1, compression is enabled in the Netty pipeline by adding a listener after a packet is written.
|
||||||
|
* If we simply "cancel" or don't forward the packet, then the listener is never called.
|
||||||
|
*/
|
||||||
|
public class GeyserModCompressionDisabler extends ChannelOutboundHandlerAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
|
Class<?> msgClass = msg.getClass();
|
||||||
|
// Don't let any compression packet get through
|
||||||
|
if (!ClientboundLoginCompressionPacket.class.isAssignableFrom(msgClass)) {
|
||||||
|
if (ClientboundGameProfilePacket.class.isAssignableFrom(msgClass)) {
|
||||||
|
|
||||||
|
// We're past the point that a compression packet can be sent, so we can safely yeet ourselves away
|
||||||
|
ctx.channel().pipeline().remove(this);
|
||||||
|
}
|
||||||
|
super.write(ctx, msg, promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,23 +23,20 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric;
|
package org.geysermc.geyser.platform.mod;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
import net.fabricmc.loader.api.ModContainer;
|
|
||||||
import org.geysermc.geyser.FloodgateKeyLoader;
|
import org.geysermc.geyser.FloodgateKeyLoader;
|
||||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class GeyserFabricConfiguration extends GeyserJacksonConfiguration {
|
public class GeyserModConfiguration extends GeyserJacksonConfiguration {
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private Path floodgateKeyPath;
|
private Path floodgateKeyPath;
|
||||||
|
|
||||||
public void loadFloodgate(GeyserFabricMod geyser, ModContainer floodgate) {
|
public void loadFloodgate(GeyserModBootstrap geyser, Path floodgateDataFolder) {
|
||||||
Path geyserDataFolder = geyser.getConfigFolder();
|
Path geyserDataFolder = geyser.getConfigFolder();
|
||||||
Path floodgateDataFolder = floodgate != null ? FabricLoader.getInstance().getConfigDir().resolve("floodgate") : null;
|
|
||||||
|
|
||||||
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, geyser.getGeyserLogger());
|
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, geyser.getGeyserLogger());
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
* 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.mod;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.DefaultEventLoopGroup;
|
||||||
|
import io.netty.channel.local.LocalAddress;
|
||||||
|
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.network.ServerConnectionListener;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.network.netty.GeyserInjector;
|
||||||
|
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
|
||||||
|
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GeyserModInjector extends GeyserInjector {
|
||||||
|
|
||||||
|
private final MinecraftServer server;
|
||||||
|
private final GeyserModPlatform platform;
|
||||||
|
private DefaultEventLoopGroup eventLoopGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to uninject ourselves on shutdown.
|
||||||
|
*/
|
||||||
|
private List<ChannelFuture> allServerChannels;
|
||||||
|
|
||||||
|
public GeyserModInjector(MinecraftServer server, GeyserModPlatform platform) {
|
||||||
|
this.server = server;
|
||||||
|
this.platform = platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception {
|
||||||
|
ServerConnectionListener connection = this.server.getConnection();
|
||||||
|
|
||||||
|
// Find the channel that Minecraft uses to listen to connections
|
||||||
|
ChannelFuture listeningChannel = null;
|
||||||
|
this.allServerChannels = ((GeyserChannelGetter) connection).geyser$getChannels();
|
||||||
|
for (ChannelFuture o : allServerChannels) {
|
||||||
|
listeningChannel = o;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listeningChannel == null) {
|
||||||
|
throw new RuntimeException("Unable to find listening channel!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Making this a function prevents childHandler from being treated as a non-final variable
|
||||||
|
ChannelInitializer<Channel> childHandler = getChildHandler(bootstrap, listeningChannel);
|
||||||
|
// This method is what initializes the connection in Java Edition, after Netty is all set.
|
||||||
|
Method initChannel = childHandler.getClass().getDeclaredMethod("initChannel", Channel.class);
|
||||||
|
initChannel.setAccessible(true);
|
||||||
|
|
||||||
|
// Separate variable so we can shut it down later
|
||||||
|
eventLoopGroup = new DefaultEventLoopGroup(0, new DefaultThreadFactory("Geyser " + this.platform.platformType().platformName() + " connection thread", Thread.MAX_PRIORITY));
|
||||||
|
ChannelFuture channelFuture = (new ServerBootstrap()
|
||||||
|
.channel(LocalServerChannelWrapper.class)
|
||||||
|
.childHandler(new ChannelInitializer<>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(@NonNull Channel ch) throws Exception {
|
||||||
|
initChannel.invoke(childHandler, ch);
|
||||||
|
|
||||||
|
int index = ch.pipeline().names().indexOf("encoder");
|
||||||
|
String baseName = index != -1 ? "encoder" : "outbound_config";
|
||||||
|
|
||||||
|
if (bootstrap.getGeyserConfig().isDisableCompression()) {
|
||||||
|
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Set to MAX_PRIORITY as MultithreadEventLoopGroup#newDefaultThreadFactory which DefaultEventLoopGroup implements does by default
|
||||||
|
.group(eventLoopGroup)
|
||||||
|
.localAddress(LocalAddress.ANY))
|
||||||
|
.bind()
|
||||||
|
.syncUninterruptibly();
|
||||||
|
// We don't need to add to the list, but plugins like ProtocolSupport and ProtocolLib that add to the main pipeline
|
||||||
|
// will work when we add to the list.
|
||||||
|
allServerChannels.add(channelFuture);
|
||||||
|
this.localChannel = channelFuture;
|
||||||
|
this.serverSocketAddress = channelFuture.channel().localAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private ChannelInitializer<Channel> getChildHandler(GeyserBootstrap bootstrap, ChannelFuture listeningChannel) {
|
||||||
|
List<String> names = listeningChannel.channel().pipeline().names();
|
||||||
|
ChannelInitializer<Channel> childHandler = null;
|
||||||
|
for (String name : names) {
|
||||||
|
ChannelHandler handler = listeningChannel.channel().pipeline().get(name);
|
||||||
|
try {
|
||||||
|
Field childHandlerField = handler.getClass().getDeclaredField("childHandler");
|
||||||
|
childHandlerField.setAccessible(true);
|
||||||
|
childHandler = (ChannelInitializer<Channel>) childHandlerField.get(handler);
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (bootstrap.getGeyserConfig().isDebugMode()) {
|
||||||
|
bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (childHandler == null) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
return childHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
if (this.allServerChannels != null) {
|
||||||
|
this.allServerChannels.remove(this.localChannel);
|
||||||
|
this.allServerChannels = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventLoopGroup != null) {
|
||||||
|
try {
|
||||||
|
eventLoopGroup.shutdownGracefully().sync();
|
||||||
|
eventLoopGroup = null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Unable to shut down injector! " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric;
|
package org.geysermc.geyser.platform.mod;
|
||||||
|
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
|
|
@ -32,15 +32,11 @@ import org.apache.logging.log4j.Logger;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.text.ChatColor;
|
import org.geysermc.geyser.text.ChatColor;
|
||||||
|
|
||||||
public class GeyserFabricLogger implements GeyserLogger {
|
public class GeyserModLogger implements GeyserLogger {
|
||||||
private final Logger logger = LogManager.getLogger("geyser-fabric");
|
private final Logger logger = LogManager.getLogger("geyser");
|
||||||
|
|
||||||
private boolean debug;
|
private boolean debug;
|
||||||
|
|
||||||
public GeyserFabricLogger(boolean isDebug) {
|
|
||||||
debug = isDebug;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void severe(String message) {
|
public void severe(String message) {
|
||||||
logger.fatal(message);
|
logger.fatal(message);
|
||||||
|
|
@ -73,7 +69,7 @@ public class GeyserFabricLogger implements GeyserLogger {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(Component message) {
|
public void sendMessage(Component message) {
|
||||||
// As of Java Edition 1.19.2, Fabric's console doesn't natively support legacy format
|
// As of Java Edition 1.19.2, Minecraft's console doesn't natively support legacy format
|
||||||
String flattened = LegacyComponentSerializer.legacySection().serialize(message);
|
String flattened = LegacyComponentSerializer.legacySection().serialize(message);
|
||||||
// Add the reset at the end, or else format will persist... forever.
|
// Add the reset at the end, or else format will persist... forever.
|
||||||
// https://cdn.discordapp.com/attachments/573909525132738590/1033904509170225242/unknown.png
|
// https://cdn.discordapp.com/attachments/573909525132738590/1033904509170225242/unknown.png
|
||||||
|
|
@ -23,21 +23,22 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric;
|
package org.geysermc.geyser.platform.mod;
|
||||||
|
|
||||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import org.geysermc.geyser.Constants;
|
import org.geysermc.geyser.Constants;
|
||||||
import org.geysermc.geyser.platform.fabric.command.FabricCommandSender;
|
import org.geysermc.geyser.platform.mod.command.ModCommandSender;
|
||||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||||
|
|
||||||
public final class GeyserFabricUpdateListener {
|
public final class GeyserModUpdateListener {
|
||||||
public static void onPlayReady(ServerGamePacketListenerImpl handler) {
|
public static void onPlayReady(Player player) {
|
||||||
if (Permissions.check(handler.player, Constants.UPDATE_PERMISSION, 2)) {
|
CommandSourceStack stack = player.createCommandSourceStack();
|
||||||
VersionCheckUtils.checkForGeyserUpdate(() -> new FabricCommandSender(handler.player.createCommandSourceStack()));
|
if (GeyserModBootstrap.getInstance().hasPermission(stack, Constants.UPDATE_PERMISSION, 2)) {
|
||||||
|
VersionCheckUtils.checkForGeyserUpdate(() -> new ModCommandSender(stack));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private GeyserFabricUpdateListener() {
|
private GeyserModUpdateListener() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric;
|
package org.geysermc.geyser.platform.mod;
|
||||||
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
|
|
@ -23,12 +23,13 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric;
|
package org.geysermc.geyser.platform.mod;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
import net.minecraft.network.Connection;
|
import net.minecraft.network.Connection;
|
||||||
import net.minecraft.network.PacketSendListener;
|
import net.minecraft.network.PacketSendListener;
|
||||||
import net.minecraft.network.protocol.Packet;
|
import net.minecraft.network.protocol.Packet;
|
||||||
|
|
@ -39,10 +40,11 @@ import net.minecraft.network.protocol.status.ServerStatusPacketListener;
|
||||||
import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket;
|
import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.network.ServerStatusPacketListenerImpl;
|
import net.minecraft.server.network.ServerStatusPacketListenerImpl;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
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 org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -68,7 +70,7 @@ public class ModPingPassthrough implements IGeyserPingPassthrough {
|
||||||
StatusInterceptor connection = new StatusInterceptor();
|
StatusInterceptor connection = new StatusInterceptor();
|
||||||
ServerStatusPacketListener statusPacketListener = new ServerStatusPacketListenerImpl(status, connection);
|
ServerStatusPacketListener statusPacketListener = new ServerStatusPacketListenerImpl(status, connection);
|
||||||
|
|
||||||
statusPacketListener.handleStatusRequest(new ServerboundStatusRequestPacket());
|
statusPacketListener.handleStatusRequest(ServerboundStatusRequestPacket.INSTANCE);
|
||||||
// mods like MiniMOTD (that inject into the above method) have now processed the response
|
// mods like MiniMOTD (that inject into the above method) have now processed the response
|
||||||
status = Objects.requireNonNull(connection.status, "status response");
|
status = Objects.requireNonNull(connection.status, "status response");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
@ -78,7 +80,7 @@ public class ModPingPassthrough implements IGeyserPingPassthrough {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String jsonDescription = net.minecraft.network.chat.Component.Serializer.toJson(status.description());
|
String jsonDescription = net.minecraft.network.chat.Component.Serializer.toJson(status.description(), RegistryAccess.EMPTY);
|
||||||
String legacyDescription = LEGACY_SERIALIZER.serialize(GSON_SERIALIZER.deserializeOr(jsonDescription, Component.empty()));
|
String legacyDescription = LEGACY_SERIALIZER.serialize(GSON_SERIALIZER.deserializeOr(jsonDescription, Component.empty()));
|
||||||
|
|
||||||
return new GeyserPingInfo(
|
return new GeyserPingInfo(
|
||||||
|
|
@ -100,7 +102,7 @@ public class ModPingPassthrough implements IGeyserPingPassthrough {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(Packet<?> packet, @Nullable PacketSendListener packetSendListener, boolean bl) {
|
public void send(@NonNull Packet<?> packet, @Nullable PacketSendListener packetSendListener, boolean bl) {
|
||||||
if (packet instanceof ClientboundStatusResponsePacket statusResponse) {
|
if (packet instanceof ClientboundStatusResponsePacket statusResponse) {
|
||||||
status = statusResponse.status();
|
status = statusResponse.status();
|
||||||
}
|
}
|
||||||
|
|
@ -23,32 +23,31 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric.command;
|
package org.geysermc.geyser.platform.mod.command;
|
||||||
|
|
||||||
import com.mojang.brigadier.Command;
|
import com.mojang.brigadier.Command;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||||
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
|
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.ChatColor;
|
import org.geysermc.geyser.text.ChatColor;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implements Command<CommandSourceStack> {
|
public class GeyserModCommandExecutor extends GeyserCommandExecutor implements Command<CommandSourceStack> {
|
||||||
private final GeyserCommand command;
|
private final GeyserCommand command;
|
||||||
|
|
||||||
public GeyserFabricCommandExecutor(GeyserImpl connector, GeyserCommand command) {
|
public GeyserModCommandExecutor(GeyserImpl geyser, GeyserCommand command) {
|
||||||
super(connector, Collections.singletonMap(command.name(), command));
|
super(geyser, Collections.singletonMap(command.name(), command));
|
||||||
this.command = command;
|
this.command = command;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean testPermission(CommandSourceStack source) {
|
public boolean testPermission(CommandSourceStack source) {
|
||||||
return Permissions.check(source, command.permission(), command.isSuggestedOpOnly() ? 2 : 0);
|
return GeyserModBootstrap.getInstance().hasPermission(source, command.permission(), command.isSuggestedOpOnly() ? 2 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -58,15 +57,12 @@ public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implement
|
||||||
|
|
||||||
public int runWithArgs(CommandContext<CommandSourceStack> context, String args) {
|
public int runWithArgs(CommandContext<CommandSourceStack> context, String args) {
|
||||||
CommandSourceStack source = context.getSource();
|
CommandSourceStack source = context.getSource();
|
||||||
FabricCommandSender sender = new FabricCommandSender(source);
|
ModCommandSender sender = new ModCommandSender(source);
|
||||||
GeyserSession session = getGeyserSession(sender);
|
GeyserSession session = getGeyserSession(sender);
|
||||||
if (!testPermission(source)) {
|
if (!testPermission(source)) {
|
||||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
|
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (this.command.name().equals("reload")) {
|
|
||||||
GeyserFabricMod.getInstance().setReloading(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command.isBedrockOnly() && session == null) {
|
if (command.isBedrockOnly() && session == null) {
|
||||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.locale()));
|
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.locale()));
|
||||||
|
|
@ -23,25 +23,26 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric.command;
|
package org.geysermc.geyser.platform.mod.command;
|
||||||
|
|
||||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
|
||||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
|
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||||
import org.geysermc.geyser.text.ChatColor;
|
import org.geysermc.geyser.text.ChatColor;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class FabricCommandSender implements GeyserCommandSource {
|
public class ModCommandSender implements GeyserCommandSource {
|
||||||
|
|
||||||
private final CommandSourceStack source;
|
private final CommandSourceStack source;
|
||||||
|
|
||||||
public FabricCommandSender(CommandSourceStack source) {
|
public ModCommandSender(CommandSourceStack source) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +64,7 @@ public class FabricCommandSender implements GeyserCommandSource {
|
||||||
public void sendMessage(net.kyori.adventure.text.Component message) {
|
public void sendMessage(net.kyori.adventure.text.Component message) {
|
||||||
if (source.getEntity() instanceof ServerPlayer player) {
|
if (source.getEntity() instanceof ServerPlayer player) {
|
||||||
String decoded = GsonComponentSerializer.gson().serialize(message);
|
String decoded = GsonComponentSerializer.gson().serialize(message);
|
||||||
player.displayClientMessage(Objects.requireNonNull(Component.Serializer.fromJson(decoded)), false);
|
player.displayClientMessage(Objects.requireNonNull(Component.Serializer.fromJson(decoded, RegistryAccess.EMPTY)), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GeyserCommandSource.super.sendMessage(message);
|
GeyserCommandSource.super.sendMessage(message);
|
||||||
|
|
@ -76,6 +77,6 @@ public class FabricCommandSender implements GeyserCommandSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(String permission) {
|
public boolean hasPermission(String permission) {
|
||||||
return Permissions.check(source, permission, source.getServer().getOperatorUserPermissionLevel());
|
return GeyserModBootstrap.getInstance().hasPermission(source, permission, source.getServer().getOperatorUserPermissionLevel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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,18 +23,16 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric.mixin.client;
|
package org.geysermc.geyser.platform.mod.mixin.client;
|
||||||
|
|
||||||
import net.fabricmc.api.EnvType;
|
|
||||||
import net.fabricmc.api.Environment;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.server.IntegratedServer;
|
import net.minecraft.client.server.IntegratedServer;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.world.level.GameType;
|
import net.minecraft.world.level.GameType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
|
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||||
import org.geysermc.geyser.platform.fabric.GeyserServerPortGetter;
|
import org.geysermc.geyser.platform.mod.GeyserServerPortGetter;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
@ -45,7 +43,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
|
||||||
@Mixin(IntegratedServer.class)
|
@Mixin(IntegratedServer.class)
|
||||||
public class IntegratedServerMixin implements GeyserServerPortGetter {
|
public class IntegratedServerMixin implements GeyserServerPortGetter {
|
||||||
@Shadow
|
@Shadow
|
||||||
|
|
@ -57,7 +54,10 @@ public class IntegratedServerMixin implements GeyserServerPortGetter {
|
||||||
private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, CallbackInfoReturnable<Boolean> cir) {
|
private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, CallbackInfoReturnable<Boolean> cir) {
|
||||||
if (cir.getReturnValueZ()) {
|
if (cir.getReturnValueZ()) {
|
||||||
// If the LAN is opened, starts Geyser.
|
// If the LAN is opened, starts Geyser.
|
||||||
GeyserFabricMod.getInstance().startGeyser((MinecraftServer) (Object) this);
|
GeyserModBootstrap instance = GeyserModBootstrap.getInstance();
|
||||||
|
instance.setServer((MinecraftServer) (Object) this);
|
||||||
|
instance.getGeyserConfig().getRemote().setPort(port);
|
||||||
|
instance.onGeyserEnable();
|
||||||
// Ensure player locale has been loaded, in case it's different from Java system language
|
// Ensure player locale has been loaded, in case it's different from Java system language
|
||||||
GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode);
|
GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode);
|
||||||
// Give indication that Geyser is loaded
|
// Give indication that Geyser is loaded
|
||||||
|
|
@ -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,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric.mixin.server;
|
package org.geysermc.geyser.platform.mod.mixin.server;
|
||||||
|
|
||||||
import com.mojang.datafixers.DataFixer;
|
import com.mojang.datafixers.DataFixer;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
@ -33,14 +33,14 @@ import net.minecraft.server.dedicated.DedicatedServer;
|
||||||
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
|
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
|
||||||
import net.minecraft.server.packs.repository.PackRepository;
|
import net.minecraft.server.packs.repository.PackRepository;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import org.geysermc.geyser.platform.fabric.GeyserServerPortGetter;
|
import org.geysermc.geyser.platform.mod.GeyserServerPortGetter;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
|
|
||||||
@Mixin(DedicatedServer.class)
|
@Mixin(DedicatedServer.class)
|
||||||
public abstract class MinecraftDedicatedServerMixin extends MinecraftServer implements GeyserServerPortGetter {
|
public abstract class DedicatedServerMixin extends MinecraftServer implements GeyserServerPortGetter {
|
||||||
public MinecraftDedicatedServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer dataFixer, Services services, ChunkProgressListenerFactory chunkProgressListenerFactory) {
|
public DedicatedServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer dataFixer, Services services, ChunkProgressListenerFactory chunkProgressListenerFactory) {
|
||||||
super(thread, levelStorageAccess, packRepository, worldStem, proxy, dataFixer, services, chunkProgressListenerFactory);
|
super(thread, levelStorageAccess, packRepository, worldStem, proxy, dataFixer, services, chunkProgressListenerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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.mod.mixin.server;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import net.minecraft.server.network.ServerConnectionListener;
|
||||||
|
import org.geysermc.geyser.platform.mod.GeyserChannelGetter;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mixin(ServerConnectionListener.class)
|
||||||
|
public abstract class ServerConnectionListenerMixin implements GeyserChannelGetter {
|
||||||
|
|
||||||
|
@Shadow @Final private List<ChannelFuture> channels;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ChannelFuture> geyser$getChannels() {
|
||||||
|
return this.channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* 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.mod.platform;
|
||||||
|
|
||||||
|
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 java.io.InputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface which holds common methods that have different
|
||||||
|
* APIs on their respective mod platforms.
|
||||||
|
*/
|
||||||
|
public interface GeyserModPlatform {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link PlatformType} of the mod platform.
|
||||||
|
*
|
||||||
|
* @return the platform type of the mod platform
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
PlatformType platformType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the config path of the mod platform.
|
||||||
|
*
|
||||||
|
* @return the config path of the mod platform
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String configPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the data folder of the mod platform.
|
||||||
|
*
|
||||||
|
* @return the data folder of the mod platform
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Path dataFolder(@NonNull String modId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the dump info of the mod platform.
|
||||||
|
*
|
||||||
|
* @param server the server to get the dump info from
|
||||||
|
* @return the dump info of the mod platform
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
BootstrapDumpInfo dumpInfo(@NonNull MinecraftServer server);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the Floodgate plugin is present on the mod platform.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the Floodgate plugin is present on the mod platform, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a resource from the mod jar.
|
||||||
|
*
|
||||||
|
* @param resource the name of the resource
|
||||||
|
* @return the input stream of the resource
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
InputStream resolveResource(@NonNull String resource);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
/*
|
||||||
|
* 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.mod.world;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||||
|
import net.minecraft.SharedConstants;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.DecoratedPotBlockEntity;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
|
import org.geysermc.geyser.level.GeyserWorldManager;
|
||||||
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
|
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.util.MinecraftKey;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class GeyserModWorldManager extends GeyserWorldManager {
|
||||||
|
|
||||||
|
private static final GsonComponentSerializer GSON_SERIALIZER = GsonComponentSerializer.gson();
|
||||||
|
private final MinecraftServer server;
|
||||||
|
|
||||||
|
public GeyserModWorldManager(MinecraftServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||||
|
// If the protocol version of Geyser and the server are not the
|
||||||
|
// same, fallback to the chunk cache. May be able to update this
|
||||||
|
// in the future to use ViaVersion however, like Spigot does.
|
||||||
|
if (SharedConstants.getCurrentVersion().getProtocolVersion() != GameProtocol.getJavaProtocolVersion()) {
|
||||||
|
return super.getBlockAt(session, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerPlayer player = this.getPlayer(session);
|
||||||
|
if (player == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Level level = player.level();
|
||||||
|
if (y < level.getMinBuildHeight()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkAccess chunk = level.getChunkSource().getChunk(x >> 4, z >> 4, ChunkStatus.FULL, false);
|
||||||
|
if (chunk == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int worldOffset = level.getMinBuildHeight() >> 4;
|
||||||
|
int chunkOffset = (y >> 4) - worldOffset;
|
||||||
|
if (chunkOffset < chunk.getSections().length) {
|
||||||
|
LevelChunkSection section = chunk.getSections()[chunkOffset];
|
||||||
|
if (section != null && !section.hasOnlyAir()) {
|
||||||
|
return Block.getId(section.getBlockState(x & 15, y & 15, z & 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasOwnChunkCache() {
|
||||||
|
return SharedConstants.getCurrentVersion().getProtocolVersion() == GameProtocol.getJavaProtocolVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(GeyserSession session, String permission) {
|
||||||
|
ServerPlayer player = getPlayer(session);
|
||||||
|
return GeyserModBootstrap.getInstance().hasPermission(player, permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameMode getDefaultGameMode(GeyserSession session) {
|
||||||
|
return GameMode.byId(server.getDefaultGameType().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||||
|
CompletableFuture<org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents> 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
|
||||||
|
//noinspection resource - level() is just a getter
|
||||||
|
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();
|
||||||
|
|
||||||
|
org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents components =
|
||||||
|
new org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents(new HashMap<>());
|
||||||
|
|
||||||
|
components.put(DataComponentType.DAMAGE, itemStack.getDamageValue());
|
||||||
|
|
||||||
|
Component customName = itemStack.getComponents().get(DataComponents.CUSTOM_NAME);
|
||||||
|
if (customName != null) {
|
||||||
|
components.put(DataComponentType.CUSTOM_NAME, toKyoriComponent(customName));
|
||||||
|
}
|
||||||
|
|
||||||
|
BannerPatternLayers pattern = itemStack.get(DataComponents.BANNER_PATTERNS);
|
||||||
|
if (pattern != null) {
|
||||||
|
components.put(DataComponentType.BANNER_PATTERNS, toPatternList(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
future.complete(components);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
future.complete(null);
|
||||||
|
});
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer<List<String>> apply) {
|
||||||
|
server.execute(() -> {
|
||||||
|
ServerPlayer player = getPlayer(session);
|
||||||
|
if (player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos blockPos = new BlockPos(pos.getX(), pos.getY(), pos.getZ());
|
||||||
|
// Don't create a new block entity if invalid
|
||||||
|
//noinspection resource - level() is just a getter
|
||||||
|
BlockEntity blockEntity = player.level().getChunkAt(blockPos).getBlockEntity(blockPos);
|
||||||
|
if (blockEntity instanceof DecoratedPotBlockEntity pot) {
|
||||||
|
List<String> sherds = pot.getDecorations().ordered()
|
||||||
|
.stream().map(item -> BuiltInRegistries.ITEM.getKey(item).toString())
|
||||||
|
.toList();
|
||||||
|
apply.accept(sherds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerPlayer getPlayer(GeyserSession session) {
|
||||||
|
return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static net.kyori.adventure.text.Component toKyoriComponent(Component component) {
|
||||||
|
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
|
||||||
|
return GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BannerPatternLayer> toPatternList(BannerPatternLayers patternLayers) {
|
||||||
|
return patternLayers.layers().stream()
|
||||||
|
.map(layer -> {
|
||||||
|
BannerPatternLayer.BannerPattern pattern = new BannerPatternLayer.BannerPattern(
|
||||||
|
MinecraftKey.key(layer.pattern().value().assetId().toString()), layer.pattern().value().translationKey()
|
||||||
|
);
|
||||||
|
return new BannerPatternLayer(Holder.ofCustom(pattern), layer.color().getId());
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
bootstrap/mod/src/main/resources/geyser.mixins.json
Normal file
18
bootstrap/mod/src/main/resources/geyser.mixins.json
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "org.geysermc.geyser.platform.mod.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_17",
|
||||||
|
"mixins": [
|
||||||
|
"server.ServerConnectionListenerMixin"
|
||||||
|
],
|
||||||
|
"server": [
|
||||||
|
"server.DedicatedServerMixin"
|
||||||
|
],
|
||||||
|
"client": [
|
||||||
|
"client.IntegratedServerMixin"
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,25 +4,27 @@ dependencies {
|
||||||
isTransitive = false
|
isTransitive = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implementation(libs.erosion.bukkit.nms) {
|
||||||
|
attributes {
|
||||||
|
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
implementation(variantOf(libs.adapters.spigot) {
|
implementation(variantOf(libs.adapters.spigot) {
|
||||||
classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations
|
classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations
|
||||||
})
|
})
|
||||||
|
implementation(variantOf(libs.adapters.paper) {
|
||||||
|
classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations
|
||||||
|
})
|
||||||
|
|
||||||
implementation(libs.commodore)
|
implementation(libs.commodore)
|
||||||
|
|
||||||
implementation(libs.adventure.text.serializer.bungeecord)
|
implementation(libs.adventure.text.serializer.bungeecord)
|
||||||
|
|
||||||
// Both folia-api and paper-mojangapi only provide Java 17 versions for 1.19
|
compileOnly(libs.folia.api)
|
||||||
compileOnly(libs.folia.api) {
|
compileOnly(libs.paper.mojangapi)
|
||||||
attributes {
|
|
||||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
compileOnlyApi(libs.viaversion)
|
||||||
}
|
|
||||||
}
|
|
||||||
compileOnly(libs.paper.mojangapi) {
|
|
||||||
attributes {
|
|
||||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
platformRelocate("it.unimi.dsi.fastutil")
|
platformRelocate("it.unimi.dsi.fastutil")
|
||||||
|
|
@ -41,6 +43,12 @@ application {
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
|
|
||||||
|
// Prevents Paper 1.20.5+ from remapping Geyser
|
||||||
|
manifest {
|
||||||
|
attributes["paperweight-mappings-namespace"] = "mojang"
|
||||||
|
}
|
||||||
|
|
||||||
archiveBaseName.set("Geyser-Spigot")
|
archiveBaseName.set("Geyser-Spigot")
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
@ -48,6 +56,7 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
|
|
||||||
// We cannot shade Netty, or else native libraries will not load
|
// We cannot shade Netty, or else native libraries will not load
|
||||||
// Needed because older Spigot builds do not provide the haproxy module
|
// Needed because older Spigot builds do not provide the haproxy module
|
||||||
|
exclude(dependency("io.netty.incubator:.*"))
|
||||||
exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
|
exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
|
||||||
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:.*"))
|
||||||
|
|
@ -67,3 +76,8 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
exclude(dependency("com.mojang:.*"))
|
exclude(dependency("com.mojang:.*"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modrinth {
|
||||||
|
uploadFile.set(tasks.getByPath("shadowJar"))
|
||||||
|
loaders.addAll("spigot", "paper")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ import java.util.logging.Logger;
|
||||||
public final class GeyserPaperLogger extends GeyserSpigotLogger {
|
public final class GeyserPaperLogger extends GeyserSpigotLogger {
|
||||||
private final ComponentLogger componentLogger;
|
private final ComponentLogger componentLogger;
|
||||||
|
|
||||||
public GeyserPaperLogger(Plugin plugin, Logger logger, boolean debug) {
|
public GeyserPaperLogger(Plugin plugin, Logger logger) {
|
||||||
super(logger, debug);
|
super(logger);
|
||||||
componentLogger = plugin.getComponentLogger();
|
componentLogger = plugin.getComponentLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.spigot;
|
package org.geysermc.geyser.platform.spigot;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
|
||||||
import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer;
|
import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.*;
|
import io.netty.channel.*;
|
||||||
|
|
@ -119,8 +119,11 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
||||||
protected void initChannel(@NonNull Channel ch) throws Exception {
|
protected void initChannel(@NonNull Channel ch) throws Exception {
|
||||||
initChannel.invoke(childHandler, ch);
|
initChannel.invoke(childHandler, ch);
|
||||||
|
|
||||||
|
int index = ch.pipeline().names().indexOf("encoder");
|
||||||
|
String baseName = index != -1 ? "encoder" : "outbound_config";
|
||||||
|
|
||||||
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
|
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
|
||||||
ch.pipeline().addAfter("encoder", "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
|
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -177,6 +180,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
||||||
bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress,
|
bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress,
|
||||||
InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper());
|
InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper());
|
||||||
session.connect();
|
session.connect();
|
||||||
|
session.disconnect("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,15 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.spigot;
|
package org.geysermc.geyser.platform.spigot;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
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;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class GeyserSpigotLogger implements GeyserLogger {
|
public class GeyserSpigotLogger implements GeyserLogger {
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
@Getter @Setter
|
@Getter @Setter
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.Constants;
|
import org.geysermc.geyser.Constants;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.adapters.paper.PaperAdapters;
|
||||||
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||||
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;
|
||||||
|
|
@ -78,18 +79,14 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
/**
|
|
||||||
* Determines if the plugin has been ran once before, including before /geyser reload.
|
|
||||||
*/
|
|
||||||
private static boolean INITIALIZED = false;
|
|
||||||
|
|
||||||
private GeyserSpigotCommandManager geyserCommandManager;
|
private GeyserSpigotCommandManager geyserCommandManager;
|
||||||
private GeyserSpigotConfiguration geyserConfig;
|
private GeyserSpigotConfiguration geyserConfig;
|
||||||
private GeyserSpigotInjector geyserInjector;
|
private GeyserSpigotInjector geyserInjector;
|
||||||
private GeyserSpigotLogger geyserLogger;
|
private final GeyserSpigotLogger geyserLogger = GeyserPaperLogger.supported() ?
|
||||||
|
new GeyserPaperLogger(this, getLogger()) : new GeyserSpigotLogger(getLogger());
|
||||||
private IGeyserPingPassthrough geyserSpigotPingPassthrough;
|
private IGeyserPingPassthrough geyserSpigotPingPassthrough;
|
||||||
private GeyserSpigotWorldManager geyserWorldManager;
|
private GeyserSpigotWorldManager geyserWorldManager;
|
||||||
|
|
||||||
|
|
@ -102,6 +99,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
|
onGeyserInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserInitialize() {
|
||||||
GeyserLocale.init(this);
|
GeyserLocale.init(this);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -112,12 +114,13 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
// We depend on this as a fallback in certain scenarios
|
// We depend on this as a fallback in certain scenarios
|
||||||
BlockData.class.getMethod("getAsString");
|
BlockData.class.getMethod("getAsString");
|
||||||
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||||
getLogger().severe("*********************************************");
|
geyserLogger.error("*********************************************");
|
||||||
getLogger().severe("");
|
geyserLogger.error("");
|
||||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header"));
|
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header"));
|
||||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
|
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
|
||||||
getLogger().severe("");
|
geyserLogger.error("");
|
||||||
getLogger().severe("*********************************************");
|
geyserLogger.error("*********************************************");
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,12 +128,13 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
Class.forName("net.md_5.bungee.chat.ComponentSerializer");
|
Class.forName("net.md_5.bungee.chat.ComponentSerializer");
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
if (!PaperAdventure.canSendMessageUsingComponent()) { // Prepare for Paper eventually removing Bungee chat
|
if (!PaperAdventure.canSendMessageUsingComponent()) { // Prepare for Paper eventually removing Bungee chat
|
||||||
getLogger().severe("*********************************************");
|
geyserLogger.error("*********************************************");
|
||||||
getLogger().severe("");
|
geyserLogger.error("");
|
||||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName()));
|
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName()));
|
||||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
|
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
|
||||||
getLogger().severe("");
|
geyserLogger.error("");
|
||||||
getLogger().severe("*********************************************");
|
geyserLogger.error("*********************************************");
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -138,91 +142,76 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
try {
|
try {
|
||||||
Class.forName("io.netty.util.internal.ObjectPool$ObjectCreator");
|
Class.forName("io.netty.util.internal.ObjectPool$ObjectCreator");
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
getLogger().severe("*********************************************");
|
geyserLogger.error("*********************************************");
|
||||||
getLogger().severe("");
|
geyserLogger.error("");
|
||||||
getLogger().severe("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
|
geyserLogger.error("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
|
||||||
getLogger().severe("");
|
geyserLogger.error("");
|
||||||
getLogger().severe("*********************************************");
|
geyserLogger.error("*********************************************");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
|
|
||||||
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, GeyserSpigotConfiguration.class);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode())
|
if (!loadConfig()) {
|
||||||
: new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
|
return;
|
||||||
|
}
|
||||||
|
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
|
||||||
|
// Turn "(MC: 1.16.4)" into 1.16.4.
|
||||||
|
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this);
|
this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
if (this.geyserConfig == null) {
|
|
||||||
// We failed to initialize correctly
|
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
|
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
|
||||||
this.geyserCommandManager.init();
|
this.geyserCommandManager.init();
|
||||||
|
|
||||||
if (!INITIALIZED) {
|
// Because Bukkit locks its command map upon startup, we need to
|
||||||
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
// add our plugin commands in onEnable, but populating the executor
|
||||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
// can happen at any time (later in #onGeyserEnable())
|
||||||
|
CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap();
|
||||||
|
for (Extension extension : this.geyserCommandManager.extensionCommands().keySet()) {
|
||||||
|
// Thanks again, Bukkit
|
||||||
|
try {
|
||||||
|
Constructor<PluginCommand> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
|
||||||
@EventHandler
|
PluginCommand pluginCommand = constructor.newInstance(extension.description().id(), this);
|
||||||
public void onServerLoaded(ServerLoadEvent event) {
|
pluginCommand.setDescription("The main command for the " + extension.name() + " Geyser extension!");
|
||||||
// Wait until all plugins have loaded so Geyser can start
|
|
||||||
postStartup();
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
// Because Bukkit locks its command map upon startup, we need to
|
commandMap.register(extension.description().id(), "geyserext", pluginCommand);
|
||||||
// add our plugin commands in onEnable, but populating the executor
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
|
||||||
// can happen at any time
|
this.geyserLogger.error("Failed to construct PluginCommand for extension " + extension.name(), ex);
|
||||||
CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap();
|
|
||||||
for (Extension extension : this.geyserCommandManager.extensionCommands().keySet()) {
|
|
||||||
// Thanks again, Bukkit
|
|
||||||
try {
|
|
||||||
Constructor<PluginCommand> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
|
|
||||||
PluginCommand pluginCommand = constructor.newInstance(extension.description().id(), this);
|
|
||||||
pluginCommand.setDescription("The main command for the " + extension.name() + " Geyser extension!");
|
|
||||||
|
|
||||||
commandMap.register(extension.description().id(), "geyserext", pluginCommand);
|
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
|
|
||||||
this.geyserLogger.error("Failed to construct PluginCommand for extension " + extension.name(), ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (INITIALIZED) {
|
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
||||||
// Reload; continue with post startup
|
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||||
postStartup();
|
|
||||||
}
|
@EventHandler
|
||||||
|
public void onServerLoaded(ServerLoadEvent event) {
|
||||||
|
if (event.getType() == ServerLoadEvent.LoadType.RELOAD) {
|
||||||
|
geyser.setShuttingDown(false);
|
||||||
|
}
|
||||||
|
onGeyserEnable();
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void postStartup() {
|
public void onGeyserEnable() {
|
||||||
GeyserImpl.start();
|
// Configs are loaded once early - so we can create the logger, then load extensions and finally register
|
||||||
|
// extension commands in #onEnable. To ensure reloading geyser also reloads the geyser config, this exists
|
||||||
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
|
if (!loadConfig()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.geyserLogger.setDebug(this.geyserConfig.isDebugMode());
|
||||||
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
}
|
||||||
|
|
||||||
// Turn "(MC: 1.16.4)" into 1.16.4.
|
GeyserImpl.start();
|
||||||
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||||
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
|
|
@ -238,20 +227,16 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass()));
|
geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass()));
|
||||||
|
|
||||||
boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null;
|
// Don't need to re-create the world manager/re-register commands/reinject when reloading
|
||||||
if (isViaVersion) {
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
try {
|
return;
|
||||||
// Ensure that we have the latest 4.0.0 changes and not an older ViaVersion version
|
|
||||||
Class.forName("com.viaversion.viaversion.api.ViaManager");
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
GeyserSpigotVersionChecker.sendOutdatedViaVersionMessage(geyserLogger);
|
|
||||||
isViaVersion = false;
|
|
||||||
if (this.geyserConfig.isDebugMode()) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null;
|
||||||
|
|
||||||
|
// Check to ensure the current setup can support the protocol version Geyser uses
|
||||||
|
GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion);
|
||||||
|
|
||||||
// We want to do this late in the server startup process to allow plugins such as ViaVersion and ProtocolLib
|
// We want to do this late in the server startup process to allow plugins such as ViaVersion and ProtocolLib
|
||||||
// To do their job injecting, then connect into *that*
|
// To do their job injecting, then connect into *that*
|
||||||
this.geyserInjector = new GeyserSpigotInjector(isViaVersion);
|
this.geyserInjector = new GeyserSpigotInjector(isViaVersion);
|
||||||
|
|
@ -259,17 +244,29 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
|
|
||||||
if (Boolean.parseBoolean(System.getProperty("Geyser.UseDirectAdapters", "true"))) {
|
if (Boolean.parseBoolean(System.getProperty("Geyser.UseDirectAdapters", "true"))) {
|
||||||
try {
|
try {
|
||||||
String name = Bukkit.getServer().getClass().getPackage().getName();
|
boolean isPaper = false;
|
||||||
String nmsVersion = name.substring(name.lastIndexOf('.') + 1);
|
try {
|
||||||
SpigotAdapters.registerWorldAdapter(nmsVersion);
|
String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||||
|
String nmsVersion = name.substring(name.lastIndexOf('.') + 1);
|
||||||
|
SpigotAdapters.registerWorldAdapter(nmsVersion);
|
||||||
|
geyserLogger.debug("Using spigot NMS adapter for nms version: " + nmsVersion);
|
||||||
|
} catch (Exception e) { // Likely running on Paper 1.20.5+
|
||||||
|
geyserLogger.debug("Unable to find spigot world manager: " + e.getMessage());
|
||||||
|
//noinspection deprecation
|
||||||
|
int protocolVersion = Bukkit.getUnsafe().getProtocolVersion();
|
||||||
|
PaperAdapters.registerClosestWorldAdapter(protocolVersion);
|
||||||
|
isPaper = true;
|
||||||
|
geyserLogger.debug("Using paper world adapter for protocol version: " + protocolVersion);
|
||||||
|
}
|
||||||
|
|
||||||
if (isViaVersion && isViaVersionNeeded()) {
|
if (isViaVersion && isViaVersionNeeded()) {
|
||||||
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this);
|
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, isPaper);
|
||||||
} else {
|
} else {
|
||||||
// No ViaVersion
|
// No ViaVersion
|
||||||
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this);
|
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, isPaper);
|
||||||
}
|
}
|
||||||
geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
|
geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName());
|
||||||
} catch (Exception e) {
|
} catch (Throwable e) {
|
||||||
if (geyserConfig.isDebugMode()) {
|
if (geyserConfig.isDebugMode()) {
|
||||||
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
|
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
@ -278,6 +275,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
} else {
|
} else {
|
||||||
geyserLogger.debug("Not using NMS adapter as it is disabled via system property.");
|
geyserLogger.debug("Not using NMS adapter as it is disabled via system property.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.geyserWorldManager == null) {
|
if (this.geyserWorldManager == null) {
|
||||||
// No NMS adapter
|
// No NMS adapter
|
||||||
this.geyserWorldManager = new GeyserSpigotWorldManager(this);
|
this.geyserWorldManager = new GeyserSpigotWorldManager(this);
|
||||||
|
|
@ -302,72 +300,72 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
command.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands));
|
command.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!INITIALIZED) {
|
// Register permissions so they appear in, for example, LuckPerms' UI
|
||||||
// Register permissions so they appear in, for example, LuckPerms' UI
|
// Re-registering permissions throws an error
|
||||||
// Re-registering permissions throws an error
|
for (Map.Entry<String, Command> entry : geyserCommandManager.commands().entrySet()) {
|
||||||
for (Map.Entry<String, Command> entry : geyserCommandManager.commands().entrySet()) {
|
Command command = entry.getValue();
|
||||||
|
if (command.aliases().contains(entry.getKey())) {
|
||||||
|
// Don't register aliases
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
||||||
|
GeyserLocale.getLocaleStringLog(command.description()),
|
||||||
|
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register permissions for extension commands
|
||||||
|
for (Map.Entry<Extension, Map<String, Command>> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||||
|
for (Map.Entry<String, Command> entry : commandEntry.getValue().entrySet()) {
|
||||||
Command command = entry.getValue();
|
Command command = entry.getValue();
|
||||||
if (command.aliases().contains(entry.getKey())) {
|
if (command.aliases().contains(entry.getKey())) {
|
||||||
// Don't register aliases
|
// Don't register aliases
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.permission().isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid registering the same permission twice, e.g. for the extension help commands
|
||||||
|
if (Bukkit.getPluginManager().getPermission(command.permission()) != null) {
|
||||||
|
GeyserImpl.getInstance().getLogger().debug("Skipping permission " + command.permission() + " as it is already registered");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
||||||
GeyserLocale.getLocaleStringLog(command.description()),
|
GeyserLocale.getLocaleStringLog(command.description()),
|
||||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register permissions for extension commands
|
|
||||||
for (Map.Entry<Extension, Map<String, Command>> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
|
||||||
for (Map.Entry<String, Command> entry : commandEntry.getValue().entrySet()) {
|
|
||||||
Command command = entry.getValue();
|
|
||||||
if (command.aliases().contains(entry.getKey())) {
|
|
||||||
// Don't register aliases
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command.permission().isBlank()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid registering the same permission twice, e.g. for the extension help commands
|
|
||||||
if (Bukkit.getPluginManager().getPermission(command.permission()) != null) {
|
|
||||||
GeyserImpl.getInstance().getLogger().debug("Skipping permission " + command.permission() + " as it is already registered");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
|
||||||
GeyserLocale.getLocaleStringLog(command.description()),
|
|
||||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION,
|
|
||||||
"Whether update notifications can be seen", PermissionDefault.OP));
|
|
||||||
|
|
||||||
// Events cannot be unregistered - re-registering results in duplicate firings
|
|
||||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
|
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
|
||||||
|
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
|
||||||
|
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigotUpdateListener(), this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION,
|
||||||
|
"Whether update notifications can be seen", PermissionDefault.OP));
|
||||||
|
|
||||||
|
// Events cannot be unregistered - re-registering results in duplicate firings
|
||||||
|
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||||
|
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
||||||
|
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigotUpdateListener(), this);
|
||||||
|
|
||||||
boolean brigadierSupported = CommodoreProvider.isSupported();
|
boolean brigadierSupported = CommodoreProvider.isSupported();
|
||||||
geyserLogger.debug("Brigadier supported? " + brigadierSupported);
|
geyserLogger.debug("Brigadier supported? " + brigadierSupported);
|
||||||
if (brigadierSupported) {
|
if (brigadierSupported) {
|
||||||
GeyserBrigadierSupport.loadBrigadier(this, geyserCommand);
|
GeyserBrigadierSupport.loadBrigadier(this, geyserCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to ensure the current setup can support the protocol version Geyser uses
|
|
||||||
GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion);
|
|
||||||
|
|
||||||
INITIALIZED = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserDisable() {
|
||||||
|
if (geyser != null) {
|
||||||
|
geyser.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserShutdown() {
|
||||||
if (geyser != null) {
|
if (geyser != null) {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
}
|
}
|
||||||
|
|
@ -376,6 +374,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
this.onGeyserShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserSpigotConfiguration getGeyserConfig() {
|
public GeyserSpigotConfiguration getGeyserConfig() {
|
||||||
return geyserConfig;
|
return geyserConfig;
|
||||||
|
|
@ -442,7 +445,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
||||||
MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
|
MappingData mappingData = protocolList.get(i).protocol().getMappingData();
|
||||||
if (mappingData != null) {
|
if (mappingData != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -470,4 +473,25 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
private boolean loadConfig() {
|
||||||
|
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
|
||||||
|
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, GeyserSpigotConfiguration.class);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||||
|
ex.printStackTrace();
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.spigot;
|
package org.geysermc.geyser.platform.spigot;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
|
import org.geysermc.mcprotocollib.protocol.data.DefaultComponentSerializer;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,8 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.spigot.world;
|
package org.geysermc.geyser.platform.spigot.world;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
@ -40,13 +38,17 @@ import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.block.BlockPistonEvent;
|
import org.bukkit.event.block.BlockPistonEvent;
|
||||||
import org.bukkit.event.block.BlockPistonExtendEvent;
|
import org.bukkit.event.block.BlockPistonExtendEvent;
|
||||||
import org.bukkit.event.block.BlockPistonRetractEvent;
|
import org.bukkit.event.block.BlockPistonRetractEvent;
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
import org.geysermc.geyser.level.block.property.Properties;
|
||||||
|
import org.geysermc.geyser.level.block.type.BlockState;
|
||||||
import org.geysermc.geyser.level.physics.Direction;
|
import org.geysermc.geyser.level.physics.Direction;
|
||||||
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.session.cache.PistonCache;
|
import org.geysermc.geyser.session.cache.PistonCache;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -85,7 +87,7 @@ public class GeyserPistonListener implements Listener {
|
||||||
PistonValueType type = isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING;
|
PistonValueType type = isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING;
|
||||||
boolean sticky = event.isSticky();
|
boolean sticky = event.isSticky();
|
||||||
|
|
||||||
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>();
|
Object2ObjectMap<Vector3i, BlockState> attachedBlocks = new Object2ObjectArrayMap<>();
|
||||||
boolean blocksFilled = false;
|
boolean blocksFilled = false;
|
||||||
|
|
||||||
for (Map.Entry<UUID, GeyserSession> entry : geyser.getSessionManager().getSessions().entrySet()) {
|
for (Map.Entry<UUID, GeyserSession> entry : geyser.getSessionManager().getSessions().entrySet()) {
|
||||||
|
|
@ -108,10 +110,10 @@ public class GeyserPistonListener implements Listener {
|
||||||
List<Block> blocks = isExtend ? ((BlockPistonExtendEvent) event).getBlocks() : ((BlockPistonRetractEvent) event).getBlocks();
|
List<Block> blocks = isExtend ? ((BlockPistonExtendEvent) event).getBlocks() : ((BlockPistonRetractEvent) event).getBlocks();
|
||||||
for (Block block : blocks) {
|
for (Block block : blocks) {
|
||||||
Location attachedLocation = block.getLocation();
|
Location attachedLocation = block.getLocation();
|
||||||
int blockId = worldManager.getBlockNetworkId(block);
|
BlockState state = BlockState.of(worldManager.getBlockNetworkId(block));
|
||||||
// Ignore blocks that will be destroyed
|
// Ignore blocks that will be destroyed
|
||||||
if (BlockStateValues.canPistonMoveBlock(blockId, isExtend)) {
|
if (BlockStateValues.canPistonMoveBlock(state, isExtend)) {
|
||||||
attachedBlocks.put(getVector(attachedLocation), blockId);
|
attachedBlocks.put(getVector(attachedLocation), state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
blocksFilled = true;
|
blocksFilled = true;
|
||||||
|
|
@ -119,7 +121,7 @@ public class GeyserPistonListener implements Listener {
|
||||||
|
|
||||||
int pistonBlockId = worldManager.getBlockNetworkId(event.getBlock());
|
int pistonBlockId = worldManager.getBlockNetworkId(event.getBlock());
|
||||||
// event.getDirection() is unreliable
|
// event.getDirection() is unreliable
|
||||||
Direction orientation = BlockStateValues.getPistonOrientation(pistonBlockId);
|
Direction orientation = BlockState.of(pistonBlockId).getValue(Properties.FACING);
|
||||||
|
|
||||||
session.executeInEventLoop(() -> {
|
session.executeInEventLoop(() -> {
|
||||||
PistonCache pistonCache = session.getPistonCache();
|
PistonCache pistonCache = session.getPistonCache();
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.block.BlockPlaceEvent;
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.type.Block;
|
||||||
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
@ -59,11 +59,11 @@ public class GeyserSpigotBlockPlaceListener implements Listener {
|
||||||
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
|
event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())));
|
||||||
} else {
|
} else {
|
||||||
String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
|
String javaBlockId = event.getBlockPlaced().getBlockData().getAsString();
|
||||||
placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getOrDefault(javaBlockId, BlockStateValues.JAVA_AIR_ID)));
|
placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getOrDefault(javaBlockId, Block.JAVA_AIR_ID)));
|
||||||
}
|
}
|
||||||
placeBlockSoundPacket.setIdentifier(":");
|
placeBlockSoundPacket.setIdentifier(":");
|
||||||
session.sendUpstreamPacket(placeBlockSoundPacket);
|
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||||
session.setLastBlockPlacePosition(null);
|
session.setLastBlockPlacePosition(null);
|
||||||
session.setLastBlockPlacedId(null);
|
session.setLastBlockPlaced(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl
|
||||||
|
|
||||||
private final Int2IntMap oldToNewBlockId;
|
private final Int2IntMap oldToNewBlockId;
|
||||||
|
|
||||||
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin) {
|
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean isPaper) {
|
||||||
super(plugin);
|
super(plugin, isPaper);
|
||||||
IntList allBlockStates = adapter.getAllBlockStates();
|
IntList allBlockStates = adapter.getAllBlockStates();
|
||||||
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
|
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
|
||||||
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
|
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
|
||||||
|
|
@ -58,7 +58,7 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl
|
||||||
int newBlockId = oldBlockId;
|
int newBlockId = oldBlockId;
|
||||||
// protocolList should *not* be null; we checked for that before initializing this class
|
// protocolList should *not* be null; we checked for that before initializing this class
|
||||||
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
for (int i = protocolList.size() - 1; i >= 0; i--) {
|
||||||
MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
|
MappingData mappingData = protocolList.get(i).protocol().getMappingData();
|
||||||
if (mappingData != null) {
|
if (mappingData != null) {
|
||||||
newBlockId = mappingData.getNewBlockStateId(newBlockId);
|
newBlockId = mappingData.getNewBlockStateId(newBlockId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,27 +26,34 @@
|
||||||
package org.geysermc.geyser.platform.spigot.world.manager;
|
package org.geysermc.geyser.platform.spigot.world.manager;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.adapters.WorldAdapter;
|
||||||
|
import org.geysermc.geyser.adapters.paper.PaperAdapters;
|
||||||
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||||
import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
|
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
import org.geysermc.geyser.level.block.type.Block;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
||||||
protected final SpigotWorldAdapter adapter;
|
protected final WorldAdapter<World> adapter;
|
||||||
|
|
||||||
public GeyserSpigotNativeWorldManager(Plugin plugin) {
|
public GeyserSpigotNativeWorldManager(Plugin plugin, boolean isPaper) {
|
||||||
super(plugin);
|
super(plugin);
|
||||||
adapter = SpigotAdapters.getWorldAdapter();
|
if (isPaper) {
|
||||||
|
adapter = PaperAdapters.getWorldAdapter();
|
||||||
|
} else {
|
||||||
|
adapter = SpigotAdapters.getWorldAdapter();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||||
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
|
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
return BlockStateValues.JAVA_AIR_ID;
|
return Block.JAVA_AIR_ID;
|
||||||
}
|
}
|
||||||
return adapter.getBlockAt(player.getWorld(), x, y, z);
|
return adapter.getBlockAt(player.getWorld(), x, y, z);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,56 +25,52 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.spigot.world.manager;
|
package org.geysermc.geyser.platform.spigot.world.manager;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.DecoratedPot;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
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.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.geysermc.erosion.bukkit.BukkitLecterns;
|
|
||||||
import org.geysermc.erosion.bukkit.BukkitUtils;
|
import org.geysermc.erosion.bukkit.BukkitUtils;
|
||||||
import org.geysermc.erosion.bukkit.PickBlockUtils;
|
import org.geysermc.erosion.bukkit.PickBlockUtils;
|
||||||
import org.geysermc.erosion.bukkit.SchedulerUtils;
|
import org.geysermc.erosion.bukkit.SchedulerUtils;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.level.GameRule;
|
import org.geysermc.geyser.level.GameRule;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base world manager to use when there is no supported NMS revision
|
* The base world manager to use when there is no supported NMS revision
|
||||||
*/
|
*/
|
||||||
public class GeyserSpigotWorldManager extends WorldManager {
|
public class GeyserSpigotWorldManager extends WorldManager {
|
||||||
private final Plugin plugin;
|
private final Plugin plugin;
|
||||||
private final BukkitLecterns lecterns;
|
|
||||||
|
|
||||||
public GeyserSpigotWorldManager(Plugin plugin) {
|
public GeyserSpigotWorldManager(Plugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.lecterns = new BukkitLecterns(plugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||||
Player bukkitPlayer;
|
Player bukkitPlayer;
|
||||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||||
return BlockStateValues.JAVA_AIR_ID;
|
return org.geysermc.geyser.level.block.type.Block.JAVA_AIR_ID;
|
||||||
}
|
}
|
||||||
World world = bukkitPlayer.getWorld();
|
World world = bukkitPlayer.getWorld();
|
||||||
if (!world.isChunkLoaded(x >> 4, z >> 4)) {
|
if (!world.isChunkLoaded(x >> 4, z >> 4)) {
|
||||||
// If the chunk isn't loaded, how could we even be here?
|
// If the chunk isn't loaded, how could we even be here?
|
||||||
return BlockStateValues.JAVA_AIR_ID;
|
return org.geysermc.geyser.level.block.type.Block.JAVA_AIR_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getBlockNetworkId(world.getBlockAt(x, y, z));
|
return getBlockNetworkId(world.getBlockAt(x, y, z));
|
||||||
|
|
@ -85,9 +81,9 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||||
// Terrible behavior, but this is basically what's always been happening behind the scenes anyway.
|
// Terrible behavior, but this is basically what's always been happening behind the scenes anyway.
|
||||||
CompletableFuture<String> blockData = new CompletableFuture<>();
|
CompletableFuture<String> blockData = new CompletableFuture<>();
|
||||||
Bukkit.getRegionScheduler().execute(this.plugin, block.getLocation(), () -> blockData.complete(block.getBlockData().getAsString()));
|
Bukkit.getRegionScheduler().execute(this.plugin, block.getLocation(), () -> blockData.complete(block.getBlockData().getAsString()));
|
||||||
return BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(blockData.join(), BlockStateValues.JAVA_AIR_ID);
|
return BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(blockData.join(), org.geysermc.geyser.level.block.type.Block.JAVA_AIR_ID);
|
||||||
}
|
}
|
||||||
return BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(block.getBlockData().getAsString(), BlockStateValues.JAVA_AIR_ID);
|
return BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(block.getBlockData().getAsString(), org.geysermc.geyser.level.block.type.Block.JAVA_AIR_ID); // TODO could just make this a BlockState lookup?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -95,69 +91,6 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
|
||||||
Player bukkitPlayer;
|
|
||||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
|
||||||
// Run as a task to prevent async issues
|
|
||||||
SchedulerUtils.runTask(this.plugin, () -> sendLecternData(session, block, false), block);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
|
||||||
Player bukkitPlayer;
|
|
||||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (SchedulerUtils.FOLIA) {
|
|
||||||
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
|
|
||||||
if (chunk == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Bukkit.getRegionScheduler().execute(this.plugin, bukkitPlayer.getWorld(), x, z, () ->
|
|
||||||
sendLecternData(session, chunk, blockEntityInfos));
|
|
||||||
} else {
|
|
||||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
|
||||||
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
|
|
||||||
if (chunk == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sendLecternData(session, chunk, blockEntityInfos);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private @Nullable Chunk getChunk(World world, int x, int z) {
|
|
||||||
if (!world.isChunkLoaded(x, z)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return world.getChunkAt(x, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendLecternData(GeyserSession session, Chunk chunk, List<BlockEntityInfo> blockEntityInfos) {
|
|
||||||
//noinspection ForLoopReplaceableByForEach - avoid constructing Iterator
|
|
||||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
|
||||||
BlockEntityInfo info = blockEntityInfos.get(i);
|
|
||||||
Block block = chunk.getBlock(info.getX(), info.getY(), info.getZ());
|
|
||||||
sendLecternData(session, block, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendLecternData(GeyserSession session, Block block, boolean isChunkLoad) {
|
|
||||||
NbtMap blockEntityTag = this.lecterns.getLecternData(block, isChunkLoad);
|
|
||||||
if (blockEntityTag != null) {
|
|
||||||
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, BukkitUtils.getVector(block.getLocation()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
||||||
org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID());
|
org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID());
|
||||||
if (bukkitGameRule == null) {
|
if (bukkitGameRule == null) {
|
||||||
|
|
@ -205,18 +138,32 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
public @NonNull CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||||
CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>();
|
|
||||||
Player bukkitPlayer;
|
Player bukkitPlayer;
|
||||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
||||||
future.complete(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
return future;
|
|
||||||
}
|
}
|
||||||
|
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
|
||||||
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
||||||
// Paper 1.19.3 complains about async access otherwise.
|
// Paper 1.19.3 complains about async access otherwise.
|
||||||
// java.lang.IllegalStateException: Tile is null, asynchronous access?
|
// java.lang.IllegalStateException: Tile is null, asynchronous access?
|
||||||
SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
|
SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
|
||||||
return future;
|
return future.thenApply(RAW_TRANSFORMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getDecoratedPotData(GeyserSession session, Vector3i pos, Consumer<List<String>> apply) {
|
||||||
|
Player bukkitPlayer;
|
||||||
|
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Block block = bukkitPlayer.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
|
||||||
|
SchedulerUtils.runTask(this.plugin, () -> {
|
||||||
|
var state = BukkitUtils.getBlockState(block);
|
||||||
|
if (!(state instanceof DecoratedPot pot)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
apply.accept(pot.getShards().stream().map(material -> material.getKey().toString()).toList());
|
||||||
|
}, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@ import org.apache.logging.log4j.core.Appender;
|
||||||
import org.apache.logging.log4j.core.Logger;
|
import org.apache.logging.log4j.core.Logger;
|
||||||
import org.apache.logging.log4j.core.appender.ConsoleAppender;
|
import org.apache.logging.log4j.core.appender.ConsoleAppender;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
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.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.configuration.GeyserJacksonConfiguration;
|
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
||||||
|
|
@ -59,20 +59,24 @@ import java.lang.reflect.Method;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.*;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
|
|
||||||
private GeyserCommandManager geyserCommandManager;
|
private GeyserCommandManager geyserCommandManager;
|
||||||
private GeyserStandaloneConfiguration geyserConfig;
|
private GeyserStandaloneConfiguration geyserConfig;
|
||||||
private GeyserStandaloneLogger geyserLogger;
|
private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger();
|
||||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||||
|
|
||||||
private GeyserStandaloneGUI gui;
|
private GeyserStandaloneGUI gui;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private boolean useGui = System.console() == null && !isHeadless();
|
private boolean useGui = System.console() == null && !isHeadless();
|
||||||
|
private Logger log4jLogger;
|
||||||
private String configFilename = "config.yml";
|
private String configFilename = "config.yml";
|
||||||
|
|
||||||
private GeyserImpl geyser;
|
private GeyserImpl geyser;
|
||||||
|
|
@ -161,28 +165,22 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bootstrap.onEnable(useGuiOpts, configFilenameOpt);
|
bootstrap.useGui = useGuiOpts;
|
||||||
}
|
bootstrap.configFilename = configFilenameOpt;
|
||||||
|
bootstrap.onGeyserInitialize();
|
||||||
public void onEnable(boolean useGui, String configFilename) {
|
|
||||||
this.configFilename = configFilename;
|
|
||||||
this.useGui = useGui;
|
|
||||||
this.onEnable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onGeyserInitialize() {
|
||||||
Logger logger = (Logger) LogManager.getRootLogger();
|
log4jLogger = (Logger) LogManager.getRootLogger();
|
||||||
for (Appender appender : logger.getAppenders().values()) {
|
for (Appender appender : log4jLogger.getAppenders().values()) {
|
||||||
// Remove the appender that is not in use
|
// Remove the appender that is not in use
|
||||||
// Prevents multiple appenders/double logging and removes harmless errors
|
// Prevents multiple appenders/double logging and removes harmless errors
|
||||||
if ((useGui && appender instanceof TerminalConsoleAppender) || (!useGui && appender instanceof ConsoleAppender)) {
|
if ((useGui && appender instanceof TerminalConsoleAppender) || (!useGui && appender instanceof ConsoleAppender)) {
|
||||||
logger.removeAppender(appender);
|
log4jLogger.removeAppender(appender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyserLogger = new GeyserStandaloneLogger();
|
|
||||||
|
|
||||||
if (useGui && gui == null) {
|
if (useGui && gui == null) {
|
||||||
gui = new GeyserStandaloneGUI(geyserLogger);
|
gui = new GeyserStandaloneGUI(geyserLogger);
|
||||||
gui.redirectSystemStreams();
|
gui.redirectSystemStreams();
|
||||||
|
|
@ -190,7 +188,12 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
LoopbackUtil.checkAndApplyLoopback(geyserLogger);
|
LoopbackUtil.checkAndApplyLoopback(geyserLogger);
|
||||||
|
|
||||||
|
this.onGeyserEnable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserEnable() {
|
||||||
try {
|
try {
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml",
|
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml",
|
||||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||||
|
|
@ -215,14 +218,15 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
|
||||||
// Allow libraries like Protocol to have their debug information passthrough
|
// Allow libraries like Protocol to have their debug information passthrough
|
||||||
logger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO);
|
log4jLogger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO);
|
||||||
|
|
||||||
geyser = GeyserImpl.load(PlatformType.STANDALONE, this);
|
geyser = GeyserImpl.load(PlatformType.STANDALONE, this);
|
||||||
GeyserImpl.start();
|
|
||||||
|
|
||||||
geyserCommandManager = new GeyserCommandManager(geyser);
|
geyserCommandManager = new GeyserCommandManager(geyser);
|
||||||
geyserCommandManager.init();
|
geyserCommandManager.init();
|
||||||
|
|
||||||
|
GeyserImpl.start();
|
||||||
|
|
||||||
if (gui != null) {
|
if (gui != null) {
|
||||||
gui.enableCommands(geyser.getScheduledThread(), geyserCommandManager);
|
gui.enableCommands(geyser.getScheduledThread(), geyserCommandManager);
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +254,14 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserDisable() {
|
||||||
|
// We can re-register commands on standalone, so why not
|
||||||
|
GeyserImpl.getInstance().commandManager().getCommands().clear();
|
||||||
|
geyser.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserShutdown() {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void shutdown() {
|
protected void shutdown() {
|
||||||
GeyserImpl.getInstance().getBootstrap().onDisable();
|
GeyserImpl.getInstance().getBootstrap().onGeyserShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ public class GeyserStandaloneGUI {
|
||||||
Container cp = frame.getContentPane();
|
Container cp = frame.getContentPane();
|
||||||
|
|
||||||
// Fetch and set the icon for the frame
|
// Fetch and set the icon for the frame
|
||||||
URL image = getClass().getClassLoader().getResource("icon.png");
|
URL image = getClass().getClassLoader().getResource("assets/geyser/icon.png");
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
ImageIcon icon = new ImageIcon(image);
|
ImageIcon icon = new ImageIcon(image);
|
||||||
frame.setIconImage(icon.getImage());
|
frame.setIconImage(icon.getImage());
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
dependencies {
|
dependencies {
|
||||||
annotationProcessor(libs.velocity.api)
|
annotationProcessor(libs.velocity.api)
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
|
|
||||||
|
compileOnlyApi(libs.velocity.api)
|
||||||
}
|
}
|
||||||
|
|
||||||
platformRelocate("com.fasterxml.jackson")
|
platformRelocate("com.fasterxml.jackson")
|
||||||
platformRelocate("it.unimi.dsi.fastutil")
|
platformRelocate("it.unimi.dsi.fastutil")
|
||||||
platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl")
|
platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl")
|
||||||
|
platformRelocate("org.yaml")
|
||||||
|
|
||||||
exclude("com.google.*:*")
|
exclude("com.google.*:*")
|
||||||
|
|
||||||
// Needed because Velocity provides every dependency except netty-resolver-dns
|
// Needed because Velocity provides every dependency except netty-resolver-dns
|
||||||
|
exclude("io.netty.incubator:.*")
|
||||||
exclude("io.netty:netty-transport-native-epoll:*")
|
exclude("io.netty:netty-transport-native-epoll:*")
|
||||||
exclude("io.netty:netty-transport-native-unix-common:*")
|
exclude("io.netty:netty-transport-native-unix-common:*")
|
||||||
exclude("io.netty:netty-transport-native-kqueue:*")
|
exclude("io.netty:netty-transport-native-kqueue:*")
|
||||||
|
|
@ -54,6 +58,7 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
exclude(dependency("io.netty:netty-transport:.*"))
|
exclude(dependency("io.netty:netty-transport:.*"))
|
||||||
exclude(dependency("io.netty:netty-codec:.*"))
|
exclude(dependency("io.netty:netty-codec:.*"))
|
||||||
exclude(dependency("io.netty:netty-codec-haproxy:.*"))
|
exclude(dependency("io.netty:netty-codec-haproxy:.*"))
|
||||||
|
exclude(dependency("io.netty.incubator:.*"))
|
||||||
exclude(dependency("org.slf4j:.*"))
|
exclude(dependency("org.slf4j:.*"))
|
||||||
exclude(dependency("org.ow2.asm:.*"))
|
exclude(dependency("org.ow2.asm:.*"))
|
||||||
// Exclude all Kyori dependencies except the legacy NBT serializer
|
// Exclude all Kyori dependencies except the legacy NBT serializer
|
||||||
|
|
@ -64,4 +69,9 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||||
exclude(dependency("net.kyori:adventure-text-serializer-legacy:.*"))
|
exclude(dependency("net.kyori:adventure-text-serializer-legacy:.*"))
|
||||||
exclude(dependency("net.kyori:adventure-nbt:.*"))
|
exclude(dependency("net.kyori:adventure-nbt:.*"))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modrinth {
|
||||||
|
uploadFile.set(tasks.getByPath("shadowJar"))
|
||||||
|
loaders.addAll("velocity")
|
||||||
}
|
}
|
||||||
|
|
@ -47,8 +47,14 @@ public class GeyserVelocityCompressionDisabler extends ChannelDuplexHandler {
|
||||||
Method setCompressionMethod = null;
|
Method setCompressionMethod = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
compressionPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.SetCompression");
|
try {
|
||||||
loginSuccessPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess");
|
compressionPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.SetCompressionPacket");
|
||||||
|
loginSuccessPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.ServerLoginSuccessPacket");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// Velocity renamed packet classes in https://github.com/PaperMC/Velocity/commit/2ac8751337befd04f4663575f5d752c748384110
|
||||||
|
compressionPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.SetCompression");
|
||||||
|
loginSuccessPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess");
|
||||||
|
}
|
||||||
compressionEnabledEvent = Class.forName("com.velocitypowered.proxy.protocol.VelocityConnectionEvent")
|
compressionEnabledEvent = Class.forName("com.velocitypowered.proxy.protocol.VelocityConnectionEvent")
|
||||||
.getDeclaredField("COMPRESSION_ENABLED").get(null);
|
.getDeclaredField("COMPRESSION_ENABLED").get(null);
|
||||||
setCompressionMethod = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection")
|
setCompressionMethod = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection")
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,13 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.velocity;
|
package org.geysermc.geyser.platform.velocity;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
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 org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class GeyserVelocityLogger implements GeyserLogger {
|
public class GeyserVelocityLogger implements GeyserLogger {
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
@Getter @Setter
|
@Getter @Setter
|
||||||
|
|
|
||||||
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