Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-16 04:50:07 +01:00
Merge remote-tracking branch 'origin/master' into feature/floodgate-merge
# Conflicts: # bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java # bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricMod.java # bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java # bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java # bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java # bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/floodgate/FloodgateModule.java # common/src/main/java/org/geysermc/floodgate/crypto/AesKeyProducer.java # common/src/main/java/org/geysermc/floodgate/crypto/FloodgateCipher.java # common/src/main/java/org/geysermc/floodgate/news/NewsItem.java # common/src/main/java/org/geysermc/floodgate/news/NewsItemMessage.java # common/src/main/java/org/geysermc/floodgate/news/NewsType.java # common/src/main/java/org/geysermc/floodgate/news/data/BuildSpecificData.java # common/src/main/java/org/geysermc/floodgate/news/data/ConfigSpecificData.java # common/src/main/java/org/geysermc/floodgate/util/BedrockData.java # common/src/main/java/org/geysermc/floodgate/util/LinkedPlayer.java # common/src/main/java/org/geysermc/floodgate/util/WebsocketEventType.java # core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java # core/src/main/java/org/geysermc/geyser/GeyserImpl.java # core/src/main/java/org/geysermc/geyser/floodgate/FloodgateProvider.java # core/src/main/java/org/geysermc/geyser/floodgate/GeyserLoadStage.java # core/src/main/java/org/geysermc/geyser/floodgate/NoFloodgateProvider.java # core/src/main/java/org/geysermc/geyser/network/netty/LocalSession.java # core/src/main/java/org/geysermc/geyser/session/GeyserSession.java # gradle.properties
Dieser Commit ist enthalten in:
Commit
c8fd024e4a
@ -19,32 +19,28 @@ public class LongClassName {
|
||||
public int nameWithMultipleWords = 0;
|
||||
|
||||
/**
|
||||
* Javadoc comment to explain what a function does.
|
||||
*/
|
||||
* Javadoc comment to explain what a function does.
|
||||
*/
|
||||
@RandomAnnotation(stuff = true, moreStuff = "might exist")
|
||||
public void applyStuff() {
|
||||
Variable variable = new Variable();
|
||||
Variable otherVariable = new Variable();
|
||||
|
||||
if (condition) {
|
||||
// Do stuff.
|
||||
// Do stuff.
|
||||
} else if (anotherCondition) {
|
||||
// Do something else.
|
||||
// Do something else.
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
case 0:
|
||||
stuff();
|
||||
break;
|
||||
case 1:
|
||||
differentStuff();
|
||||
break;
|
||||
case 0 -> stuff();
|
||||
case 1 -> differentStuff();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Make sure to comment your code where possible.
|
||||
Make sure to comment your code where possible. To mark nullable methods, use `@Nullable` (and subsequently, `@NonNull`) from the `org.checkerframework.checker.nullness.qual` package.
|
||||
|
||||
The nature of our software requires a lot of arrays and maps to be stored - where possible, use Fastutil's specialized maps. For example, if you're storing block state translations, use an `Int2IntMap`.
|
||||
|
||||
|
28
Jenkinsfile
vendored
28
Jenkinsfile
vendored
@ -1,28 +0,0 @@
|
||||
pipeline {
|
||||
agent any
|
||||
tools {
|
||||
gradle 'Gradle 7'
|
||||
jdk 'Java 17'
|
||||
}
|
||||
options {
|
||||
buildDiscarder(logRotator(artifactNumToKeepStr: '20'))
|
||||
}
|
||||
stages {
|
||||
stage ('Build') {
|
||||
steps {
|
||||
sh 'git submodule update --init --recursive'
|
||||
rtGradleRun(
|
||||
usesPlugin: true,
|
||||
tool: 'Gradle 7',
|
||||
buildFile: 'build.gradle.kts',
|
||||
tasks: 'clean build',
|
||||
)
|
||||
}
|
||||
post {
|
||||
success {
|
||||
archiveArtifacts artifacts: 'bootstrap/**/build/libs/Geyser-*.jar', fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.0 - 1.20.32 and Minecraft Java 1.20.2
|
||||
### 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.
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.processor;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
@ -159,7 +161,7 @@ public class ClassProcessor extends AbstractProcessor {
|
||||
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Completed processing for " + this.annotationClassName);
|
||||
}
|
||||
|
||||
private BufferedReader createReader() throws IOException {
|
||||
private @Nullable BufferedReader createReader() throws IOException {
|
||||
if (this.outputPath != null) {
|
||||
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Reading existing " + this.annotationClassName + " list from " + this.outputPath);
|
||||
return Files.newBufferedReader(this.outputPath);
|
||||
|
@ -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;
|
||||
@ -77,6 +79,7 @@ public interface GeyserApi extends GeyserApiBase {
|
||||
* @param apiClass the builder class
|
||||
* @param <R> the implementation type
|
||||
* @param <T> the API type
|
||||
* @throws IllegalArgumentException if there is no provider for the specified API class
|
||||
* @return the builder instance
|
||||
*/
|
||||
@NonNull
|
||||
@ -133,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.
|
||||
*
|
||||
|
148
api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraData.java
Normale Datei
148
api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraData.java
Normale Datei
@ -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,7 +25,10 @@
|
||||
|
||||
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;
|
||||
ROTATIONAL
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ public interface CustomBlockComponents {
|
||||
|
||||
Builder placeAir(boolean placeAir);
|
||||
|
||||
Builder tags(Set<String> tags);
|
||||
Builder tags(@Nullable Set<String> tags);
|
||||
|
||||
CustomBlockComponents build();
|
||||
}
|
||||
|
@ -25,11 +25,11 @@
|
||||
|
||||
package org.geysermc.geyser.api.block.custom.component;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Set;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* This class is used to store conditions for a placement filter for a custom block.
|
||||
*
|
||||
@ -43,7 +43,7 @@ public record PlacementConditions(@NonNull Set<Face> allowedFaces, @NonNull Link
|
||||
NORTH,
|
||||
SOUTH,
|
||||
WEST,
|
||||
EAST;
|
||||
EAST
|
||||
}
|
||||
|
||||
public enum BlockFilterType {
|
||||
|
@ -39,7 +39,7 @@ public interface JavaBlockState {
|
||||
*
|
||||
* @return whether the block state is waterlogged
|
||||
*/
|
||||
@NonNull boolean waterlogged();
|
||||
boolean waterlogged();
|
||||
|
||||
/**
|
||||
* Gets the collision of the block state
|
||||
@ -53,7 +53,7 @@ public interface JavaBlockState {
|
||||
*
|
||||
* @return whether the block state can be broken with hand
|
||||
*/
|
||||
@NonNull boolean canBreakWithHand();
|
||||
boolean canBreakWithHand();
|
||||
|
||||
/**
|
||||
* Gets the pick item of the block state
|
||||
@ -74,7 +74,7 @@ public interface JavaBlockState {
|
||||
*
|
||||
* @return whether the block state has block entity
|
||||
*/
|
||||
@Nullable boolean hasBlockEntity();
|
||||
boolean hasBlockEntity();
|
||||
|
||||
/**
|
||||
* Creates a new {@link JavaBlockState.Builder} instance
|
||||
@ -94,17 +94,17 @@ public interface JavaBlockState {
|
||||
|
||||
Builder blockHardness(@NonNegative float blockHardness);
|
||||
|
||||
Builder waterlogged(@NonNull boolean waterlogged);
|
||||
Builder waterlogged(boolean waterlogged);
|
||||
|
||||
Builder collision(@NonNull JavaBoundingBox[] collision);
|
||||
|
||||
Builder canBreakWithHand(@NonNull boolean canBreakWithHand);
|
||||
Builder canBreakWithHand(boolean canBreakWithHand);
|
||||
|
||||
Builder pickItem(@Nullable String pickItem);
|
||||
|
||||
Builder pistonBehavior(@Nullable String pistonBehavior);
|
||||
|
||||
Builder hasBlockEntity(@Nullable boolean hasBlockEntity);
|
||||
Builder hasBlockEntity(boolean hasBlockEntity);
|
||||
|
||||
JavaBlockState build();
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
package org.geysermc.geyser.api.block.custom.nonvanilla;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
public record JavaBoundingBox(@NonNull double middleX, @NonNull double middleY, @NonNull double middleZ, @NonNull double sizeX, @NonNull double sizeY, @NonNull double sizeZ) {
|
||||
public record JavaBoundingBox(double middleX, double middleY, double middleZ, double sizeX, double sizeY, double sizeZ) {
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
84
api/src/main/java/org/geysermc/geyser/api/entity/EntityData.java
Normale Datei
84
api/src/main/java/org/geysermc/geyser/api/entity/EntityData.java
Normale Datei
@ -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();
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ package org.geysermc.geyser.api.event;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.event.Event;
|
||||
import org.geysermc.event.bus.OwnedEventBus;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -27,7 +27,6 @@ package org.geysermc.geyser.api.event;
|
||||
|
||||
import org.geysermc.event.Event;
|
||||
import org.geysermc.event.subscribe.OwnedSubscriber;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
|
||||
/**
|
||||
* Represents a subscribed listener to a {@link Event}. Wraps around
|
||||
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.event.bedrock;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||
|
||||
/**
|
||||
* Called when a Geyser session disconnects.
|
||||
*/
|
||||
public class SessionDisconnectEvent extends ConnectionEvent {
|
||||
private String disconnectReason;
|
||||
|
||||
public SessionDisconnectEvent(@NonNull GeyserConnection connection, @NonNull String reason) {
|
||||
super(connection);
|
||||
this.disconnectReason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the disconnect reason.
|
||||
*
|
||||
* @return the reason for the disconnect
|
||||
*/
|
||||
public @NonNull String disconnectReason() {
|
||||
return disconnectReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the disconnect reason, thereby overriding th original reason.
|
||||
*
|
||||
* @param disconnectReason the reason for the disconnect
|
||||
*/
|
||||
public void disconnectReason(@NonNull String disconnectReason) {
|
||||
this.disconnectReason = disconnectReason;
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* Called whenever Geyser gets pinged
|
||||
*
|
||||
* <p>
|
||||
* This event allows you to modify/obtain the MOTD, maximum player count, and current number of players online,
|
||||
* Geyser will reply to the client with what was given.
|
||||
*/
|
||||
|
@ -33,7 +33,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* Called when commands are defined within Geyser.
|
||||
*
|
||||
* <p>
|
||||
* This event allows you to register new commands using the {@link #register(Command)}
|
||||
* method and retrieve the default commands defined.
|
||||
*/
|
||||
|
@ -28,13 +28,12 @@ package org.geysermc.geyser.api.event.lifecycle;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockItem;
|
||||
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
|
||||
import org.geysermc.event.Event;
|
||||
|
||||
/**
|
||||
* Called on Geyser's startup when looking for custom blocks. Custom blocks must be registered through this event.
|
||||
*
|
||||
* <p>
|
||||
* This event will not be called if the "add-non-bedrock-items" setting is disabled in the Geyser config.
|
||||
*/
|
||||
public abstract class GeyserDefineCustomBlocksEvent implements Event {
|
||||
@ -48,8 +47,8 @@ public abstract class GeyserDefineCustomBlocksEvent implements Event {
|
||||
/**
|
||||
* Registers the given {@link CustomBlockState} as an override for the
|
||||
* given java state identifier
|
||||
* Java state identifiers are listed in
|
||||
* https://raw.githubusercontent.com/GeyserMC/mappings/master/blocks.json
|
||||
* Java state identifiers are listed
|
||||
* <a href="https://raw.githubusercontent.com/GeyserMC/mappings/master/blocks.json">here</a>
|
||||
*
|
||||
* @param javaIdentifier the java state identifier to override
|
||||
* @param customBlockState the custom block state with which to override java state identifier
|
||||
|
@ -36,7 +36,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* Called on Geyser's startup when looking for custom items. Custom items must be registered through this event.
|
||||
*
|
||||
* <p>
|
||||
* This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config.
|
||||
*/
|
||||
public interface GeyserDefineCustomItemsEvent extends Event {
|
||||
|
@ -5,7 +5,7 @@ import org.geysermc.event.Event;
|
||||
|
||||
/**
|
||||
* Called on Geyser's startup when looking for custom skulls. Custom skulls must be registered through this event.
|
||||
*
|
||||
* <p>
|
||||
* This event will not be called if the "add-non-bedrock-items" setting is disabled in the Geyser config.
|
||||
*/
|
||||
public abstract class GeyserDefineCustomSkullsEvent implements Event {
|
||||
|
@ -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 {
|
||||
}
|
@ -25,10 +25,16 @@
|
||||
|
||||
package org.geysermc.geyser.api.extension.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* Thrown when an extension's description is invalid.
|
||||
*/
|
||||
public class InvalidDescriptionException extends Exception {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public InvalidDescriptionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
@ -25,10 +25,16 @@
|
||||
|
||||
package org.geysermc.geyser.api.extension.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* Thrown when an extension is invalid.
|
||||
*/
|
||||
public class InvalidExtensionException extends Exception {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public InvalidExtensionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This is used to store data for a custom item.
|
||||
*/
|
||||
@ -89,6 +91,14 @@ public interface CustomItemData {
|
||||
*/
|
||||
@Nullable CustomRenderOffsets renderOffsets();
|
||||
|
||||
/**
|
||||
* Gets the item's set of tags that can be used in Molang.
|
||||
* Equivalent to "tag:some_tag"
|
||||
*
|
||||
* @return the item's tags, if they exist
|
||||
*/
|
||||
@NonNull Set<String> tags();
|
||||
|
||||
static CustomItemData.Builder builder() {
|
||||
return GeyserApi.api().provider(CustomItemData.Builder.class);
|
||||
}
|
||||
@ -113,6 +123,8 @@ public interface CustomItemData {
|
||||
|
||||
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
||||
|
||||
Builder tags(@Nullable Set<String> tags);
|
||||
|
||||
CustomItemData build();
|
||||
}
|
||||
}
|
||||
|
@ -239,6 +239,9 @@ public interface NonVanillaCustomItemData extends CustomItemData {
|
||||
@Override
|
||||
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
||||
|
||||
@Override
|
||||
Builder tags(@Nullable Set<String> tags);
|
||||
|
||||
NonVanillaCustomItemData build();
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -51,7 +51,7 @@ public enum CreativeCategory {
|
||||
*
|
||||
* @return the name of the category
|
||||
*/
|
||||
@NonNull public String internalName() {
|
||||
public @NonNull String internalName() {
|
||||
return internalName;
|
||||
}
|
||||
|
||||
|
49
api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java
Normale Datei
49
api/src/main/java/org/geysermc/geyser/api/util/MinecraftVersion.java
Normale Datei
@ -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();
|
||||
}
|
@ -30,7 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* This is a way to represent a boolean, but with a non set value added.
|
||||
* This class was inspired by adventure's version https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/TriState.java
|
||||
* This class was inspired by adventure's <a href="https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/TriState.java">TriState</a>
|
||||
*/
|
||||
public enum TriState {
|
||||
/**
|
||||
|
@ -32,18 +32,26 @@ import net.md_5.bungee.protocol.packet.LoginSuccess;
|
||||
import net.md_5.bungee.protocol.packet.SetCompression;
|
||||
|
||||
public class GeyserBungeeCompressionDisabler extends ChannelOutboundHandlerAdapter {
|
||||
private boolean compressionDisabled = false;
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
if (!(msg instanceof SetCompression)) {
|
||||
if (msg instanceof LoginSuccess) {
|
||||
// We're past the point that compression can be enabled
|
||||
// Fixes https://github.com/GeyserMC/Geyser/issues/4281
|
||||
// The server may send a LoginDisconnect packet after compression is set.
|
||||
if (!compressionDisabled) {
|
||||
if (ctx.pipeline().get("compress") != null) {
|
||||
ctx.pipeline().remove("compress");
|
||||
compressionDisabled = true;
|
||||
}
|
||||
if (ctx.pipeline().get("decompress") != null) {
|
||||
ctx.pipeline().remove("decompress");
|
||||
compressionDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg instanceof LoginSuccess) {
|
||||
// We're past the point that compression can be enabled
|
||||
ctx.pipeline().remove(this);
|
||||
}
|
||||
super.write(ctx, msg, promise);
|
||||
|
@ -30,6 +30,7 @@ import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -51,7 +52,8 @@ public class GeyserBungeeDumpInfo extends BootstrapDumpInfo {
|
||||
this.plugins = new ArrayList<>();
|
||||
|
||||
for (net.md_5.bungee.api.config.ListenerInfo listener : proxy.getConfig().getListeners()) {
|
||||
this.listeners.add(new ListenerInfo(listener.getHost().getHostString(), listener.getHost().getPort()));
|
||||
InetSocketAddress address = (InetSocketAddress) listener.getSocketAddress();
|
||||
this.listeners.add(new ListenerInfo(address.getHostString(), address.getPort()));
|
||||
}
|
||||
|
||||
for (Plugin plugin : proxy.getPluginManager().getPlugins()) {
|
||||
|
@ -39,6 +39,7 @@ import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.network.netty.GeyserInjector;
|
||||
@ -125,7 +126,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
||||
.channel(LocalServerChannelWrapper.class)
|
||||
.childHandler(new ChannelInitializer<>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
protected void initChannel(@NonNull Channel ch) throws Exception {
|
||||
if (proxy.getConfig().getServers() == null) {
|
||||
// Proxy hasn't finished loading all plugins - it loads the config after all plugins
|
||||
// Probably doesn't need to be translatable?
|
||||
|
@ -35,12 +35,12 @@ import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.ping.GeyserPingInfo;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@ -61,16 +61,11 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
||||
}));
|
||||
ProxyPingEvent event = future.join();
|
||||
ServerPing response = event.getResponse();
|
||||
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(
|
||||
return new GeyserPingInfo(
|
||||
response.getDescriptionComponent().toLegacyText(),
|
||||
new GeyserPingInfo.Players(response.getPlayers().getMax(), response.getPlayers().getOnline()),
|
||||
new GeyserPingInfo.Version(response.getVersion().getName(), response.getVersion().getProtocol())
|
||||
response.getPlayers().getMax(),
|
||||
response.getPlayers().getOnline()
|
||||
);
|
||||
if (event.getResponse().getPlayers().getSample() != null) {
|
||||
Arrays.stream(event.getResponse().getPlayers().getSample()).forEach(proxiedPlayer ->
|
||||
geyserPingInfo.getPlayerList().add(proxiedPlayer.getName()));
|
||||
}
|
||||
return geyserPingInfo;
|
||||
}
|
||||
|
||||
// This is static so pending connection can use it
|
||||
@ -110,7 +105,7 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getVirtualHost() {
|
||||
public @Nullable InetSocketAddress getVirtualHost() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -30,14 +30,15 @@ import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
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.floodgate.core.skin.SkinApplier;
|
||||
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.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;
|
||||
@ -46,7 +47,6 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -74,12 +74,17 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
onGeyserInitialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserInitialize() {
|
||||
GeyserLocale.init(this);
|
||||
|
||||
// Copied from ViaVersion.
|
||||
// https://github.com/ViaVersion/ViaVersion/blob/b8072aad86695cc8ec6f5e4103e43baf3abf6cc5/bungee/src/main/java/us/myles/ViaVersion/BungeePlugin.java#L43
|
||||
try {
|
||||
ProtocolConstants.class.getField("MINECRAFT_1_20_2");
|
||||
ProtocolConstants.class.getField("MINECRAFT_1_20_3");
|
||||
} catch (NoSuchFieldException e) {
|
||||
getLogger().warning(" / \\");
|
||||
getLogger().warning(" / \\");
|
||||
@ -90,29 +95,63 @@ 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, null);
|
||||
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 {
|
||||
// For consistency with other platforms - create command manager before GeyserImpl#start()
|
||||
// This ensures the command events are called before the item/block ones are
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
}
|
||||
|
||||
// Force-disable query if enabled, or else Geyser won't enable
|
||||
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
|
||||
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
|
||||
@ -139,48 +178,21 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
getProxy().getPluginManager().registerListener(this, new BungeeHybridListener());
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
GeyserImpl.start();
|
||||
|
||||
@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();
|
||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
} else {
|
||||
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
||||
}
|
||||
|
||||
// No need to re-register commands or re-init injector when reloading
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners");
|
||||
listenersField.setAccessible(true);
|
||||
|
||||
Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
|
||||
if (listeners.isEmpty()) {
|
||||
this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS);
|
||||
} else {
|
||||
this.awaitStartupCompletion(++tries);
|
||||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void postStartup() {
|
||||
GeyserImpl.start();
|
||||
|
||||
this.geyserInjector = new GeyserBungeeInjector(this);
|
||||
this.geyserInjector.initializeLocalChannel(this);
|
||||
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
|
||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands()));
|
||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||
Map<String, Command> commands = entry.getValue();
|
||||
@ -190,16 +202,17 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
|
||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands));
|
||||
}
|
||||
}
|
||||
|
||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
} else {
|
||||
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
||||
@Override
|
||||
public void onGeyserDisable() {
|
||||
if (geyser != null) {
|
||||
geyser.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
public void onGeyserShutdown() {
|
||||
if (geyser != null) {
|
||||
geyser.shutdown();
|
||||
}
|
||||
@ -208,6 +221,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.onGeyserShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserBungeeConfiguration getGeyserConfig() {
|
||||
return geyserConfig;
|
||||
@ -249,7 +267,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
return this.geyserInjector.getServerSocketAddress();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
@Override
|
||||
public String getServerBindAddress() {
|
||||
return findCompatibleListener().map(InetSocketAddress::getHostString).orElse("");
|
||||
@ -276,6 +294,22 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
.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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkinApplier createSkinApplier() {
|
||||
// new BungeePlatform(this); // TODO hack to ensure ReflectionUtils prefix is applied and I don't forget about dealing with it
|
||||
|
@ -29,6 +29,7 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
@ -50,7 +51,7 @@ public class BungeeCommandSource implements GeyserCommandSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
public void sendMessage(@NonNull String message) {
|
||||
handle.sendMessage(TextComponent.fromLegacyText(message));
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,6 @@ plugins {
|
||||
id("com.modrinth.minotaur") version "2.+"
|
||||
}
|
||||
|
||||
java {
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
dependencies {
|
||||
//to change the versions see the gradle.properties file
|
||||
minecraft(libs.fabric.minecraft)
|
||||
@ -118,7 +113,7 @@ modrinth {
|
||||
syncBodyFrom.set(rootProject.file("README.md").readText())
|
||||
|
||||
uploadFile.set(tasks.getByPath("remapModrinthJar"))
|
||||
gameVersions.addAll("1.20.2")
|
||||
gameVersions.addAll("1.20.4")
|
||||
|
||||
loaders.add("fabric")
|
||||
failSilently.set(true)
|
||||
|
@ -43,21 +43,27 @@ import java.util.stream.Collectors;
|
||||
@Getter
|
||||
public class GeyserFabricDumpInfo extends BootstrapDumpInfo {
|
||||
|
||||
private String platformVersion = null;
|
||||
private final String platformName;
|
||||
private String platformVersion;
|
||||
private final String minecraftVersion;
|
||||
private final EnvType environmentType;
|
||||
|
||||
@AsteriskSerializer.Asterisk(isIp = true)
|
||||
private final String serverIP;
|
||||
private final int serverPort;
|
||||
private final boolean onlineMode;
|
||||
private final List<ModInfo> mods;
|
||||
|
||||
public GeyserFabricDumpInfo(MinecraftServer server) {
|
||||
this.platformName = server.getServerModName();
|
||||
FabricLoader.getInstance().getModContainer("fabricloader").ifPresent(mod ->
|
||||
this.platformVersion = mod.getMetadata().getVersion().getFriendlyString());
|
||||
|
||||
this.minecraftVersion = server.getServerVersion();
|
||||
this.environmentType = FabricLoader.getInstance().getEnvironmentType();
|
||||
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
|
||||
this.serverPort = server.getPort();
|
||||
this.onlineMode = server.usesAuthentication();
|
||||
this.mods = new ArrayList<>();
|
||||
|
||||
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {
|
||||
|
@ -27,6 +27,8 @@ package org.geysermc.geyser.platform.fabric;
|
||||
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
@ -37,6 +39,8 @@ import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
@ -54,8 +58,6 @@ import org.geysermc.geyser.platform.fabric.command.GeyserFabricCommandExecutor;
|
||||
import org.geysermc.geyser.platform.fabric.world.GeyserFabricWorldManager;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -66,13 +68,14 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||
|
||||
@Getter
|
||||
private static GeyserFabricMod instance;
|
||||
|
||||
private boolean reloading;
|
||||
|
||||
private GeyserImpl geyser;
|
||||
private ModContainer mod;
|
||||
private Path dataFolder;
|
||||
|
||||
@Setter
|
||||
private MinecraftServer server;
|
||||
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
@ -85,69 +88,58 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||
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);
|
||||
}
|
||||
onGeyserInitialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
dataFolder = FabricLoader.getInstance().getConfigDir().resolve("Geyser-Fabric");
|
||||
if (!dataFolder.toFile().exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dataFolder.toFile().mkdir();
|
||||
public void onGeyserInitialize() {
|
||||
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.server = server;
|
||||
onGeyserEnable();
|
||||
});
|
||||
}
|
||||
|
||||
// Init dataFolder first as local language overrides call getConfigFolder()
|
||||
GeyserLocale.init(this);
|
||||
// These are only registered once
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onGeyserShutdown());
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserFabricUpdateListener.onPlayReady(handler));
|
||||
|
||||
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();
|
||||
dataFolder = FabricLoader.getInstance().getConfigDir().resolve("Geyser-Fabric");
|
||||
GeyserLocale.init(this);
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.geyserLogger = new GeyserFabricLogger(geyserConfig.isDebugMode());
|
||||
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
|
||||
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this, null);
|
||||
|
||||
if (server == null) {
|
||||
// Server has yet to start
|
||||
// Register onDisable so players are properly kicked
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onDisable());
|
||||
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserFabricUpdateListener.onPlayReady(handler));
|
||||
} else {
|
||||
// Server has started and this is a reload
|
||||
startGeyser(this.server);
|
||||
reloading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize core Geyser.
|
||||
* A function, as it needs to be called in different places depending on if Geyser is being reloaded or not.
|
||||
*
|
||||
* @param server The minecraft server.
|
||||
*/
|
||||
public void startGeyser(MinecraftServer server) {
|
||||
this.server = server;
|
||||
@Override
|
||||
public void onGeyserEnable() {
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
} else {
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
}
|
||||
|
||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
} else {
|
||||
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
|
||||
}
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
// No need to re-register commands, or re-recreate the world manager when reloading
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.geyserWorldManager = new GeyserFabricWorldManager(server);
|
||||
|
||||
@ -197,14 +189,19 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
public void onGeyserDisable() {
|
||||
if (geyser != null) {
|
||||
geyser.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserShutdown() {
|
||||
if (geyser != null) {
|
||||
geyser.shutdown();
|
||||
geyser = null;
|
||||
}
|
||||
if (!reloading) {
|
||||
this.server = null;
|
||||
}
|
||||
this.server = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -247,7 +244,7 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||
return this.server.getServerVersion();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
@Override
|
||||
public String getServerBindAddress() {
|
||||
String ip = this.server.getLocalIp();
|
||||
@ -287,11 +284,22 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
public void setReloading(boolean reloading) {
|
||||
this.reloading = reloading;
|
||||
}
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean loadConfig() {
|
||||
try {
|
||||
if (!dataFolder.toFile().exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dataFolder.toFile().mkdir();
|
||||
}
|
||||
|
||||
public static GeyserFabricMod getInstance() {
|
||||
return instance;
|
||||
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);
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
LogManager.getLogger("geyser-fabric").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 lombok.AllArgsConstructor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.PacketSendListener;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket;
|
||||
import net.minecraft.network.protocol.status.ServerStatus;
|
||||
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.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;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class ModPingPassthrough implements IGeyserPingPassthrough {
|
||||
|
||||
private static final GsonComponentSerializer GSON_SERIALIZER = GsonComponentSerializer.gson();
|
||||
private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection();
|
||||
|
||||
private final MinecraftServer server;
|
||||
private final GeyserLogger logger;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
|
||||
ServerStatus status = server.getStatus();
|
||||
if (status == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
StatusInterceptor connection = new StatusInterceptor();
|
||||
ServerStatusPacketListener statusPacketListener = new ServerStatusPacketListenerImpl(status, connection);
|
||||
|
||||
statusPacketListener.handleStatusRequest(new ServerboundStatusRequestPacket());
|
||||
// mods like MiniMOTD (that inject into the above method) have now processed the response
|
||||
status = Objects.requireNonNull(connection.status, "status response");
|
||||
} catch (Exception e) {
|
||||
if (logger.isDebug()) {
|
||||
logger.debug("Failed to listen for modified ServerStatus: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
String jsonDescription = net.minecraft.network.chat.Component.Serializer.toJson(status.description());
|
||||
String legacyDescription = LEGACY_SERIALIZER.serialize(GSON_SERIALIZER.deserializeOr(jsonDescription, Component.empty()));
|
||||
|
||||
return new GeyserPingInfo(
|
||||
legacyDescription,
|
||||
status.players().map(ServerStatus.Players::max).orElse(1),
|
||||
status.players().map(ServerStatus.Players::online).orElse(0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Connection that intercepts the status response right before it is sent
|
||||
*/
|
||||
private static class StatusInterceptor extends Connection {
|
||||
|
||||
ServerStatus status;
|
||||
|
||||
StatusInterceptor() {
|
||||
super(PacketFlow.SERVERBOUND); // we are the server.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Packet<?> packet, @Nullable PacketSendListener packetSendListener, boolean bl) {
|
||||
if (packet instanceof ClientboundStatusResponsePacket statusResponse) {
|
||||
status = statusResponse.status();
|
||||
}
|
||||
super.send(packet, packetSendListener, bl);
|
||||
}
|
||||
}
|
||||
}
|
@ -30,11 +30,12 @@ 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.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Objects;
|
||||
|
||||
public class FabricCommandSender implements GeyserCommandSource {
|
||||
|
||||
@ -50,7 +51,7 @@ public class FabricCommandSender implements GeyserCommandSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(@Nonnull String message) {
|
||||
public void sendMessage(@NonNull String message) {
|
||||
if (source.getEntity() instanceof ServerPlayer) {
|
||||
((ServerPlayer) source.getEntity()).displayClientMessage(Component.literal(message), false);
|
||||
} else {
|
||||
@ -62,7 +63,7 @@ public class FabricCommandSender implements GeyserCommandSource {
|
||||
public void sendMessage(net.kyori.adventure.text.Component message) {
|
||||
if (source.getEntity() instanceof ServerPlayer player) {
|
||||
String decoded = GsonComponentSerializer.gson().serialize(message);
|
||||
player.displayClientMessage(Component.Serializer.fromJson(decoded), false);
|
||||
player.displayClientMessage(Objects.requireNonNull(Component.Serializer.fromJson(decoded)), false);
|
||||
return;
|
||||
}
|
||||
GeyserCommandSource.super.sendMessage(message);
|
||||
|
@ -32,7 +32,6 @@ import net.minecraft.commands.CommandSourceStack;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
@ -52,21 +51,18 @@ public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public int run(CommandContext context) {
|
||||
public int run(CommandContext<CommandSourceStack> context) {
|
||||
return runWithArgs(context, "");
|
||||
}
|
||||
|
||||
public int runWithArgs(CommandContext context, String args) {
|
||||
CommandSourceStack source = (CommandSourceStack) context.getSource();
|
||||
public int runWithArgs(CommandContext<CommandSourceStack> context, String args) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
FabricCommandSender sender = new FabricCommandSender(source);
|
||||
GeyserSession session = getGeyserSession(sender);
|
||||
if (!testPermission(source)) {
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
|
||||
return 0;
|
||||
}
|
||||
if (this.command.name().equals("reload")) {
|
||||
GeyserFabricMod.getInstance().setReloading(true);
|
||||
}
|
||||
|
||||
if (command.isBedrockOnly() && session == null) {
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.locale()));
|
||||
|
@ -32,6 +32,7 @@ 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.text.GeyserLocale;
|
||||
@ -42,6 +43,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@Mixin(IntegratedServer.class)
|
||||
public class IntegratedServerMixin implements GeyserServerPortGetter {
|
||||
@ -54,12 +57,14 @@ 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);
|
||||
GeyserFabricMod.getInstance().setServer((MinecraftServer) (Object) this);
|
||||
GeyserFabricMod.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
|
||||
Objects.requireNonNull(this.minecraft.player);
|
||||
this.minecraft.player.displayClientMessage(Component.literal(GeyserLocale.getPlayerLocaleString("geyser.core.start",
|
||||
this.minecraft.options.languageCode, "localhost", String.valueOf(this.publishedPort))), false);
|
||||
this.minecraft.options.languageCode, "localhost", String.valueOf(GeyserImpl.getInstance().bedrockListener().port()))), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
@ -48,9 +49,9 @@ import org.geysermc.geyser.level.GeyserWorldManager;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
@ -73,9 +74,11 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
return;
|
||||
}
|
||||
|
||||
//noinspection resource - level() is just a getter
|
||||
LevelChunk chunk = player.level().getChunk(x, z);
|
||||
final int chunkBlockX = x << 4;
|
||||
final int chunkBlockZ = z << 4;
|
||||
//noinspection ForLoopReplaceableByForEach - avoid constructing iterator
|
||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||
BlockEntityInfo blockEntityInfo = blockEntityInfos.get(i);
|
||||
BlockEntity blockEntity = chunk.getBlockEntity(new BlockPos(chunkBlockX + blockEntityInfo.getX(),
|
||||
@ -92,7 +95,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
//noinspection resource - level() is just a getter
|
||||
BlockEntity blockEntity = player.level().getBlockEntity(new BlockPos(x, y, z));
|
||||
sendLecternData(session, blockEntity, false);
|
||||
});
|
||||
@ -159,7 +162,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
return GameMode.byId(server.getDefaultGameType().getId());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
public CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||
CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> future = new CompletableFuture<>();
|
||||
@ -172,6 +175,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
// Don't create a new block entity if invalid
|
||||
//noinspection resource - level() is just a getter
|
||||
BlockEntity blockEntity = player.level().getChunkAt(pos).getBlockEntity(pos);
|
||||
if (blockEntity instanceof BannerBlockEntity banner) {
|
||||
// Potentially exposes other NBT data? But we need to get the NBT data for the banner patterns *and*
|
||||
@ -263,7 +267,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompound(CompoundTag compoundTag) {
|
||||
public void visitCompound(@NonNull CompoundTag compoundTag) {
|
||||
currentTag = convert(currentKey, compoundTag);
|
||||
}
|
||||
|
||||
@ -271,7 +275,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
OpenNbtTagVisitor visitor = new OpenNbtTagVisitor(name);
|
||||
for (String key : compoundTag.getAllKeys()) {
|
||||
visitor.currentKey = key;
|
||||
Tag tag = compoundTag.get(key);
|
||||
Tag tag = Objects.requireNonNull(compoundTag.get(key));
|
||||
tag.accept(visitor);
|
||||
visitor.root.put(visitor.currentTag);
|
||||
}
|
||||
@ -279,7 +283,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd(EndTag endTag) {
|
||||
public void visitEnd(@NonNull EndTag endTag) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,9 @@
|
||||
"geyser-fabric.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.14.21",
|
||||
"fabricloader": ">=0.15.2",
|
||||
"fabric": "*",
|
||||
"minecraft": ">=1.20.2",
|
||||
"minecraft": ">=1.20.4",
|
||||
"fabric-permissions-api-v0": "*"
|
||||
}
|
||||
}
|
||||
|
@ -27,13 +27,12 @@ package org.geysermc.geyser.platform.spigot;
|
||||
|
||||
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
|
||||
import com.destroystokyo.paper.network.StatusClient;
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.ping.GeyserPingInfo;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.InetSocketAddress;
|
||||
@ -51,6 +50,7 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Nullable
|
||||
@Override
|
||||
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
|
||||
@ -81,16 +81,7 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough
|
||||
players = new GeyserPingInfo.Players(event.getMaxPlayers(), event.getNumPlayers());
|
||||
}
|
||||
|
||||
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(), players,
|
||||
new GeyserPingInfo.Version(Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion()));
|
||||
|
||||
if (!event.shouldHidePlayers()) {
|
||||
for (PlayerProfile profile : event.getPlayerSample()) {
|
||||
geyserPingInfo.getPlayerList().add(profile.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return geyserPingInfo;
|
||||
return new GeyserPingInfo(event.getMotd(), players);
|
||||
} catch (Exception | LinkageError e) { // LinkageError in the event that method/constructor signatures change
|
||||
logger.debug("Error while getting Paper ping passthrough: " + e);
|
||||
return null;
|
||||
@ -99,7 +90,7 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough
|
||||
|
||||
private record GeyserStatusClient(InetSocketAddress address) implements StatusClient {
|
||||
@Override
|
||||
public @NotNull InetSocketAddress getAddress() {
|
||||
public @NonNull InetSocketAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ import org.geysermc.geyser.GeyserImpl;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -47,6 +47,7 @@ public class GeyserSpigotDumpInfo extends BootstrapDumpInfo {
|
||||
private final int serverPort;
|
||||
private final List<PluginInfo> plugins;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
GeyserSpigotDumpInfo() {
|
||||
super();
|
||||
this.platformName = Bukkit.getName();
|
||||
|
@ -32,6 +32,7 @@ import io.netty.channel.*;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.network.AuthType;
|
||||
@ -76,12 +77,10 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
||||
Object connection = null;
|
||||
// Find the class that manages network IO
|
||||
for (Method m : serverClazz.getDeclaredMethods()) {
|
||||
if (m.getReturnType() != null) {
|
||||
// First is Spigot-mapped name, second is Mojang-mapped name which is implemented as future-proofing
|
||||
if (m.getReturnType().getSimpleName().equals("ServerConnection") || m.getReturnType().getSimpleName().equals("ServerConnectionListener")) {
|
||||
if (m.getParameterTypes().length == 0) {
|
||||
connection = m.invoke(server);
|
||||
}
|
||||
// First is Spigot-mapped name, second is Mojang-mapped name which is implemented as future-proofing
|
||||
if (m.getReturnType().getSimpleName().equals("ServerConnection") || m.getReturnType().getSimpleName().equals("ServerConnectionListener")) {
|
||||
if (m.getParameterTypes().length == 0) {
|
||||
connection = m.invoke(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,7 +118,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
||||
.channel(LocalServerChannelWrapper.class)
|
||||
.childHandler(new ChannelInitializer<>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
protected void initChannel(@NonNull Channel ch) throws Exception {
|
||||
initChannel.invoke(childHandler, ch);
|
||||
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
|
||||
ch.pipeline().addAfter("encoder", "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
|
||||
@ -160,7 +159,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) {
|
||||
|
@ -30,11 +30,12 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.server.ServerListPingEvent;
|
||||
import org.bukkit.util.CachedServerIcon;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.ping.GeyserPingInfo;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
@ -45,17 +46,13 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough {
|
||||
|
||||
private final GeyserSpigotLogger logger;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
|
||||
public @Nullable GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
|
||||
try {
|
||||
ServerListPingEvent event = new GeyserPingEvent(inetSocketAddress.getAddress(), Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), Bukkit.getMaxPlayers());
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(event.getMotd(),
|
||||
new GeyserPingInfo.Players(event.getMaxPlayers(), event.getNumPlayers()),
|
||||
new GeyserPingInfo.Version(Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion()) // thanks Spigot for not exposing this, just default to latest
|
||||
);
|
||||
Bukkit.getOnlinePlayers().stream().map(Player::getName).forEach(geyserPingInfo.getPlayerList()::add);
|
||||
return geyserPingInfo;
|
||||
return new GeyserPingInfo(event.getMotd(), event.getMaxPlayers(), event.getNumPlayers());
|
||||
} catch (Exception | LinkageError e) { // LinkageError in the event that method/constructor signatures change
|
||||
logger.debug("Error while getting Bukkit ping passthrough: " + e);
|
||||
return null;
|
||||
@ -73,7 +70,7 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough {
|
||||
public void setServerIcon(CachedServerIcon icon) throws IllegalArgumentException, UnsupportedOperationException {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@NonNull
|
||||
@Override
|
||||
public Iterator<Player> iterator() throws UnsupportedOperationException {
|
||||
return Collections.emptyIterator();
|
||||
|
@ -43,13 +43,14 @@ import org.bukkit.permissions.PermissionDefault;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.geysermc.floodgate.core.skin.SkinApplier;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||
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.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
@ -67,7 +68,6 @@ import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotNativeWorld
|
||||
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -77,14 +77,11 @@ import java.net.SocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
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 GeyserSpigotCommandManager geyserCommandManager;
|
||||
private GeyserSpigotConfiguration geyserConfig;
|
||||
@ -102,6 +99,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
onGeyserInitialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserInitialize() {
|
||||
GeyserLocale.init(this);
|
||||
|
||||
try {
|
||||
@ -118,6 +120,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;
|
||||
}
|
||||
|
||||
@ -131,41 +134,38 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
|
||||
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, GeyserSpigotConfiguration.class);
|
||||
} catch (IOException ex) {
|
||||
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
Class.forName("io.netty.util.internal.ObjectPool$ObjectCreator");
|
||||
} catch (ClassNotFoundException e) {
|
||||
getLogger().severe("*********************************************");
|
||||
getLogger().severe("");
|
||||
getLogger().severe("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
|
||||
getLogger().severe("");
|
||||
getLogger().severe("*********************************************");
|
||||
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, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (this.geyserConfig == null) {
|
||||
// We failed to initialize correctly
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Bukkit.getPluginManager().getPlugin("floodgate") != null) {
|
||||
geyserLogger.severe("WHY DO YOU HAVE FLOODGATE INSTALLED!!!!!!! REMOVE IT!!!!");
|
||||
}
|
||||
@ -173,48 +173,50 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
|
||||
if (!INITIALIZED) {
|
||||
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||
// Because Bukkit locks its command map upon startup, we need to
|
||||
// add our plugin commands in onEnable, but populating the executor
|
||||
// can happen at any time (later in #onGeyserEnable())
|
||||
CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap();
|
||||
for (Extension extension : this.geyserCommandManager.extensionCommands().keySet()) {
|
||||
// Thanks again, Bukkit
|
||||
try {
|
||||
Constructor<PluginCommand> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
||||
constructor.setAccessible(true);
|
||||
|
||||
@EventHandler
|
||||
public void onServerLoaded(ServerLoadEvent event) {
|
||||
// Wait until all plugins have loaded so Geyser can start
|
||||
postStartup();
|
||||
}
|
||||
}, this);
|
||||
PluginCommand pluginCommand = constructor.newInstance(extension.description().id(), this);
|
||||
pluginCommand.setDescription("The main command for the " + extension.name() + " Geyser extension!");
|
||||
|
||||
// Because Bukkit locks its command map upon startup, we need to
|
||||
// add our plugin commands in onEnable, but populating the executor
|
||||
// can happen at any time
|
||||
CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap();
|
||||
for (Extension extension : this.geyserCommandManager.extensionCommands().keySet()) {
|
||||
// Thanks again, Bukkit
|
||||
try {
|
||||
Constructor<PluginCommand> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
||||
constructor.setAccessible(true);
|
||||
|
||||
PluginCommand pluginCommand = constructor.newInstance(extension.description().id(), this);
|
||||
pluginCommand.setDescription("The main command for the " + extension.name() + " Geyser extension!");
|
||||
|
||||
commandMap.register(extension.description().id(), "geyserext", pluginCommand);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
|
||||
this.geyserLogger.error("Failed to construct PluginCommand for extension " + extension.name(), ex);
|
||||
}
|
||||
commandMap.register(extension.description().id(), "geyserext", pluginCommand);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
|
||||
this.geyserLogger.error("Failed to construct PluginCommand for extension " + extension.name(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (INITIALIZED) {
|
||||
// Reload; continue with post startup
|
||||
postStartup();
|
||||
}
|
||||
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||
|
||||
@EventHandler
|
||||
public void onServerLoaded(ServerLoadEvent event) {
|
||||
if (event.getType() == ServerLoadEvent.LoadType.RELOAD) {
|
||||
geyser.setShuttingDown(false);
|
||||
}
|
||||
onGeyserEnable();
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
private void postStartup() {
|
||||
GeyserImpl.start();
|
||||
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);
|
||||
}
|
||||
|
||||
// Turn "(MC: 1.16.4)" into 1.16.4.
|
||||
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
||||
GeyserImpl.start();
|
||||
|
||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
@ -230,20 +232,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);
|
||||
@ -270,6 +268,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);
|
||||
@ -277,6 +276,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
}
|
||||
|
||||
PluginCommand geyserCommand = this.getCommand("geyser");
|
||||
Objects.requireNonNull(geyserCommand, "base command cannot be null");
|
||||
geyserCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands()));
|
||||
|
||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||
@ -293,72 +293,72 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
command.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands));
|
||||
}
|
||||
|
||||
if (!INITIALIZED) {
|
||||
// Register permissions so they appear in, for example, LuckPerms' UI
|
||||
// Re-registering permissions throws an error
|
||||
for (Map.Entry<String, Command> entry : geyserCommandManager.commands().entrySet()) {
|
||||
// Register permissions so they appear in, for example, LuckPerms' UI
|
||||
// Re-registering permissions throws an error
|
||||
for (Map.Entry<String, Command> entry : geyserCommandManager.commands().entrySet()) {
|
||||
Command command = entry.getValue();
|
||||
if (command.aliases().contains(entry.getKey())) {
|
||||
// Don't register aliases
|
||||
continue;
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
||||
GeyserLocale.getLocaleStringLog(command.description()),
|
||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||
}
|
||||
|
||||
// Register permissions for extension commands
|
||||
for (Map.Entry<Extension, Map<String, Command>> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||
for (Map.Entry<String, Command> entry : commandEntry.getValue().entrySet()) {
|
||||
Command command = entry.getValue();
|
||||
if (command.aliases().contains(entry.getKey())) {
|
||||
// Don't register aliases
|
||||
continue;
|
||||
}
|
||||
|
||||
if (command.permission().isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Avoid registering the same permission twice, e.g. for the extension help commands
|
||||
if (Bukkit.getPluginManager().getPermission(command.permission()) != null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Skipping permission " + command.permission() + " as it is already registered");
|
||||
continue;
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
||||
GeyserLocale.getLocaleStringLog(command.description()),
|
||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||
}
|
||||
|
||||
// Register permissions for extension commands
|
||||
for (Map.Entry<Extension, Map<String, Command>> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||
for (Map.Entry<String, Command> entry : commandEntry.getValue().entrySet()) {
|
||||
Command command = entry.getValue();
|
||||
if (command.aliases().contains(entry.getKey())) {
|
||||
// Don't register aliases
|
||||
continue;
|
||||
}
|
||||
|
||||
if (command.permission().isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Avoid registering the same permission twice, e.g. for the extension help commands
|
||||
if (Bukkit.getPluginManager().getPermission(command.permission()) != null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Skipping permission " + command.permission() + " as it is already registered");
|
||||
continue;
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
||||
GeyserLocale.getLocaleStringLog(command.description()),
|
||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||
}
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION,
|
||||
"Whether update notifications can be seen", PermissionDefault.OP));
|
||||
|
||||
// Events cannot be unregistered - re-registering results in duplicate firings
|
||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
|
||||
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
||||
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigotUpdateListener(), this);
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION,
|
||||
"Whether update notifications can be seen", PermissionDefault.OP));
|
||||
|
||||
// Events cannot be unregistered - re-registering results in duplicate firings
|
||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
|
||||
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
||||
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigotUpdateListener(), this);
|
||||
|
||||
boolean brigadierSupported = CommodoreProvider.isSupported();
|
||||
geyserLogger.debug("Brigadier supported? " + brigadierSupported);
|
||||
if (brigadierSupported) {
|
||||
GeyserBrigadierSupport.loadBrigadier(this, geyserCommand);
|
||||
}
|
||||
|
||||
// Check to ensure the current setup can support the protocol version Geyser uses
|
||||
GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion);
|
||||
|
||||
INITIALIZED = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
public void onGeyserDisable() {
|
||||
if (geyser != null) {
|
||||
geyser.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeyserShutdown() {
|
||||
if (geyser != null) {
|
||||
geyser.shutdown();
|
||||
}
|
||||
@ -367,6 +367,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.onGeyserShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserSpigotConfiguration getGeyserConfig() {
|
||||
return geyserConfig;
|
||||
@ -448,7 +453,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
@Override
|
||||
public String getServerBindAddress() {
|
||||
return Bukkit.getIp();
|
||||
@ -467,4 +472,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,6 +39,7 @@ import java.lang.reflect.Modifier;
|
||||
public final class GeyserSpigotVersionChecker {
|
||||
private static final String VIAVERSION_DOWNLOAD_URL = "https://ci.viaversion.com/job/ViaVersion/";
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void checkForSupportedProtocol(GeyserLogger logger, boolean viaversion) {
|
||||
if (viaversion) {
|
||||
checkViaVersionSupportedVersions(logger);
|
||||
|
@ -28,8 +28,8 @@ package org.geysermc.geyser.platform.spigot;
|
||||
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -39,8 +39,8 @@ import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Utility class for converting our shaded Adventure into the Adventure bundled in Paper.
|
||||
*
|
||||
* Code mostly taken from https://github.com/KyoriPowered/adventure-platform/blob/94d5821f2e755170f42bd8a5fe1d5bf6f66d04ad/platform-bukkit/src/main/java/net/kyori/adventure/platform/bukkit/PaperFacet.java#L46
|
||||
* <p>
|
||||
* Code mostly taken from <a href="https://github.com/KyoriPowered/adventure-platform/blob/94d5821f2e755170f42bd8a5fe1d5bf6f66d04ad/platform-bukkit/src/main/java/net/kyori/adventure/platform/bukkit/PaperFacet.java#L46">here</a>
|
||||
* and the MinecraftReflection class.
|
||||
*/
|
||||
public final class PaperAdventure {
|
||||
@ -102,7 +102,7 @@ public final class PaperAdventure {
|
||||
SEND_MESSAGE_COMPONENT = playerComponentSendMessage;
|
||||
}
|
||||
|
||||
public static Object toNativeComponent(final Component component) {
|
||||
public static @Nullable Object toNativeComponent(final Component component) {
|
||||
if (NATIVE_GSON_COMPONENT_SERIALIZER_DESERIALIZE_METHOD_BOUND == null) {
|
||||
GeyserImpl.getInstance().getLogger().error("Illegal state where Component serialization was called when it wasn't available!");
|
||||
return null;
|
||||
|
@ -29,8 +29,8 @@ import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
|
||||
import com.destroystokyo.paper.network.StatusClient;
|
||||
import org.bukkit.event.server.ServerListPingEvent;
|
||||
import org.bukkit.util.CachedServerIcon;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.InetAddress;
|
||||
|
||||
@ -40,15 +40,8 @@ import java.net.InetAddress;
|
||||
public final class ReflectedNames {
|
||||
|
||||
static boolean checkPaperPingEvent() {
|
||||
return classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if this class name exists
|
||||
*/
|
||||
private static boolean classExists(String clazz) {
|
||||
try {
|
||||
Class.forName(clazz);
|
||||
Class.forName("com.destroystokyo.paper.event.server.PaperServerListPingEvent");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
@ -59,7 +52,7 @@ public final class ReflectedNames {
|
||||
return getConstructor(ServerListPingEvent.class, InetAddress.class, String.class, boolean.class, int.class, int.class) != null;
|
||||
}
|
||||
|
||||
static Constructor<PaperServerListPingEvent> getOldPaperPingConstructor() {
|
||||
static @Nullable Constructor<PaperServerListPingEvent> getOldPaperPingConstructor() {
|
||||
if (getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, int.class,
|
||||
int.class, String.class, int.class, CachedServerIcon.class) != null) {
|
||||
// @NotNull StatusClient client, @NotNull String motd, int numPlayers, int maxPlayers,
|
||||
|
@ -39,8 +39,8 @@ import java.util.Map;
|
||||
|
||||
public final class GeyserPaperCommandListener implements Listener {
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@EventHandler
|
||||
@SuppressWarnings("deprecation") // Used to indicate an unstable event
|
||||
public void onCommandSend(AsyncPlayerSendCommandsEvent<?> event) {
|
||||
// Documentation says to check (event.isAsynchronous() || !event.hasFiredAsync()), but as of Paper 1.18.2
|
||||
// event.hasFiredAsync is never true
|
||||
|
@ -29,6 +29,7 @@ import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||
@ -47,7 +48,7 @@ public class GeyserSpigotCommandExecutor extends GeyserCommandExecutor implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NonNull CommandSender sender, @NonNull Command command, @NonNull String label, String[] args) {
|
||||
SpigotCommandSource commandSender = new SpigotCommandSource(sender);
|
||||
GeyserSession session = getGeyserSession(commandSender);
|
||||
|
||||
@ -78,7 +79,7 @@ public class GeyserSpigotCommandExecutor extends GeyserCommandExecutor implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
|
||||
public List<String> onTabComplete(@NonNull CommandSender sender, @NonNull Command command, @NonNull String label, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return tabComplete(new SpigotCommandSource(sender));
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.platform.spigot.PaperAdventure;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
@ -48,10 +49,11 @@ public class SpigotCommandSource implements GeyserCommandSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
public void sendMessage(@NonNull String message) {
|
||||
handle.sendMessage(message);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
if (PaperAdventure.canSendMessageUsingComponent()) {
|
||||
@ -68,9 +70,11 @@ public class SpigotCommandSource implements GeyserCommandSource {
|
||||
return handle instanceof ConsoleCommandSender;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public String locale() {
|
||||
if (this.handle instanceof Player player) {
|
||||
// getLocale() is deprecated on Paper, but not on Spigot
|
||||
return player.getLocale();
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ import org.geysermc.geyser.platform.spigot.GeyserSpigotPlugin;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Used when block IDs need to be translated to the latest version
|
||||
@ -52,6 +53,7 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl
|
||||
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
|
||||
List<ProtocolPathEntry> protocolList = Via.getManager().getProtocolManager().getProtocolPath(GameProtocol.getJavaProtocolVersion(),
|
||||
serverVersion.getVersion());
|
||||
Objects.requireNonNull(protocolList, "protocolList cannot be null");
|
||||
for (int oldBlockId : allBlockStates) {
|
||||
int newBlockId = oldBlockId;
|
||||
// protocolList should *not* be null; we checked for that before initializing this class
|
||||
|
@ -28,11 +28,11 @@ package org.geysermc.geyser.platform.spigot.world.manager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||
import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
||||
protected final SpigotWorldAdapter adapter;
|
||||
|
@ -34,21 +34,23 @@ import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.erosion.bukkit.BukkitLecterns;
|
||||
import org.geysermc.erosion.bukkit.BukkitUtils;
|
||||
import org.geysermc.erosion.bukkit.PickBlockUtils;
|
||||
import org.geysermc.erosion.bukkit.SchedulerUtils;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.level.GameRule;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@ -128,7 +130,7 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||
}
|
||||
}
|
||||
|
||||
private Chunk getChunk(World world, int x, int z) {
|
||||
private @Nullable Chunk getChunk(World world, int x, int z) {
|
||||
if (!world.isChunkLoaded(x, z)) {
|
||||
return null;
|
||||
}
|
||||
@ -136,6 +138,7 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||
}
|
||||
|
||||
private void sendLecternData(GeyserSession session, Chunk chunk, List<BlockEntityInfo> blockEntityInfos) {
|
||||
//noinspection ForLoopReplaceableByForEach - avoid constructing Iterator
|
||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||
BlockEntityInfo info = blockEntityInfos.get(i);
|
||||
Block block = chunk.getBlock(info.getX(), info.getY(), info.getZ());
|
||||
@ -156,19 +159,34 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||
}
|
||||
|
||||
public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
||||
String value = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID());
|
||||
if (!value.isEmpty()) {
|
||||
return Boolean.parseBoolean(value);
|
||||
org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID());
|
||||
if (bukkitGameRule == null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Unknown game rule " + gameRule.getJavaID());
|
||||
return gameRule.getDefaultBooleanValue();
|
||||
}
|
||||
|
||||
Player bukkitPlayer = Objects.requireNonNull(Bukkit.getPlayer(session.getPlayerEntity().getUuid()));
|
||||
Object value = bukkitPlayer.getWorld().getGameRuleValue(bukkitGameRule);
|
||||
if (value instanceof Boolean booleanValue) {
|
||||
return booleanValue;
|
||||
}
|
||||
GeyserImpl.getInstance().getLogger().debug("Expected a bool for " + gameRule + " but got " + value);
|
||||
return gameRule.getDefaultBooleanValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGameRuleInt(GeyserSession session, GameRule gameRule) {
|
||||
String value = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID());
|
||||
if (!value.isEmpty()) {
|
||||
return Integer.parseInt(value);
|
||||
org.bukkit.GameRule<?> bukkitGameRule = org.bukkit.GameRule.getByName(gameRule.getJavaID());
|
||||
if (bukkitGameRule == null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Unknown game rule " + gameRule.getJavaID());
|
||||
return gameRule.getDefaultIntValue();
|
||||
}
|
||||
Player bukkitPlayer = Objects.requireNonNull(Bukkit.getPlayer(session.getPlayerEntity().getUuid()));
|
||||
Object value = bukkitPlayer.getWorld().getGameRuleValue(bukkitGameRule);
|
||||
if (value instanceof Integer intValue) {
|
||||
return intValue;
|
||||
}
|
||||
GeyserImpl.getInstance().getLogger().debug("Expected an int for " + gameRule + " but got " + value);
|
||||
return gameRule.getDefaultIntValue();
|
||||
}
|
||||
|
||||
@ -179,12 +197,15 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(GeyserSession session, String permission) {
|
||||
return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(permission);
|
||||
Player player = Bukkit.getPlayer(session.javaUuid());
|
||||
if (player != null) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||
public @NonNull CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||
CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>();
|
||||
Player bukkitPlayer;
|
||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
||||
|
@ -38,9 +38,10 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.Logger;
|
||||
import org.apache.logging.log4j.core.appender.ConsoleAppender;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
||||
@ -51,7 +52,6 @@ import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI;
|
||||
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;
|
||||
@ -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,14 +220,15 @@ 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, null);
|
||||
GeyserImpl.start();
|
||||
|
||||
geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
geyserCommandManager.init();
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
if (gui != null) {
|
||||
gui.enableCommands(geyser.getScheduledThread(), geyserCommandManager);
|
||||
}
|
||||
@ -250,7 +256,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);
|
||||
}
|
||||
@ -292,7 +305,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
return new GeyserStandaloneDumpInfo(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
@Override
|
||||
public String getServerBindAddress() {
|
||||
throw new IllegalStateException();
|
||||
@ -325,7 +338,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
|
||||
// Get the ignored properties
|
||||
Set<String> ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector()
|
||||
.findPropertyIgnorals(beanDescription.getClassInfo()).getIgnored();
|
||||
.findPropertyIgnoralByName(OBJECT_MAPPER.getSerializationConfig() ,beanDescription.getClassInfo()).getIgnored();
|
||||
|
||||
// Filter properties removing the ignored ones
|
||||
return properties.stream()
|
||||
@ -340,7 +353,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
* @param parentObject The object to alter
|
||||
* @param value The new value of the property
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // Required for enum usage
|
||||
@SuppressWarnings({"unchecked", "rawtypes"}) // Required for enum usage
|
||||
private static void setConfigOption(BeanPropertyDefinition property, Object parentObject, Object value) {
|
||||
Object parsedValue = value;
|
||||
|
||||
|
@ -49,7 +49,7 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
|
||||
|
||||
@Override
|
||||
protected void shutdown() {
|
||||
GeyserImpl.getInstance().getBootstrap().onDisable();
|
||||
GeyserImpl.getInstance().getBootstrap().onGeyserShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,11 +28,16 @@ package org.geysermc.geyser.platform.standalone.gui;
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
import java.awt.*;
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* This class was based on this code: https://stackoverflow.com/a/6899478/5299903
|
||||
* This class was based on this <a href="https://stackoverflow.com/a/6899478/5299903">code</a>
|
||||
*/
|
||||
public class ColorPane extends JTextPane {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static Color colorCurrent = ANSIColor.RESET.getColor();
|
||||
private String remaining = "";
|
||||
|
||||
@ -62,7 +67,7 @@ public class ColorPane extends JTextPane {
|
||||
int aPos = 0; // current char position in addString
|
||||
int aIndex; // index of next Escape sequence
|
||||
int mIndex; // index of "m" terminating Escape sequence
|
||||
String tmpString = "";
|
||||
String tmpString;
|
||||
boolean stillSearching = true; // true until no more Escape sequences
|
||||
String addString = remaining + s;
|
||||
remaining = "";
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.platform.standalone.gui;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
@ -64,7 +65,6 @@ public class GeyserStandaloneGUI {
|
||||
private final List<Integer> ramValues = new ArrayList<>();
|
||||
|
||||
private final DefaultTableModel playerTableModel = new DefaultTableModel();
|
||||
private final JTable playerTable = new JTable(playerTableModel);
|
||||
|
||||
/**
|
||||
* Create and show the Geyser-Standalone GUI
|
||||
@ -158,6 +158,7 @@ public class GeyserStandaloneGUI {
|
||||
playerTableModel.addColumn(GeyserLocale.getLocaleStringLog("geyser.gui.table.ip"));
|
||||
playerTableModel.addColumn(GeyserLocale.getLocaleStringLog("geyser.gui.table.username"));
|
||||
|
||||
JTable playerTable = new JTable(playerTableModel);
|
||||
JScrollPane playerScrollPane = new JScrollPane(playerTable);
|
||||
rightContentPane.add(playerScrollPane);
|
||||
|
||||
@ -253,12 +254,12 @@ public class GeyserStandaloneGUI {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
public void write(byte @NonNull [] b, int off, int len) {
|
||||
appendConsole(new String(b, off, len));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) {
|
||||
public void write(byte @NonNull[] b) {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
};
|
||||
|
@ -29,15 +29,20 @@ import lombok.Setter;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.Serial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This has been modified to fit Geyser more but is based on
|
||||
* https://gist.github.com/roooodcastro/6325153#gistcomment-3107524
|
||||
* <a href="https://gist.github.com/roooodcastro/6325153#gistcomment-3107524">this Github gist</a>
|
||||
*/
|
||||
public final class GraphPanel extends JPanel {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static int padding = 10;
|
||||
private final static int labelPadding = 25;
|
||||
private final static int pointWidth = 4;
|
||||
@ -103,7 +108,7 @@ public final class GraphPanel extends JPanel {
|
||||
g.drawLine(padding + labelPadding + 1 + pointWidth, y, width - padding, y);
|
||||
|
||||
g.setColor(Color.BLACK);
|
||||
final int tickValue = (int) (minScore + ((scoreRange * i) / numberYDivisions));
|
||||
final int tickValue = minScore + ((scoreRange * i) / numberYDivisions);
|
||||
final String yLabel = tickValue + "";
|
||||
final int labelWidth = fontMetrics.stringWidth(yLabel);
|
||||
g.drawString(yLabel, x1 - labelWidth - 5, y + (fontHeight / 2) - 3);
|
||||
|
@ -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")
|
||||
|
@ -29,6 +29,7 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.network.netty.GeyserInjector;
|
||||
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
|
||||
@ -76,7 +77,7 @@ public class GeyserVelocityInjector extends GeyserInjector {
|
||||
.channel(LocalServerChannelWrapper.class)
|
||||
.childHandler(new ChannelInitializer<>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
protected void initChannel(@NonNull Channel ch) throws Exception {
|
||||
initChannel.invoke(channelInitializer, ch);
|
||||
|
||||
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserVelocityCompressionDisabler.ENABLED) {
|
||||
|
@ -54,19 +54,11 @@ public class GeyserVelocityPingPassthrough implements IGeyserPingPassthrough {
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(
|
||||
return new GeyserPingInfo(
|
||||
LegacyComponentSerializer.legacy('§').serialize(event.getPing().getDescriptionComponent()),
|
||||
new GeyserPingInfo.Players(
|
||||
event.getPing().getPlayers().orElseThrow(IllegalStateException::new).getMax(),
|
||||
event.getPing().getPlayers().orElseThrow(IllegalStateException::new).getOnline()
|
||||
),
|
||||
new GeyserPingInfo.Version(
|
||||
event.getPing().getVersion().getName(),
|
||||
event.getPing().getVersion().getProtocol()
|
||||
)
|
||||
event.getPing().getPlayers().map(ServerPing.Players::getMax).orElse(1),
|
||||
event.getPing().getPlayers().map(ServerPing.Players::getOnline).orElse(0)
|
||||
);
|
||||
event.getPing().getPlayers().get().getSample().stream().map(ServerPing.SamplePlayer::getName).forEach(geyserPingInfo.getPlayerList()::add);
|
||||
return geyserPingInfo;
|
||||
}
|
||||
|
||||
private static class GeyserInboundConnection implements InboundConnection {
|
||||
|
@ -33,29 +33,30 @@ 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.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.floodgate.core.FloodgatePlatform;
|
||||
import org.geysermc.floodgate.velocity.VelocityPlatform;
|
||||
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.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.network.GameProtocol;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandExecutor;
|
||||
import org.geysermc.geyser.platform.velocity.floodgate.FloodgateModule;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
@ -68,7 +69,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 {
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
|
||||
@ -93,31 +93,22 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||
private Injector guice;
|
||||
|
||||
@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);
|
||||
|
||||
@ -127,29 +118,34 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||
}
|
||||
|
||||
this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this, platform);
|
||||
|
||||
// if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && proxyServer.getPluginManager().getPlugin("floodgate").isEmpty()) {
|
||||
// geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " "
|
||||
// + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
|
||||
// return;
|
||||
// } else if (geyserConfig.isAutoconfiguredRemote() && proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) {
|
||||
// // Floodgate installed means that the user wants Floodgate authentication
|
||||
// geyserLogger.debug("Auto-setting to Floodgate authentication.");
|
||||
// geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE);
|
||||
// }
|
||||
|
||||
geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile());
|
||||
|
||||
this.geyserInjector = new GeyserVelocityInjector(proxyServer);
|
||||
}
|
||||
|
||||
private void postStartup() {
|
||||
@Override
|
||||
public void onGeyserEnable() {
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
if (!loadConfig()) {
|
||||
return;
|
||||
}
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
} else {
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
}
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
this.geyserInjector = new GeyserVelocityInjector(proxyServer);
|
||||
// Will be initialized after the proxy has been bound
|
||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
} else {
|
||||
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
|
||||
}
|
||||
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
// 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()) {
|
||||
@ -161,17 +157,18 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||
this.commandManager.register(entry.getKey().description().id(), new GeyserVelocityCommandExecutor(this.geyser, commands));
|
||||
}
|
||||
|
||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
} else {
|
||||
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
@ -202,19 +199,19 @@ 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
|
||||
@ -234,7 +231,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||
return this.geyserInjector.getServerSocketAddress();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
@Override
|
||||
public String getServerBindAddress() {
|
||||
return proxyServer.getBoundAddress().getHostString();
|
||||
@ -254,4 +251,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;
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
@ -56,7 +57,7 @@ public class VelocityCommandSource implements GeyserCommandSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
public void sendMessage(@NonNull String message) {
|
||||
handle.sendMessage(LegacyComponentSerializer.legacy('§').deserialize(message));
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
// Ensure AP works in eclipse (no effect on other IDEs)
|
||||
`eclipse`
|
||||
id("geyser.build-logic")
|
||||
id("io.freefair.lombok") version "8.3" apply false
|
||||
}
|
||||
@ -12,7 +14,7 @@ allprojects {
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(16))
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,4 +37,4 @@ subprojects {
|
||||
in platforms -> plugins.apply("geyser.platform-conventions")
|
||||
else -> plugins.apply("geyser.base-conventions")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.floodgate.core.skin.SkinApplier;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
@ -33,8 +35,6 @@ import org.geysermc.geyser.level.GeyserWorldManager;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.InputStream;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.file.Path;
|
||||
@ -45,14 +45,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
|
||||
@ -80,6 +94,7 @@ public interface GeyserBootstrap {
|
||||
*
|
||||
* @return The current PingPassthrough manager
|
||||
*/
|
||||
@Nullable
|
||||
IGeyserPingPassthrough getGeyserPingPassthrough();
|
||||
|
||||
/**
|
||||
@ -159,7 +174,7 @@ public interface GeyserBootstrap {
|
||||
* @param resource Resource to get
|
||||
* @return InputStream of the given resource
|
||||
*/
|
||||
default @Nonnull InputStream getResource(String resource) {
|
||||
default @NonNull InputStream getResourceOrThrow(@NonNull String resource) {
|
||||
InputStream stream = getResourceOrNull(resource);
|
||||
if (stream == null) {
|
||||
throw new AssertionError("Unable to find resource: " + resource);
|
||||
@ -170,7 +185,7 @@ public interface GeyserBootstrap {
|
||||
/**
|
||||
* @return the bind address being used by the Java server.
|
||||
*/
|
||||
@Nonnull
|
||||
@NonNull
|
||||
String getServerBindAddress();
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,11 @@ 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.geyser.api.command.CommandSource;
|
||||
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||
import org.geysermc.erosion.packet.Packets;
|
||||
@ -51,13 +55,10 @@ import org.geysermc.floodgate.news.NewsItemAction;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
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.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
@ -68,10 +69,13 @@ import org.geysermc.geyser.floodgate.FloodgateProvider;
|
||||
import org.geysermc.geyser.floodgate.IntegratedFloodgateProvider;
|
||||
import org.geysermc.geyser.floodgate.NoFloodgateProvider;
|
||||
import org.geysermc.geyser.floodgate.ProxyFloodgateProvider;
|
||||
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;
|
||||
import org.geysermc.geyser.registry.provider.ProviderSupplier;
|
||||
import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
||||
@ -110,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}";
|
||||
@ -139,6 +143,7 @@ public class GeyserImpl implements GeyserApi {
|
||||
|
||||
private UnixSocketClientListener erosionUnixListener;
|
||||
|
||||
@Setter
|
||||
private volatile boolean shuttingDown = false;
|
||||
|
||||
private ScheduledExecutorService scheduledThread;
|
||||
@ -156,9 +161,15 @@ 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;
|
||||
|
||||
private GeyserImpl(PlatformType platformType, GeyserBootstrap bootstrap, FloodgatePlatform floodgatePlatform) {
|
||||
instance = this;
|
||||
|
||||
@ -180,13 +191,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));
|
||||
}
|
||||
@ -244,11 +258,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();
|
||||
|
||||
@ -322,6 +342,22 @@ public class GeyserImpl implements GeyserApi {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent() || floodgateProvider != null; //todo
|
||||
if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) {
|
||||
logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " "
|
||||
@ -526,12 +562,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
|
||||
@ -590,9 +629,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()));
|
||||
@ -606,7 +644,6 @@ public class GeyserImpl implements GeyserApi {
|
||||
skinUploader.close();
|
||||
}
|
||||
newsHandler.shutdown();
|
||||
this.commandManager().getCommands().clear();
|
||||
|
||||
if (this.erosionUnixListener != null) {
|
||||
this.erosionUnixListener.close();
|
||||
@ -614,16 +651,29 @@ public class GeyserImpl implements GeyserApi {
|
||||
|
||||
Registries.RESOURCE_PACKS.get().clear();
|
||||
|
||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done"));
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
shuttingDown = true;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -634,7 +684,7 @@ public class GeyserImpl implements GeyserApi {
|
||||
*/
|
||||
public boolean isProductionEnvironment() {
|
||||
// First is if Blossom runs, second is if Blossom doesn't run
|
||||
// noinspection ConstantConditions - changes in production
|
||||
//noinspection ConstantConditions,MismatchedStringCase - changes in production
|
||||
return !("git-local/dev-0000000".equals(GeyserImpl.GIT_VERSION) || "${gitVersion}".equals(GeyserImpl.GIT_VERSION));
|
||||
}
|
||||
|
||||
@ -650,8 +700,13 @@ public class GeyserImpl implements GeyserApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <R extends T, T> @NonNull R provider(@NonNull Class<T> apiClass, @Nullable Object... args) {
|
||||
return (R) Registries.PROVIDERS.get(apiClass).create(args);
|
||||
ProviderSupplier provider = Registries.PROVIDERS.get(apiClass);
|
||||
if (provider == null) {
|
||||
throw new IllegalArgumentException("No provider found for " + apiClass);
|
||||
}
|
||||
return (R) provider.create(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -689,6 +744,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;
|
||||
@ -710,9 +784,7 @@ 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();
|
||||
@ -763,8 +835,4 @@ public class GeyserImpl implements GeyserApi {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static GeyserImpl getInstance() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
@ -26,9 +26,10 @@
|
||||
package org.geysermc.geyser;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface GeyserLogger extends GeyserCommandSource {
|
||||
|
||||
@ -119,7 +120,7 @@ public interface GeyserLogger extends GeyserCommandSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
default void sendMessage(String message) {
|
||||
default void sendMessage(@NonNull String message) {
|
||||
info(message);
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class GeyserMain {
|
||||
@ -61,8 +62,8 @@ public class GeyserMain {
|
||||
helpStream = GeyserMain.class.getClassLoader().getResourceAsStream("languages/run-help/en_US.txt");
|
||||
}
|
||||
|
||||
Scanner help = new Scanner(helpStream, StandardCharsets.UTF_8).useDelimiter("\\Z");
|
||||
String line = "";
|
||||
Scanner help = new Scanner(Objects.requireNonNull(helpStream), StandardCharsets.UTF_8).useDelimiter("\\Z");
|
||||
String line;
|
||||
while (help.hasNext()) {
|
||||
line = help.next();
|
||||
|
||||
|
@ -29,10 +29,10 @@ import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@ -73,15 +73,6 @@ public abstract class GeyserCommand implements Command {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to {@link #subCommands()} ()}{@code .isEmpty()}.
|
||||
*
|
||||
* @return true if there are subcommand present for this command.
|
||||
*/
|
||||
public boolean hasSubCommands() {
|
||||
return !this.subCommands().isEmpty();
|
||||
}
|
||||
|
||||
public void setAliases(List<String> aliases) {
|
||||
this.aliases = aliases;
|
||||
}
|
||||
|
@ -26,11 +26,11 @@
|
||||
package org.geysermc.geyser.command;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -29,6 +29,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
@ -53,8 +54,6 @@ import org.geysermc.geyser.event.type.GeyserDefineCommandsEventImpl;
|
||||
import org.geysermc.geyser.extension.command.GeyserExtensionCommand;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -87,7 +86,7 @@ public class GeyserCommandManager {
|
||||
registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop"));
|
||||
}
|
||||
|
||||
if (this.geyser.extensionManager().extensions().size() > 0) {
|
||||
if (!this.geyser.extensionManager().extensions().isEmpty()) {
|
||||
registerBuiltInCommand(new ExtensionsCommand(this.geyser, "extensions", "geyser.commands.extensions.desc", "geyser.command.extensions"));
|
||||
}
|
||||
|
||||
@ -136,12 +135,12 @@ public class GeyserCommandManager {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
public Map<String, Command> commands() {
|
||||
return Collections.unmodifiableMap(this.commands);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
public Map<Extension, Map<String, Command>> extensionCommands() {
|
||||
return Collections.unmodifiableMap(this.extensionCommands);
|
||||
}
|
||||
|
@ -26,16 +26,19 @@
|
||||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
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;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@ -82,7 +85,7 @@ public class ConnectionTestCommand extends GeyserCommand {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
port = 19132;
|
||||
port = geyser.getConfig().getBedrock().broadcastPort();
|
||||
}
|
||||
String ip = fullAddress[0];
|
||||
|
||||
@ -104,30 +107,47 @@ public class ConnectionTestCommand extends GeyserCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
// Issue: do the ports not line up?
|
||||
if (port != geyser.getConfig().getBedrock().port()) {
|
||||
if (fullAddress.length == 2) {
|
||||
sender.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration ("
|
||||
+ geyser.getConfig().getBedrock().port() + ")");
|
||||
sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config.");
|
||||
if (geyser.getConfig().getBedrock().isCloneRemotePort()) {
|
||||
sender.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead.");
|
||||
// Issue: port out of bounds
|
||||
if (port <= 0 || port >= 65535) {
|
||||
sender.sendMessage("The port you specified is invalid! Please specify a valid port.");
|
||||
return;
|
||||
}
|
||||
|
||||
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 (fullAddress.length == 2) {
|
||||
sender.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration ("
|
||||
+ config.getBedrock().port() + ")");
|
||||
sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config.");
|
||||
if (config.getBedrock().isCloneRemotePort()) {
|
||||
sender.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead.");
|
||||
}
|
||||
} else {
|
||||
sender.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() + ")!");
|
||||
sender.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`.");
|
||||
}
|
||||
} else {
|
||||
sender.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 ("
|
||||
+ geyser.getConfig().getBedrock().port() + ")!");
|
||||
sender.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`.");
|
||||
}
|
||||
} else {
|
||||
if (config.getBedrock().broadcastPort() != port) {
|
||||
sender.sendMessage("The port you are testing with (" + port + ") is not the same as the broadcast port set in your Geyser configuration ("
|
||||
+ config.getBedrock().broadcastPort() + "). ");
|
||||
sender.sendMessage("You ONLY need to change the broadcast port if clients connects with a port different from the port Geyser is running on.");
|
||||
sender.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")) {
|
||||
sender.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()) {
|
||||
sender.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.");
|
||||
}
|
||||
@ -158,23 +178,24 @@ public class ConnectionTestCommand extends GeyserCommand {
|
||||
String connectionTestMotd = "Geyser Connection Test " + randomStr;
|
||||
CONNECTION_TEST_MOTD = connectionTestMotd;
|
||||
|
||||
sender.sendMessage("Testing server connection now. Please wait...");
|
||||
sender.sendMessage("Testing server connection to " + ip + " with port: " + port + " now. Please wait...");
|
||||
JsonNode output;
|
||||
try {
|
||||
output = WebUtils.getJson("https://checker.geysermc.org/ping?hostname=" + ip + "&port=" + port);
|
||||
String hostname = URLEncoder.encode(ip, StandardCharsets.UTF_8);
|
||||
output = WebUtils.getJson("https://checker.geysermc.org/ping?hostname=" + hostname + "&port=" + port);
|
||||
} finally {
|
||||
CONNECTION_TEST_MOTD = null;
|
||||
}
|
||||
|
||||
JsonNode cache = output.get("cache");
|
||||
String when;
|
||||
if (cache.get("fromCache").asBoolean()) {
|
||||
when = cache.get("secondsSince").asInt() + " seconds ago";
|
||||
} else {
|
||||
when = "now";
|
||||
}
|
||||
|
||||
if (output.get("success").asBoolean()) {
|
||||
JsonNode cache = output.get("cache");
|
||||
String when;
|
||||
if (cache.get("fromCache").asBoolean()) {
|
||||
when = cache.get("secondsSince").asInt() + " seconds ago";
|
||||
} else {
|
||||
when = "now";
|
||||
}
|
||||
|
||||
JsonNode ping = output.get("ping");
|
||||
JsonNode pong = ping.get("pong");
|
||||
String remoteMotd = pong.get("motd").asText();
|
||||
@ -196,7 +217,11 @@ public class ConnectionTestCommand extends GeyserCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage("Your server is likely unreachable from outside the network as of " + when + ".");
|
||||
sender.sendMessage("Your server is likely unreachable from outside the network!");
|
||||
JsonNode message = output.get("message");
|
||||
if (message != null && !message.asText().isEmpty()) {
|
||||
sender.sendMessage("Got the error message: " + message.asText());
|
||||
}
|
||||
sendLinks(sender);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage("An error occurred while trying to check your connection! Check the console for more information.");
|
||||
|
@ -98,7 +98,7 @@ public class DumpCommand extends GeyserCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
String uploadedDumpUrl = "";
|
||||
String uploadedDumpUrl;
|
||||
|
||||
if (offlineDump) {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.writing", sender.locale()));
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
@ -32,7 +33,6 @@ import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
@ -32,6 +32,8 @@ import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ReloadCommand extends GeyserCommand {
|
||||
|
||||
private final GeyserImpl geyser;
|
||||
@ -52,7 +54,8 @@ public class ReloadCommand extends GeyserCommand {
|
||||
sender.sendMessage(message);
|
||||
|
||||
geyser.getSessionManager().disconnectAll("geyser.commands.reload.kick");
|
||||
geyser.reload();
|
||||
//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::reloadGeyser, 10, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,7 +52,7 @@ public class StopCommand extends GeyserCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
geyser.getBootstrap().onDisable();
|
||||
geyser.getBootstrap().onGeyserShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,8 +27,8 @@ package org.geysermc.geyser.command.defaults;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
@ -37,7 +37,6 @@ import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.WebUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
@ -77,7 +76,7 @@ public class VersionCommand extends GeyserCommand {
|
||||
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale()));
|
||||
try {
|
||||
String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" +
|
||||
URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber");
|
||||
URLEncoder.encode(GeyserImpl.BRANCH, StandardCharsets.UTF_8) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber");
|
||||
if (buildXML.startsWith("<buildNumber>")) {
|
||||
int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim());
|
||||
int buildNum = this.geyser.buildNumber();
|
||||
@ -90,7 +89,7 @@ public class VersionCommand extends GeyserCommand {
|
||||
} else {
|
||||
throw new AssertionError("buildNumber missing");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.version.failed"), e);
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.version.failed", sender.locale()));
|
||||
}
|
||||
|
@ -54,14 +54,12 @@ public interface GeyserConfiguration {
|
||||
|
||||
List<String> getSavedUserLogins();
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
boolean isCommandSuggestions();
|
||||
|
||||
@JsonIgnore
|
||||
boolean isPassthroughMotd();
|
||||
|
||||
@JsonIgnore
|
||||
boolean isPassthroughProtocolName();
|
||||
|
||||
@JsonIgnore
|
||||
boolean isPassthroughPlayerCounts();
|
||||
|
||||
@ -98,6 +96,7 @@ public interface GeyserConfiguration {
|
||||
|
||||
boolean isForceResourcePacks();
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
boolean isXboxAchievementsEnabled();
|
||||
|
||||
int getCacheImages();
|
||||
@ -125,6 +124,8 @@ public interface GeyserConfiguration {
|
||||
|
||||
void setPort(int port);
|
||||
|
||||
void setBroadcastPort(int broadcastPort);
|
||||
|
||||
boolean isCloneRemotePort();
|
||||
|
||||
int getCompressionLevel();
|
||||
|
@ -34,6 +34,7 @@ import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.network.AuthType;
|
||||
import org.geysermc.geyser.network.CIDRMatcher;
|
||||
@ -87,9 +88,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
@JsonProperty("passthrough-player-counts")
|
||||
private boolean isPassthroughPlayerCounts = false;
|
||||
|
||||
@JsonProperty("passthrough-protocol-name")
|
||||
private boolean isPassthroughProtocolName = false;
|
||||
|
||||
@JsonProperty("legacy-ping-passthrough")
|
||||
private boolean isLegacyPingPassthrough = false;
|
||||
|
||||
@ -173,7 +171,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
private String address = "0.0.0.0";
|
||||
|
||||
@Override
|
||||
public String address() {
|
||||
public @NonNull String address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
@ -186,6 +184,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;
|
||||
@ -275,7 +282,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
private AuthType authType = AuthType.ONLINE;
|
||||
|
||||
@Override
|
||||
public AuthType authType() {
|
||||
public @NonNull AuthType authType() {
|
||||
return authType;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,6 @@ import com.google.common.io.ByteSource;
|
||||
import com.google.common.io.Files;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.geysermc.api.util.BedrockPlatform;
|
||||
@ -105,8 +104,8 @@ public class DumpInfo {
|
||||
// https://stackoverflow.com/questions/304268/getting-a-files-md5-checksum-in-java
|
||||
File file = new File(DumpInfo.class.getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||
ByteSource byteSource = Files.asByteSource(file);
|
||||
// Jenkins uses MD5 for its hash
|
||||
//noinspection UnstableApiUsage
|
||||
// Jenkins uses MD5 for its hash - TODO remove
|
||||
//noinspection UnstableApiUsage,deprecation
|
||||
md5Hash = byteSource.hash(Hashing.md5()).toString();
|
||||
//noinspection UnstableApiUsage
|
||||
sha256Hash = byteSource.hash(Hashing.sha256()).toString();
|
||||
@ -117,7 +116,7 @@ public class DumpInfo {
|
||||
}
|
||||
this.hashInfo = new HashInfo(md5Hash, sha256Hash);
|
||||
|
||||
this.ramInfo = new DumpInfo.RamInfo();
|
||||
this.ramInfo = new RamInfo();
|
||||
|
||||
if (addLog) {
|
||||
this.logsInfo = new LogsInfo();
|
||||
@ -201,7 +200,7 @@ public class DumpInfo {
|
||||
private boolean checkDockerBasic() {
|
||||
try {
|
||||
String OS = System.getProperty("os.name").toLowerCase();
|
||||
if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0) {
|
||||
if (OS.contains("nix") || OS.contains("nux") || OS.indexOf("aix") > 0) {
|
||||
String output = new String(java.nio.file.Files.readAllBytes(Paths.get("/proc/1/cgroup")));
|
||||
|
||||
if (output.contains("docker")) {
|
||||
@ -261,60 +260,30 @@ public class DumpInfo {
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public static class HashInfo {
|
||||
private final String md5Hash;
|
||||
private final String sha256Hash;
|
||||
public record HashInfo(String md5Hash, String sha256Hash) {
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class RamInfo {
|
||||
private final long free;
|
||||
private final long total;
|
||||
private final long max;
|
||||
|
||||
RamInfo() {
|
||||
this.free = Runtime.getRuntime().freeMemory() / MEGABYTE;
|
||||
this.total = Runtime.getRuntime().totalMemory() / MEGABYTE;
|
||||
this.max = Runtime.getRuntime().maxMemory() / MEGABYTE;
|
||||
public record RamInfo(long free, long total, long max) {
|
||||
public RamInfo() {
|
||||
this(Runtime.getRuntime().freeMemory() / MEGABYTE,
|
||||
Runtime.getRuntime().totalMemory() / MEGABYTE,
|
||||
Runtime.getRuntime().maxMemory() / MEGABYTE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* E.G. `-Xmx1024M` - all runtime JVM flags on this machine
|
||||
*/
|
||||
@Getter
|
||||
public static class FlagsInfo {
|
||||
private final List<String> flags;
|
||||
|
||||
FlagsInfo() {
|
||||
this.flags = ManagementFactory.getRuntimeMXBean().getInputArguments();
|
||||
public record FlagsInfo(List<String> flags) {
|
||||
public FlagsInfo() {
|
||||
this(ManagementFactory.getRuntimeMXBean().getInputArguments());
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class ExtensionInfo {
|
||||
public boolean enabled;
|
||||
public String name;
|
||||
public String version;
|
||||
public String apiVersion;
|
||||
public String main;
|
||||
public List<String> authors;
|
||||
public record ExtensionInfo(boolean enabled, String name, String version, String apiVersion, String main, List<String> authors) {
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class GitInfo {
|
||||
private final String buildNumber;
|
||||
@JsonProperty("git.commit.id.abbrev")
|
||||
private final String commitHashAbbrev;
|
||||
@JsonProperty("git.commit.id")
|
||||
private final String commitHash;
|
||||
@JsonProperty("git.branch")
|
||||
private final String branchName;
|
||||
@JsonProperty("git.remote.origin.url")
|
||||
private final String originUrl;
|
||||
public record GitInfo(String buildNumber, @JsonProperty("git.commit.id.abbrev") String commitHashAbbrev, @JsonProperty("git.commit.id") String commitHash,
|
||||
@JsonProperty("git.branch") String branchName, @JsonProperty("git.remote.origin.url") String originUrl) {
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, Entit
|
||||
return new Builder<>(factory);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <M> void translateMetadata(T entity, EntityMetadata<M, ? extends MetadataType<M>> metadata) {
|
||||
EntityMetadataTranslator<? super T, M, EntityMetadata<M, ? extends MetadataType<M>>> translator = (EntityMetadataTranslator<? super T, M, EntityMetadata<M, ? extends MetadataType<M>>>) this.translators.get(metadata.getId());
|
||||
if (translator == null) {
|
||||
|
@ -299,8 +299,9 @@ public final class EntityDefinitions {
|
||||
.build();
|
||||
|
||||
EntityDefinition<Entity> displayBase = EntityDefinition.inherited(entityBase.factory(), entityBase)
|
||||
.addTranslator(null) // Interpolation start ticks
|
||||
.addTranslator(null) // Interpolation duration ID
|
||||
.addTranslator(null) // Interpolation delay
|
||||
.addTranslator(null) // Transformation interpolation duration
|
||||
.addTranslator(null) // Position/Rotation interpolation duration
|
||||
.addTranslator(null) // Translation
|
||||
.addTranslator(null) // Scale
|
||||
.addTranslator(null) // Left rotation
|
||||
@ -318,6 +319,10 @@ public final class EntityDefinitions {
|
||||
.type(EntityType.TEXT_DISPLAY)
|
||||
.identifier("minecraft:armor_stand")
|
||||
.addTranslator(MetadataType.CHAT, TextDisplayEntity::setText)
|
||||
.addTranslator(null) // Line width
|
||||
.addTranslator(null) // Background color
|
||||
.addTranslator(null) // Text opacity
|
||||
.addTranslator(null) // Bit mask
|
||||
.build();
|
||||
|
||||
INTERACTION = EntityDefinition.inherited(InteractionEntity::new, entityBase)
|
||||
|
99
core/src/main/java/org/geysermc/geyser/entity/GeyserEntityData.java
Normale Datei
99
core/src/main/java/org/geysermc/geyser/entity/GeyserEntityData.java
Normale Datei
@ -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();
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.entity.attribute;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@ -69,7 +70,7 @@ public enum GeyserAttributeType {
|
||||
return getAttribute(value, maximum);
|
||||
}
|
||||
|
||||
public AttributeData getAttribute(float value, float maximum) {
|
||||
public @Nullable AttributeData getAttribute(float value, float maximum) {
|
||||
if (bedrockIdentifier == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.ParticleMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -36,6 +36,7 @@ import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
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.entity.EntityDataTypes;
|
||||
@ -580,7 +581,7 @@ public class Entity implements GeyserEntity {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <I extends Entity> I as(Class<I> entityClass) {
|
||||
public <I extends Entity> @Nullable I as(Class<I> entityClass) {
|
||||
return entityClass.isInstance(this) ? (I) this : null;
|
||||
}
|
||||
}
|
||||
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren