mirror of https://github.com/GeyserMC/Geyser.git
Merge remote-tracking branch 'upstream/master' into feature/cloud-updated
This commit is contained in:
commit
f6b0f36441
|
@ -6,7 +6,7 @@ on:
|
|||
paths-ignore:
|
||||
- '.github/ISSUE_TEMPLATE/*.yml'
|
||||
- '.github/actions/pullrequest.yml'
|
||||
- '.idea/copyright/*.xml'
|
||||
- '.idea/copyright/*.xml'
|
||||
- '.gitignore'
|
||||
- 'CONTRIBUTING.md'
|
||||
- 'LICENSE'
|
||||
|
@ -20,67 +20,81 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout repository and submodules
|
||||
# See https://github.com/actions/checkout/commits
|
||||
uses: actions/checkout@72f2cec99f417b1a1c5e2e88945068983b7965f9
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
# See https://github.com/gradle/wrapper-validation-action/commits
|
||||
uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4
|
||||
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
|
||||
|
||||
# See https://github.com/actions/setup-java/commits
|
||||
- uses: actions/setup-java@4075bfc1b51bf22876335ae1cd589602d60d8758
|
||||
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
||||
- name: Build
|
||||
# See https://github.com/gradle/gradle-build-action/commits
|
||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 from https://github.com/gradle/actions/commits
|
||||
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
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Fabric
|
||||
path: bootstrap/fabric/build/libs/Geyser-Fabric.jar
|
||||
path: bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser NeoForge)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser NeoForge
|
||||
path: bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Standalone)
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Standalone
|
||||
path: bootstrap/standalone/build/libs/Geyser-Standalone.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Spigot)
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
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
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
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
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Velocity
|
||||
path: bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser ViaProxy)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser ViaProxy
|
||||
path: bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Publish to Maven Repository
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
||||
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
|
||||
|
@ -106,20 +120,30 @@ jobs:
|
|||
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/
|
||||
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" bootstrap/mod/**/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
|
||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
||||
- name: Publish to Modrinth (Fabric)
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
env:
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
with:
|
||||
arguments: fabric:modrinth
|
||||
gradle-home-cache-cleanup: true
|
||||
|
||||
|
||||
- name: Publish to Modrinth (NeoForge)
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||
env:
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
with:
|
||||
arguments: neoforge:modrinth
|
||||
gradle-home-cache-cleanup: true
|
||||
|
||||
- name: Notify Discord
|
||||
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||
# See https://github.com/Tim203/actions-git-discord-webhook/commits
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
steps:
|
||||
- name: Set up JDK 17
|
||||
# See https://github.com/actions/setup-java/commits
|
||||
uses: actions/setup-java@4075bfc1b51bf22876335ae1cd589602d60d8758
|
||||
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
@ -35,55 +35,69 @@ jobs:
|
|||
|
||||
- name: Checkout repository and submodules
|
||||
# See https://github.com/actions/checkout/commits
|
||||
uses: actions/checkout@72f2cec99f417b1a1c5e2e88945068983b7965f9
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
submodules: recursive
|
||||
path: geyser
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
# See https://github.com/gradle/wrapper-validation-action/commits
|
||||
uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4
|
||||
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
|
||||
|
||||
- name: Build Geyser
|
||||
# See https://github.com/gradle/gradle-build-action/commits
|
||||
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 from https://github.com/gradle/actions/commits
|
||||
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
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Fabric
|
||||
path: geyser/bootstrap/fabric/build/libs/Geyser-Fabric.jar
|
||||
path: geyser/bootstrap/mod/fabric/build/libs/Geyser-Fabric.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser NeoForge)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser NeoForge
|
||||
path: geyser/bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser Standalone)
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
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
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
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
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
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
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser Velocity
|
||||
path: geyser/bootstrap/velocity/build/libs/Geyser-Velocity.jar
|
||||
if-no-files-found: error
|
||||
- name: Archive artifacts (Geyser ViaProxy)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
|
||||
if: success()
|
||||
with:
|
||||
name: Geyser ViaProxy
|
||||
path: geyser/bootstrap/viaproxy/build/libs/Geyser-ViaProxy.jar
|
||||
if-no-files-found: error
|
||||
|
|
|
@ -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!
|
||||
|
||||
### Currently supporting Minecraft Bedrock 1.20.40 - 1.20.51 and Minecraft Java 1.20.4
|
||||
### Currently supporting Minecraft Bedrock 1.20.40 - 1.20.61 and Minecraft Java 1.20.4
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
||||
|
|
|
@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
|
|||
import javax.lang.model.SourceVersion;
|
||||
|
||||
@SupportedAnnotationTypes("*")
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_16)
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||
public class BlockEntityProcessor extends ClassProcessor {
|
||||
public BlockEntityProcessor() {
|
||||
super("org.geysermc.geyser.translator.level.block.entity.BlockEntity");
|
||||
|
|
|
@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
|
|||
import javax.lang.model.SourceVersion;
|
||||
|
||||
@SupportedAnnotationTypes("*")
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_16)
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||
public class CollisionRemapperProcessor extends ClassProcessor {
|
||||
public CollisionRemapperProcessor() {
|
||||
super("org.geysermc.geyser.translator.collision.CollisionRemapper");
|
||||
|
|
|
@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
|
|||
import javax.lang.model.SourceVersion;
|
||||
|
||||
@SupportedAnnotationTypes("*")
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_16)
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||
public class PacketTranslatorProcessor extends ClassProcessor {
|
||||
public PacketTranslatorProcessor() {
|
||||
super("org.geysermc.geyser.translator.protocol.Translator");
|
||||
|
|
|
@ -30,7 +30,7 @@ import javax.annotation.processing.SupportedSourceVersion;
|
|||
import javax.lang.model.SourceVersion;
|
||||
|
||||
@SupportedAnnotationTypes("*")
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_16)
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||
public class SoundHandlerProcessor extends ClassProcessor {
|
||||
public SoundHandlerProcessor() {
|
||||
super("org.geysermc.geyser.translator.sound.SoundTranslator");
|
||||
|
|
|
@ -4,4 +4,5 @@ plugins {
|
|||
|
||||
dependencies {
|
||||
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.geysermc.api.Geyser;
|
||||
import org.geysermc.api.GeyserApiBase;
|
||||
import org.geysermc.geyser.api.command.CommandSource;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.event.EventBus;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
import org.geysermc.geyser.api.extension.ExtensionManager;
|
||||
import org.geysermc.geyser.api.network.BedrockListener;
|
||||
import org.geysermc.geyser.api.network.RemoteServer;
|
||||
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
@ -134,6 +136,30 @@ public interface GeyserApi extends GeyserApiBase {
|
|||
@NonNull
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* Represents a camera shake instruction. Can be sent in {@link CameraData#shakeCamera(float, float, CameraShake)}
|
||||
*/
|
||||
public enum CameraShake {
|
||||
POSITIONAL,
|
||||
ROTATIONAL
|
||||
|
|
|
@ -129,8 +129,11 @@ public interface CustomBlockComponents {
|
|||
* Gets the unit cube component
|
||||
* Equivalent to "minecraft:unit_cube"
|
||||
*
|
||||
* @deprecated Use {@link #geometry()} and compare with `minecraft:geometry.full_block` instead.
|
||||
*
|
||||
* @return The rotation.
|
||||
*/
|
||||
@Deprecated
|
||||
boolean unitCube();
|
||||
|
||||
/**
|
||||
|
@ -181,6 +184,10 @@ public interface CustomBlockComponents {
|
|||
|
||||
Builder transformation(TransformationComponent transformation);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #geometry(GeometryComponent)} with `minecraft:geometry.full_block` instead.
|
||||
*/
|
||||
@Deprecated
|
||||
Builder unitCube(boolean unitCube);
|
||||
|
||||
Builder placeAir(boolean placeAir);
|
||||
|
|
|
@ -29,8 +29,10 @@ import org.checkerframework.checker.index.qual.NonNegative;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||
import org.geysermc.geyser.api.command.CommandSource;
|
||||
import org.geysermc.geyser.api.entity.EntityData;
|
||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
* @return a {@link GeyserEntity} if present in this connection's entity tracker.
|
||||
* @deprecated Use {@link EntityData#entityByJavaId(int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull
|
||||
CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
|
||||
|
||||
|
@ -53,11 +74,14 @@ public interface GeyserConnection extends Connection, CommandSource {
|
|||
*
|
||||
* @param emoter the player entity emoting.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
|
@ -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 duration the time in seconds that the shake will occur for
|
||||
* @param type the type of shake
|
||||
*
|
||||
* @deprecated Use {@link CameraData#shakeCamera(float, float, CameraShake)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
void shakeCamera(float intensity, float duration, @NonNull CameraShake type);
|
||||
|
||||
/**
|
||||
* Stops all camera shake of any type.
|
||||
*
|
||||
* @deprecated Use {@link CameraData#stopCameraShake()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
void stopCameraShake();
|
||||
|
||||
/**
|
||||
|
@ -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>
|
||||
*
|
||||
* @param fogNameSpaces the fog IDs to add. If empty, the existing cached IDs will still be sent.
|
||||
* @deprecated Use {@link CameraData#sendFog(String...)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
void sendFog(String... fogNameSpaces);
|
||||
|
||||
/**
|
||||
* Removes the given fog IDs from the fog cache, then sends all fog IDs in the cache to the client.
|
||||
*
|
||||
* @param fogNameSpaces the fog IDs to remove. If empty, all fog IDs will be removed.
|
||||
* @deprecated Use {@link CameraData#removeFog(String...)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
void removeFog(String... fogNameSpaces);
|
||||
|
||||
/**
|
||||
* Returns an immutable copy of all fog affects currently applied to this client.
|
||||
*
|
||||
* @deprecated Use {@link CameraData#fogEffects()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull
|
||||
Set<String> fogEffects();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.entity;
|
||||
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* This class holds all the methods that relate to entities.
|
||||
* Can be accessed through {@link GeyserConnection#entities()}.
|
||||
*/
|
||||
public interface EntityData {
|
||||
|
||||
/**
|
||||
* Returns a {@link GeyserEntity} to e.g. make them play an emote.
|
||||
*
|
||||
* @param javaId the Java entity ID to look up
|
||||
* @return a {@link GeyserEntity} if present in this connection's entity tracker
|
||||
*/
|
||||
@NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
|
||||
|
||||
/**
|
||||
* Displays a player entity as emoting to this client.
|
||||
*
|
||||
* @param emoter the player entity emoting
|
||||
* @param emoteId the emote ID to send to this client
|
||||
*/
|
||||
void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId);
|
||||
|
||||
/**
|
||||
* Gets the {@link GeyserPlayerEntity} of this connection.
|
||||
*
|
||||
* @return the {@link GeyserPlayerEntity} of this connection
|
||||
*/
|
||||
@NonNull GeyserPlayerEntity playerEntity();
|
||||
|
||||
/**
|
||||
* (Un)locks the client's movement inputs, so that they cannot move.
|
||||
* To ensure that movement is only unlocked when all locks are released, you must supply
|
||||
* a UUID with this method, and use the same UUID to unlock the camera.
|
||||
*
|
||||
* @param lock whether to lock the movement
|
||||
* @param owner the owner of the lock
|
||||
* @return if the movement is locked after this method call
|
||||
*/
|
||||
boolean lockMovement(boolean lock, @NonNull UUID owner);
|
||||
|
||||
/**
|
||||
* Returns whether the client's movement is currently locked.
|
||||
*
|
||||
* @return whether the movement is locked
|
||||
*/
|
||||
boolean isMovementLocked();
|
||||
}
|
|
@ -25,7 +25,15 @@
|
|||
|
||||
package org.geysermc.geyser.api.entity.type.player;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
|
||||
public interface GeyserPlayerEntity extends GeyserEntity {
|
||||
|
||||
/**
|
||||
* Gets the position of the player.
|
||||
*
|
||||
* @return the position of the player.
|
||||
*/
|
||||
Vector3f position();
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -50,6 +50,14 @@ public interface BedrockListener {
|
|||
*/
|
||||
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.
|
||||
* <p>
|
||||
|
|
|
@ -42,4 +42,4 @@ public abstract class PathPackCodec extends PackCodec {
|
|||
*/
|
||||
@NonNull
|
||||
public abstract Path path();
|
||||
}
|
||||
}
|
|
@ -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 BUNGEECORD = new PlatformType("BungeeCord");
|
||||
public static final PlatformType FABRIC = new PlatformType("Fabric");
|
||||
public static final PlatformType NEOFORGE = new PlatformType("NeoForge");
|
||||
public static final PlatformType SPIGOT = new PlatformType("Spigot");
|
||||
|
||||
@Deprecated
|
||||
public static final PlatformType SPONGE = new PlatformType("Sponge");
|
||||
public static final PlatformType STANDALONE = new PlatformType("Standalone");
|
||||
public static final PlatformType VELOCITY = new PlatformType("Velocity");
|
||||
public static final PlatformType VIAPROXY = new PlatformType("ViaProxy");
|
||||
}
|
||||
|
|
|
@ -36,9 +36,12 @@ import net.md_5.bungee.api.plugin.Plugin;
|
|||
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||
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.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
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.CommandSourceConverter;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
|
@ -73,11 +76,13 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
|
||||
private GeyserImpl geyser;
|
||||
|
||||
private static boolean INITIALIZED = false;
|
||||
|
||||
@SuppressWarnings({"JavaReflectionMemberAccess", "ResultOfMethodCallIgnored"})
|
||||
@Override
|
||||
public void onLoad() {
|
||||
onGeyserInitialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserInitialize() {
|
||||
GeyserLocale.init(this);
|
||||
|
||||
// Copied from ViaVersion.
|
||||
|
@ -94,29 +99,71 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
getLogger().warning("/_____________\\");
|
||||
}
|
||||
|
||||
if (!getDataFolder().exists())
|
||||
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();
|
||||
if (!this.loadConfig()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
|
||||
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
|
||||
this.geyserInjector = new GeyserBungeeInjector(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
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 {
|
||||
var sourceConverter = new CommandSourceConverter<>(
|
||||
CommandSender.class,
|
||||
id -> getProxy().getPlayer(id),
|
||||
() -> getProxy().getConsole()
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new BungeeCommandManager<>(
|
||||
this,
|
||||
CommandExecutionCoordinator.simpleCoordinator(),
|
||||
BungeeCommandSource::new,
|
||||
sourceConverter::convert
|
||||
);
|
||||
this.commandRegistry = new CommandRegistry(geyser, cloud);
|
||||
|
||||
}
|
||||
|
||||
// Force-disable query if enabled, or else Geyser won't enable
|
||||
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
|
||||
|
@ -136,76 +183,31 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
if (!INITIALIZED) {
|
||||
this.awaitStartupCompletion(0);
|
||||
} else {
|
||||
// No need to "wait" for startup completion, just start Geyser - we're reloading.
|
||||
this.postStartup();
|
||||
}
|
||||
}
|
||||
|
||||
@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.postStartup();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners");
|
||||
listenersField.setAccessible(true);
|
||||
|
||||
Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
|
||||
if (listeners.isEmpty()) {
|
||||
this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS);
|
||||
} else {
|
||||
this.awaitStartupCompletion(++tries);
|
||||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void postStartup() {
|
||||
var sourceConverter = new CommandSourceConverter<>(
|
||||
CommandSender.class,
|
||||
id -> getProxy().getPlayer(id),
|
||||
() -> getProxy().getConsole()
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new BungeeCommandManager<>(
|
||||
this,
|
||||
CommandExecutionCoordinator.simpleCoordinator(),
|
||||
BungeeCommandSource::new,
|
||||
sourceConverter::convert
|
||||
);
|
||||
this.commandRegistry = new CommandRegistry(geyser, cloud);
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
if (!INITIALIZED) {
|
||||
this.geyserInjector = new GeyserBungeeInjector(this);
|
||||
this.geyserInjector.initializeLocalChannel(this);
|
||||
}
|
||||
|
||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
} else {
|
||||
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
||||
}
|
||||
|
||||
INITIALIZED = true;
|
||||
// No need to re-register commands or re-init injector when reloading
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.geyserInjector.initializeLocalChannel(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
public void onGeyserDisable() {
|
||||
if (geyser != null) {
|
||||
geyser.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserShutdown() {
|
||||
if (geyser != null) {
|
||||
geyser.shutdown();
|
||||
}
|
||||
|
@ -214,6 +216,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.onGeyserShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserBungeeConfiguration getGeyserConfig() {
|
||||
return geyserConfig;
|
||||
|
@ -281,4 +288,20 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
.map(info -> (InetSocketAddress) info.getSocketAddress())
|
||||
.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) {
|
||||
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,96 +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.command;
|
||||
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FabricCommandSource implements GeyserCommandSource {
|
||||
|
||||
private final CommandSourceStack source;
|
||||
|
||||
public FabricCommandSource(CommandSourceStack source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return source.getTextName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NonNull String message) {
|
||||
if (source.getEntity() instanceof ServerPlayer) {
|
||||
((ServerPlayer) source.getEntity()).displayClientMessage(Component.literal(message), false);
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().info(ChatColor.toANSI(message + ChatColor.RESET));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(net.kyori.adventure.text.Component message) {
|
||||
if (source.getEntity() instanceof ServerPlayer player) {
|
||||
String decoded = GsonComponentSerializer.gson().serialize(message);
|
||||
player.displayClientMessage(Objects.requireNonNull(Component.Serializer.fromJson(decoded)), false);
|
||||
return;
|
||||
}
|
||||
GeyserCommandSource.super.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsole() {
|
||||
return !(source.getEntity() instanceof ServerPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID playerUuid() {
|
||||
if (source.getEntity() instanceof ServerPlayer player) {
|
||||
return player.getUUID();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return Permissions.check(source, permission, source.getServer().getOperatorUserPermissionLevel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
return source;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
architectury {
|
||||
common("neoforge", "fabric")
|
||||
}
|
||||
|
||||
loom {
|
||||
mixin.defaultRefmapName.set("geyser-refmap.json")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.core)
|
||||
compileOnly(libs.mixin)
|
||||
|
||||
// Only here to suppress "unknown enum constant EnvType.CLIENT" warnings. DO NOT USE!
|
||||
compileOnly(libs.fabric.loader)
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
plugins {
|
||||
application
|
||||
}
|
||||
|
||||
architectury {
|
||||
platformSetupLoomIde()
|
||||
fabric()
|
||||
}
|
||||
|
||||
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) {
|
||||
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")
|
||||
}
|
||||
|
||||
modImplementation(libs.fabric.permissions)
|
||||
include(libs.fabric.permissions)
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
|
||||
}
|
||||
|
||||
tasks {
|
||||
remapJar {
|
||||
archiveBaseName.set("Geyser-Fabric")
|
||||
}
|
||||
|
||||
remapModrinthJar {
|
||||
archiveBaseName.set("geyser-fabric")
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
loaders.add("fabric")
|
||||
dependencies {
|
||||
required.project("fabric-api")
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
loom.platform=fabric
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.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.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInitializer {
|
||||
|
||||
public GeyserFabricBootstrap() {
|
||||
super(new GeyserFabricPlatform());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
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((server) -> {
|
||||
this.setServer(server);
|
||||
onGeyserEnable();
|
||||
});
|
||||
}
|
||||
|
||||
// These are only registered once
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onGeyserShutdown());
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserModUpdateListener.onPlayReady(handler.getPlayer()));
|
||||
|
||||
this.onGeyserInitialize();
|
||||
}
|
||||
|
||||
@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,18 +9,18 @@
|
|||
],
|
||||
"contact": {
|
||||
"website": "${url}",
|
||||
"repo": "https://github.com/GeyserMC/Geyser-Fabric"
|
||||
"repo": "https://github.com/GeyserMC/Geyser"
|
||||
},
|
||||
"license": "MIT",
|
||||
"icon": "assets/geyser-fabric/icon.png",
|
||||
"icon": "assets/geyser/icon.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"org.geysermc.geyser.platform.fabric.GeyserFabricMod"
|
||||
"org.geysermc.geyser.platform.fabric.GeyserFabricBootstrap"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"geyser-fabric.mixins.json"
|
||||
"geyser.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.15.2",
|
|
@ -0,0 +1,52 @@
|
|||
plugins {
|
||||
application
|
||||
}
|
||||
|
||||
architectury {
|
||||
platformSetupLoomIde()
|
||||
neoForge()
|
||||
}
|
||||
|
||||
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) {
|
||||
exclude(group = "com.google.guava", module = "guava")
|
||||
exclude(group = "com.google.code.gson", module = "gson")
|
||||
exclude(group = "org.slf4j")
|
||||
exclude(group = "io.netty.incubator")
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.forge.GeyserNeoForgeMain")
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate("it.unimi.dsi.fastutil", "org.geysermc.relocate.fastutil")
|
||||
}
|
||||
|
||||
remapJar {
|
||||
archiveBaseName.set("Geyser-NeoForge")
|
||||
}
|
||||
|
||||
remapModrinthJar {
|
||||
archiveBaseName.set("geyser-neoforge")
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
loaders.add("neoforge")
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
loom.platform=neoforge
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.api.distmarker.Dist;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.fml.loading.FMLLoader;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
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() {
|
||||
super(new GeyserNeoForgePlatform());
|
||||
|
||||
if (FMLLoader.getDist() == Dist.DEDICATED_SERVER) {
|
||||
// Set as an event so we can get the proper IP and port if needed
|
||||
NeoForge.EVENT_BUS.addListener(this::onServerStarted);
|
||||
}
|
||||
|
||||
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) {
|
||||
this.onGeyserShutdown();
|
||||
}
|
||||
|
||||
private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
GeyserModUpdateListener.onPlayReady(event.getEntity());
|
||||
}
|
||||
|
||||
@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,72 @@
|
|||
/*
|
||||
* 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.loading.FMLPaths;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
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.InputStream;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class GeyserNeoForgePlatform implements GeyserModPlatform {
|
||||
|
||||
@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) {
|
||||
return false; // No Floodgate mod for NeoForge yet
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InputStream resolveResource(@NonNull String resource) {
|
||||
return GeyserBootstrap.class.getClassLoader().getResourceAsStream(resource);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class ModConstants {
|
||||
public static final String MOD_ID = "geyser_neoforge";
|
||||
}
|
|
@ -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="[20.4.48-beta,)"
|
||||
ordering="NONE"
|
||||
side="BOTH"
|
||||
[[dependencies.geyser_neoforge]]
|
||||
modId="minecraft"
|
||||
type="required"
|
||||
versionRange="[1.20,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,17 +23,13 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.fabric;
|
||||
package org.geysermc.geyser.platform.mod;
|
||||
|
||||
import cloud.commandframework.CommandManager;
|
||||
import cloud.commandframework.execution.CommandExecutionCoordinator;
|
||||
import cloud.commandframework.fabric.FabricServerCommandManager;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
@ -43,17 +39,19 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.fabric.command.FabricCommandSource;
|
||||
import org.geysermc.geyser.platform.fabric.world.GeyserFabricWorldManager;
|
||||
import org.geysermc.geyser.platform.mod.command.GeyserModCommandExecutor;
|
||||
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.util.FileUtils;
|
||||
|
||||
|
@ -61,96 +59,66 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||
private static GeyserFabricMod INSTANCE;
|
||||
@RequiredArgsConstructor
|
||||
public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||
|
||||
@Getter
|
||||
private static GeyserModBootstrap instance;
|
||||
|
||||
private final GeyserModPlatform platform;
|
||||
|
||||
private GeyserImpl geyser;
|
||||
private ModContainer mod;
|
||||
private Path dataFolder;
|
||||
|
||||
@Setter
|
||||
private MinecraftServer server;
|
||||
|
||||
private CommandRegistry commandRegistry;
|
||||
private GeyserFabricConfiguration geyserConfig;
|
||||
private GeyserFabricLogger geyserLogger;
|
||||
private GeyserModConfiguration geyserConfig;
|
||||
private GeyserModInjector geyserInjector;
|
||||
private GeyserModLogger geyserLogger;
|
||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||
private WorldManager geyserWorldManager;
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
INSTANCE = this;
|
||||
mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
|
||||
|
||||
this.onEnable();
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
|
||||
// Set as an event so we can get the proper IP and port if needed
|
||||
ServerLifecycleEvents.SERVER_STARTED.register(this::startGeyser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
dataFolder = FabricLoader.getInstance().getConfigDir().resolve("Geyser-Fabric");
|
||||
if (!dataFolder.toFile().exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dataFolder.toFile().mkdir();
|
||||
}
|
||||
|
||||
// Init dataFolder first as local language overrides call getConfigFolder()
|
||||
public void onGeyserInitialize() {
|
||||
instance = this;
|
||||
dataFolder = this.platform.dataFolder(this.platform.configPath());
|
||||
GeyserLocale.init(this);
|
||||
|
||||
try {
|
||||
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
|
||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserFabricConfiguration.class);
|
||||
} catch (IOException ex) {
|
||||
LogManager.getLogger("geyser-fabric").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.geyserLogger = new GeyserFabricLogger(geyserConfig.isDebugMode());
|
||||
|
||||
this.geyserLogger = new GeyserModLogger(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
this.geyser = GeyserImpl.load(this.platform.platformType(), this);
|
||||
|
||||
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this);
|
||||
|
||||
if (server != null) {
|
||||
// Server has started and this is a reload
|
||||
startGeyser(server);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
// Create command manager here, since the permission handler on neo needs it
|
||||
//TODO differentiate Fabric/NeoForge!?
|
||||
var sourceConverter = CommandSourceConverter.layered(
|
||||
CommandSourceStack.class,
|
||||
id -> server.getPlayerList().getPlayer(id),
|
||||
Player::createCommandSourceStack,
|
||||
() -> server.createCommandSourceStack() // NPE if method reference is used, since server is not available yet
|
||||
CommandSourceStack.class,
|
||||
id -> server.getPlayerList().getPlayer(id),
|
||||
Player::createCommandSourceStack,
|
||||
() -> server.createCommandSourceStack() // NPE if method reference is used, since server is not available yet
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new FabricServerCommandManager<>(
|
||||
CommandExecutionCoordinator.simpleCoordinator(),
|
||||
FabricCommandSource::new,
|
||||
sourceConverter::convert
|
||||
CommandExecutionCoordinator.simpleCoordinator(),
|
||||
FabricCommandSource::new,
|
||||
sourceConverter::convert
|
||||
);
|
||||
commandRegistry = new CommandRegistry(geyser, cloud);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize core Geyser.
|
||||
* A function, as it needs to be called in different places depending on if Geyser is being reloaded or not.
|
||||
*
|
||||
* @param server The minecraft server.
|
||||
*/
|
||||
public void startGeyser(MinecraftServer server) {
|
||||
this.server = server;
|
||||
public void onGeyserEnable() {
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
}
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
|
@ -160,19 +128,40 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
|||
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
|
||||
}
|
||||
|
||||
this.geyserWorldManager = new GeyserFabricWorldManager(server);
|
||||
// No need to re-register commands, or try to re-inject
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
this.geyserInjector.initializeLocalChannel(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
public void onGeyserDisable() {
|
||||
if (geyser != null) {
|
||||
geyser.shutdown();
|
||||
geyser = null;
|
||||
geyser.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserConfiguration getGeyserConfig() {
|
||||
public void onGeyserShutdown() {
|
||||
if (geyser != null) {
|
||||
geyser.shutdown();
|
||||
geyser = null;
|
||||
}
|
||||
if (geyserInjector != null) {
|
||||
geyserInjector.shutdown();
|
||||
this.server = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserModConfiguration getGeyserConfig() {
|
||||
return geyserConfig;
|
||||
}
|
||||
|
||||
|
@ -203,7 +192,7 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
|||
|
||||
@Override
|
||||
public BootstrapDumpInfo getDumpInfo() {
|
||||
return new GeyserFabricDumpInfo(server);
|
||||
return this.platform.dumpInfo(this.server);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -226,33 +215,35 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
|||
|
||||
@Override
|
||||
public boolean testFloodgatePluginPresent() {
|
||||
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
|
||||
if (floodgate.isPresent()) {
|
||||
geyserConfig.loadFloodgate(this, floodgate.orElse(null));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return this.platform.testFloodgatePluginPresent(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InputStream getResourceOrNull(String resource) {
|
||||
// We need to handle this differently, because Fabric shares the classloader across multiple mods
|
||||
Path path = this.mod.findPath(resource).orElse(null);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return path.getFileSystem()
|
||||
.provider()
|
||||
.newInputStream(path);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
return this.platform.resolveResource(resource);
|
||||
}
|
||||
|
||||
public static GeyserFabricMod getInstance() {
|
||||
return INSTANCE;
|
||||
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 {
|
||||
if (!dataFolder.toFile().exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dataFolder.toFile().mkdir();
|
||||
}
|
||||
|
||||
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) {
|
||||
LogManager.getLogger("geyser").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.fabric;
|
||||
package org.geysermc.geyser.platform.mod;
|
||||
|
||||
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.configuration.GeyserJacksonConfiguration;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class GeyserFabricConfiguration extends GeyserJacksonConfiguration {
|
||||
public class GeyserModConfiguration extends GeyserJacksonConfiguration {
|
||||
@JsonIgnore
|
||||
private Path floodgateKeyPath;
|
||||
|
||||
public void loadFloodgate(GeyserFabricMod geyser, ModContainer floodgate) {
|
||||
public void loadFloodgate(GeyserModBootstrap geyser, Path floodgateDataFolder) {
|
||||
Path geyserDataFolder = geyser.getConfigFolder();
|
||||
Path floodgateDataFolder = floodgate != null ? FabricLoader.getInstance().getConfigDir().resolve("floodgate") : null;
|
||||
|
||||
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, geyser.getGeyserLogger());
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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);
|
||||
|
||||
if (bootstrap.getGeyserConfig().isDisableCompression()) {
|
||||
ch.pipeline().addAfter("encoder", "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
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.fabric;
|
||||
package org.geysermc.geyser.platform.mod;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
@ -32,12 +32,12 @@ import org.apache.logging.log4j.Logger;
|
|||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
|
||||
public class GeyserFabricLogger implements GeyserLogger {
|
||||
private final Logger logger = LogManager.getLogger("geyser-fabric");
|
||||
public class GeyserModLogger implements GeyserLogger {
|
||||
private final Logger logger = LogManager.getLogger("geyser");
|
||||
|
||||
private boolean debug;
|
||||
|
||||
public GeyserFabricLogger(boolean isDebug) {
|
||||
public GeyserModLogger(boolean isDebug) {
|
||||
debug = isDebug;
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ public class GeyserFabricLogger implements GeyserLogger {
|
|||
|
||||
@Override
|
||||
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);
|
||||
// Add the reset at the end, or else format will persist... forever.
|
||||
// https://cdn.discordapp.com/attachments/573909525132738590/1033904509170225242/unknown.png
|
|
@ -23,21 +23,22 @@
|
|||
* @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.server.network.ServerGamePacketListenerImpl;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.platform.fabric.command.FabricCommandSource;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
public final class GeyserFabricUpdateListener {
|
||||
public static void onPlayReady(ServerGamePacketListenerImpl handler) {
|
||||
if (Permissions.check(handler.player, Constants.UPDATE_PERMISSION, 2)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new FabricCommandSource(handler.player.createCommandSourceStack()));
|
||||
public final class GeyserModUpdateListener {
|
||||
public static void onPlayReady(Player player) {
|
||||
CommandSourceStack stack = 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
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.fabric;
|
||||
package org.geysermc.geyser.platform.mod;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.fabric;
|
||||
package org.geysermc.geyser.platform.mod;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
@ -39,10 +39,11 @@ import net.minecraft.network.protocol.status.ServerStatusPacketListener;
|
|||
import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
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.ping.GeyserPingInfo;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Objects;
|
||||
|
@ -100,7 +101,7 @@ public class ModPingPassthrough implements IGeyserPingPassthrough {
|
|||
}
|
||||
|
||||
@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) {
|
||||
status = statusResponse.status();
|
||||
}
|
|
@ -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
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -23,18 +23,16 @@
|
|||
* @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.server.IntegratedServer;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
|
||||
import org.geysermc.geyser.platform.fabric.GeyserServerPortGetter;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.platform.mod.GeyserServerPortGetter;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
@ -45,7 +43,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@Mixin(IntegratedServer.class)
|
||||
public class IntegratedServerMixin implements GeyserServerPortGetter {
|
||||
@Shadow
|
||||
|
@ -57,7 +54,8 @@ public class IntegratedServerMixin implements GeyserServerPortGetter {
|
|||
private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, CallbackInfoReturnable<Boolean> cir) {
|
||||
if (cir.getReturnValueZ()) {
|
||||
// If the LAN is opened, starts Geyser.
|
||||
GeyserFabricMod.getInstance().startGeyser((MinecraftServer) (Object) this);
|
||||
GeyserModBootstrap.getInstance().setServer((MinecraftServer) (Object) this);
|
||||
GeyserModBootstrap.getInstance().onGeyserEnable();
|
||||
// Ensure player locale has been loaded, in case it's different from Java system language
|
||||
GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode);
|
||||
// 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
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -23,7 +23,7 @@
|
|||
* @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 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.packs.repository.PackRepository;
|
||||
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 java.net.Proxy;
|
||||
|
||||
@Mixin(DedicatedServer.class)
|
||||
public abstract class MinecraftDedicatedServerMixin extends MinecraftServer implements GeyserServerPortGetter {
|
||||
public MinecraftDedicatedServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer dataFixer, Services services, ChunkProgressListenerFactory chunkProgressListenerFactory) {
|
||||
public abstract class DedicatedServerMixin extends MinecraftServer implements GeyserServerPortGetter {
|
||||
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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -23,22 +23,41 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.fabric.world;
|
||||
package org.geysermc.geyser.platform.mod.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.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.nbt.ByteArrayTag;
|
||||
import net.minecraft.nbt.ByteTag;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.DoubleTag;
|
||||
import net.minecraft.nbt.EndTag;
|
||||
import net.minecraft.nbt.FloatTag;
|
||||
import net.minecraft.nbt.IntArrayTag;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.LongArrayTag;
|
||||
import net.minecraft.nbt.LongTag;
|
||||
import net.minecraft.nbt.ShortTag;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.TagVisitor;
|
||||
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.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
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.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
|
@ -46,6 +65,8 @@ 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.network.GameProtocol;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
|
||||
|
@ -54,13 +75,54 @@ import java.util.List;
|
|||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
public class GeyserModWorldManager extends GeyserWorldManager {
|
||||
private final MinecraftServer server;
|
||||
|
||||
public GeyserFabricWorldManager(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 shouldExpectLecternHandled(GeyserSession session) {
|
||||
return true;
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -150,7 +150,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
|||
childHandler = (ChannelInitializer<Channel>) childHandlerField.get(handler);
|
||||
// ViaVersion non-Paper-injector workaround so we aren't double-injecting
|
||||
if (isViaVersion && childHandler instanceof BukkitChannelInitializer) {
|
||||
childHandler = ((BukkitChannelInitializer) childHandler).getOriginal();
|
||||
childHandler = ((BukkitChannelInitializer) childHandler).original();
|
||||
}
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -78,10 +78,6 @@ import java.util.UUID;
|
|||
import java.util.logging.Level;
|
||||
|
||||
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 CommandRegistry commandRegistry;
|
||||
private GeyserSpigotConfiguration geyserConfig;
|
||||
|
@ -99,6 +95,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
onGeyserInitialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserInitialize() {
|
||||
GeyserLocale.init(this);
|
||||
|
||||
try {
|
||||
|
@ -115,6 +116,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
|
||||
getLogger().severe("");
|
||||
getLogger().severe("*********************************************");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -128,6 +130,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
|
||||
getLogger().severe("");
|
||||
getLogger().severe("*********************************************");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -140,49 +143,33 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
getLogger().severe("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
|
||||
getLogger().severe("");
|
||||
getLogger().severe("*********************************************");
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode())
|
||||
: new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (this.geyserConfig == null) {
|
||||
// We failed to initialize correctly
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceConverter = new CommandSourceConverter<>(CommandSender.class, Bukkit::getPlayer, Bukkit::getConsoleSender);
|
||||
PaperCommandManager<GeyserCommandSource> cloud;
|
||||
try {
|
||||
cloud = new PaperCommandManager<>(
|
||||
this,
|
||||
CommandExecutionCoordinator.simpleCoordinator(),
|
||||
SpigotCommandSource::new,
|
||||
sourceConverter::convert
|
||||
this,
|
||||
CommandExecutionCoordinator.simpleCoordinator(),
|
||||
SpigotCommandSource::new,
|
||||
sourceConverter::convert
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -201,32 +188,34 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
|
||||
this.commandRegistry = new SpigotCommandRegistry(geyser, cloud);
|
||||
|
||||
if (!INITIALIZED) {
|
||||
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||
|
||||
@EventHandler
|
||||
public void onServerLoaded(ServerLoadEvent event) {
|
||||
// Wait until all plugins have loaded so Geyser can start
|
||||
postStartup();
|
||||
@EventHandler
|
||||
public void onServerLoaded(ServerLoadEvent event) {
|
||||
if (event.getType() == ServerLoadEvent.LoadType.RELOAD) {
|
||||
geyser.setShuttingDown(false);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (INITIALIZED) {
|
||||
// Reload; continue with post startup
|
||||
postStartup();
|
||||
}
|
||||
onGeyserEnable();
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
private void postStartup() {
|
||||
public void onGeyserEnable() {
|
||||
// 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);
|
||||
}
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
PluginManager pluginManager = Bukkit.getPluginManager();
|
||||
|
||||
// Turn "(MC: 1.16.4)" into 1.16.4.
|
||||
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
||||
|
||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
} else {
|
||||
|
@ -241,20 +230,16 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
}
|
||||
geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass()));
|
||||
|
||||
boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null;
|
||||
if (isViaVersion) {
|
||||
try {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
// Don't need to re-create the world manager/re-register commands/reinject when reloading
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
// To do their job injecting, then connect into *that*
|
||||
this.geyserInjector = new GeyserSpigotInjector(isViaVersion);
|
||||
|
@ -281,6 +266,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
} else {
|
||||
geyserLogger.debug("Not using NMS adapter as it is disabled via system property.");
|
||||
}
|
||||
|
||||
if (this.geyserWorldManager == null) {
|
||||
// No NMS adapter
|
||||
this.geyserWorldManager = new GeyserSpigotWorldManager(this);
|
||||
|
@ -288,49 +274,49 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
|
||||
if (!INITIALIZED) {
|
||||
// Register permissions so they appear in, for example, LuckPerms' UI
|
||||
// Re-registering permissions without removing it throws an error
|
||||
// Register permissions so they appear in, for example, LuckPerms' UI
|
||||
// Re-registering permissions without removing it throws an error
|
||||
|
||||
// todo: this can probably always be run regardless if geyser has been initialized once or not, since we are removing the permission
|
||||
geyser.eventBus().fire((GeyserRegisterPermissionsEvent) (permission, def) -> {
|
||||
PermissionDefault permissionDefault = switch (def) {
|
||||
case TRUE -> PermissionDefault.TRUE;
|
||||
case FALSE -> PermissionDefault.FALSE;
|
||||
case NOT_SET -> PermissionDefault.OP;
|
||||
};
|
||||
// todo: this can probably always be run regardless if geyser has been initialized once or not, since we are removing the permission
|
||||
geyser.eventBus().fire((GeyserRegisterPermissionsEvent) (permission, def) -> {
|
||||
PermissionDefault permissionDefault = switch (def) {
|
||||
case TRUE -> PermissionDefault.TRUE;
|
||||
case FALSE -> PermissionDefault.FALSE;
|
||||
case NOT_SET -> PermissionDefault.OP;
|
||||
};
|
||||
|
||||
Permission existingPermission = pluginManager.getPermission(permission);
|
||||
if (existingPermission != null) {
|
||||
geyserLogger.debug("permission " + permission + " with a default of " +
|
||||
Permission existingPermission = pluginManager.getPermission(permission);
|
||||
if (existingPermission != null) {
|
||||
geyserLogger.debug("permission " + permission + " with a default of " +
|
||||
existingPermission.getDefault() + " is being overriden by " + permissionDefault);
|
||||
|
||||
pluginManager.removePermission(permission);
|
||||
}
|
||||
pluginManager.removePermission(permission);
|
||||
}
|
||||
|
||||
pluginManager.addPermission(new Permission(permission, permissionDefault));
|
||||
});
|
||||
pluginManager.addPermission(new Permission(permission, permissionDefault));
|
||||
});
|
||||
|
||||
pluginManager.addPermission(new Permission(Constants.UPDATE_PERMISSION,
|
||||
"Whether update notifications can be seen", PermissionDefault.OP));
|
||||
pluginManager.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);
|
||||
pluginManager.registerEvents(blockPlaceListener, this);
|
||||
// Events cannot be unregistered - re-registering results in duplicate firings
|
||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
|
||||
pluginManager.registerEvents(blockPlaceListener, this);
|
||||
|
||||
pluginManager.registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
||||
pluginManager.registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
||||
|
||||
pluginManager.registerEvents(new GeyserSpigotUpdateListener(), this);
|
||||
}
|
||||
|
||||
// Check to ensure the current setup can support the protocol version Geyser uses
|
||||
GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion);
|
||||
|
||||
INITIALIZED = true;
|
||||
pluginManager.registerEvents(new GeyserSpigotUpdateListener(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
public void onGeyserDisable() {
|
||||
if (geyser != null) {
|
||||
geyser.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserShutdown() {
|
||||
if (geyser != null) {
|
||||
geyser.shutdown();
|
||||
}
|
||||
|
@ -339,6 +325,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.onGeyserShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserSpigotConfiguration getGeyserConfig() {
|
||||
return geyserConfig;
|
||||
|
@ -405,7 +396,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
return false;
|
||||
}
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
|
@ -433,4 +424,25 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
}
|
||||
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) {
|
||||
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,9 +39,9 @@ import org.apache.logging.log4j.core.Appender;
|
|||
import org.apache.logging.log4j.core.Logger;
|
||||
import org.apache.logging.log4j.core.appender.ConsoleAppender;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
||||
|
@ -59,7 +59,12 @@ import java.lang.reflect.Method;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
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;
|
||||
|
||||
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
|
@ -68,11 +73,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
private GeyserStandaloneConfiguration geyserConfig;
|
||||
private GeyserStandaloneLogger geyserLogger;
|
||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||
|
||||
private GeyserStandaloneGUI gui;
|
||||
|
||||
@Getter
|
||||
private boolean useGui = System.console() == null && !isHeadless();
|
||||
private Logger log4jLogger;
|
||||
private String configFilename = "config.yml";
|
||||
|
||||
private GeyserImpl geyser;
|
||||
|
@ -161,23 +165,19 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
}
|
||||
}
|
||||
}
|
||||
bootstrap.onEnable(useGuiOpts, configFilenameOpt);
|
||||
}
|
||||
|
||||
public void onEnable(boolean useGui, String configFilename) {
|
||||
this.configFilename = configFilename;
|
||||
this.useGui = useGui;
|
||||
this.onEnable();
|
||||
bootstrap.useGui = useGuiOpts;
|
||||
bootstrap.configFilename = configFilenameOpt;
|
||||
bootstrap.onGeyserInitialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Logger logger = (Logger) LogManager.getRootLogger();
|
||||
for (Appender appender : logger.getAppenders().values()) {
|
||||
public void onGeyserInitialize() {
|
||||
log4jLogger = (Logger) LogManager.getRootLogger();
|
||||
for (Appender appender : log4jLogger.getAppenders().values()) {
|
||||
// Remove the appender that is not in use
|
||||
// Prevents multiple appenders/double logging and removes harmless errors
|
||||
if ((useGui && appender instanceof TerminalConsoleAppender) || (!useGui && appender instanceof ConsoleAppender)) {
|
||||
logger.removeAppender(appender);
|
||||
log4jLogger.removeAppender(appender);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,12 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
LoopbackUtil.checkAndApplyLoopback(geyserLogger);
|
||||
|
||||
|
||||
this.onGeyserEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserEnable() {
|
||||
try {
|
||||
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml",
|
||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||
|
@ -215,7 +220,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
|
||||
// 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);
|
||||
|
||||
|
@ -254,7 +259,14 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
@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();
|
||||
System.exit(0);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
|
|||
|
||||
@Override
|
||||
protected void shutdown() {
|
||||
GeyserImpl.getInstance().getBootstrap().onDisable();
|
||||
GeyserImpl.getInstance().getBootstrap().onGeyserShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -100,7 +100,7 @@ public class GeyserStandaloneGUI {
|
|||
Container cp = frame.getContentPane();
|
||||
|
||||
// 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) {
|
||||
ImageIcon icon = new ImageIcon(image);
|
||||
frame.setIconImage(icon.getImage());
|
||||
|
|
|
@ -47,8 +47,14 @@ public class GeyserVelocityCompressionDisabler extends ChannelDuplexHandler {
|
|||
Method setCompressionMethod = null;
|
||||
|
||||
try {
|
||||
compressionPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.SetCompression");
|
||||
loginSuccessPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess");
|
||||
try {
|
||||
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")
|
||||
.getDeclaredField("COMPRESSION_ENABLED").get(null);
|
||||
setCompressionMethod = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection")
|
||||
|
|
|
@ -35,11 +35,11 @@ import com.velocitypowered.api.event.proxy.ListenerBoundEvent;
|
|||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
import com.velocitypowered.api.network.ListenerType;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.PluginContainer;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.util.Codec;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
|
@ -50,6 +50,7 @@ import org.geysermc.geyser.command.CommandRegistry;
|
|||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.velocity.command.VelocityCommandSource;
|
||||
|
@ -66,12 +67,6 @@ import java.util.UUID;
|
|||
|
||||
@Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
|
||||
public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||
|
||||
/**
|
||||
* Determines if the plugin has been ran once before, including before /geyser reload.
|
||||
*/
|
||||
private static boolean INITIALIZED = false;
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
|
||||
|
@ -93,75 +88,88 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
private final Path configFolder = Paths.get("plugins/" + GeyserImpl.NAME + "-Velocity/");
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
try {
|
||||
Codec.class.getMethod("codec", Codec.Decoder.class, Codec.Encoder.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// velocitypowered.com has a build that is very outdated
|
||||
logger.error("Please download Velocity from https://papermc.io/downloads#Velocity - the 'stable' Velocity version " +
|
||||
"that has likely been downloaded is very outdated and does not support 1.19.");
|
||||
return;
|
||||
}
|
||||
|
||||
public void onGeyserInitialize() {
|
||||
GeyserLocale.init(this);
|
||||
|
||||
try {
|
||||
if (!configFolder.toFile().exists())
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
configFolder.toFile().mkdirs();
|
||||
File configFile = FileUtils.fileOrCopiedFromResource(configFolder.resolve("config.yml").toFile(),
|
||||
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class);
|
||||
} catch (IOException ex) {
|
||||
logger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return;
|
||||
if (!ProtocolVersion.isSupported(GameProtocol.getJavaProtocolVersion())) {
|
||||
logger.error(" / \\");
|
||||
logger.error(" / \\");
|
||||
logger.error(" / | \\");
|
||||
logger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", proxyServer.getVersion().getName()));
|
||||
logger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||
logger.error(" / o \\");
|
||||
logger.error("/_____________\\");
|
||||
}
|
||||
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
|
||||
this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this);
|
||||
|
||||
// Hack: Normally triggered by ListenerBoundEvent, but that doesn't fire on /geyser reload
|
||||
if (INITIALIZED) {
|
||||
this.postStartup();
|
||||
}
|
||||
this.geyserInjector = new GeyserVelocityInjector(proxyServer);
|
||||
}
|
||||
|
||||
private void postStartup() {
|
||||
var sourceConverter = new CommandSourceConverter<>(
|
||||
CommandSource.class,
|
||||
id -> proxyServer.getPlayer(id).orElse(null),
|
||||
proxyServer::getConsoleCommandSource
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new VelocityCommandManager<>(
|
||||
container,
|
||||
proxyServer,
|
||||
CommandExecutionCoordinator.simpleCoordinator(),
|
||||
VelocityCommandSource::new,
|
||||
sourceConverter::convert
|
||||
);
|
||||
this.commandRegistry = new CommandRegistry(geyser, cloud);
|
||||
@Override
|
||||
public void onGeyserEnable() {
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
} else {
|
||||
var sourceConverter = new CommandSourceConverter<>(
|
||||
CommandSource.class,
|
||||
id -> proxyServer.getPlayer(id).orElse(null),
|
||||
proxyServer::getConsoleCommandSource
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new VelocityCommandManager<>(
|
||||
container,
|
||||
proxyServer,
|
||||
CommandExecutionCoordinator.simpleCoordinator(),
|
||||
VelocityCommandSource::new,
|
||||
sourceConverter::convert
|
||||
);
|
||||
this.commandRegistry = new CommandRegistry(geyser, cloud);
|
||||
}
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
if (!INITIALIZED) {
|
||||
// Will be initialized after the proxy has been bound
|
||||
this.geyserInjector = new GeyserVelocityInjector(proxyServer);
|
||||
}
|
||||
|
||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
} else {
|
||||
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
|
||||
}
|
||||
|
||||
// No need to re-register commands when reloading
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.getCommands()));
|
||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||
Map<String, Command> commands = entry.getValue();
|
||||
if (commands.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.commandManager.register(entry.getKey().description().id(), new GeyserVelocityCommandExecutor(this.geyser, commands));
|
||||
}
|
||||
|
||||
proxyServer.getEventManager().register(this, new GeyserVelocityUpdateListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
public void onGeyserDisable() {
|
||||
if (geyser != null) {
|
||||
geyser.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserShutdown() {
|
||||
if (geyser != null) {
|
||||
geyser.shutdown();
|
||||
}
|
||||
|
@ -192,26 +200,24 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
|
||||
@Subscribe
|
||||
public void onInit(ProxyInitializeEvent event) {
|
||||
onEnable();
|
||||
this.onGeyserInitialize();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onShutdown(ProxyShutdownEvent event) {
|
||||
onDisable();
|
||||
this.onGeyserShutdown();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyBound(ListenerBoundEvent event) {
|
||||
if (event.getListenerType() == ListenerType.MINECRAFT) {
|
||||
// Once listener is bound, do our startup process
|
||||
this.postStartup();
|
||||
this.onGeyserEnable();
|
||||
|
||||
if (geyserInjector != null) {
|
||||
// After this bound, we know that the channel initializer cannot change without it being ineffective for Velocity, too
|
||||
geyserInjector.initializeLocalChannel(this);
|
||||
}
|
||||
|
||||
INITIALIZED = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,4 +252,21 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean loadConfig() {
|
||||
try {
|
||||
if (!configFolder.toFile().exists())
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
configFolder.toFile().mkdirs();
|
||||
File configFile = FileUtils.fileOrCopiedFromResource(configFolder.resolve("config.yml").toFile(),
|
||||
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class);
|
||||
} catch (IOException ex) {
|
||||
logger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
dependencies {
|
||||
api(projects.core)
|
||||
}
|
||||
|
||||
platformRelocate("net.kyori")
|
||||
platformRelocate("org.yaml")
|
||||
platformRelocate("it.unimi.dsi.fastutil")
|
||||
platformRelocate("org.cloudburstmc.netty")
|
||||
|
||||
// These dependencies are already present on the platform
|
||||
provided(libs.viaproxy)
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.viaproxy.GeyserViaProxyMain")
|
||||
}
|
||||
|
||||
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||
archiveBaseName.set("Geyser-ViaProxy")
|
||||
|
||||
dependencies {
|
||||
exclude(dependency("com.google.*:.*"))
|
||||
exclude(dependency("io.netty:.*"))
|
||||
exclude(dependency("org.slf4j:.*"))
|
||||
exclude(dependency("org.ow2.asm:.*"))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.viaproxy;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import net.raphimc.vialegacy.api.LegacyProtocolVersion;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class GeyserViaProxyConfiguration extends GeyserJacksonConfiguration {
|
||||
|
||||
@Override
|
||||
public Path getFloodgateKeyPath() {
|
||||
return new File(GeyserViaProxyPlugin.ROOT_FOLDER, this.getFloodgateKeyFile()).toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPingPassthroughInterval() {
|
||||
int interval = super.getPingPassthroughInterval();
|
||||
if (interval < 15 && Options.PROTOCOL_VERSION != null && Options.PROTOCOL_VERSION.olderThanOrEqualTo(LegacyProtocolVersion.r1_6_4)) {
|
||||
// <= 1.6.4 servers sometimes block incoming connections from an IP address if too many connections are made
|
||||
interval = 15;
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.viaproxy;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.raphimc.viaproxy.ViaProxy;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.text.AsteriskSerializer;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class GeyserViaProxyDumpInfo extends BootstrapDumpInfo {
|
||||
|
||||
private final String platformVersion;
|
||||
private final boolean onlineMode;
|
||||
|
||||
@AsteriskSerializer.Asterisk(isIp = true)
|
||||
private final String serverIP;
|
||||
private final int serverPort;
|
||||
private final List<PluginInfo> plugins;
|
||||
|
||||
public GeyserViaProxyDumpInfo() {
|
||||
this.platformVersion = ViaProxy.VERSION;
|
||||
this.onlineMode = Options.ONLINE_MODE;
|
||||
if (Options.BIND_ADDRESS instanceof InetSocketAddress inetSocketAddress) {
|
||||
this.serverIP = inetSocketAddress.getHostString();
|
||||
this.serverPort = inetSocketAddress.getPort();
|
||||
} else {
|
||||
this.serverIP = "unsupported";
|
||||
this.serverPort = 0;
|
||||
}
|
||||
this.plugins = new ArrayList<>();
|
||||
|
||||
for (ViaProxyPlugin plugin : ViaProxy.getPluginManager().getPlugins()) {
|
||||
this.plugins.add(new PluginInfo(true, plugin.getName(), plugin.getVersion(), "unknown", Collections.singletonList(plugin.getAuthor())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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.viaproxy;
|
||||
|
||||
import net.raphimc.viaproxy.cli.ConsoleFormatter;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
|
||||
public class GeyserViaProxyLogger implements GeyserLogger, GeyserCommandSource {
|
||||
|
||||
private final Logger logger;
|
||||
private boolean debug;
|
||||
|
||||
public GeyserViaProxyLogger(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
this.logger.fatal(ConsoleFormatter.convert(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String message, Throwable error) {
|
||||
this.logger.fatal(ConsoleFormatter.convert(message), error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message) {
|
||||
this.logger.error(ConsoleFormatter.convert(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message, Throwable error) {
|
||||
this.logger.error(ConsoleFormatter.convert(message), error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String message) {
|
||||
this.logger.warn(ConsoleFormatter.convert(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String message) {
|
||||
this.logger.info(ConsoleFormatter.convert(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message) {
|
||||
if (this.debug) {
|
||||
this.logger.debug(ConsoleFormatter.convert(message));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return this.debug;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.viaproxy;
|
||||
|
||||
import net.raphimc.viaproxy.plugins.PluginManager;
|
||||
import org.geysermc.geyser.GeyserMain;
|
||||
|
||||
public class GeyserViaProxyMain extends GeyserMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new GeyserViaProxyMain().displayMessage();
|
||||
}
|
||||
|
||||
public String getPluginType() {
|
||||
return "ViaProxy";
|
||||
}
|
||||
|
||||
public String getPluginFolder() {
|
||||
return PluginManager.PLUGINS_DIR.getName();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* 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.viaproxy;
|
||||
|
||||
import net.lenni0451.lambdaevents.EventHandler;
|
||||
import net.raphimc.vialegacy.api.LegacyProtocolVersion;
|
||||
import net.raphimc.viaproxy.ViaProxy;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.plugins.PluginManager;
|
||||
import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
|
||||
import net.raphimc.viaproxy.plugins.events.ConsoleCommandEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.ProxyStartEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.ProxyStopEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.ShouldVerifyOnlineModeEvent;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.api.network.AuthType;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.geysermc.geyser.util.LoopbackUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootstrap {
|
||||
|
||||
public static final File ROOT_FOLDER = new File(PluginManager.PLUGINS_DIR, "Geyser");
|
||||
|
||||
private final GeyserViaProxyLogger logger = new GeyserViaProxyLogger(LogManager.getLogger("Geyser"));
|
||||
private GeyserViaProxyConfiguration config;
|
||||
private GeyserImpl geyser;
|
||||
private GeyserCommandManager commandManager;
|
||||
private IGeyserPingPassthrough pingPassthrough;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
ROOT_FOLDER.mkdirs();
|
||||
|
||||
GeyserLocale.init(this);
|
||||
this.onGeyserInitialize();
|
||||
|
||||
ViaProxy.EVENT_MANAGER.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.onGeyserShutdown();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onConsoleCommand(final ConsoleCommandEvent event) {
|
||||
final String command = event.getCommand().startsWith("/") ? event.getCommand().substring(1) : event.getCommand();
|
||||
if (this.getGeyserCommandManager().runCommand(this.getGeyserLogger(), command + " " + String.join(" ", event.getArgs()))) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onShouldVerifyOnlineModeEvent(final ShouldVerifyOnlineModeEvent event) {
|
||||
final UUID uuid = event.getProxyConnection().getGameProfile().getId();
|
||||
if (uuid == null) return;
|
||||
|
||||
final GeyserSession connection = GeyserImpl.getInstance().onlineConnections().stream().filter(s -> s.javaUuid().equals(uuid)).findAny().orElse(null);
|
||||
if (connection == null) return;
|
||||
|
||||
if (connection.javaUsername().equals(event.getProxyConnection().getGameProfile().getName())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onProxyStart(final ProxyStartEvent event) {
|
||||
this.onGeyserEnable();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onProxyStop(final ProxyStopEvent event) {
|
||||
this.onGeyserDisable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserInitialize() {
|
||||
if (!this.loadConfig()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.geyser = GeyserImpl.load(PlatformType.VIAPROXY, this);
|
||||
LoopbackUtil.checkAndApplyLoopback(this.logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserEnable() {
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
if (!this.loadConfig()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.commandManager = new GeyserCommandManager(this.geyser);
|
||||
this.commandManager.init();
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
if (Options.PROTOCOL_VERSION != null && Options.PROTOCOL_VERSION.newerThanOrEqualTo(LegacyProtocolVersion.b1_8tob1_8_1)) {
|
||||
// Only initialize the ping passthrough if the protocol version is above beta 1.7.3, as that's when the status protocol was added
|
||||
this.pingPassthrough = GeyserLegacyPingPassthrough.init(this.geyser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserDisable() {
|
||||
this.geyser.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserShutdown() {
|
||||
this.geyser.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserConfiguration getGeyserConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserLogger getGeyserLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserCommandManager getGeyserCommandManager() {
|
||||
return this.commandManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IGeyserPingPassthrough getGeyserPingPassthrough() {
|
||||
return this.pingPassthrough;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getConfigFolder() {
|
||||
return ROOT_FOLDER.toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BootstrapDumpInfo getDumpInfo() {
|
||||
return new GeyserViaProxyDumpInfo();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getServerBindAddress() {
|
||||
if (Options.BIND_ADDRESS instanceof InetSocketAddress socketAddress) {
|
||||
return socketAddress.getHostString();
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported bind address type: " + Options.BIND_ADDRESS.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getServerPort() {
|
||||
if (Options.BIND_ADDRESS instanceof InetSocketAddress socketAddress) {
|
||||
return socketAddress.getPort();
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported bind address type: " + Options.BIND_ADDRESS.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testFloodgatePluginPresent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean loadConfig() {
|
||||
try {
|
||||
final File configFile = FileUtils.fileOrCopiedFromResource(new File(ROOT_FOLDER, "config.yml"), "config.yml", s -> s.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||
this.config = FileUtils.loadConfig(configFile, GeyserViaProxyConfiguration.class);
|
||||
} catch (IOException e) {
|
||||
this.logger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e);
|
||||
return false;
|
||||
}
|
||||
this.config.getRemote().setAuthType(Files.isRegularFile(this.config.getFloodgateKeyPath()) ? AuthType.FLOODGATE : AuthType.OFFLINE);
|
||||
this.logger.setDebug(this.config.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(this.config, this.logger);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
name: "${name}-ViaProxy"
|
||||
version: "${version}"
|
||||
author: "${author}"
|
||||
main: "org.geysermc.geyser.platform.viaproxy.GeyserViaProxyPlugin"
|
||||
min-version: "3.2.0"
|
|
@ -4,14 +4,17 @@ plugins {
|
|||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven("https://repo.opencollab.dev/maven-snapshots")
|
||||
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/")
|
||||
maven("https://maven.fabricmc.net/")
|
||||
maven("https://maven.neoforged.net/releases")
|
||||
maven("https://maven.architectury.dev/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("net.kyori", "indra-common", "3.1.1")
|
||||
implementation("com.github.johnrengelman", "shadow", "7.1.3-SNAPSHOT")
|
||||
|
||||
// Within the gradle plugin classpath, there is a version conflict between loom and some other
|
||||
// plugin for databind. This fixes it: minimum 2.13.2 is required by loom.
|
||||
implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0")
|
||||
implementation(libs.indra)
|
||||
implementation(libs.shadow)
|
||||
implementation(libs.architectury.plugin)
|
||||
implementation(libs.architectury.loom)
|
||||
implementation(libs.minotaur)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "build-logic"
|
|
@ -22,8 +22,8 @@ indra {
|
|||
|
||||
tasks {
|
||||
processResources {
|
||||
// Spigot, BungeeCord, Velocity, Fabric
|
||||
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json")) {
|
||||
// Spigot, BungeeCord, Velocity, Fabric, ViaProxy, NeoForge
|
||||
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json", "viaproxy.yml", "META-INF/mods.toml")) {
|
||||
expand(
|
||||
"id" to "geyser",
|
||||
"name" to "Geyser",
|
||||
|
|
|
@ -1,53 +1,22 @@
|
|||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import net.fabricmc.loom.task.RemapJarTask
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.maven
|
||||
|
||||
plugins {
|
||||
id("fabric-loom") version "1.4-SNAPSHOT"
|
||||
id("com.modrinth.minotaur") version "2.+"
|
||||
id("geyser.publish-conventions")
|
||||
id("architectury-plugin")
|
||||
id("dev.architectury.loom")
|
||||
id("com.modrinth.minotaur")
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
modImplementation(libs.cloud.fabric)
|
||||
include(libs.cloud.fabric)
|
||||
|
||||
// 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")
|
||||
}
|
||||
architectury {
|
||||
minecraft = "1.20.4"
|
||||
}
|
||||
|
||||
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")
|
||||
silentMojangMappingsLicense()
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
@ -62,7 +31,7 @@ tasks {
|
|||
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
|
||||
// The remapped shadowJar is the final desired mod jar
|
||||
archiveVersion.set(project.version.toString())
|
||||
archiveClassifier.set("shaded")
|
||||
|
||||
|
@ -92,20 +61,32 @@ tasks {
|
|||
remapJar {
|
||||
dependsOn(shadowJar)
|
||||
inputFile.set(shadowJar.get().archiveFile)
|
||||
archiveBaseName.set("Geyser-Fabric")
|
||||
archiveVersion.set("")
|
||||
archiveClassifier.set("")
|
||||
archiveVersion.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("")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft("com.mojang:minecraft:1.20.4")
|
||||
mappings(loom.officialMojangMappings())
|
||||
}
|
||||
|
||||
repositories {
|
||||
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/")
|
||||
maven("https://maven.neoforged.net/releases")
|
||||
}
|
||||
|
||||
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")
|
||||
|
@ -117,11 +98,5 @@ modrinth {
|
|||
|
||||
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
||||
gameVersions.addAll("1.20.4")
|
||||
|
||||
loaders.add("fabric")
|
||||
failSilently.set(true)
|
||||
|
||||
dependencies {
|
||||
required.project("fabric-api")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
plugins {
|
||||
`java-library`
|
||||
// Ensure AP works in eclipse (no effect on other IDEs)
|
||||
`eclipse`
|
||||
eclipse
|
||||
id("geyser.build-logic")
|
||||
id("io.freefair.lombok") version "8.4" apply false
|
||||
alias(libs.plugins.lombok) apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
@ -12,18 +12,18 @@ allprojects {
|
|||
description = properties["description"] as String
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
|
||||
val platforms = setOf(
|
||||
projects.fabric,
|
||||
val basePlatforms = setOf(
|
||||
projects.bungeecord,
|
||||
projects.spigot,
|
||||
projects.standalone,
|
||||
projects.velocity
|
||||
projects.velocity,
|
||||
projects.viaproxy
|
||||
).map { it.dependencyProject }
|
||||
|
||||
val moddedPlatforms = setOf(
|
||||
projects.fabric,
|
||||
projects.neoforge,
|
||||
projects.mod
|
||||
).map { it.dependencyProject }
|
||||
|
||||
subprojects {
|
||||
|
@ -34,7 +34,8 @@ subprojects {
|
|||
}
|
||||
|
||||
when (this) {
|
||||
in platforms -> plugins.apply("geyser.platform-conventions")
|
||||
in basePlatforms -> plugins.apply("geyser.platform-conventions")
|
||||
in moddedPlatforms -> plugins.apply("geyser.modded-conventions")
|
||||
else -> plugins.apply("geyser.base-conventions")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import net.kyori.blossom.BlossomExtension
|
||||
|
||||
plugins {
|
||||
id("net.kyori.blossom")
|
||||
id("net.kyori.indra.git")
|
||||
alias(libs.plugins.blossom)
|
||||
id("geyser.publish-conventions")
|
||||
}
|
||||
|
||||
|
|
|
@ -44,14 +44,28 @@ public interface GeyserBootstrap {
|
|||
GeyserWorldManager DEFAULT_CHUNK_MANAGER = new GeyserWorldManager();
|
||||
|
||||
/**
|
||||
* Called when the GeyserBootstrap is enabled
|
||||
* Called when the GeyserBootstrap is initialized.
|
||||
* This will only be called once, when Geyser is loading. Calling this must
|
||||
* happen before {@link #onGeyserEnable()}, since this "sets up" Geyser.
|
||||
*/
|
||||
void onEnable();
|
||||
void onGeyserInitialize();
|
||||
|
||||
/**
|
||||
* Called when the GeyserBootstrap is disabled
|
||||
* Called when the GeyserBootstrap is enabled/reloaded.
|
||||
* This starts Geyser, after which, Geyser is in a player-accepting state.
|
||||
*/
|
||||
void onDisable();
|
||||
void onGeyserEnable();
|
||||
|
||||
/**
|
||||
* Called when the GeyserBootstrap is disabled - either before a reload,
|
||||
* of before fully shutting down.
|
||||
*/
|
||||
void onGeyserDisable();
|
||||
|
||||
/**
|
||||
* Called when the GeyserBootstrap is shutting down.
|
||||
*/
|
||||
void onGeyserShutdown();
|
||||
|
||||
/**
|
||||
* Returns the current GeyserConfiguration
|
||||
|
|
|
@ -42,6 +42,7 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
|||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.geysermc.api.Geyser;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||
|
@ -52,14 +53,14 @@ import org.geysermc.floodgate.crypto.Base64Topping;
|
|||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||
import org.geysermc.floodgate.news.NewsItemAction;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.api.command.CommandSource;
|
||||
import org.geysermc.geyser.api.event.EventBus;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.*;
|
||||
import org.geysermc.geyser.api.network.AuthType;
|
||||
import org.geysermc.geyser.api.network.BedrockListener;
|
||||
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.command.CommandRegistry;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
|
@ -67,7 +68,9 @@ import org.geysermc.geyser.entity.EntityDefinitions;
|
|||
import org.geysermc.geyser.erosion.UnixSocketClientListener;
|
||||
import org.geysermc.geyser.event.GeyserEventBus;
|
||||
import org.geysermc.geyser.extension.GeyserExtensionManager;
|
||||
import org.geysermc.geyser.impl.MinecraftVersionImpl;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.network.netty.GeyserServer;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
|
@ -111,8 +114,8 @@ public class GeyserImpl implements GeyserApi {
|
|||
.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
|
||||
|
||||
public static final String NAME = "Geyser";
|
||||
public static final String GIT_VERSION = "${gitVersion}"; // A fallback for running in IDEs
|
||||
public static final String VERSION = "${version}"; // A fallback for running in IDEs
|
||||
public static final String GIT_VERSION = "${gitVersion}";
|
||||
public static final String VERSION = "${version}";
|
||||
|
||||
public static final String BUILD_NUMBER = "${buildNumber}";
|
||||
public static final String BRANCH = "${branch}";
|
||||
|
@ -140,6 +143,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
|
||||
private UnixSocketClientListener erosionUnixListener;
|
||||
|
||||
@Setter
|
||||
private volatile boolean shuttingDown = false;
|
||||
|
||||
private ScheduledExecutorService scheduledThread;
|
||||
|
@ -157,8 +161,20 @@ public class GeyserImpl implements GeyserApi {
|
|||
@Getter(AccessLevel.NONE)
|
||||
private Map<String, String> savedRefreshTokens;
|
||||
|
||||
@Getter
|
||||
private static GeyserImpl instance;
|
||||
|
||||
/**
|
||||
* Determines if we're currently reloading. Replaces per-bootstrap reload checks
|
||||
*/
|
||||
private volatile boolean isReloading;
|
||||
|
||||
/**
|
||||
* Determines if Geyser is currently enabled. This is used to determine if {@link #disable()} should be called during {@link #shutdown()}.
|
||||
*/
|
||||
@Setter
|
||||
private boolean isEnabled;
|
||||
|
||||
private GeyserImpl(PlatformType platformType, GeyserBootstrap bootstrap) {
|
||||
instance = this;
|
||||
|
||||
|
@ -167,13 +183,16 @@ public class GeyserImpl implements GeyserApi {
|
|||
this.platformType = platformType;
|
||||
this.bootstrap = bootstrap;
|
||||
|
||||
GeyserLocale.finalizeDefaultLocale(this);
|
||||
|
||||
/* Initialize event bus */
|
||||
this.eventBus = new GeyserEventBus();
|
||||
|
||||
/* Load Extensions */
|
||||
/* Create Extension Manager */
|
||||
this.extensionManager = new GeyserExtensionManager();
|
||||
|
||||
/* Finalize locale loading now that we know the default locale from the config */
|
||||
GeyserLocale.finalizeDefaultLocale(this);
|
||||
|
||||
/* Load Extensions */
|
||||
this.extensionManager.init();
|
||||
this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus));
|
||||
}
|
||||
|
@ -202,6 +221,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
if (ex != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MinecraftLocale.ensureEN_US();
|
||||
String locale = GeyserLocale.getDefaultLocale();
|
||||
if (!"en_us".equals(locale)) {
|
||||
|
@ -231,11 +251,17 @@ public class GeyserImpl implements GeyserApi {
|
|||
} else if (config.getRemote().authType() == AuthType.FLOODGATE) {
|
||||
VersionCheckUtils.checkForOutdatedFloodgate(logger);
|
||||
}
|
||||
|
||||
VersionCheckUtils.checkForOutdatedJava(logger);
|
||||
}
|
||||
|
||||
private void startInstance() {
|
||||
this.scheduledThread = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("Geyser Scheduled Thread"));
|
||||
|
||||
if (isReloading) {
|
||||
// If we're reloading, the default locale in the config might have changed.
|
||||
GeyserLocale.finalizeDefaultLocale(this);
|
||||
}
|
||||
GeyserLogger logger = bootstrap.getGeyserLogger();
|
||||
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
||||
|
||||
|
@ -309,15 +335,33 @@ public class GeyserImpl implements GeyserApi {
|
|||
}
|
||||
}
|
||||
|
||||
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent();
|
||||
if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) {
|
||||
logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " "
|
||||
+ GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
|
||||
return;
|
||||
} else if (config.isAutoconfiguredRemote() && floodgatePresent) {
|
||||
// Floodgate installed means that the user wants Floodgate authentication
|
||||
logger.debug("Auto-setting to Floodgate authentication.");
|
||||
config.getRemote().setAuthType(AuthType.FLOODGATE);
|
||||
String broadcastPort = System.getProperty("geyserBroadcastPort", "");
|
||||
if (!broadcastPort.isEmpty()) {
|
||||
int parsedPort;
|
||||
try {
|
||||
parsedPort = Integer.parseInt(broadcastPort);
|
||||
if (parsedPort < 1 || parsedPort > 65535) {
|
||||
throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.error(String.format("Invalid broadcast port: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")"));
|
||||
parsedPort = config.getBedrock().port();
|
||||
}
|
||||
config.getBedrock().setBroadcastPort(parsedPort);
|
||||
logger.info("Broadcast port set from system property: " + parsedPort);
|
||||
}
|
||||
|
||||
if (platformType != PlatformType.VIAPROXY) {
|
||||
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent();
|
||||
if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) {
|
||||
logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " "
|
||||
+ GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
|
||||
return;
|
||||
} else if (config.isAutoconfiguredRemote() && floodgatePresent) {
|
||||
// Floodgate installed means that the user wants Floodgate authentication
|
||||
logger.debug("Auto-setting to Floodgate authentication.");
|
||||
config.getRemote().setAuthType(AuthType.FLOODGATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -515,12 +559,15 @@ public class GeyserImpl implements GeyserApi {
|
|||
|
||||
newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED);
|
||||
|
||||
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
|
||||
if (isReloading) {
|
||||
this.eventBus.fire(new GeyserPostReloadEvent(this.extensionManager, this.eventBus));
|
||||
} else {
|
||||
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
|
||||
}
|
||||
|
||||
if (config.isNotifyOnNewBedrockUpdate()) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(this::getLogger);
|
||||
}
|
||||
|
||||
VersionCheckUtils.checkForOutdatedJava(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -579,9 +626,8 @@ public class GeyserImpl implements GeyserApi {
|
|||
return session.transfer(address, port);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
public void disable() {
|
||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown"));
|
||||
shuttingDown = true;
|
||||
|
||||
if (sessionManager.size() >= 1) {
|
||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.kick.log", sessionManager.size()));
|
||||
|
@ -602,16 +648,31 @@ public class GeyserImpl implements GeyserApi {
|
|||
|
||||
Registries.RESOURCE_PACKS.get().clear();
|
||||
|
||||
this.setEnabled(false);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
shuttingDown = true;
|
||||
if (isEnabled) {
|
||||
this.disable();
|
||||
}
|
||||
this.commandManager().getCommands().clear();
|
||||
|
||||
// Disable extensions, fire the shutdown event
|
||||
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
|
||||
this.extensionManager.disableExtensions();
|
||||
|
||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done"));
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
shutdown();
|
||||
this.extensionManager.enableExtensions();
|
||||
bootstrap.onEnable();
|
||||
public void reloadGeyser() {
|
||||
isReloading = true;
|
||||
this.eventBus.fire(new GeyserPreReloadEvent(this.extensionManager, this.eventBus));
|
||||
|
||||
bootstrap.onGeyserDisable();
|
||||
bootstrap.onGeyserEnable();
|
||||
|
||||
isReloading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -685,6 +746,25 @@ public class GeyserImpl implements GeyserApi {
|
|||
return platformType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MinecraftVersion supportedJavaVersion() {
|
||||
return new MinecraftVersionImpl(GameProtocol.getJavaMinecraftVersion(), GameProtocol.getJavaProtocolVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<MinecraftVersion> supportedBedrockVersions() {
|
||||
ArrayList<MinecraftVersion> versions = new ArrayList<>();
|
||||
for (BedrockCodec codec : GameProtocol.SUPPORTED_BEDROCK_CODECS) {
|
||||
versions.add(new MinecraftVersionImpl(codec.getMinecraftVersion(), codec.getProtocolVersion()));
|
||||
}
|
||||
return Collections.unmodifiableList(versions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CommandSource consoleCommandSource() {
|
||||
return getLogger();
|
||||
}
|
||||
|
||||
public int buildNumber() {
|
||||
if (!this.isProductionEnvironment()) {
|
||||
return 0;
|
||||
|
@ -706,13 +786,12 @@ public class GeyserImpl implements GeyserApi {
|
|||
throw new RuntimeException("Geyser has not been loaded yet!");
|
||||
}
|
||||
|
||||
// We've been reloaded
|
||||
if (instance.isShuttingDown()) {
|
||||
instance.shuttingDown = false;
|
||||
if (getInstance().isReloading()) {
|
||||
instance.startInstance();
|
||||
} else {
|
||||
instance.initialize();
|
||||
}
|
||||
instance.setEnabled(true);
|
||||
}
|
||||
|
||||
public GeyserLogger getLogger() {
|
||||
|
@ -759,8 +838,4 @@ public class GeyserImpl implements GeyserApi {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static GeyserImpl getInstance() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ import org.geysermc.geyser.GeyserImpl;
|
|||
import org.geysermc.geyser.api.util.TriState;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.LoopbackUtil;
|
||||
import org.geysermc.geyser.util.WebUtils;
|
||||
|
||||
|
@ -81,7 +84,7 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||
|
||||
// Replace "<" and ">" symbols if they are present to avoid the common issue of people including them
|
||||
final String ip = ipArgument.replace("<", "").replace(">", "");
|
||||
final int port = portArgument != null ? portArgument : 19132; // default bedrock port
|
||||
final int port = portArgument != null ? portArgument : geyser.getConfig().getBedrock().broadcastPort(); // default bedrock port
|
||||
|
||||
// Issue: people commonly checking placeholders
|
||||
if (ip.equals("ip")) {
|
||||
|
@ -107,30 +110,41 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||
return;
|
||||
}
|
||||
|
||||
// Issue: do the ports not line up?
|
||||
if (port != geyser.getConfig().getBedrock().port()) {
|
||||
if (portArgument != null) {
|
||||
source.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration ("
|
||||
+ geyser.getConfig().getBedrock().port() + ")");
|
||||
source.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config.");
|
||||
if (geyser.getConfig().getBedrock().isCloneRemotePort()) {
|
||||
source.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead.");
|
||||
GeyserConfiguration config = geyser.getConfig();
|
||||
|
||||
// Issue: do the ports not line up? We only check this if players don't override the broadcast port - if they do, they (hopefully) know what they're doing
|
||||
if (config.getBedrock().broadcastPort() == config.getBedrock().port()) {
|
||||
if (port != config.getBedrock().port()) {
|
||||
if (portArgument != null) {
|
||||
source.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration ("
|
||||
+ config.getBedrock().port() + ")");
|
||||
source.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config.");
|
||||
if (config.getBedrock().isCloneRemotePort()) {
|
||||
source.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead.");
|
||||
}
|
||||
} else {
|
||||
source.sendMessage("You did not specify the port to check (add it with \":<port>\"), " +
|
||||
"and the default port 19132 does not match the port in your Geyser configuration ("
|
||||
+ config.getBedrock().port() + ")!");
|
||||
source.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`.");
|
||||
}
|
||||
} else {
|
||||
source.sendMessage("You did not specify the port to check, " +
|
||||
"and the default port 19132 does not match the port in your Geyser configuration ("
|
||||
+ geyser.getConfig().getBedrock().port() + ")!");
|
||||
source.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`.");
|
||||
}
|
||||
} else {
|
||||
if (config.getBedrock().broadcastPort() != port) {
|
||||
source.sendMessage("The port you are testing with (" + port + ") is not the same as the broadcast port set in your Geyser configuration ("
|
||||
+ config.getBedrock().broadcastPort() + "). ");
|
||||
source.sendMessage("You ONLY need to change the broadcast port if clients connects with a port different from the port Geyser is running on.");
|
||||
source.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `broadcast-port` in the config.");
|
||||
}
|
||||
}
|
||||
|
||||
// Issue: is the `bedrock` `address` in the config different?
|
||||
if (!geyser.getConfig().getBedrock().address().equals("0.0.0.0")) {
|
||||
if (!config.getBedrock().address().equals("0.0.0.0")) {
|
||||
source.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional.");
|
||||
}
|
||||
|
||||
// Issue: did someone turn on enable-proxy-protocol, and they didn't mean it?
|
||||
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
|
||||
if (config.getBedrock().isEnableProxyProtocol()) {
|
||||
source.sendMessage("You have the `enable-proxy-protocol` setting enabled. " +
|
||||
"Unless you're deliberately using additional software that REQUIRES this setting, you may not need it enabled.");
|
||||
}
|
||||
|
@ -161,7 +175,7 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||
String connectionTestMotd = "Geyser Connection Test " + randomStr;
|
||||
CONNECTION_TEST_MOTD = connectionTestMotd;
|
||||
|
||||
source.sendMessage("Testing server connection now. Please wait...");
|
||||
source.sendMessage("Testing server connection to " + ip + " with port: " + port + " now. Please wait...");
|
||||
JsonNode output;
|
||||
try {
|
||||
String hostname = URLEncoder.encode(ip, StandardCharsets.UTF_8);
|
||||
|
|
|
@ -50,6 +50,6 @@ public class ReloadCommand extends GeyserCommand {
|
|||
|
||||
geyser.getSessionManager().disconnectAll("geyser.commands.reload.kick");
|
||||
//FIXME Without the tiny wait, players do not get kicked - same happens when Geyser tries to disconnect all sessions on shutdown
|
||||
geyser.getScheduledThread().schedule(geyser::reload, 10, TimeUnit.MILLISECONDS);
|
||||
geyser.getScheduledThread().schedule(geyser::reloadGeyser, 10, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,6 @@ public class StopCommand extends GeyserCommand {
|
|||
|
||||
@Override
|
||||
public void execute(CommandContext<GeyserCommandSource> context) {
|
||||
geyser.getBootstrap().onDisable();
|
||||
geyser.getBootstrap().onGeyserShutdown();
|
||||
}
|
||||
}
|
|
@ -122,6 +122,8 @@ public interface GeyserConfiguration {
|
|||
|
||||
void setPort(int port);
|
||||
|
||||
void setBroadcastPort(int broadcastPort);
|
||||
|
||||
boolean isCloneRemotePort();
|
||||
|
||||
int getCompressionLevel();
|
||||
|
|
|
@ -172,6 +172,15 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
return port;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@JsonProperty("broadcast-port")
|
||||
private int broadcastPort = 0;
|
||||
|
||||
@Override
|
||||
public int broadcastPort() {
|
||||
return broadcastPort;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@JsonProperty("clone-remote-port")
|
||||
private boolean cloneRemotePort = false;
|
||||
|
|
|
@ -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.entity;
|
||||
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.EmotePacket;
|
||||
import org.geysermc.geyser.api.entity.EntityData;
|
||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GeyserEntityData implements EntityData {
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
private final Set<UUID> movementLockOwners = new HashSet<>();
|
||||
|
||||
public GeyserEntityData(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId) {
|
||||
CompletableFuture<GeyserEntity> future = new CompletableFuture<>();
|
||||
session.ensureInEventLoop(() -> future.complete(session.getEntityCache().getEntityByJavaId(javaId)));
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId) {
|
||||
Objects.requireNonNull(emoter, "emoter must not be null!");
|
||||
Entity entity = (Entity) emoter;
|
||||
if (entity.getSession() != session) {
|
||||
throw new IllegalStateException("Given entity must be from this session!");
|
||||
}
|
||||
|
||||
EmotePacket packet = new EmotePacket();
|
||||
packet.setRuntimeEntityId(entity.getGeyserId());
|
||||
packet.setXuid("");
|
||||
packet.setPlatformId(""); // BDS sends empty
|
||||
packet.setEmoteId(emoteId);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GeyserPlayerEntity playerEntity() {
|
||||
return session.getPlayerEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lockMovement(boolean lock, @NonNull UUID owner) {
|
||||
Objects.requireNonNull(owner, "owner must not be null!");
|
||||
if (lock) {
|
||||
movementLockOwners.add(owner);
|
||||
} else {
|
||||
movementLockOwners.remove(owner);
|
||||
}
|
||||
|
||||
session.lockInputs(session.camera().isCameraLocked(), isMovementLocked());
|
||||
return isMovementLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMovementLocked() {
|
||||
return !movementLockOwners.isEmpty();
|
||||
}
|
||||
}
|
|
@ -39,12 +39,20 @@ import net.kyori.adventure.text.Component;
|
|||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.*;
|
||||
import org.cloudburstmc.protocol.bedrock.data.Ability;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AbilityLayer;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.GameType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.PlayerPermission;
|
||||
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.*;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
|
@ -143,6 +151,10 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
addPlayerPacket.setGameType(GameType.SURVIVAL); //TODO
|
||||
addPlayerPacket.setAbilityLayers(BASE_ABILITY_LAYER); // Recommended to be added since 1.19.10, but only needed here for permissions viewing
|
||||
addPlayerPacket.getMetadata().putFlags(flags);
|
||||
|
||||
// Since 1.20.60, the nametag does not show properly if this is not set :/
|
||||
// The nametag does disappear properly when the player is invisible though.
|
||||
dirtyMetadata.put(EntityDataTypes.NAMETAG_ALWAYS_SHOW, (byte) 1);
|
||||
dirtyMetadata.apply(addPlayerPacket.getMetadata());
|
||||
|
||||
setFlagsDirty(false);
|
||||
|
@ -433,4 +445,9 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
public UUID getTabListUuid() {
|
||||
return getUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3f position() {
|
||||
return this.position.clone();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.impl;
|
||||
|
||||
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||
|
||||
public record MinecraftVersionImpl(String versionString, int protocolVersion) implements MinecraftVersion {
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.impl.camera;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraAudioListener;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraPreset;
|
||||
import org.cloudburstmc.protocol.common.DefinitionRegistry;
|
||||
import org.cloudburstmc.protocol.common.NamedDefinition;
|
||||
import org.cloudburstmc.protocol.common.SimpleDefinitionRegistry;
|
||||
import org.cloudburstmc.protocol.common.util.OptionalBoolean;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CameraDefinitions {
|
||||
|
||||
public static final DefinitionRegistry<NamedDefinition> CAMERA_DEFINITIONS;
|
||||
|
||||
public static final List<CameraPreset> CAMERA_PRESETS;
|
||||
|
||||
static {
|
||||
CAMERA_PRESETS = List.of(
|
||||
new CameraPreset(CameraPerspective.FIRST_PERSON.id(), "", null, null, null, null, OptionalBoolean.empty()),
|
||||
new CameraPreset(CameraPerspective.FREE.id(), "", null, null, null, null, OptionalBoolean.empty()),
|
||||
new CameraPreset(CameraPerspective.THIRD_PERSON.id(), "", null, null, null, null, OptionalBoolean.empty()),
|
||||
new CameraPreset(CameraPerspective.THIRD_PERSON_FRONT.id(), "", null, null, null, null, OptionalBoolean.empty()),
|
||||
new CameraPreset("geyser:free_audio", "minecraft:free", null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.of(false)),
|
||||
new CameraPreset("geyser:free_effects", "minecraft:free", null, null, null, CameraAudioListener.CAMERA, OptionalBoolean.of(true)),
|
||||
new CameraPreset("geyser:free_audio_effects", "minecraft:free", null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.of(true)));
|
||||
|
||||
SimpleDefinitionRegistry.Builder<NamedDefinition> builder = SimpleDefinitionRegistry.builder();
|
||||
for (int i = 0; i < CAMERA_PRESETS.size(); i++) {
|
||||
builder.add(CameraDefinition.of(CAMERA_PRESETS.get(i).getIdentifier(), i));
|
||||
}
|
||||
CAMERA_DEFINITIONS = builder.build();
|
||||
}
|
||||
|
||||
public static NamedDefinition getById(int id) {
|
||||
return CAMERA_DEFINITIONS.getDefinition(id);
|
||||
}
|
||||
|
||||
public static NamedDefinition getByFunctionality(boolean audio, boolean effects) {
|
||||
if (!audio && !effects) {
|
||||
return getById(1); // FREE
|
||||
}
|
||||
if (audio) {
|
||||
if (effects) {
|
||||
return getById(6); // FREE_AUDIO_EFFECTS
|
||||
} else {
|
||||
return getById(4); // FREE_AUDIO
|
||||
}
|
||||
} else {
|
||||
return getById(5); // FREE_EFFECTS
|
||||
}
|
||||
}
|
||||
|
||||
public record CameraDefinition(String identifier, int runtimeId) implements NamedDefinition {
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRuntimeId() {
|
||||
return runtimeId;
|
||||
}
|
||||
|
||||
public static CameraDefinition of(String identifier, int runtimeId) {
|
||||
return new CameraDefinition(identifier, runtimeId);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* 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.impl.camera;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeAction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraEase;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraFadeInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraSetInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraInstructionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraShakePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerFogPacket;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraEaseType;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserCameraData implements CameraData {
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
@Getter
|
||||
private CameraPerspective cameraPerspective;
|
||||
|
||||
/**
|
||||
* All fog effects that are currently applied to the client.
|
||||
*/
|
||||
private final Set<String> appliedFog = new HashSet<>();
|
||||
|
||||
private final Set<UUID> cameraLockOwners = new HashSet<>();
|
||||
|
||||
public GeyserCameraData(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCameraInstructions() {
|
||||
this.cameraPerspective = null;
|
||||
CameraInstructionPacket packet = new CameraInstructionPacket();
|
||||
packet.setClear(true);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceCameraPerspective(@NonNull CameraPerspective perspective) {
|
||||
Objects.requireNonNull(perspective, "perspective cannot be null!");
|
||||
|
||||
if (perspective == cameraPerspective) {
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
this.cameraPerspective = perspective;
|
||||
CameraInstructionPacket packet = new CameraInstructionPacket();
|
||||
CameraSetInstruction setInstruction = new CameraSetInstruction();
|
||||
|
||||
if (perspective == CameraPerspective.FREE) {
|
||||
throw new IllegalArgumentException("Cannot force a stationary camera (CameraPerspective#FREE) on the player!" +
|
||||
"Send a CameraPosition with an exact position instead");
|
||||
}
|
||||
|
||||
setInstruction.setPreset(CameraDefinitions.getById(perspective.ordinal()));
|
||||
packet.setSetInstruction(setInstruction);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable CameraPerspective forcedCameraPerspective() {
|
||||
return this.cameraPerspective;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCameraFade(@NonNull CameraFade fade) {
|
||||
Objects.requireNonNull(fade, "fade cannot be null!");
|
||||
CameraFadeInstruction fadeInstruction = new CameraFadeInstruction();
|
||||
fadeInstruction.setColor(fade.color());
|
||||
fadeInstruction.setTimeData(
|
||||
new CameraFadeInstruction.TimeData(
|
||||
fade.fadeInSeconds(),
|
||||
fade.fadeHoldSeconds(),
|
||||
fade.fadeOutSeconds()
|
||||
)
|
||||
);
|
||||
|
||||
CameraInstructionPacket packet = new CameraInstructionPacket();
|
||||
packet.setFadeInstruction(fadeInstruction);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCameraPosition(@NonNull CameraPosition movement) {
|
||||
Objects.requireNonNull(movement, "movement cannot be null!");
|
||||
this.cameraPerspective = CameraPerspective.FREE; // Movements only work with the free preset
|
||||
CameraSetInstruction setInstruction = new CameraSetInstruction();
|
||||
|
||||
CameraEaseType easeType = movement.easeType();
|
||||
if (easeType != null) {
|
||||
setInstruction.setEase(new CameraSetInstruction.EaseData(
|
||||
CameraEase.fromName(easeType.id()),
|
||||
movement.easeSeconds()
|
||||
));
|
||||
}
|
||||
|
||||
Vector3f facingPosition = movement.facingPosition();
|
||||
if (facingPosition != null) {
|
||||
setInstruction.setFacing(facingPosition);
|
||||
}
|
||||
|
||||
setInstruction.setPos(movement.position());
|
||||
setInstruction.setRot(Vector2f.from(movement.rotationX(), movement.rotationY()));
|
||||
setInstruction.setPreset(CameraDefinitions.getByFunctionality(movement.playerPositionForAudio(), movement.renderPlayerEffects()));
|
||||
|
||||
CameraInstructionPacket packet = new CameraInstructionPacket();
|
||||
packet.setSetInstruction(setInstruction);
|
||||
|
||||
// If present, also send the fade
|
||||
CameraFade fade = movement.cameraFade();
|
||||
if (fade != null) {
|
||||
CameraFadeInstruction fadeInstruction = new CameraFadeInstruction();
|
||||
fadeInstruction.setColor(fade.color());
|
||||
fadeInstruction.setTimeData(
|
||||
new CameraFadeInstruction.TimeData(
|
||||
fade.fadeInSeconds(),
|
||||
fade.fadeHoldSeconds(),
|
||||
fade.fadeOutSeconds()
|
||||
)
|
||||
);
|
||||
packet.setFadeInstruction(fadeInstruction);
|
||||
}
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shakeCamera(float intensity, float duration, @NonNull CameraShake type) {
|
||||
Objects.requireNonNull(type, "camera shake type must be non null!");
|
||||
CameraShakePacket packet = new CameraShakePacket();
|
||||
packet.setIntensity(intensity);
|
||||
packet.setDuration(duration);
|
||||
packet.setShakeType(type == CameraShake.POSITIONAL ? CameraShakeType.POSITIONAL : CameraShakeType.ROTATIONAL);
|
||||
packet.setShakeAction(CameraShakeAction.ADD);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopCameraShake() {
|
||||
CameraShakePacket packet = new CameraShakePacket();
|
||||
// CameraShakeAction.STOP removes all types regardless of the given type, but regardless it can't be null
|
||||
packet.setShakeType(CameraShakeType.POSITIONAL);
|
||||
packet.setShakeAction(CameraShakeAction.STOP);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFog(String... fogNameSpaces) {
|
||||
Collections.addAll(this.appliedFog, fogNameSpaces);
|
||||
|
||||
PlayerFogPacket packet = new PlayerFogPacket();
|
||||
packet.getFogStack().addAll(this.appliedFog);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFog(String... fogNameSpaces) {
|
||||
if (fogNameSpaces.length == 0) {
|
||||
this.appliedFog.clear();
|
||||
} else {
|
||||
for (String id : fogNameSpaces) {
|
||||
this.appliedFog.remove(id);
|
||||
}
|
||||
}
|
||||
PlayerFogPacket packet = new PlayerFogPacket();
|
||||
packet.getFogStack().addAll(this.appliedFog);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Set<String> fogEffects() {
|
||||
// Use a copy so that sendFog/removeFog can be called while iterating the returned set (avoid CME)
|
||||
return Set.copyOf(this.appliedFog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lockCamera(boolean lock, @NonNull UUID owner) {
|
||||
Objects.requireNonNull(owner, "owner cannot be null!");
|
||||
if (lock) {
|
||||
this.cameraLockOwners.add(owner);
|
||||
} else {
|
||||
this.cameraLockOwners.remove(owner);
|
||||
}
|
||||
|
||||
session.lockInputs(isCameraLocked(), session.entities().isMovementLocked());
|
||||
return isCameraLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCameraLocked() {
|
||||
return !this.cameraLockOwners.isEmpty();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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.impl.camera;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.common.value.qual.IntRange;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.Objects;
|
||||
|
||||
public record GeyserCameraFade(
|
||||
Color color,
|
||||
float fadeInSeconds,
|
||||
float fadeHoldSeconds,
|
||||
float fadeOutSeconds
|
||||
|
||||
) implements CameraFade {
|
||||
public static class Builder implements CameraFade.Builder {
|
||||
private Color color;
|
||||
private float fadeInSeconds;
|
||||
private float fadeHoldSeconds;
|
||||
private float fadeOutSeconds;
|
||||
|
||||
@Override
|
||||
public CameraFade.Builder color(@NonNull Color color) {
|
||||
Objects.requireNonNull(color, "color cannot be null!");
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraFade.Builder fadeInSeconds(@IntRange(from = 0, to = 10) float fadeInSeconds) {
|
||||
if (fadeInSeconds < 0f) {
|
||||
throw new IllegalArgumentException("Fade in seconds must be at least 0 seconds");
|
||||
}
|
||||
|
||||
if (fadeInSeconds > 10f) {
|
||||
throw new IllegalArgumentException("Fade in seconds must be at most 10 seconds");
|
||||
}
|
||||
this.fadeInSeconds = fadeInSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraFade.Builder fadeHoldSeconds(@IntRange(from = 0, to = 10) float fadeHoldSeconds) {
|
||||
if (fadeHoldSeconds < 0f) {
|
||||
throw new IllegalArgumentException("Fade hold seconds must be at least 0 seconds");
|
||||
}
|
||||
|
||||
if (fadeHoldSeconds > 10f) {
|
||||
throw new IllegalArgumentException("Fade hold seconds must be at most 10 seconds");
|
||||
}
|
||||
this.fadeHoldSeconds = fadeHoldSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraFade.Builder fadeOutSeconds(@IntRange(from = 0, to = 10) float fadeOutSeconds) {
|
||||
if (fadeOutSeconds < 0f) {
|
||||
throw new IllegalArgumentException("Fade out seconds must be at least 0 seconds");
|
||||
}
|
||||
|
||||
if (fadeOutSeconds > 10f) {
|
||||
throw new IllegalArgumentException("Fade out seconds must be at most 10 seconds");
|
||||
}
|
||||
this.fadeOutSeconds = fadeOutSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraFade build() {
|
||||
Objects.requireNonNull(color, "color must be non null!");
|
||||
if (fadeInSeconds + fadeHoldSeconds + fadeOutSeconds < 0.5f) {
|
||||
throw new IllegalArgumentException("Total fade time (in, hold, out) must be at least 0.5 seconds");
|
||||
}
|
||||
|
||||
return new GeyserCameraFade(color, fadeInSeconds, fadeHoldSeconds, fadeOutSeconds);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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.impl.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.bedrock.camera.CameraEaseType;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record GeyserCameraPosition(CameraFade cameraFade,
|
||||
boolean renderPlayerEffects,
|
||||
boolean playerPositionForAudio,
|
||||
CameraEaseType easeType,
|
||||
float easeSeconds,
|
||||
Vector3f position,
|
||||
@IntRange(from = -90, to = 90) int rotationX,
|
||||
int rotationY,
|
||||
Vector3f facingPosition
|
||||
) implements CameraPosition {
|
||||
|
||||
public static class Builder implements CameraPosition.Builder {
|
||||
private CameraFade cameraFade;
|
||||
private boolean renderPlayerEffects;
|
||||
private boolean playerPositionForAudio;
|
||||
private CameraEaseType easeType;
|
||||
private float easeSeconds;
|
||||
private Vector3f position;
|
||||
private @IntRange(from = -90, to = 90) int rotationX;
|
||||
private int rotationY;
|
||||
private Vector3f facingPosition;
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder cameraFade(@Nullable CameraFade cameraFade) {
|
||||
this.cameraFade = cameraFade;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder renderPlayerEffects(boolean renderPlayerEffects) {
|
||||
this.renderPlayerEffects = renderPlayerEffects;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder playerPositionForAudio(boolean playerPositionForAudio) {
|
||||
this.playerPositionForAudio = playerPositionForAudio;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder easeType(@Nullable CameraEaseType easeType) {
|
||||
this.easeType = easeType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder easeSeconds(float easeSeconds) {
|
||||
if (easeSeconds < 0) {
|
||||
throw new IllegalArgumentException("Camera ease duration cannot be negative!");
|
||||
}
|
||||
this.easeSeconds = easeSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder position(@NonNull Vector3f position) {
|
||||
Objects.requireNonNull(position, "camera position cannot be null!");
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder rotationX(int rotationX) {
|
||||
if (rotationX < -90 || rotationX > 90) {
|
||||
throw new IllegalArgumentException("x-axis rotation needs to be between -90 and 90 degrees.");
|
||||
}
|
||||
this.rotationX = rotationX;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder rotationY(int rotationY) {
|
||||
this.rotationY = rotationY;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition.Builder facingPosition(@Nullable Vector3f facingPosition) {
|
||||
this.facingPosition = facingPosition;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition build() {
|
||||
if (easeSeconds > 0 && easeType == null) {
|
||||
throw new IllegalArgumentException("Camera ease type cannot be null if ease duration is greater than 0");
|
||||
}
|
||||
|
||||
Objects.requireNonNull(position, "camera position must be non null!");
|
||||
return new GeyserCameraPosition(cameraFade, renderPlayerEffects, playerPositionForAudio, easeType, easeSeconds, position, rotationX, rotationY, facingPosition);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.ContainerClosePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.inventory.Container;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.LecternContainer;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
@ -151,13 +153,27 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||
|
||||
@Override
|
||||
public void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||
if (((Container) inventory).isUsingRealBlock()) {
|
||||
// No need to reset a block since we didn't change any blocks
|
||||
// But send a container close packet because we aren't destroying the original.
|
||||
ContainerClosePacket packet = new ContainerClosePacket();
|
||||
packet.setId((byte) inventory.getBedrockId());
|
||||
packet.setServerInitiated(true);
|
||||
session.sendUpstreamPacket(packet);
|
||||
if (inventory instanceof Container container) {
|
||||
if (container.isUsingRealBlock() && !(inventory instanceof LecternContainer)) {
|
||||
// No need to reset a block since we didn't change any blocks
|
||||
// But send a container close packet because we aren't destroying the original.
|
||||
ContainerClosePacket packet = new ContainerClosePacket();
|
||||
packet.setId((byte) inventory.getBedrockId());
|
||||
packet.setServerInitiated(true);
|
||||
session.sendUpstreamPacket(packet);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().warning("Tried to close a non-container inventory in a block inventory holder! ");
|
||||
if (GeyserImpl.getInstance().getLogger().isDebug()) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Current inventory: " + inventory);
|
||||
GeyserImpl.getInstance().getLogger().debug("Open inventory: " + session.getOpenInventory());
|
||||
}
|
||||
// Try to save ourselves? maybe?
|
||||
// https://github.com/GeyserMC/Geyser/issues/4141
|
||||
// TODO: improve once this issue is pinned down properly
|
||||
session.setOpenInventory(null);
|
||||
session.setInventoryTranslator(InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ public class StoredItemMappings {
|
|||
private final ItemMapping upgradeTemplate;
|
||||
private final ItemMapping wheat;
|
||||
private final ItemMapping writableBook;
|
||||
private final ItemMapping writtenBook;
|
||||
|
||||
public StoredItemMappings(Map<Item, ItemMapping> itemMappings) {
|
||||
this.bamboo = load(itemMappings, Items.BAMBOO);
|
||||
|
@ -68,6 +69,7 @@ public class StoredItemMappings {
|
|||
this.upgradeTemplate = load(itemMappings, Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE);
|
||||
this.wheat = load(itemMappings, Items.WHEAT);
|
||||
this.writableBook = load(itemMappings, Items.WRITABLE_BOOK);
|
||||
this.writtenBook = load(itemMappings, Items.WRITTEN_BOOK);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
|
|
@ -115,7 +115,7 @@ public class GeyserCustomItemData implements CustomItemData {
|
|||
return tags;
|
||||
}
|
||||
|
||||
public static class CustomItemDataBuilder implements Builder {
|
||||
public static class Builder implements CustomItemData.Builder {
|
||||
protected String name = null;
|
||||
protected CustomItemOptions customItemOptions = null;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public record GeyserCustomItemOptions(TriState unbreakable,
|
|||
boolean defaultItem) implements CustomItemOptions {
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static class CustomItemOptionsBuilder implements CustomItemOptions.Builder {
|
||||
public static class Builder implements CustomItemOptions.Builder {
|
||||
private TriState unbreakable = TriState.NOT_SET;
|
||||
private OptionalInt customModelData = OptionalInt.empty();
|
||||
private OptionalInt damagePredicate = OptionalInt.empty();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue