3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-12-26 00:00:41 +01:00

Merge remote-tracking branch 'upstream/master' into feature/blocky

Dieser Commit ist enthalten in:
Joshua Castle 2022-12-20 22:21:10 -08:00
Commit f2d4176054
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: F674F38216C35D5D
279 geänderte Dateien mit 16911 neuen und 3919 gelöschten Zeilen

Datei anzeigen

@ -19,12 +19,19 @@ jobs:
uses: snickerbockers/submodules-init@v4 uses: snickerbockers/submodules-init@v4
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew build run: ./gradlew build
- name: Archive artifacts (Geyser Fabric)
uses: actions/upload-artifact@v2
if: success()
with:
name: Geyser Fabric
path: bootstrap/fabric/build/libs/Geyser-Fabric.jar
- name: Archive artifacts (Geyser Standalone) - name: Archive artifacts (Geyser Standalone)
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
if: success() if: success()
with: with:
name: Geyser Standalone name: Geyser Standalone
path: bootstrap/standalone/build/libs/Geyser.jar path: bootstrap/standalone/build/libs/Geyser-Standalone.jar
- name: Archive artifacts (Geyser Spigot) - name: Archive artifacts (Geyser Spigot)
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
if: success() if: success()

Datei anzeigen

@ -1,36 +0,0 @@
name: SonarCloud
on:
push:
branches:
- master
jobs:
build:
name: SonarCloud
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
submodules: true
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: 17
- name: Cache SonarCloud packages
uses: actions/cache@v1
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
uses: actions/cache@v1
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=GeyserMC_Geyser

6
Jenkinsfile vendored
Datei anzeigen

@ -20,7 +20,7 @@ pipeline {
} }
post { post {
success { success {
archiveArtifacts artifacts: 'bootstrap/**/build/libs/*.jar', excludes: 'bootstrap/**/build/libs/*-sources.jar,bootstrap/**/build/libs/*-unshaded.jar', fingerprint: true archiveArtifacts artifacts: 'bootstrap/**/build/libs/Geyser-*.jar', fingerprint: true
} }
} }
} }
@ -29,7 +29,6 @@ pipeline {
when { when {
anyOf { anyOf {
branch "master" branch "master"
branch "feature/extensions"
} }
} }
@ -50,7 +49,7 @@ pipeline {
rootDir: "", rootDir: "",
useWrapper: true, useWrapper: true,
buildFile: 'build.gradle.kts', buildFile: 'build.gradle.kts',
tasks: 'build artifactoryPublish', tasks: 'artifactoryPublish',
deployerId: "GRADLE_DEPLOYER", deployerId: "GRADLE_DEPLOYER",
resolverId: "GRADLE_RESOLVER" resolverId: "GRADLE_RESOLVER"
) )
@ -102,7 +101,6 @@ pipeline {
success { success {
script { script {
if (env.BRANCH_NAME == 'master') { if (env.BRANCH_NAME == 'master') {
build propagate: false, wait: false, job: 'GeyserMC/Geyser-Fabric/java-1.18', parameters: [booleanParam(name: 'SKIP_DISCORD', value: true)]
build propagate: false, wait: false, job: 'GeyserMC/GeyserConnect/master', parameters: [booleanParam(name: 'SKIP_DISCORD', value: true)] build propagate: false, wait: false, job: 'GeyserMC/GeyserConnect/master', parameters: [booleanParam(name: 'SKIP_DISCORD', value: true)]
} }
} }

Datei anzeigen

@ -17,7 +17,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
### Currently supporting Minecraft Bedrock 1.19.0 - 1.19.10 and Minecraft Java 1.19.0. ### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.51 and Minecraft Java 1.19.3.
## Setting Up ## Setting Up
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser. Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
@ -34,7 +34,6 @@ Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Ge
## What's Left to be Added/Fixed ## What's Left to be Added/Fixed
- Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you) - Near-perfect movement (to the point where anticheat on large servers is unlikely to ban you)
- Resource pack conversion/CustomModelData
- Some Entity Flags - Some Entity Flags
- Structure block UI - Structure block UI
@ -43,9 +42,8 @@ There are a few things Geyser is unable to support due to various differences be
## Compiling ## Compiling
1. Clone the repo to your computer 1. Clone the repo to your computer
2. [Install Maven](https://maven.apache.org/install.html) 2. Navigate to the Geyser root directory and run `git submodule update --init --recursive`. This command downloads all the needed submodules for Geyser and is a crucial step in this process.
3. Navigate to the Geyser root directory and run `git submodule update --init --recursive`. This command downloads all the needed submodules for Geyser and is a crucial step in this process. 3. Run `gradlew build` and locate to `bootstrap/build` folder.
4. Run `mvn clean install` and locate to the `target` folder.
## Contributing ## Contributing
Any contributions are appreciated. Please feel free to reach out to us on [Discord](http://discord.geysermc.org/) if Any contributions are appreciated. Please feel free to reach out to us on [Discord](http://discord.geysermc.org/) if

Datei anzeigen

@ -1 +1,7 @@
provided("net.kyori", "event-api", Versions.eventVersion) dependencies {
api(libs.cumulus)
api(libs.events) {
exclude(group = "com.google.guava", module = "guava")
exclude(group = "org.lanternpowered", module = "lmbda")
}
}

Datei anzeigen

@ -39,6 +39,7 @@ public class Geyser {
* *
* @return the base api * @return the base api
*/ */
@NonNull
public static GeyserApiBase api() { public static GeyserApiBase api() {
if (api == null) { if (api == null) {
throw new RuntimeException("Api has not been registered yet!"); throw new RuntimeException("Api has not been registered yet!");
@ -69,7 +70,7 @@ public class Geyser {
/** /**
* Registers the given api type. The api cannot be * Registers the given api type. The api cannot be
* registered if {@link #registered()} is true as * registered if {@link #isRegistered()} is true as
* an api has already been specified. * an api has already been specified.
* *
* @param api the api * @param api the api
@ -88,7 +89,7 @@ public class Geyser {
* *
* @return if the api has been registered * @return if the api has been registered
*/ */
public static boolean registered() { public static boolean isRegistered() {
return api != null; return api != null;
} }
} }

Datei anzeigen

@ -25,9 +25,13 @@
package org.geysermc.api; package org.geysermc.api;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.value.qual.IntRange;
import org.geysermc.api.connection.Connection; import org.geysermc.api.connection.Connection;
import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormBuilder;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -37,52 +41,88 @@ import java.util.UUID;
*/ */
public interface GeyserApiBase { public interface GeyserApiBase {
/** /**
* Gets the session from the given UUID, if applicable. The player must be logged in to the Java server * Gets the connection from the given UUID, if applicable. The player must be logged in to the Java server
* for this to return a non-null value. * for this to return a non-null value.
* *
* @param uuid the UUID of the session * @param uuid the UUID of the connection
* @return the session from the given UUID, if applicable * @return the connection from the given UUID, if applicable
*/ */
@Nullable @Nullable
Connection connectionByUuid(@NonNull UUID uuid); Connection connectionByUuid(@NonNull UUID uuid);
/** /**
* Gets the session from the given * Gets the connection from the given XUID, if applicable. This method only works for online connections.
* XUID, if applicable.
* *
* @param xuid the XUID of the session * @param xuid the XUID of the session
* @return the session from the given UUID, if applicable * @return the connection from the given UUID, if applicable
*/ */
@Nullable @Nullable
Connection connectionByXuid(@NonNull String xuid); Connection connectionByXuid(@NonNull String xuid);
/** /**
* Gets the session from the given * Method to determine if the given <b>online</b> player is a Bedrock player.
* name, if applicable.
* *
* @param name the uuid of the session * @param uuid the uuid of the online player
* @return the session from the given name, if applicable * @return true if the given online player is a Bedrock player
*/ */
@Nullable boolean isBedrockPlayer(@NonNull UUID uuid);
Connection connectionByName(@NonNull String name);
/** /**
* Gets all the online sessions. * Sends a form to the given connection and opens it.
* *
* @return all the online sessions * @param uuid the uuid of the connection to open it on
* @param form the form to send
* @return whether the form was successfully sent
*/
boolean sendForm(@NonNull UUID uuid, @NonNull Form form);
/**
* Sends a form to the given connection and opens it.
*
* @param uuid the uuid of the connection to open it on
* @param formBuilder the formBuilder to send
* @return whether the form was successfully sent
*/
boolean sendForm(@NonNull UUID uuid, @NonNull FormBuilder<?, ?, ?> formBuilder);
/**
* Transfer the given connection to a server. A Bedrock player can successfully transfer to the same server they are
* currently playing on.
*
* @param uuid the uuid of the connection
* @param address the address of the server
* @param port the port of the server
* @return true if the transfer was a success
*/
boolean transfer(@NonNull UUID uuid, @NonNull String address, @IntRange(from = 0, to = 65535) int port);
/**
* Returns all the online connections.
*/ */
@NonNull @NonNull
List<? extends Connection> onlineConnections(); List<? extends Connection> onlineConnections();
/** /**
* @return the major API version. Bumped whenever a significant breaking change or feature addition is added. * Returns the amount of online connections.
*/
int onlineConnectionsCount();
/**
* Returns the prefix used by Floodgate. Will be null when the auth-type isn't Floodgate.
*/
@MonotonicNonNull
String usernamePrefix();
/**
* Returns the major API version. Bumped whenever a significant breaking change or feature addition is added.
*/ */
default int majorApiVersion() { default int majorApiVersion() {
return 1; return 1;
} }
/** /**
* @return the minor API version. May be bumped for new API additions. * Returns the minor API version. May be bumped for new API additions.
*/ */
default int minorApiVersion() { default int minorApiVersion() {
return 0; return 0;

Datei anzeigen

@ -25,43 +25,96 @@
package org.geysermc.api.connection; package org.geysermc.api.connection;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.common.value.qual.IntRange; import org.checkerframework.common.value.qual.IntRange;
import org.geysermc.api.util.BedrockPlatform;
import org.geysermc.api.util.InputMode;
import org.geysermc.api.util.UiProfile;
import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormBuilder;
import java.util.UUID; import java.util.UUID;
/** /**
* Represents a player connection. * Represents a player connection.
*/ */
@NonNull
public interface Connection { public interface Connection {
/** /**
* Gets the name of the connection. * Returns the bedrock name of the connection.
*
* @return the name of the connection
*/ */
String name(); @NonNull String bedrockUsername();
/** /**
* Gets the {@link UUID} of the connection. * Returns the java name of the connection.
*
* @return the UUID of the connection
*/ */
UUID uuid(); @MonotonicNonNull
String javaUsername();
/** /**
* Gets the XUID of the connection. * Returns the UUID of the connection.
*
* @return the XUID of the connection
*/ */
String xuid(); @MonotonicNonNull
UUID javaUuid();
/**
* Returns the XUID of the connection.
*/
@NonNull String xuid();
/**
* Returns the version of the Bedrock client.
*/
@NonNull String version();
/**
* Returns the platform that the connection is playing on.
*/
@NonNull BedrockPlatform platform();
/**
* Returns the language code of the connection.
*/
@NonNull String languageCode();
/**
* Returns the User Interface Profile of the connection.
*/
@NonNull UiProfile uiProfile();
/**
* Returns the Input Mode of the Bedrock client.
*/
@NonNull InputMode inputMode();
/**
* Returns whether the connection is linked.
* This will always return false when the auth-type isn't Floodgate.
*/
boolean isLinked();
/**
* Sends a form to the connection and opens it.
*
* @param form the form to send
* @return whether the form was successfully sent
*/
boolean sendForm(@NonNull Form form);
/**
* Sends a form to the connection and opens it.
*
* @param formBuilder the formBuilder to send
* @return whether the form was successfully sent
*/
boolean sendForm(@NonNull FormBuilder<?, ?, ?> formBuilder);
/** /**
* Transfer the connection to a server. A Bedrock player can successfully transfer to the same server they are * Transfer the connection to a server. A Bedrock player can successfully transfer to the same server they are
* currently playing on. * currently playing on.
* *
* @param address The address of the server * @param address the address of the server
* @param port The port of the server * @param port the port of the server
* @return true if the transfer was a success * @return true if the transfer was a success
*/ */
boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port); boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port);

Datei anzeigen

@ -23,60 +23,51 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.api.event; package org.geysermc.api.util;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.extension.Extension;
/** public enum BedrockPlatform {
* Represents a subscribed listener to a {@link Event}. Wraps around UNKNOWN("Unknown"),
* the event and is capable of unsubscribing from the event or give GOOGLE("Android"),
* information about it. IOS("iOS"),
* OSX("macOS"),
* @param <T> the class of the event AMAZON("Amazon"),
*/ GEARVR("Gear VR"),
public interface EventSubscription<T extends Event> { HOLOLENS("Hololens"),
UWP("Windows"),
WIN32("Windows x86"),
DEDICATED("Dedicated"),
TVOS("Apple TV"),
PS4("PS4"),
NX("Switch"),
XBOX("Xbox One"),
WINDOWS_PHONE("Windows Phone");
/** private static final BedrockPlatform[] VALUES = values();
* Gets the event class.
*
* @return the event class
*/
@NonNull
Class<T> eventClass();
/** private final String displayName;
* Gets the {@link Extension} that owns this
* event subscription.
*
* @return the extension that owns this subscription
*/
@NonNull
Extension owner();
/** BedrockPlatform(String displayName) {
* Gets the post order of this event subscription. this.displayName = displayName;
* }
* @return the post order of this event subscription
*/ /**
Subscribe.PostOrder order(); * Get the BedrockPlatform from the identifier.
*
/** * @param id the BedrockPlatform identifier
* Gets if this event subscription is active. * @return The BedrockPlatform or {@link #UNKNOWN} if the platform wasn't found
* */
* @return if this event subscription is active @NonNull
*/ public static BedrockPlatform fromId(int id) {
boolean isActive(); return id < VALUES.length ? VALUES[id] : VALUES[0];
}
/**
* Unsubscribes from this event listener /**
*/ * @return friendly display name of platform.
void unsubscribe(); */
@Override
/** public String toString() {
* Invokes the given event return displayName;
* }
* @param event the event
*/
void invoke(@NonNull T event) throws Throwable;
} }

Datei anzeigen

@ -0,0 +1,49 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.api.util;
import org.checkerframework.checker.nullness.qual.NonNull;
public enum InputMode {
UNKNOWN,
KEYBOARD_MOUSE,
TOUCH,
CONTROLLER,
VR;
private static final InputMode[] VALUES = values();
/**
* Get the InputMode from the identifier.
*
* @param id the InputMode identifier
* @return The InputMode or {@link #UNKNOWN} if the mode wasn't found
*/
@NonNull
public static InputMode fromId(int id) {
return VALUES.length > id ? VALUES[id] : VALUES[0];
}
}

Datei anzeigen

@ -23,19 +23,23 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.platform.standalone.command; package org.geysermc.api.util;
import org.geysermc.geyser.GeyserImpl; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.command.GeyserCommandManager;
public class GeyserStandaloneCommandManager extends GeyserCommandManager { public enum UiProfile {
CLASSIC, POCKET;
public GeyserStandaloneCommandManager(GeyserImpl geyser) { private static final UiProfile[] VALUES = values();
super(geyser);
}
@Override /**
public String description(String command) { * Get the UiProfile from the identifier.
return ""; // this is not sent over the protocol, so we return none *
* @param id the UiProfile identifier
* @return The UiProfile or {@link #CLASSIC} if the profile wasn't found
*/
@NonNull
public static UiProfile fromId(int id) {
return VALUES.length > id ? VALUES[id] : VALUES[0];
} }
} }

Datei anzeigen

@ -29,9 +29,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.api.Geyser; import org.geysermc.api.Geyser;
import org.geysermc.api.GeyserApiBase; import org.geysermc.api.GeyserApiBase;
import org.geysermc.geyser.api.command.CommandManager;
import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventBus;
import org.geysermc.geyser.api.event.EventRegistrar;
import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.extension.ExtensionManager;
import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.BedrockListener;
import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.api.network.RemoteServer;
@ -55,12 +55,6 @@ public interface GeyserApi extends GeyserApiBase {
@Override @Override
@Nullable GeyserConnection connectionByXuid(@NonNull String xuid); @Nullable GeyserConnection connectionByXuid(@NonNull String xuid);
/**
* {@inheritDoc}
*/
@Override
@Nullable GeyserConnection connectionByName(@NonNull String name);
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -72,15 +66,9 @@ public interface GeyserApi extends GeyserApiBase {
* *
* @return the extension manager * @return the extension manager
*/ */
@NonNull
ExtensionManager extensionManager(); ExtensionManager extensionManager();
/**
* Gets the {@link CommandManager}.
*
* @return the command manager
*/
CommandManager commandManager();
/** /**
* Provides an implementation for the specified API type. * Provides an implementation for the specified API type.
* *
@ -98,7 +86,8 @@ public interface GeyserApi extends GeyserApiBase {
* *
* @return the event bus * @return the event bus
*/ */
EventBus eventBus(); @NonNull
EventBus<EventRegistrar> eventBus();
/** /**
* Gets the default {@link RemoteServer} configured * Gets the default {@link RemoteServer} configured
@ -106,6 +95,7 @@ public interface GeyserApi extends GeyserApiBase {
* *
* @return the default remote server used within Geyser * @return the default remote server used within Geyser
*/ */
@NonNull
RemoteServer defaultRemoteServer(); RemoteServer defaultRemoteServer();
/** /**
@ -114,6 +104,7 @@ public interface GeyserApi extends GeyserApiBase {
* *
* @return the listener used for Bedrock client connectins * @return the listener used for Bedrock client connectins
*/ */
@NonNull
BedrockListener bedrockListener(); BedrockListener bedrockListener();
/** /**
@ -121,6 +112,7 @@ public interface GeyserApi extends GeyserApiBase {
* *
* @return the current geyser api instance * @return the current geyser api instance
*/ */
@NonNull
static GeyserApi api() { static GeyserApi api() {
return Geyser.api(GeyserApi.class); return Geyser.api(GeyserApi.class);
} }

Datei anzeigen

@ -27,6 +27,8 @@ package org.geysermc.geyser.api.command;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.GeyserApi;
import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.extension.Extension;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -104,19 +106,39 @@ public interface Command {
return false; return false;
} }
static <T extends CommandSource> Command.Builder<T> builder(Class<T> sourceType) { /**
return GeyserApi.api().provider(Builder.class, sourceType); * Creates a new {@link Command.Builder} used to construct commands.
*
* @param extension the extension
* @param <T> the source type
* @return a new command builder used to construct commands
*/
static <T extends CommandSource> Command.Builder<T> builder(@NonNull Extension extension) {
return GeyserApi.api().provider(Builder.class, extension);
} }
interface Builder<T extends CommandSource> { interface Builder<T extends CommandSource> {
/**
* Defines the source type to use for this command.
* <p>
* Command source types can be anything that extend
* {@link CommandSource}, such as {@link GeyserConnection}.
* This will guarantee that the source used in the executor
* is an instance of this source.
*
* @param sourceType the source type
* @return the builder
*/
Builder<T> source(@NonNull Class<? extends T> sourceType);
/** /**
* Sets the command name. * Sets the command name.
* *
* @param name the command name * @param name the command name
* @return the builder * @return the builder
*/ */
Builder<T> name(String name); Builder<T> name(@NonNull String name);
/** /**
* Sets the command description. * Sets the command description.
@ -124,7 +146,7 @@ public interface Command {
* @param description the command description * @param description the command description
* @return the builder * @return the builder
*/ */
Builder<T> description(String description); Builder<T> description(@NonNull String description);
/** /**
* Sets the permission node. * Sets the permission node.
@ -132,7 +154,7 @@ public interface Command {
* @param permission the permission node * @param permission the permission node
* @return the builder * @return the builder
*/ */
Builder<T> permission(String permission); Builder<T> permission(@NonNull String permission);
/** /**
* Sets the aliases. * Sets the aliases.
@ -140,7 +162,7 @@ public interface Command {
* @param aliases the aliases * @param aliases the aliases
* @return the builder * @return the builder
*/ */
Builder<T> aliases(List<String> aliases); Builder<T> aliases(@NonNull List<String> aliases);
/** /**
* Sets if this command is designed to be used only by server operators. * Sets if this command is designed to be used only by server operators.
@ -164,7 +186,7 @@ public interface Command {
* @param subCommands the subcommands * @param subCommands the subcommands
* @return the builder * @return the builder
*/ */
Builder<T> subCommands(List<String> subCommands); Builder<T> subCommands(@NonNull List<String> subCommands);
/** /**
* Sets if this command is bedrock only. * Sets if this command is bedrock only.
@ -180,13 +202,14 @@ public interface Command {
* @param executor the command executor * @param executor the command executor
* @return the builder * @return the builder
*/ */
Builder<T> executor(CommandExecutor<T> executor); Builder<T> executor(@NonNull CommandExecutor<T> executor);
/** /**
* Builds the command. * Builds the command.
* *
* @return the command * @return the command
*/ */
@NonNull
Command build(); Command build();
} }
} }

Datei anzeigen

@ -25,13 +25,14 @@
package org.geysermc.geyser.api.command; package org.geysermc.geyser.api.command;
import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* Handles executing a command. * Handles executing a command.
* *
* @param <T> the command source * @param <T> the command source
*/ */
public interface CommandExecutor<T extends CommandSource> { public interface CommandExecutor<T extends CommandSource> {
/** /**
* Executes the given {@link Command} with the given * Executes the given {@link Command} with the given
* {@link CommandSource}. * {@link CommandSource}.
@ -40,5 +41,5 @@ public interface CommandExecutor<T extends CommandSource> {
* @param command the command * @param command the command
* @param args the arguments * @param args the arguments
*/ */
void execute(T source, Command command, String[] args); void execute(@NonNull T source, @NonNull Command command, @NonNull String[] args);
} }

Datei anzeigen

@ -25,6 +25,8 @@
package org.geysermc.geyser.api.command; package org.geysermc.geyser.api.command;
import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* Represents an instance capable of sending commands. * Represents an instance capable of sending commands.
*/ */
@ -42,7 +44,7 @@ public interface CommandSource {
* *
* @param message the message to send * @param message the message to send
*/ */
void sendMessage(String message); void sendMessage(@NonNull String message);
/** /**
* Sends the given messages to the command source * Sends the given messages to the command source

Datei anzeigen

@ -26,71 +26,18 @@
package org.geysermc.geyser.api.event; package org.geysermc.geyser.api.event;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.event.Event;
import org.geysermc.event.bus.OwnedEventBus;
import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.Extension;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
/** /**
* Represents a bus capable of subscribing * Represents a bus capable of subscribing
* or "listening" to events and firing them. * or "listening" to events and firing them.
*/ */
public interface EventBus { public interface EventBus<R extends EventRegistrar> extends OwnedEventBus<R, Event, EventSubscriber<R, ? extends Event>> {
@Override
/**
* Subscribes to the given event see {@link EventSubscription}.
*
* The difference between this method and {@link ExtensionEventBus#subscribe(Class, Consumer)}
* is that this method takes in an extension parameter which allows for
* the event to be unsubscribed upon extension disable and reloads.
*
* @param extension the extension to subscribe the event to
* @param eventClass the class of the event
* @param consumer the consumer for handling the event
* @param <T> the event class
* @return the event subscription
*/
@NonNull @NonNull
<T extends Event> EventSubscription<T> subscribe(@NonNull Extension extension, @NonNull Class<T> eventClass, @NonNull Consumer<? super T> consumer); <T extends Event> Set<? extends EventSubscriber<R, T>> subscribers(@NonNull Class<T> eventClass);
/**
* Unsubscribes the given {@link EventSubscription}.
*
* @param subscription the event subscription
*/
<T extends Event> void unsubscribe(@NonNull EventSubscription<T> subscription);
/**
* Registers events for the given listener.
*
* @param extension the extension registering the event
* @param eventHolder the listener
*/
void register(@NonNull Extension extension, @NonNull Object eventHolder);
/**
* Unregisters all events from a given {@link Extension}.
*
* @param extension the extension
*/
void unregisterAll(@NonNull Extension extension);
/**
* Fires the given {@link Event} and returns the result.
*
* @param event the event to fire
*
* @return true if the event successfully fired
*/
boolean fire(@NonNull Event event);
/**
* Gets the subscriptions for the given event class.
*
* @param eventClass the event class
* @param <T> the value
* @return the subscriptions for the event class
*/
@NonNull
<T extends Event> Set<EventSubscription<T>> subscriptions(@NonNull Class<T> eventClass);
} }

Datei anzeigen

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.event;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.GeyserApi;
/**
* Represents an owner for an event that allows it
* to be registered through an {@link EventBus}.
*/
public interface EventRegistrar {
/**
* Creates an {@link EventRegistrar} instance.
*
* @param object the object to wrap around
* @return an event registrar instance
*/
@NonNull
static EventRegistrar of(@NonNull Object object) {
return GeyserApi.api().provider(EventRegistrar.class, object);
}
}

Datei anzeigen

@ -0,0 +1,40 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.event;
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
* the event and is capable of unsubscribing from the event or give
* information about it.
*
* @param <T> the class of the event
*/
public interface EventSubscriber<R extends EventRegistrar, T extends Event> extends OwnedSubscriber<R, T> {
}

Datei anzeigen

@ -26,36 +26,16 @@
package org.geysermc.geyser.api.event; package org.geysermc.geyser.api.event;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.event.Event;
import org.geysermc.geyser.api.extension.Extension;
import java.util.function.Consumer; import java.util.Set;
/** /**
* An {@link EventBus} with additional methods that implicitly * An {@link EventBus} with additional methods that implicitly
* set the extension instance. * set the extension instance.
*
*/ */
public interface ExtensionEventBus extends EventBus { public interface ExtensionEventBus extends org.geysermc.event.bus.EventBus<Event, EventSubscriber<Extension, ? extends Event>> {
@Override
/** @NonNull <T extends Event> Set<? extends EventSubscriber<EventRegistrar, T>> subscribers(@NonNull Class<T> eventClass);
* Subscribes to the given event see {@link EventSubscription}.
*
* @param eventClass the class of the event
* @param consumer the consumer for handling the event
* @param <T> the event class
* @return the event subscription
*/
@NonNull
<T extends Event> EventSubscription<T> subscribe(@NonNull Class<T> eventClass, @NonNull Consumer<? super T> consumer);
/**
* Registers events for the given listener.
*
* @param eventHolder the listener
*/
void register(@NonNull Object eventHolder);
/**
* Unregisters all events for this extension.
*/
void unregisterAll();
} }

Datei anzeigen

@ -25,17 +25,8 @@
package org.geysermc.geyser.api.event; package org.geysermc.geyser.api.event;
/** import org.geysermc.event.Event;
* Represents an event. import org.geysermc.event.subscribe.Subscriber;
*/
public interface Event {
/** public interface ExtensionEventSubscriber<T extends Event> extends Subscriber<T> {
* Gets if the event is async.
*
* @return if the event is async
*/
default boolean isAsync() {
return false;
}
} }

Datei anzeigen

@ -1,103 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.event;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation used to signify the given method is
* an {@link Event}. Only should be applied to methods
* where the class containing them is designated for
* events specifically.
*
* When using {@link EventBus#subscribe}, this annotation should
* not be applied whatsoever as it will have no use and potentially
* throw errors due to it being used wrongly.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Subscribe {
/**
* The {@link PostOrder} of the event
*
* @return the post order of the event
*/
Subscribe.PostOrder postOrder() default PostOrder.NORMAL;
/**
* Represents the post order of an event.
*/
enum PostOrder {
/**
* The lowest priority. Called first to
* allow for other events to customize
* the outcome
*/
FIRST(net.kyori.event.PostOrders.FIRST),
/**
* The second lowest priority.
*/
EARLY(net.kyori.event.PostOrders.EARLY),
/**
* Normal priority. Event is neither
* important nor unimportant
*/
NORMAL(net.kyori.event.PostOrders.NORMAL),
/**
* The second highest priority
*/
LATE(net.kyori.event.PostOrders.LATE),
/**
* The highest of importance! Event is called
* last and has the final say in the outcome
*/
LAST(net.kyori.event.PostOrders.LAST);
private final int postOrder;
PostOrder(int postOrder) {
this.postOrder = postOrder;
}
/**
* The numerical post order value.
*
* @return numerical post order value
*/
public int postOrder() {
return this.postOrder;
}
}
}

Datei anzeigen

@ -26,8 +26,8 @@
package org.geysermc.geyser.api.event.connection; package org.geysermc.geyser.api.event.connection;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.event.Event;
import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.event.Event;
/** /**
* An event that contains a {@link GeyserConnection}. * An event that contains a {@link GeyserConnection}.

Datei anzeigen

@ -26,8 +26,8 @@
package org.geysermc.geyser.api.event.downstream; package org.geysermc.geyser.api.event.downstream;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.event.Cancellable;
import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.event.Cancellable;
import org.geysermc.geyser.api.event.connection.ConnectionEvent; import org.geysermc.geyser.api.event.connection.ConnectionEvent;
import java.util.Set; import java.util.Set;

Datei anzeigen

@ -23,36 +23,35 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.api.command; package org.geysermc.geyser.api.event.lifecycle;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.event.Event;
import org.geysermc.geyser.api.command.Command;
import java.util.Map; import java.util.Map;
/** /**
* Manages Bedrock commands within Geyser. * Called when commands are defined within Geyser.
*
* This event allows you to register new commands using the {@link #register(Command)}
* method and retrieve the default commands defined.
*/ */
public abstract class CommandManager { public interface GeyserDefineCommandsEvent extends Event {
/** /**
* Registers the given {@link Command}. * Registers the given {@link Command} into the Geyser
* command manager.
* *
* @param command the command to register * @param command the command to register
*/ */
public abstract void register(@NonNull Command command); void register(@NonNull Command command);
/** /**
* Unregisters the given {@link Command}. * Gets all the registered built-in {@link Command}s.
* *
* @param command the command to unregister * @return all the registered built-in commands
*/
public abstract void unregister(@NonNull Command command);
/**
* Gets all the registered {@link Command}s.
*
* @return all the registered commands
*/ */
@NonNull @NonNull
public abstract Map<String, Command> commands(); Map<String, Command> commands();
} }

Datei anzeigen

@ -25,45 +25,36 @@
package org.geysermc.geyser.api.event.lifecycle; package org.geysermc.geyser.api.event.lifecycle;
import com.google.common.collect.Multimap;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.event.Event; import org.geysermc.event.Event;
import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomItemData;
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
import java.util.*; import java.util.Collection;
import java.util.List;
import java.util.Map;
/** /**
* Called on Geyser's startup when looking for custom items. Custom items must be registered through this event. * Called on Geyser's startup when looking for custom items. Custom items must be registered through this event.
* *
* This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config. * This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config.
*/ */
public abstract class GeyserDefineCustomItemsEvent implements Event { public interface GeyserDefineCustomItemsEvent extends Event {
private final Multimap<String, CustomItemData> customItems;
private final List<NonVanillaCustomItemData> nonVanillaCustomItems;
public GeyserDefineCustomItemsEvent(Multimap<String, CustomItemData> customItems, List<NonVanillaCustomItemData> nonVanillaCustomItems) {
this.customItems = customItems;
this.nonVanillaCustomItems = nonVanillaCustomItems;
}
/** /**
* Gets a multimap of all the already registered custom items indexed by the item's extended java item's identifier. * Gets a multimap of all the already registered custom items indexed by the item's extended java item's identifier.
* *
* @return a multimap of all the already registered custom items * @return a multimap of all the already registered custom items
*/ */
public Map<String, Collection<CustomItemData>> getExistingCustomItems() { @NonNull
return Collections.unmodifiableMap(this.customItems.asMap()); Map<String, Collection<CustomItemData>> getExistingCustomItems();
}
/** /**
* Gets the list of the already registered non-vanilla custom items. * Gets the list of the already registered non-vanilla custom items.
* *
* @return the list of the already registered non-vanilla custom items * @return the list of the already registered non-vanilla custom items
*/ */
public List<NonVanillaCustomItemData> getExistingNonVanillaCustomItems() { @NonNull
return Collections.unmodifiableList(this.nonVanillaCustomItems); List<NonVanillaCustomItemData> getExistingNonVanillaCustomItems();
}
/** /**
* Registers a custom item with a base Java item. This is used to register items with custom textures and properties * Registers a custom item with a base Java item. This is used to register items with custom textures and properties
@ -73,7 +64,7 @@ public abstract class GeyserDefineCustomItemsEvent implements Event {
* @param customItemData the custom item data to register * @param customItemData the custom item data to register
* @return if the item was registered * @return if the item was registered
*/ */
public abstract boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData); boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData);
/** /**
* Registers a custom item with no base item. This is used for mods. * Registers a custom item with no base item. This is used for mods.
@ -81,5 +72,5 @@ public abstract class GeyserDefineCustomItemsEvent implements Event {
* @param customItemData the custom item data to register * @param customItemData the custom item data to register
* @return if the item was registered * @return if the item was registered
*/ */
public abstract boolean register(@NonNull NonVanillaCustomItemData customItemData); boolean register(@NonNull NonVanillaCustomItemData customItemData);
} }

Datei anzeigen

@ -26,7 +26,7 @@
package org.geysermc.geyser.api.event.lifecycle; package org.geysermc.geyser.api.event.lifecycle;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.event.Event; import org.geysermc.event.Event;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;

Datei anzeigen

@ -26,8 +26,9 @@
package org.geysermc.geyser.api.event.lifecycle; package org.geysermc.geyser.api.event.lifecycle;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.event.Event; import org.geysermc.event.Event;
import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventBus;
import org.geysermc.geyser.api.event.EventRegistrar;
import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.extension.ExtensionManager;
/** /**
@ -36,5 +37,5 @@ import org.geysermc.geyser.api.extension.ExtensionManager;
* @param extensionManager the extension manager * @param extensionManager the extension manager
* @param eventBus the event bus * @param eventBus the event bus
*/ */
public record GeyserPostInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { public record GeyserPostInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
} }

Datei anzeigen

@ -26,8 +26,9 @@
package org.geysermc.geyser.api.event.lifecycle; package org.geysermc.geyser.api.event.lifecycle;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.event.Event; import org.geysermc.event.Event;
import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventBus;
import org.geysermc.geyser.api.event.EventRegistrar;
import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.extension.ExtensionManager;
/** /**
@ -36,5 +37,5 @@ import org.geysermc.geyser.api.extension.ExtensionManager;
* @param extensionManager the extension manager * @param extensionManager the extension manager
* @param eventBus the event bus * @param eventBus the event bus
*/ */
public record GeyserPreInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus eventBus) implements Event { public record GeyserPreInitializeEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
} }

Datei anzeigen

@ -26,13 +26,13 @@
package org.geysermc.geyser.api.event.lifecycle; package org.geysermc.geyser.api.event.lifecycle;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.command.CommandManager; import org.geysermc.event.Event;
import org.geysermc.geyser.api.event.Event;
import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventBus;
import org.geysermc.geyser.api.event.EventRegistrar;
import org.geysermc.geyser.api.extension.ExtensionManager; import org.geysermc.geyser.api.extension.ExtensionManager;
/** /**
* Called when Geyser is shutting down. * Called when Geyser is shutting down.
*/ */
public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull CommandManager commandManager, @NonNull EventBus eventBus) implements Event { public record GeyserShutdownEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
} }

Datei anzeigen

@ -25,16 +25,19 @@
package org.geysermc.geyser.api.extension; package org.geysermc.geyser.api.extension;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.api.GeyserApiBase; import org.geysermc.api.GeyserApiBase;
import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.GeyserApi;
import org.geysermc.geyser.api.event.EventRegistrar;
import org.geysermc.geyser.api.event.ExtensionEventBus; import org.geysermc.geyser.api.event.ExtensionEventBus;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Objects;
/** /**
* Represents an extension within Geyser. * Represents an extension within Geyser.
*/ */
public interface Extension { public interface Extension extends EventRegistrar {
/** /**
* Gets if the extension is enabled * Gets if the extension is enabled
@ -59,6 +62,7 @@ public interface Extension {
* *
* @return the extension's data folder * @return the extension's data folder
*/ */
@NonNull
default Path dataFolder() { default Path dataFolder() {
return this.extensionLoader().dataFolder(this); return this.extensionLoader().dataFolder(this);
} }
@ -68,6 +72,7 @@ public interface Extension {
* *
* @return the extension event bus * @return the extension event bus
*/ */
@NonNull
default ExtensionEventBus eventBus() { default ExtensionEventBus eventBus() {
return this.extensionLoader().eventBus(this); return this.extensionLoader().eventBus(this);
} }
@ -77,6 +82,7 @@ public interface Extension {
* *
* @return the extension manager * @return the extension manager
*/ */
@NonNull
default ExtensionManager extensionManager() { default ExtensionManager extensionManager() {
return this.geyserApi().extensionManager(); return this.geyserApi().extensionManager();
} }
@ -86,6 +92,7 @@ public interface Extension {
* *
* @return the extension's name * @return the extension's name
*/ */
@NonNull
default String name() { default String name() {
return this.description().name(); return this.description().name();
} }
@ -95,6 +102,7 @@ public interface Extension {
* *
* @return the extension's description * @return the extension's description
*/ */
@NonNull
default ExtensionDescription description() { default ExtensionDescription description() {
return this.extensionLoader().description(this); return this.extensionLoader().description(this);
} }
@ -104,6 +112,7 @@ public interface Extension {
* *
* @return the extension's logger * @return the extension's logger
*/ */
@NonNull
default ExtensionLogger logger() { default ExtensionLogger logger() {
return this.extensionLoader().logger(this); return this.extensionLoader().logger(this);
} }
@ -113,8 +122,9 @@ public interface Extension {
* *
* @return the extension loader * @return the extension loader
*/ */
@NonNull
default ExtensionLoader extensionLoader() { default ExtensionLoader extensionLoader() {
return this.extensionManager().extensionLoader(this); return Objects.requireNonNull(this.extensionManager().extensionLoader());
} }
/** /**
@ -122,6 +132,7 @@ public interface Extension {
* *
* @return the geyser api instance * @return the geyser api instance
*/ */
@NonNull
default GeyserApi geyserApi() { default GeyserApi geyserApi() {
return GeyserApi.api(); return GeyserApi.api();
} }

Datei anzeigen

@ -30,12 +30,20 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List; import java.util.List;
/** /**
* This is the Geyser extension description * Represents the description of an {@link Extension}.
*/ */
public interface ExtensionDescription { public interface ExtensionDescription {
/** /**
* Gets the extension's name * Gets the extension's id.
*
* @return the extension's id
*/
@NonNull
String id();
/**
* Gets the extension's name.
* *
* @return the extension's name * @return the extension's name
*/ */
@ -43,7 +51,7 @@ public interface ExtensionDescription {
String name(); String name();
/** /**
* Gets the extension's main class * Gets the extension's main class.
* *
* @return the extension's main class * @return the extension's main class
*/ */
@ -51,15 +59,37 @@ public interface ExtensionDescription {
String main(); String main();
/** /**
* Gets the extension's api version * Gets the extension's major api version
*
* @return the extension's major api version
*/
int majorApiVersion();
/**
* Gets the extension's minor api version
*
* @return the extension's minor api version
*/
int minorApiVersion();
/**
* Gets the extension's patch api version
*
* @return the extension's patch api version
*/
int patchApiVersion();
/**
* Gets the extension's api version.
* *
* @return the extension's api version * @return the extension's api version
*/ */
@NonNull default String apiVersion() {
String apiVersion(); return majorApiVersion() + "." + minorApiVersion() + "." + patchApiVersion();
}
/** /**
* Gets the extension's description * Gets the extension's description.
* *
* @return the extension's description * @return the extension's description
*/ */
@ -67,7 +97,7 @@ public interface ExtensionDescription {
String version(); String version();
/** /**
* Gets the extension's authors * Gets the extension's authors.
* *
* @return the extension's authors * @return the extension's authors
*/ */

Datei anzeigen

@ -34,7 +34,6 @@ import java.nio.file.Path;
* The extension loader is responsible for loading, unloading, enabling and disabling extensions * The extension loader is responsible for loading, unloading, enabling and disabling extensions
*/ */
public abstract class ExtensionLoader { public abstract class ExtensionLoader {
/** /**
* Gets if the given {@link Extension} is enabled. * Gets if the given {@link Extension} is enabled.
* *
@ -101,6 +100,6 @@ public abstract class ExtensionLoader {
* @param extensionManager the extension manager * @param extensionManager the extension manager
*/ */
protected void register(@NonNull Extension extension, @NonNull ExtensionManager extensionManager) { protected void register(@NonNull Extension extension, @NonNull ExtensionManager extensionManager) {
extensionManager.register(extension, this); extensionManager.register(extension);
} }
} }

Datei anzeigen

@ -29,7 +29,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
/** /**
* Manages Geyser {@link Extension}s * Manages Geyser {@link Extension}s
@ -59,15 +58,6 @@ public abstract class ExtensionManager {
*/ */
public abstract void disable(@NonNull Extension extension); public abstract void disable(@NonNull Extension extension);
/**
* Gets the {@link ExtensionLoader} responsible for loading
* the given {@link Extension}.
*
* @return the extension loader for loading the given extension
*/
@Nullable
public abstract ExtensionLoader extensionLoader(@NonNull Extension extension);
/** /**
* Gets all the {@link Extension}s currently loaded. * Gets all the {@link Extension}s currently loaded.
* *
@ -77,37 +67,19 @@ public abstract class ExtensionManager {
public abstract Collection<Extension> extensions(); public abstract Collection<Extension> extensions();
/** /**
* Gets the {@link ExtensionLoader} with the given identifier. * Gets the {@link ExtensionLoader}.
* *
* @param identifier the identifier * @return the extension loader
* @return the extension loader at the given identifier
*/ */
@Nullable @Nullable
public abstract ExtensionLoader extensionLoader(@NonNull String identifier); public abstract ExtensionLoader extensionLoader();
/**
* Registers an {@link ExtensionLoader} with the given identifier.
*
* @param identifier the identifier
* @param extensionLoader the extension loader
*/
public abstract void registerExtensionLoader(@NonNull String identifier, @NonNull ExtensionLoader extensionLoader);
/**
* Gets all the currently registered {@link ExtensionLoader}s.
*
* @return all the currently registered extension loaders
*/
@NonNull
public abstract Map<String, ExtensionLoader> extensionLoaders();
/** /**
* Registers an {@link Extension} with the given {@link ExtensionLoader}. * Registers an {@link Extension} with the given {@link ExtensionLoader}.
* *
* @param extension the extension * @param extension the extension
* @param loader the loader
*/ */
public abstract void register(@NonNull Extension extension, @NonNull ExtensionLoader loader); public abstract void register(@NonNull Extension extension);
/** /**
* Loads all extensions from the given {@link ExtensionLoader}. * Loads all extensions from the given {@link ExtensionLoader}.

Datei anzeigen

@ -25,6 +25,8 @@
package org.geysermc.geyser.api.network; package org.geysermc.geyser.api.network;
import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* The listener that handles connections from Minecraft: * The listener that handles connections from Minecraft:
* Bedrock Edition. * Bedrock Edition.
@ -37,6 +39,7 @@ public interface BedrockListener {
* *
* @return the listening address * @return the listening address
*/ */
@NonNull
String address(); String address();
/** /**

Datei anzeigen

@ -25,6 +25,8 @@
package org.geysermc.geyser.api.network; package org.geysermc.geyser.api.network;
import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* Represents the Java server that Geyser is connecting to. * Represents the Java server that Geyser is connecting to.
*/ */
@ -63,5 +65,6 @@ public interface RemoteServer {
* *
* @return the auth type required by the remote server * @return the auth type required by the remote server
*/ */
@NonNull
AuthType authType(); AuthType authType();
} }

Datei anzeigen

@ -1,7 +1,7 @@
val bungeeVersion = "a7c6ede";
dependencies { dependencies {
api(projects.core) api(projects.core)
implementation(libs.adventure.text.serializer.bungeecord)
} }
platformRelocate("net.md_5.bungee.jni") platformRelocate("net.md_5.bungee.jni")
@ -10,7 +10,7 @@ platformRelocate("io.netty.channel.kqueue") // This is not used because relocati
platformRelocate("net.kyori") platformRelocate("net.kyori")
// These dependencies are already present on the platform // These dependencies are already present on the platform
provided("com.github.SpigotMC.BungeeCord", "bungeecord-proxy", bungeeVersion) provided(libs.bungeecord.proxy)
application { application {
mainClass.set("org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain") mainClass.set("org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain")

Datei anzeigen

@ -29,7 +29,6 @@ import lombok.Getter;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.text.AsteriskSerializer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -52,17 +51,17 @@ public class GeyserBungeeDumpInfo extends BootstrapDumpInfo {
this.plugins = new ArrayList<>(); this.plugins = new ArrayList<>();
for (net.md_5.bungee.api.config.ListenerInfo listener : proxy.getConfig().getListeners()) { for (net.md_5.bungee.api.config.ListenerInfo listener : proxy.getConfig().getListeners()) {
String hostname; this.listeners.add(new ListenerInfo(listener.getHost().getHostString(), listener.getHost().getPort()));
if (AsteriskSerializer.showSensitive || (listener.getHost().getHostString().equals("") || listener.getHost().getHostString().equals("0.0.0.0"))) {
hostname = listener.getHost().getHostString();
} else {
hostname = "***";
}
this.listeners.add(new ListenerInfo(hostname, listener.getHost().getPort()));
} }
for (Plugin plugin : proxy.getPluginManager().getPlugins()) { for (Plugin plugin : proxy.getPluginManager().getPlugins()) {
this.plugins.add(new PluginInfo(true, plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getMain(), Collections.singletonList(plugin.getDescription().getAuthor()))); this.plugins.add(new PluginInfo(
true,
plugin.getDescription().getName(),
plugin.getDescription().getVersion(),
plugin.getDescription().getMain(),
Collections.singletonList(plugin.getDescription().getAuthor()))
);
} }
} }
} }

Datei anzeigen

@ -25,12 +25,17 @@
package org.geysermc.geyser.platform.bungeecord; package org.geysermc.geyser.platform.bungeecord;
import io.netty.channel.Channel;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.protocol.ProtocolConstants;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
@ -38,22 +43,25 @@ import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor; import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor;
import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandManager;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.FileUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Collection;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
private GeyserBungeeCommandManager geyserCommandManager; private GeyserCommandManager geyserCommandManager;
private GeyserBungeeConfiguration geyserConfig; private GeyserBungeeConfiguration geyserConfig;
private GeyserBungeeInjector geyserInjector; private GeyserBungeeInjector geyserInjector;
private GeyserBungeeLogger geyserLogger; private GeyserBungeeLogger geyserLogger;
@ -62,9 +70,23 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
private GeyserImpl geyser; private GeyserImpl geyser;
@Override @Override
public void onEnable() { public void onLoad() {
GeyserLocale.init(this); 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_19_3");
} catch (NoSuchFieldException e) {
getLogger().warning(" / \\");
getLogger().warning(" / \\");
getLogger().warning(" / | \\");
getLogger().warning(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", getProxy().getName()));
getLogger().warning(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
getLogger().warning(" / o \\");
getLogger().warning("/_____________\\");
}
if (!getDataFolder().exists()) if (!getDataFolder().exists())
getDataFolder().mkdir(); getDataFolder().mkdir();
@ -80,6 +102,20 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
return; return;
} }
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
}
@Override
public void onEnable() {
// Remove this in like a year
if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) {
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/"));
return;
}
if (getProxy().getConfig().getListeners().size() == 1) { if (getProxy().getConfig().getListeners().size() == 1) {
ListenerInfo listener = getProxy().getConfig().getListeners().toArray(new ListenerInfo[0])[0]; ListenerInfo listener = getProxy().getConfig().getListeners().toArray(new ListenerInfo[0])[0];
@ -100,13 +136,22 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
} }
} }
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); // Force-disable query if enabled, or else Geyser won't enable
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); for (ListenerInfo info : getProxy().getConfig().getListeners()) {
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
// Remove this in like a year try {
if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) { Field queryField = ListenerInfo.class.getDeclaredField("queryEnabled");
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/")); queryField.setAccessible(true);
return; queryField.setBoolean(info, false);
geyserLogger.warning("We force-disabled query on port " + info.getQueryPort() + " in order for Geyser to boot up successfully. " +
"To remove this message, disable query in your proxy's config.");
} catch (NoSuchFieldException | IllegalAccessException e) {
geyserLogger.warning("Could not force-disable query. Geyser may not start correctly!");
if (geyserLogger.isDebug()) {
e.printStackTrace();
}
}
}
} }
if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) { if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && getProxy().getPluginManager().getPlugin("floodgate") == null) {
@ -120,22 +165,63 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
geyserConfig.loadFloodgate(this); geyserConfig.loadFloodgate(this);
this.geyser = GeyserImpl.start(PlatformType.BUNGEECORD, this); // 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.postStartup();
return;
}
try {
Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners");
listenersField.setAccessible(true);
Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
if (listeners.isEmpty()) {
this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS);
} else {
this.awaitStartupCompletion(++tries);
}
} catch (NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
}
}
private void postStartup() {
GeyserImpl.start();
this.geyserInjector = new GeyserBungeeInjector(this); this.geyserInjector = new GeyserBungeeInjector(this);
this.geyserInjector.initializeLocalChannel(this); this.geyserInjector.initializeLocalChannel(this);
this.geyserCommandManager = new GeyserBungeeCommandManager(geyser); this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init(); 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();
if (commands.isEmpty()) {
continue;
}
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands));
}
if (geyserConfig.isLegacyPingPassthrough()) { if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser); this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else { } else {
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy()); this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
} }
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", geyser, geyserCommandManager.getCommands()));
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyserext", geyser, geyserCommandManager.commands()));
} }
@Override @Override

Datei anzeigen

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.bungeecord;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource;
import org.geysermc.geyser.util.VersionCheckUtils;
public final class GeyserBungeeUpdateListener implements Listener {
@EventHandler
public void onPlayerJoin(final PostLoginEvent event) {
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
final ProxiedPlayer player = event.getPlayer();
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player));
}
}
}
}

Datei anzeigen

@ -25,11 +25,15 @@
package org.geysermc.geyser.platform.bungeecord.command; package org.geysermc.geyser.platform.bungeecord.command;
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.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import java.util.Locale;
public class BungeeCommandSource implements GeyserCommandSource { public class BungeeCommandSource implements GeyserCommandSource {
private final net.md_5.bungee.api.CommandSender handle; private final net.md_5.bungee.api.CommandSender handle;
@ -50,6 +54,18 @@ public class BungeeCommandSource implements GeyserCommandSource {
handle.sendMessage(TextComponent.fromLegacyText(message)); handle.sendMessage(TextComponent.fromLegacyText(message));
} }
private static final int PROTOCOL_HEX_COLOR = 713; // Added 20w17a
@Override
public void sendMessage(Component message) {
if (handle instanceof ProxiedPlayer player && player.getPendingConnection().getVersion() >= PROTOCOL_HEX_COLOR) {
// Include hex colors
handle.sendMessage(BungeeComponentSerializer.get().serialize(message));
return;
}
handle.sendMessage(BungeeComponentSerializer.legacy().serialize(message));
}
@Override @Override
public boolean isConsole() { public boolean isConsole() {
return !(handle instanceof ProxiedPlayer); return !(handle instanceof ProxiedPlayer);
@ -58,8 +74,11 @@ public class BungeeCommandSource implements GeyserCommandSource {
@Override @Override
public String locale() { public String locale() {
if (handle instanceof ProxiedPlayer player) { if (handle instanceof ProxiedPlayer player) {
String locale = player.getLocale().getLanguage() + "_" + player.getLocale().getCountry(); Locale locale = player.getLocale();
return GeyserLocale.formatLocale(locale); if (locale != null) {
// Locale can be null early on in the conneciton
return GeyserLocale.formatLocale(locale.getLanguage() + "_" + locale.getCountry());
}
} }
return GeyserLocale.getDefaultLocale(); return GeyserLocale.getDefaultLocale();
} }

Datei anzeigen

@ -69,6 +69,9 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor
return; return;
} }
command.execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]); command.execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
} else {
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.not_found", commandSender.locale());
commandSender.sendMessage(ChatColor.RED + message);
} }
} else { } else {
this.commandExecutor.getCommand("help").execute(session, commandSender, new String[0]); this.commandExecutor.getCommand("help").execute(session, commandSender, new String[0]);

Datei anzeigen

@ -0,0 +1,77 @@
plugins {
id("fabric-loom") version "1.0-SNAPSHOT"
}
java {
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_17
}
dependencies {
//to change the versions see the gradle.properties file
minecraft(libs.fabric.minecraft)
mappings(loom.officialMojangMappings())
modImplementation(libs.fabric.loader)
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation(libs.fabric.api)
// This should be in the libs TOML, but something about modImplementation AND include just doesn't work
include(modImplementation("me.lucko", "fabric-permissions-api", "0.2-SNAPSHOT"))
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
api(projects.core)
shadow(projects.core) {
exclude(group = "com.google.guava", module = "guava")
exclude(group = "com.google.code.gson", module = "gson")
exclude(group = "org.slf4j")
exclude(group = "com.nukkitx.fastutil")
exclude(group = "io.netty.incubator")
}
}
repositories {
mavenLocal()
maven("https://repo.opencollab.dev/maven-releases/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://jitpack.io")
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
application {
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
}
tasks {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
sourcesJar {
archiveClassifier.set("sources")
from(sourceSets.main.get().allSource)
}
shadowJar {
// Mirrors the example fabric project, otherwise tons of dependencies are shaded that shouldn't be
configurations = listOf(project.configurations.shadow.get())
// The remapped shadowJar is the final desired Geyser-Fabric.jar
archiveVersion.set(project.version.toString())
archiveClassifier.set("shaded")
relocate("org.objectweb.asm", "org.geysermc.relocate.asm")
relocate("org.yaml", "org.geysermc.relocate.yaml") // https://github.com/CardboardPowered/cardboard/issues/139
relocate("com.fasterxml.jackson", "org.geysermc.relocate.jackson")
relocate("net.kyori", "org.geysermc.relocate.kyori")
}
remapJar {
dependsOn(shadowJar)
inputFile.set(shadowJar.get().archiveFile)
archiveBaseName.set("Geyser-Fabric")
archiveClassifier.set("")
archiveVersion.set("")
}
}

Datei anzeigen

@ -23,34 +23,29 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.platform.spigot.world.manager; package org.geysermc.geyser.platform.fabric;
import org.bukkit.plugin.Plugin; import com.fasterxml.jackson.annotation.JsonIgnore;
import org.geysermc.geyser.level.block.BlockStateValues; import net.fabricmc.loader.api.FabricLoader;
import org.geysermc.geyser.session.GeyserSession; import net.fabricmc.loader.api.ModContainer;
import org.geysermc.geyser.FloodgateKeyLoader;
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
/** import java.nio.file.Path;
* Should only be used when we know {@link GeyserSpigotWorldManager#getBlockAt(GeyserSession, int, int, int)}
* cannot be accurate. Typically, this is when ViaVersion is not installed but a client still manages to connect. public class GeyserFabricConfiguration extends GeyserJacksonConfiguration {
* If this occurs to you somehow, please let us know!! @JsonIgnore
*/ private Path floodgateKeyPath;
public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
public GeyserSpigotFallbackWorldManager(Plugin plugin) { public void loadFloodgate(GeyserFabricMod geyser, ModContainer floodgate) {
super(plugin); Path geyserDataFolder = geyser.getConfigFolder();
Path floodgateDataFolder = floodgate != null ? FabricLoader.getInstance().getConfigDir().resolve("floodgate") : null;
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, geyser.getGeyserLogger());
} }
@Override @Override
public int getBlockAt(GeyserSession session, int x, int y, int z) { public Path getFloodgateKeyPath() {
return BlockStateValues.JAVA_AIR_ID; return floodgateKeyPath;
}
@Override
public boolean hasOwnChunkCache() {
return false;
}
@Override
public boolean isLegacy() {
return true;
} }
} }

Datei anzeigen

@ -0,0 +1,82 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.api.metadata.Person;
import net.minecraft.server.MinecraftServer;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.text.AsteriskSerializer;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Getter
public class GeyserFabricDumpInfo extends BootstrapDumpInfo {
private String platformVersion = null;
private final EnvType environmentType;
@AsteriskSerializer.Asterisk(isIp = true)
private final String serverIP;
private final int serverPort;
private final List<ModInfo> mods;
public GeyserFabricDumpInfo(MinecraftServer server) {
FabricLoader.getInstance().getModContainer("fabricloader").ifPresent(mod ->
this.platformVersion = mod.getMetadata().getVersion().getFriendlyString());
this.environmentType = FabricLoader.getInstance().getEnvironmentType();
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
this.serverPort = server.getPort();
this.mods = new ArrayList<>();
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {
ModMetadata meta = mod.getMetadata();
this.mods.add(new ModInfo(
FabricLoader.getInstance().isModLoaded(meta.getId()),
meta.getId(),
meta.getVersion().getFriendlyString(),
meta.getAuthors().stream().map(Person::getName).collect(Collectors.toList()))
);
}
}
@Getter
@AllArgsConstructor
public static class ModInfo {
public boolean enabled;
public String name;
public String version;
public List<String> authors;
}
}

Datei anzeigen

@ -0,0 +1,100 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.text.ChatColor;
public class GeyserFabricLogger implements GeyserLogger {
private final Logger logger = LogManager.getLogger("geyser-fabric");
private boolean debug;
public GeyserFabricLogger(boolean isDebug) {
debug = isDebug;
}
@Override
public void severe(String message) {
logger.fatal(message);
}
@Override
public void severe(String message, Throwable error) {
logger.fatal(message, error);
}
@Override
public void error(String message) {
logger.error(message);
}
@Override
public void error(String message, Throwable error) {
logger.error(message, error);
}
@Override
public void warning(String message) {
logger.warn(message);
}
@Override
public void info(String message) {
logger.info(message);
}
@Override
public void sendMessage(Component message) {
// As of Java Edition 1.19.2, Fabric's console doesn't natively support legacy format
String flattened = LegacyComponentSerializer.legacySection().serialize(message);
// Add the reset at the end, or else format will persist... forever.
// https://cdn.discordapp.com/attachments/573909525132738590/1033904509170225242/unknown.png
String text = ChatColor.toANSI(flattened) + ChatColor.ANSI_RESET;
info(text);
}
@Override
public void debug(String message) {
if (debug) {
logger.info(message);
}
}
@Override
public void setDebug(boolean debug) {
this.debug = debug;
}
@Override
public boolean isDebug() {
return debug;
}
}

Datei anzeigen

@ -23,19 +23,23 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.platform.bungeecord.command; package org.geysermc.geyser.platform.fabric;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserMain;
import org.geysermc.geyser.command.GeyserCommandManager;
public class GeyserBungeeCommandManager extends GeyserCommandManager { public class GeyserFabricMain extends GeyserMain {
public GeyserBungeeCommandManager(GeyserImpl geyser) { public static void main(String[] args) {
super(geyser); new GeyserFabricMain().displayMessage();
} }
@Override @Override
public String description(String command) { public String getPluginType() {
return ""; // no support for command descriptions in bungee return "Fabric";
}
@Override
public String getPluginFolder() {
return "mods";
} }
} }

Datei anzeigen

@ -0,0 +1,270 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.server.MinecraftServer;
import org.apache.logging.log4j.LogManager;
import org.geysermc.common.PlatformType;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.fabric.command.GeyserFabricCommandExecutor;
import org.geysermc.geyser.platform.fabric.world.GeyserFabricWorldManager;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.*;
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
private static GeyserFabricMod instance;
private boolean reloading;
private GeyserImpl geyser;
private ModContainer mod;
private Path dataFolder;
private MinecraftServer server;
private GeyserCommandManager geyserCommandManager;
private GeyserFabricConfiguration geyserConfig;
private GeyserFabricLogger geyserLogger;
private IGeyserPingPassthrough geyserPingPassthrough;
private WorldManager geyserWorldManager;
@Override
public void onInitialize() {
instance = this;
mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
this.onEnable();
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
// Set as an event so we can get the proper IP and port if needed
ServerLifecycleEvents.SERVER_STARTED.register(this::startGeyser);
}
}
@Override
public void onEnable() {
dataFolder = FabricLoader.getInstance().getConfigDir().resolve("Geyser-Fabric");
if (!dataFolder.toFile().exists()) {
//noinspection ResultOfMethodCallIgnored
dataFolder.toFile().mkdir();
}
// Init dataFolder first as local language overrides call getConfigFolder()
GeyserLocale.init(this);
try {
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserFabricConfiguration.class);
} catch (IOException ex) {
LogManager.getLogger("geyser-fabric").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
return;
}
this.geyserLogger = new GeyserFabricLogger(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this);
if (server == null) {
// Server has yet to start
// Register onDisable so players are properly kicked
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onDisable());
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserFabricUpdateListener.onPlayReady(handler));
} else {
// Server has started and this is a reload
startGeyser(this.server);
reloading = false;
}
}
/**
* Initialize core Geyser.
* A function, as it needs to be called in different places depending on if Geyser is being reloaded or not.
*
* @param server The minecraft server.
*/
public void startGeyser(MinecraftServer server) {
this.server = server;
if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) {
this.geyserConfig.setAutoconfiguredRemote(true);
String ip = server.getLocalIp();
int port = ((GeyserServerPortGetter) server).geyser$getServerPort();
if (ip != null && !ip.isEmpty() && !ip.equals("0.0.0.0")) {
this.geyserConfig.getRemote().setAddress(ip);
}
this.geyserConfig.getRemote().setPort(port);
}
if (geyserConfig.getBedrock().isCloneRemotePort()) {
geyserConfig.getBedrock().setPort(geyserConfig.getRemote().port());
}
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
boolean floodgatePresent = floodgate.isPresent();
if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) {
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
return;
} else if (geyserConfig.isAutoconfiguredRemote() && floodgatePresent) {
// Floodgate installed means that the user wants Floodgate authentication
geyserLogger.debug("Auto-setting to Floodgate authentication.");
geyserConfig.getRemote().setAuthType(AuthType.FLOODGATE);
}
geyserConfig.loadFloodgate(this, floodgate.orElse(null));
GeyserImpl.start();
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init();
this.geyserWorldManager = new GeyserFabricWorldManager(server);
// Start command building
// Set just "geyser" as the help command
GeyserFabricCommandExecutor helpExecutor = new GeyserFabricCommandExecutor(geyser,
(GeyserCommand) geyser.commandManager().getCommands().get("help"));
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("geyser").executes(helpExecutor);
// Register all subcommands as valid
for (Map.Entry<String, Command> command : geyser.commandManager().getCommands().entrySet()) {
GeyserFabricCommandExecutor executor = new GeyserFabricCommandExecutor(geyser, (GeyserCommand) command.getValue());
builder.then(Commands.literal(command.getKey())
.executes(executor)
// Could also test for Bedrock but depending on when this is called it may backfire
.requires(executor::testPermission));
}
server.getCommands().getDispatcher().register(builder);
}
@Override
public void onDisable() {
if (geyser != null) {
geyser.shutdown();
geyser = null;
}
if (!reloading) {
this.server = null;
}
}
@Override
public GeyserConfiguration getGeyserConfig() {
return geyserConfig;
}
@Override
public GeyserLogger getGeyserLogger() {
return geyserLogger;
}
@Override
public GeyserCommandManager getGeyserCommandManager() {
return geyserCommandManager;
}
@Override
public IGeyserPingPassthrough getGeyserPingPassthrough() {
return geyserPingPassthrough;
}
@Override
public WorldManager getWorldManager() {
return geyserWorldManager;
}
@Override
public Path getConfigFolder() {
return dataFolder;
}
@Override
public BootstrapDumpInfo getDumpInfo() {
return new GeyserFabricDumpInfo(server);
}
@Override
public String getMinecraftServerVersion() {
return this.server.getServerVersion();
}
@Nullable
@Override
public InputStream getResourceOrNull(String resource) {
// We need to handle this differently, because Fabric shares the classloader across multiple mods
Path path = this.mod.findPath(resource).orElse(null);
if (path == null) {
return null;
}
try {
return path.getFileSystem()
.provider()
.newInputStream(path);
} catch (IOException e) {
return null;
}
}
public void setReloading(boolean reloading) {
this.reloading = reloading;
}
public static GeyserFabricMod getInstance() {
return instance;
}
}

Datei anzeigen

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.platform.fabric.command.FabricCommandSender;
import org.geysermc.geyser.util.VersionCheckUtils;
public final class GeyserFabricUpdateListener {
public static void onPlayReady(ServerGamePacketListenerImpl handler) {
if (Permissions.check(handler.player, Constants.UPDATE_PERMISSION, 2)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new FabricCommandSender(handler.player.createCommandSourceStack()));
}
}
private GeyserFabricUpdateListener() {
}
}

Datei anzeigen

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
import net.minecraft.server.MinecraftServer;
/**
* Represents a getter to the server port in the dedicated server and in the integrated server.
*/
public interface GeyserServerPortGetter {
/**
* Returns the server port.
*
* <ul>
* <li>If it's a dedicated server, it will return the server port specified in the {@code server.properties} file.</li>
* <li>If it's an integrated server, it will return the LAN port if opened, else -1.</li>
* </ul>
*
* The reason is that {@link MinecraftServer#getPort()} doesn't return the LAN port if it's the integrated server,
* and changing the behavior of this method via a mixin should be avoided as it could have unexpected consequences.
*
* @return The server port.
*/
int geyser$getServerPort();
}

Datei anzeigen

@ -0,0 +1,80 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.command;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.text.ChatColor;
import javax.annotation.Nonnull;
public class FabricCommandSender implements GeyserCommandSource {
private final CommandSourceStack source;
public FabricCommandSender(CommandSourceStack source) {
this.source = source;
}
@Override
public String name() {
return source.getTextName();
}
@Override
public void sendMessage(@Nonnull String message) {
if (source.getEntity() instanceof ServerPlayer) {
((ServerPlayer) source.getEntity()).displayClientMessage(Component.literal(message), false);
} else {
GeyserImpl.getInstance().getLogger().info(ChatColor.toANSI(message + ChatColor.RESET));
}
}
@Override
public void sendMessage(net.kyori.adventure.text.Component message) {
if (source.getEntity() instanceof ServerPlayer player) {
String decoded = GsonComponentSerializer.gson().serialize(message);
player.displayClientMessage(Component.Serializer.fromJson(decoded), false);
return;
}
GeyserCommandSource.super.sendMessage(message);
}
@Override
public boolean isConsole() {
return !(source.getEntity() instanceof ServerPlayer);
}
@Override
public boolean hasPermission(String permission) {
return Permissions.check(source, permission);
}
}

Datei anzeigen

@ -0,0 +1,74 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.command;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.context.CommandContext;
import me.lucko.fabric.api.permissions.v0.Permissions;
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;
import java.util.Collections;
public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implements Command<CommandSourceStack> {
private final GeyserCommand command;
public GeyserFabricCommandExecutor(GeyserImpl connector, GeyserCommand command) {
super(connector, Collections.singletonMap(command.name(), command));
this.command = command;
}
public boolean testPermission(CommandSourceStack source) {
return Permissions.check(source, command.permission(), command.isSuggestedOpOnly() ? 2 : 0);
}
@Override
public int run(CommandContext context) {
CommandSourceStack source = (CommandSourceStack) 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()));
return 0;
}
command.execute(session, sender, new String[0]);
return 0;
}
}

Datei anzeigen

@ -0,0 +1,70 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.mixin.client;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.server.IntegratedServer;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.GameType;
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
import org.geysermc.geyser.platform.fabric.GeyserServerPortGetter;
import org.geysermc.geyser.text.GeyserLocale;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Environment(EnvType.CLIENT)
@Mixin(IntegratedServer.class)
public class IntegratedServerMixin implements GeyserServerPortGetter {
@Shadow
private int publishedPort;
@Shadow @Final private Minecraft minecraft;
@Inject(method = "publishServer", at = @At("RETURN"))
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);
// 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
this.minecraft.player.displayClientMessage(Component.literal(GeyserLocale.getPlayerLocaleString("geyser.core.start",
this.minecraft.options.languageCode, "localhost", String.valueOf(this.publishedPort))), false);
}
}
@Override
public int geyser$getServerPort() {
return this.publishedPort;
}
}

Datei anzeigen

@ -0,0 +1,51 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.mixin.server;
import com.mojang.datafixers.DataFixer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Services;
import net.minecraft.server.WorldStem;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.geysermc.geyser.platform.fabric.GeyserServerPortGetter;
import org.spongepowered.asm.mixin.Mixin;
import java.net.Proxy;
@Mixin(DedicatedServer.class)
public abstract class MinecraftDedicatedServerMixin extends MinecraftServer implements GeyserServerPortGetter {
public MinecraftDedicatedServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer dataFixer, Services services, ChunkProgressListenerFactory chunkProgressListenerFactory) {
super(thread, levelStorageAccess, packRepository, worldStem, proxy, dataFixer, services, chunkProgressListenerFactory);
}
@Override
public int geyser$getServerPort() {
return this.getPort();
}
}

Datei anzeigen

@ -0,0 +1,133 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.world;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.WritableBookItem;
import net.minecraft.world.item.WrittenBookItem;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
import org.geysermc.geyser.level.GeyserWorldManager;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
import org.geysermc.geyser.util.BlockEntityUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class GeyserFabricWorldManager extends GeyserWorldManager {
private final MinecraftServer server;
public GeyserFabricWorldManager(MinecraftServer server) {
this.server = server;
}
@Override
public boolean shouldExpectLecternHandled() {
return true;
}
@Override
public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
Runnable lecternGet = () -> {
// Mostly a reimplementation of Spigot lectern support
ServerPlayer player = getPlayer(session);
if (player != null) {
BlockEntity blockEntity = player.level.getBlockEntity(new BlockPos(x, y, z));
if (!(blockEntity instanceof LecternBlockEntity lectern)) {
return;
}
if (!lectern.hasBook()) {
if (!isChunkLoad) {
BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
}
return;
}
ItemStack book = lectern.getBook();
int pageCount = WrittenBookItem.getPageCount(book);
boolean hasBookPages = pageCount > 0;
NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1);
lecternTag.putInt("page", lectern.getPage() / 2);
NbtMapBuilder bookTag = NbtMap.builder()
.putByte("Count", (byte) book.getCount())
.putShort("Damage", (short) 0)
.putString("Name", "minecraft:writable_book");
List<NbtMap> pages = new ArrayList<>(hasBookPages ? pageCount : 1);
if (hasBookPages && WritableBookItem.makeSureTagIsValid(book.getTag())) {
ListTag listTag = book.getTag().getList("pages", 8);
for (int i = 0; i < listTag.size(); i++) {
String page = listTag.getString(i);
NbtMapBuilder pageBuilder = NbtMap.builder()
.putString("photoname", "")
.putString("text", page);
pages.add(pageBuilder.build());
}
} else {
// Empty page
NbtMapBuilder pageBuilder = NbtMap.builder()
.putString("photoname", "")
.putString("text", "");
pages.add(pageBuilder.build());
}
bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
lecternTag.putCompound("book", bookTag.build());
NbtMap blockEntityTag = lecternTag.build();
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
}
};
if (isChunkLoad) {
// Hacky hacks to allow lectern loading to be delayed
session.scheduleInEventLoop(() -> server.execute(lecternGet), 1, TimeUnit.SECONDS);
} else {
server.execute(lecternGet);
}
return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build();
}
@Override
public boolean hasPermission(GeyserSession session, String permission) {
ServerPlayer player = getPlayer(session);
return Permissions.check(player, permission);
}
private ServerPlayer getPlayer(GeyserSession session) {
return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid());
}
}

Binäre Datei nicht angezeigt.

Nachher

Breite:  |  Höhe:  |  Größe: 113 KiB

Datei anzeigen

@ -0,0 +1,31 @@
{
"schemaVersion": 1,
"id": "${id}-fabric",
"version": "${version}",
"name": "${name}-Fabric",
"description": "A bridge/proxy allowing you to connect to Minecraft: Java Edition servers with Minecraft: Bedrock Edition. ",
"authors": [
"${author}"
],
"contact": {
"website": "${url}",
"repo": "https://github.com/GeyserMC/Geyser-Fabric"
},
"license": "MIT",
"icon": "assets/geyser-fabric/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"org.geysermc.geyser.platform.fabric.GeyserFabricMod"
]
},
"mixins": [
"geyser-fabric.mixins.json"
],
"depends": {
"fabricloader": ">=0.14.8",
"fabric": "*",
"minecraft": ">=1.19",
"fabric-permissions-api-v0": "*"
}
}

Datei anzeigen

@ -0,0 +1,14 @@
{
"required": true,
"package": "org.geysermc.geyser.platform.fabric.mixin",
"compatibilityLevel": "JAVA_16",
"client": [
"client.IntegratedServerMixin"
],
"server": [
"server.MinecraftDedicatedServerMixin"
],
"injectors": {
"defaultRequire": 1
}
}

Datei anzeigen

@ -1,22 +1,19 @@
val paperVersion = "1.19-R0.1-SNAPSHOT"
val viaVersion = "4.0.0"
val adaptersVersion = "1.5-SNAPSHOT"
val commodoreVersion = "1.13"
dependencies { dependencies {
api(projects.core) api(projects.core)
implementation("org.geysermc.geyser.adapters", "spigot-all", adaptersVersion) implementation(libs.adapters.spigot)
implementation("me.lucko", "commodore", commodoreVersion) implementation(libs.commodore)
implementation(libs.adventure.text.serializer.bungeecord)
// Both paper-api and paper-mojangapi only provide Java 17 versions for 1.19 // Both paper-api and paper-mojangapi only provide Java 17 versions for 1.19
compileOnly("io.papermc.paper", "paper-api", paperVersion) { compileOnly(libs.paper.api) {
attributes { attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
} }
} }
compileOnly("io.papermc.paper", "paper-mojangapi", paperVersion) { compileOnly(libs.paper.mojangapi) {
attributes { attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
} }
@ -25,13 +22,14 @@ dependencies {
platformRelocate("it.unimi.dsi.fastutil") platformRelocate("it.unimi.dsi.fastutil")
platformRelocate("com.fasterxml.jackson") platformRelocate("com.fasterxml.jackson")
platformRelocate("net.kyori") // Relocate net.kyori but exclude the component logger
platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger")
platformRelocate("org.objectweb.asm") platformRelocate("org.objectweb.asm")
platformRelocate("me.lucko.commodore") platformRelocate("me.lucko.commodore")
platformRelocate("io.netty.channel.kqueue") platformRelocate("io.netty.channel.kqueue")
// These dependencies are already present on the platform // These dependencies are already present on the platform
provided("com.viaversion", "viaversion", viaVersion) provided(libs.viaversion)
application { application {
mainClass.set("org.geysermc.geyser.platform.spigot.GeyserSpigotMain") mainClass.set("org.geysermc.geyser.platform.spigot.GeyserSpigotMain")

Datei anzeigen

@ -0,0 +1,59 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.spigot;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
import org.bukkit.plugin.Plugin;
import java.util.logging.Logger;
public final class GeyserPaperLogger extends GeyserSpigotLogger {
private final ComponentLogger componentLogger;
public GeyserPaperLogger(Plugin plugin, Logger logger, boolean debug) {
super(logger, debug);
componentLogger = plugin.getComponentLogger();
}
/**
* Since 1.18.2 this is required so legacy format symbols don't show up in the console for colors
*/
@Override
public void sendMessage(Component message) {
// Done like this so the native component object field isn't relocated
componentLogger.info("{}", PaperAdventure.toNativeComponent(message));
}
static boolean supported() {
try {
Plugin.class.getMethod("getComponentLogger");
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
}

Datei anzeigen

@ -41,6 +41,8 @@ public class GeyserSpigotDumpInfo extends BootstrapDumpInfo {
private final String platformVersion; private final String platformVersion;
private final String platformAPIVersion; private final String platformAPIVersion;
private final boolean onlineMode; private final boolean onlineMode;
@AsteriskSerializer.Asterisk(isIp = true)
private final String serverIP; private final String serverIP;
private final int serverPort; private final int serverPort;
private final List<PluginInfo> plugins; private final List<PluginInfo> plugins;
@ -51,11 +53,7 @@ public class GeyserSpigotDumpInfo extends BootstrapDumpInfo {
this.platformVersion = Bukkit.getVersion(); this.platformVersion = Bukkit.getVersion();
this.platformAPIVersion = Bukkit.getBukkitVersion(); this.platformAPIVersion = Bukkit.getBukkitVersion();
this.onlineMode = Bukkit.getOnlineMode(); this.onlineMode = Bukkit.getOnlineMode();
if (AsteriskSerializer.showSensitive || (Bukkit.getIp().equals("") || Bukkit.getIp().equals("0.0.0.0"))) {
this.serverIP = Bukkit.getIp(); this.serverIP = Bukkit.getIp();
} else {
this.serverIP = "***";
}
this.serverPort = Bukkit.getPort(); this.serverPort = Bukkit.getPort();
this.plugins = new ArrayList<>(); this.plugins = new ArrayList<>();

Datei anzeigen

@ -32,9 +32,15 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import me.lucko.commodore.CommodoreProvider; import me.lucko.commodore.CommodoreProvider;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.block.data.BlockData;
import org.bukkit.command.CommandMap;
import org.bukkit.command.PluginCommand; import org.bukkit.command.PluginCommand;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.ServerLoadEvent;
import org.bukkit.permissions.Permission; import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault; import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.geyser.Constants; import org.geysermc.geyser.Constants;
@ -42,6 +48,7 @@ import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
@ -53,7 +60,6 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.spigot.command.GeyserBrigadierSupport; import org.geysermc.geyser.platform.spigot.command.GeyserBrigadierSupport;
import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor;
import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager;
import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource;
import org.geysermc.geyser.platform.spigot.world.GeyserPistonListener; import org.geysermc.geyser.platform.spigot.world.GeyserPistonListener;
import org.geysermc.geyser.platform.spigot.world.GeyserSpigotBlockPlaceListener; import org.geysermc.geyser.platform.spigot.world.GeyserSpigotBlockPlaceListener;
import org.geysermc.geyser.platform.spigot.world.manager.*; import org.geysermc.geyser.platform.spigot.world.manager.*;
@ -62,6 +68,8 @@ import org.geysermc.geyser.util.FileUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
@ -90,9 +98,40 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
private String minecraftVersion; private String minecraftVersion;
@Override @Override
public void onEnable() { public void onLoad() {
GeyserLocale.init(this); GeyserLocale.init(this);
try {
// AvailableCommandsSerializer_v291 complains otherwise - affects at least 1.8
ByteBuf.class.getMethod("writeShortLE", int.class);
// Only available in 1.13.x
Class.forName("org.bukkit.event.server.ServerLoadEvent");
// We depend on this as a fallback in certain scenarios
BlockData.class.getMethod("getAsString");
} catch (ClassNotFoundException | NoSuchMethodException e) {
getLogger().severe("*********************************************");
getLogger().severe("");
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header"));
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
getLogger().severe("");
getLogger().severe("*********************************************");
return;
}
try {
Class.forName("net.md_5.bungee.chat.ComponentSerializer");
} catch (ClassNotFoundException e) {
if (!PaperAdventure.canSendMessageUsingComponent()) { // Prepare for Paper eventually removing Bungee chat
getLogger().severe("*********************************************");
getLogger().severe("");
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName()));
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
getLogger().severe("");
getLogger().severe("*********************************************");
return;
}
}
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed // This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
try { try {
if (!getDataFolder().exists()) { if (!getDataFolder().exists()) {
@ -108,21 +147,29 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
return; return;
} }
try { this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode())
// AvailableCommandsSerializer_v291 complains otherwise : new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
ByteBuf.class.getMethod("writeShortLE", int.class);
} catch (NoSuchMethodException e) {
getLogger().severe("*********************************************");
getLogger().severe("");
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.header"));
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.12.2"));
getLogger().severe("");
getLogger().severe("*********************************************");
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this);
}
@Override
public void onEnable() {
if (this.geyserConfig == null) {
// We failed to initialize correctly
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
// Remove this in like a year
if (Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) {
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", Constants.FLOODGATE_DOWNLOAD_LOCATION));
this.getPluginLoader().disablePlugin(this);
return;
}
// By default this should be localhost but may need to be changed in some circumstances // By default this should be localhost but may need to be changed in some circumstances
if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) {
geyserConfig.setAutoconfiguredRemote(true); geyserConfig.setAutoconfiguredRemote(true);
@ -137,20 +184,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
geyserConfig.getBedrock().setPort(Bukkit.getPort()); geyserConfig.getBedrock().setPort(Bukkit.getPort());
} }
this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
// Remove this in like a year
if (Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) {
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", Constants.FLOODGATE_DOWNLOAD_LOCATION));
this.getPluginLoader().disablePlugin(this);
return;
}
if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && Bukkit.getPluginManager().getPlugin("floodgate") == null) { if (geyserConfig.getRemote().authType() == AuthType.FLOODGATE && Bukkit.getPluginManager().getPlugin("floodgate") == null) {
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
this.getPluginLoader().disablePlugin(this); this.getPluginLoader().disablePlugin(this);
return;
} else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate") != null) { } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate") != null) {
// Floodgate installed means that the user wants Floodgate authentication // Floodgate installed means that the user wants Floodgate authentication
geyserLogger.debug("Auto-setting to Floodgate authentication."); geyserLogger.debug("Auto-setting to Floodgate authentication.");
@ -159,11 +195,52 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
geyserConfig.loadFloodgate(this); geyserConfig.loadFloodgate(this);
if (!INITIALIZED) {
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
Bukkit.getPluginManager().registerEvents(new Listener() {
@EventHandler
public void onServerLoaded(ServerLoadEvent event) {
// Wait until all plugins have loaded so Geyser can start
postStartup();
}
}, this);
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
this.geyserCommandManager.init();
// 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.description().name(), ex);
}
}
}
if (INITIALIZED) {
// Reload; continue with post startup
postStartup();
}
}
private void postStartup() {
GeyserImpl.start();
// Turn "(MC: 1.16.4)" into 1.16.4. // Turn "(MC: 1.16.4)" into 1.16.4.
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0]; this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
this.geyser = GeyserImpl.start(PlatformType.SPIGOT, this);
if (geyserConfig.isLegacyPingPassthrough()) { if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else { } else {
@ -176,10 +253,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} }
} }
geyserLogger.info("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass())); geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass()));
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
this.geyserCommandManager.init();
boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null;
if (isViaVersion) { if (isViaVersion) {
@ -194,14 +268,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
} }
} }
} }
// Used to determine if Block.getBlockData() is present.
boolean isLegacy = !isCompatible(Bukkit.getServer().getVersion(), "1.13.0");
if (isLegacy)
geyserLogger.debug("Legacy version of Minecraft (1.12.2 or older) detected; falling back to ViaVersion for block state retrieval.");
boolean isPre1_12 = !isCompatible(Bukkit.getServer().getVersion(), "1.12.0");
// Set if we need to use a different method for getting a player's locale
SpigotCommandSource.setUseLegacyLocaleMethod(isPre1_12);
// We want to do this late in the server startup process to allow plugins such as ViaVersion and ProtocolLib // We want to do this late in the server startup process to allow plugins such as ViaVersion and ProtocolLib
// To do their job injecting, then connect into *that* // To do their job injecting, then connect into *that*
@ -214,13 +280,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
String nmsVersion = name.substring(name.lastIndexOf('.') + 1); String nmsVersion = name.substring(name.lastIndexOf('.') + 1);
SpigotAdapters.registerWorldAdapter(nmsVersion); SpigotAdapters.registerWorldAdapter(nmsVersion);
if (isViaVersion && isViaVersionNeeded()) { if (isViaVersion && isViaVersionNeeded()) {
if (isLegacy) {
// Pre-1.13
this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager(this);
} else {
// Post-1.13
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this); this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this);
}
} else { } else {
// No ViaVersion // No ViaVersion
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this); this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this);
@ -237,28 +297,31 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
} }
if (this.geyserWorldManager == null) { if (this.geyserWorldManager == null) {
// No NMS adapter // No NMS adapter
if (isLegacy && isViaVersion) {
// Use ViaVersion for converting pre-1.13 block states
this.geyserWorldManager = new GeyserSpigot1_12WorldManager(this);
} else if (isLegacy) {
// Not sure how this happens - without ViaVersion, we don't know any block states, so just assume everything is air
this.geyserWorldManager = new GeyserSpigotFallbackWorldManager(this);
} else {
// Post-1.13
this.geyserWorldManager = new GeyserSpigotWorldManager(this); this.geyserWorldManager = new GeyserSpigotWorldManager(this);
} geyserLogger.debug("Using default world manager.");
geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
} }
PluginCommand geyserCommand = this.getCommand("geyser"); PluginCommand geyserCommand = this.getCommand("geyser");
geyserCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands())); geyserCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands()));
PluginCommand geyserExtCommand = this.getCommand("geyserext");
geyserExtCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands())); for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
Map<String, Command> commands = entry.getValue();
if (commands.isEmpty()) {
continue;
}
PluginCommand command = this.getCommand(entry.getKey().description().id());
if (command == null) {
continue;
}
command.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands));
}
if (!INITIALIZED) { if (!INITIALIZED) {
// Register permissions so they appear in, for example, LuckPerms' UI // Register permissions so they appear in, for example, LuckPerms' UI
// Re-registering permissions throws an error // Re-registering permissions throws an error
for (Map.Entry<String, Command> entry : geyserCommandManager.getCommands().entrySet()) { for (Map.Entry<String, Command> entry : geyserCommandManager.commands().entrySet()) {
Command command = entry.getValue(); Command command = entry.getValue();
if (command.aliases().contains(entry.getKey())) { if (command.aliases().contains(entry.getKey())) {
// Don't register aliases // Don't register aliases
@ -270,11 +333,35 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE)); command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
} }
// Register permissions for extension commands
for (Map.Entry<Extension, Map<String, Command>> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) {
for (Map.Entry<String, Command> entry : commandEntry.getValue().entrySet()) {
Command command = entry.getValue();
if (command.aliases().contains(entry.getKey())) {
// Don't register aliases
continue;
}
if (command.permission().isBlank()) {
continue;
}
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 // Events cannot be unregistered - re-registering results in duplicate firings
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager); GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this); Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this); Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigotUpdateListener(), this);
} }
boolean brigadierSupported = CommodoreProvider.isSupported(); boolean brigadierSupported = CommodoreProvider.isSupported();

Datei anzeigen

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.spigot;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource;
import org.geysermc.geyser.util.VersionCheckUtils;
public final class GeyserSpigotUpdateListener implements Listener {
@EventHandler
public void onPlayerJoin(final PlayerJoinEvent event) {
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
final Player player = event.getPlayer();
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player));
}
}
}
}

Datei anzeigen

@ -0,0 +1,154 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.spigot;
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.geysermc.geyser.GeyserImpl;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
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
* and the MinecraftReflection class.
*/
public final class PaperAdventure {
private static final MethodHandle NATIVE_GSON_COMPONENT_SERIALIZER_DESERIALIZE_METHOD_BOUND;
private static final Method SEND_MESSAGE_COMPONENT;
static {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle nativeGsonComponentSerializerDeserializeMethodBound = null;
// String.join because otherwise the class name will be relocated
final Class<?> nativeGsonComponentSerializerClass = findClass(String.join(".",
"net", "kyori", "adventure", "text", "serializer", "gson", "GsonComponentSerializer"));
final Class<?> nativeGsonComponentSerializerImplClass = findClass(String.join(".",
"net", "kyori", "adventure", "text", "serializer", "gson", "GsonComponentSerializerImpl"));
if (nativeGsonComponentSerializerClass != null && nativeGsonComponentSerializerImplClass != null) {
MethodHandle nativeGsonComponentSerializerGsonGetter = null;
try {
nativeGsonComponentSerializerGsonGetter = lookup.findStatic(nativeGsonComponentSerializerClass,
"gson", MethodType.methodType(nativeGsonComponentSerializerClass));
} catch (final NoSuchMethodException | IllegalAccessException ignored) {
}
MethodHandle nativeGsonComponentSerializerDeserializeMethod = null;
try {
final Method method = nativeGsonComponentSerializerImplClass.getDeclaredMethod("deserialize", String.class);
method.setAccessible(true);
nativeGsonComponentSerializerDeserializeMethod = lookup.unreflect(method);
} catch (final NoSuchMethodException | IllegalAccessException ignored) {
}
if (nativeGsonComponentSerializerGsonGetter != null) {
if (nativeGsonComponentSerializerDeserializeMethod != null) {
try {
nativeGsonComponentSerializerDeserializeMethodBound = nativeGsonComponentSerializerDeserializeMethod
.bindTo(nativeGsonComponentSerializerGsonGetter.invoke());
} catch (final Throwable throwable) {
GeyserImpl.getInstance().getLogger().error("Failed to access native GsonComponentSerializer", throwable);
}
}
}
}
NATIVE_GSON_COMPONENT_SERIALIZER_DESERIALIZE_METHOD_BOUND = nativeGsonComponentSerializerDeserializeMethodBound;
Method playerComponentSendMessage = null;
final Class<?> nativeComponentClass = findClass(String.join(".",
"net", "kyori", "adventure", "text", "Component"));
if (nativeComponentClass != null) {
try {
playerComponentSendMessage = CommandSender.class.getMethod("sendMessage", nativeComponentClass);
} catch (final NoSuchMethodException e) {
if (GeyserImpl.getInstance().getLogger().isDebug()) {
e.printStackTrace();
}
}
}
SEND_MESSAGE_COMPONENT = playerComponentSendMessage;
}
public static 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;
}
try {
return NATIVE_GSON_COMPONENT_SERIALIZER_DESERIALIZE_METHOD_BOUND.invoke(DefaultComponentSerializer.get().serialize(component));
} catch (final Throwable throwable) {
GeyserImpl.getInstance().getLogger().error("Failed to create native Component message", throwable);
return null;
}
}
public static void sendMessage(final CommandSender sender, final Component component) {
if (SEND_MESSAGE_COMPONENT == null) {
GeyserImpl.getInstance().getLogger().error("Illegal state where Component sendMessage was called when it wasn't available!");
return;
}
final Object nativeComponent = toNativeComponent(component);
if (nativeComponent != null) {
try {
SEND_MESSAGE_COMPONENT.invoke(sender, nativeComponent);
} catch (final InvocationTargetException | IllegalAccessException e) {
GeyserImpl.getInstance().getLogger().error("Failed to send native Component message", e);
}
}
}
public static boolean canSendMessageUsingComponent() {
return SEND_MESSAGE_COMPONENT != null;
}
/**
* Gets a class by the first name available.
*
* @return a class or {@code null} if not found
*/
private static @Nullable Class<?> findClass(final String className) {
try {
return Class.forName(className);
} catch (final ClassNotFoundException ignored) {
}
return null;
}
private PaperAdventure() {
}
}

Datei anzeigen

@ -66,6 +66,9 @@ public class GeyserSpigotCommandExecutor extends GeyserCommandExecutor implement
} }
geyserCommand.execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]); geyserCommand.execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
return true; return true;
} else {
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.not_found", commandSender.locale());
commandSender.sendMessage(ChatColor.RED + message);
} }
} else { } else {
getCommand("help").execute(session, commandSender, new String[0]); getCommand("help").execute(session, commandSender, new String[0]);

Datei anzeigen

@ -65,4 +65,8 @@ public class GeyserSpigotCommandManager extends GeyserCommandManager {
Command cmd = COMMAND_MAP.getCommand(command.replace("/", "")); Command cmd = COMMAND_MAP.getCommand(command.replace("/", ""));
return cmd != null ? cmd.getDescription() : ""; return cmd != null ? cmd.getDescription() : "";
} }
public static CommandMap getCommandMap() {
return COMMAND_MAP;
}
} }

Datei anzeigen

@ -25,32 +25,21 @@
package org.geysermc.geyser.platform.spigot.command; package org.geysermc.geyser.platform.spigot.command;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.platform.spigot.PaperAdventure;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class SpigotCommandSource implements GeyserCommandSource { public class SpigotCommandSource implements GeyserCommandSource {
/**
* Whether to use {@code Player.getLocale()} or {@code Player.spigot().getLocale()}, depending on version.
* 1.12 or greater should not use the legacy method.
*/
private static boolean USE_LEGACY_METHOD = false;
private static Method LOCALE_METHOD;
private final org.bukkit.command.CommandSender handle; private final org.bukkit.command.CommandSender handle;
private final String locale;
public SpigotCommandSource(org.bukkit.command.CommandSender handle) { public SpigotCommandSource(org.bukkit.command.CommandSender handle) {
this.handle = handle; this.handle = handle;
this.locale = getSpigotLocale();
// Ensure even Java players' languages are loaded // Ensure even Java players' languages are loaded
GeyserLocale.loadGeyserLocale(locale); GeyserLocale.loadGeyserLocale(locale());
} }
@Override @Override
@ -63,6 +52,17 @@ public class SpigotCommandSource implements GeyserCommandSource {
handle.sendMessage(message); handle.sendMessage(message);
} }
@Override
public void sendMessage(Component message) {
if (PaperAdventure.canSendMessageUsingComponent()) {
PaperAdventure.sendMessage(handle, message);
return;
}
// CommandSender#sendMessage(BaseComponent[]) is Paper-only
handle.spigot().sendMessage(BungeeComponentSerializer.get().serialize(message));
}
@Override @Override
public boolean isConsole() { public boolean isConsole() {
return handle instanceof ConsoleCommandSender; return handle instanceof ConsoleCommandSender;
@ -70,50 +70,15 @@ public class SpigotCommandSource implements GeyserCommandSource {
@Override @Override
public String locale() { public String locale() {
return locale; if (this.handle instanceof Player player) {
return player.getLocale();
}
return GeyserLocale.getDefaultLocale();
} }
@Override @Override
public boolean hasPermission(String permission) { public boolean hasPermission(String permission) {
return handle.hasPermission(permission); return handle.hasPermission(permission);
} }
/**
* Set if we are on pre-1.12, and therefore {@code player.getLocale()} doesn't exist and we have to get
* {@code player.spigot().getLocale()}.
*
* @param useLegacyMethod if we are running pre-1.12 and therefore need to use reflection to get the player locale
*/
public static void setUseLegacyLocaleMethod(boolean useLegacyMethod) {
USE_LEGACY_METHOD = useLegacyMethod;
if (USE_LEGACY_METHOD) {
try {
//noinspection JavaReflectionMemberAccess - of course it doesn't exist; that's why we're doing it
LOCALE_METHOD = Player.Spigot.class.getMethod("getLocale");
} catch (NoSuchMethodException e) {
GeyserImpl.getInstance().getLogger().debug("Player.Spigot.getLocale() doesn't exist? Not a big deal but if you're seeing this please report it to the developers!");
}
}
}
/**
* So we only have to do nasty reflection stuff once per command
*
* @return the locale of the Spigot player
*/
private String getSpigotLocale() {
if (handle instanceof Player player) {
if (USE_LEGACY_METHOD) {
try {
// sigh
// This was the only option on older Spigot instances and now it's gone
return (String) LOCALE_METHOD.invoke(player.spigot());
} catch (IllegalAccessException | InvocationTargetException ignored) {
}
} else {
return player.getLocale();
}
}
return GeyserLocale.getDefaultLocale();
}
} }

Datei anzeigen

@ -105,13 +105,10 @@ public class GeyserPistonListener implements Listener {
// Trying to grab the blocks from the world like other platforms would result in the moving piston block // Trying to grab the blocks from the world like other platforms would result in the moving piston block
// being returned instead. // being returned instead.
if (!blocksFilled) { if (!blocksFilled) {
// Blocks currently require a player for 1.12, so let's just leech off one player to get all blocks
// and call it a day for the rest of the sessions (mostly to save on execution time)
List<Block> blocks = isExtend ? ((BlockPistonExtendEvent) event).getBlocks() : ((BlockPistonRetractEvent) event).getBlocks(); List<Block> blocks = isExtend ? ((BlockPistonExtendEvent) event).getBlocks() : ((BlockPistonRetractEvent) event).getBlocks();
for (Block block : blocks) { for (Block block : blocks) {
Location attachedLocation = block.getLocation(); Location attachedLocation = block.getLocation();
int blockId = worldManager.getBlockNetworkId(player, block, int blockId = worldManager.getBlockNetworkId(block);
attachedLocation.getBlockX(), attachedLocation.getBlockY(), attachedLocation.getBlockZ());
// Ignore blocks that will be destroyed // Ignore blocks that will be destroyed
if (BlockStateValues.canPistonMoveBlock(blockId, isExtend)) { if (BlockStateValues.canPistonMoveBlock(blockId, isExtend)) {
attachedBlocks.put(getVector(attachedLocation), blockId); attachedBlocks.put(getVector(attachedLocation), blockId);
@ -120,7 +117,7 @@ public class GeyserPistonListener implements Listener {
blocksFilled = true; blocksFilled = true;
} }
int pistonBlockId = worldManager.getBlockNetworkId(player, event.getBlock(), location.getBlockX(), location.getBlockY(), location.getBlockZ()); int pistonBlockId = worldManager.getBlockNetworkId(event.getBlock());
// event.getDirection() is unreliable // event.getDirection() is unreliable
Direction orientation = BlockStateValues.getPistonOrientation(pistonBlockId); Direction orientation = BlockStateValues.getPistonOrientation(pistonBlockId);

Datei anzeigen

@ -1,61 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.spigot.world.manager;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
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;
/**
* Used with ViaVersion and pre-1.13.
*/
public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldManager {
private final SpigotWorldAdapter adapter;
public GeyserSpigot1_12NativeWorldManager(Plugin plugin) {
super(plugin);
this.adapter = SpigotAdapters.getWorldAdapter();
// Unlike post-1.13, we can't build up a cache of block states, because block entities need some special conversion
}
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
if (player == null) {
return BlockStateValues.JAVA_AIR_ID;
}
// Get block entity storage
BlockStorage storage = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId()).get(BlockStorage.class);
int blockId = adapter.getBlockAt(player.getWorld(), x, y, z);
return getLegacyBlock(storage, blockId, x, y, z);
}
}

Datei anzeigen

@ -1,125 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.spigot.world.manager;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import java.util.List;
/**
* Should be used when ViaVersion is present, no NMS adapter is being used, and we are pre-1.13.
*
* You need ViaVersion to connect to an older server with the Geyser-Spigot plugin.
*/
public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
/**
* Specific mapping data for 1.12 to 1.13. Used to convert the 1.12 block into the 1.13 block state.
* (Block IDs did not change between server versions until 1.13 and after)
*/
private final MappingData mappingData1_12to1_13;
/**
* The list of all protocols from the client's version to 1.13.
*/
private final List<ProtocolPathEntry> protocolList;
public GeyserSpigot1_12WorldManager(Plugin plugin) {
super(plugin);
this.mappingData1_12to1_13 = Via.getManager().getProtocolManager().getProtocol(Protocol1_13To1_12_2.class).getMappingData();
this.protocolList = Via.getManager().getProtocolManager().getProtocolPath(CLIENT_PROTOCOL_VERSION,
ProtocolVersion.v1_13.getVersion());
}
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
if (player == null) {
return BlockStateValues.JAVA_AIR_ID;
}
if (!player.getWorld().isChunkLoaded(x >> 4, z >> 4)) {
// Prevent nasty async errors if a player is loading in
return BlockStateValues.JAVA_AIR_ID;
}
Block block = player.getWorld().getBlockAt(x, y, z);
return getBlockNetworkId(player, block, x, y, z);
}
@Override
@SuppressWarnings("deprecation")
public int getBlockNetworkId(Player player, Block block, int x, int y, int z) {
// Get block entity storage
BlockStorage storage = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId()).get(BlockStorage.class);
// Black magic that gets the old block state ID
int oldBlockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
return getLegacyBlock(storage, oldBlockId, x, y, z);
}
/**
*
* @param storage ViaVersion's block entity storage (used to fix block entity state differences)
* @param blockId the pre-1.13 block id
* @param x X coordinate of block
* @param y Y coordinate of block
* @param z Z coordinate of block
* @return the block state updated to the latest Minecraft version
*/
public int getLegacyBlock(BlockStorage storage, int blockId, int x, int y, int z) {
// Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2
blockId = mappingData1_12to1_13.getNewBlockId(blockId);
// Translate block entity differences - some information was stored in block tags and not block states
if (storage.isWelcome(blockId)) { // No getOrDefault method
BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z));
if (data != null && data.getReplacement() != -1) {
blockId = data.getReplacement();
}
}
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
if (mappingData != null) {
blockId = mappingData.getNewBlockStateId(blockId);
}
}
return blockId;
}
@Override
public boolean isLegacy() {
return true;
}
}

Datei anzeigen

@ -32,6 +32,7 @@ import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter; import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.jetbrains.annotations.Nullable;
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager { public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
protected final SpigotWorldAdapter adapter; protected final SpigotWorldAdapter adapter;
@ -49,4 +50,12 @@ public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
} }
return adapter.getBlockAt(player.getWorld(), x, y, z); return adapter.getBlockAt(player.getWorld(), x, y, z);
} }
@Nullable
@Override
public String[] getBiomeIdentifiers(boolean withTags) {
// Biome identifiers will basically always be the same for one server, since you have to re-send the
// ClientboundLoginPacket to change the registry. Therefore, don't bother caching for each player.
return adapter.getBiomeSuggestions(withTags);
}
} }

Datei anzeigen

@ -33,15 +33,13 @@ import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.Lectern; import org.bukkit.block.Lectern;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.geysermc.geyser.level.GameRule; import org.geysermc.geyser.level.GameRule;
import org.geysermc.geyser.level.GeyserWorldManager; import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator; import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
@ -53,12 +51,7 @@ import java.util.List;
/** /**
* The base world manager to use when there is no supported NMS revision * The base world manager to use when there is no supported NMS revision
*/ */
public class GeyserSpigotWorldManager extends GeyserWorldManager { public class GeyserSpigotWorldManager extends WorldManager {
/**
* The current client protocol version for ViaVersion usage.
*/
protected static final int CLIENT_PROTOCOL_VERSION = GameProtocol.getJavaProtocolVersion();
private final Plugin plugin; private final Plugin plugin;
public GeyserSpigotWorldManager(Plugin plugin) { public GeyserSpigotWorldManager(Plugin plugin) {
@ -77,10 +70,10 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
return BlockStateValues.JAVA_AIR_ID; return BlockStateValues.JAVA_AIR_ID;
} }
return getBlockNetworkId(bukkitPlayer, world.getBlockAt(x, y, z), x, y, z); return getBlockNetworkId(world.getBlockAt(x, y, z));
} }
public int getBlockNetworkId(Player player, Block block, int x, int y, int z) { public int getBlockNetworkId(Block block) {
return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(block.getBlockData().getAsString(), BlockStateValues.JAVA_AIR_ID); return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(block.getBlockData().getAsString(), BlockStateValues.JAVA_AIR_ID);
} }
@ -158,12 +151,12 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
return true; return true;
} }
public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
String value = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID()); String value = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID());
if (!value.isEmpty()) { if (!value.isEmpty()) {
return Boolean.parseBoolean(value); return Boolean.parseBoolean(value);
} }
return (Boolean) gameRule.getDefaultValue(); return gameRule.getDefaultBooleanValue();
} }
@Override @Override
@ -172,7 +165,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
if (!value.isEmpty()) { if (!value.isEmpty()) {
return Integer.parseInt(value); return Integer.parseInt(value);
} }
return (int) gameRule.getDefaultValue(); return gameRule.getDefaultIntValue();
} }
@Override @Override
@ -181,8 +174,6 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
} }
/** /**
* This must be set to true if we are pre-1.13, and {@link BlockData#getAsString() does not exist}.
*
* This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id * This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id
* to the current one. * to the current one.
* *

Datei anzeigen

@ -9,6 +9,3 @@ commands:
geyser: geyser:
description: The main command for Geyser. description: The main command for Geyser.
usage: /geyser <subcommand> usage: /geyser <subcommand>
geyserext:
description: The command any extensions can register to.
usage: /geyserext <subcommand>

Datei anzeigen

@ -1,5 +1,3 @@
val spongeVersion = "7.1.0"
dependencies { dependencies {
api(projects.core) api(projects.core)
} }
@ -9,16 +7,11 @@ platformRelocate("io.netty")
platformRelocate("it.unimi.dsi.fastutil") platformRelocate("it.unimi.dsi.fastutil")
platformRelocate("com.google.common") platformRelocate("com.google.common")
platformRelocate("com.google.guava") platformRelocate("com.google.guava")
platformRelocate("net.kyori") platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl")
platformRelocate("net.kyori.adventure.nbt")
// Exclude these dependencies
exclude("com.google.code.gson:*")
exclude("org.yaml:*")
exclude("org.slf4j:*")
exclude("org.ow2.asm:*")
// These dependencies are already present on the platform // These dependencies are already present on the platform
provided("org.spongepowered", "spongeapi", spongeVersion) provided(libs.sponge.api)
application { application {
mainClass.set("org.geysermc.geyser.platform.sponge.GeyserSpongeMain") mainClass.set("org.geysermc.geyser.platform.sponge.GeyserSpongeMain")
@ -32,5 +25,14 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
exclude(dependency("org.yaml:.*")) exclude(dependency("org.yaml:.*"))
exclude(dependency("org.slf4j:.*")) exclude(dependency("org.slf4j:.*"))
exclude(dependency("org.ow2.asm:.*")) exclude(dependency("org.ow2.asm:.*"))
// Exclude all Kyori dependencies except the legacy NBT serializer and NBT
exclude(dependency("net.kyori:adventure-api:.*"))
exclude(dependency("net.kyori:examination-api:.*"))
exclude(dependency("net.kyori:examination-string:.*"))
exclude(dependency("net.kyori:adventure-text-serializer-gson:.*"))
exclude(dependency("net.kyori:adventure-text-serializer-legacy:.*"))
exclude(dependency("net.kyori:adventure-text-serializer-plain:.*"))
exclude(dependency("net.kyori:adventure-key:.*"))
} }
} }

Datei anzeigen

@ -27,35 +27,45 @@ package org.geysermc.geyser.platform.sponge;
import lombok.Getter; import lombok.Getter;
import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.text.AsteriskSerializer;
import org.spongepowered.api.Platform; import org.spongepowered.api.Platform;
import org.spongepowered.api.Sponge; import org.spongepowered.api.Sponge;
import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.plugin.PluginContainer;
import org.spongepowered.plugin.metadata.PluginMetadata;
import org.spongepowered.plugin.metadata.model.PluginContributor;
import java.net.InetSocketAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Getter @Getter
public class GeyserSpongeDumpInfo extends BootstrapDumpInfo { public class GeyserSpongeDumpInfo extends BootstrapDumpInfo {
private final String platformName; private final String platformName;
private final String platformVersion; private final String platformVersion;
private final boolean onlineMode; private final boolean onlineMode;
@AsteriskSerializer.Asterisk(isIp = true)
private final String serverIP; private final String serverIP;
private final int serverPort; private final int serverPort;
private final List<PluginInfo> plugins; private final List<PluginInfo> plugins;
GeyserSpongeDumpInfo() { GeyserSpongeDumpInfo() {
super(); PluginContainer container = Sponge.platform().container(Platform.Component.IMPLEMENTATION);
PluginContainer container = Sponge.getPlatform().getContainer(Platform.Component.IMPLEMENTATION); PluginMetadata platformMeta = container.metadata();
this.platformName = container.getName(); this.platformName = platformMeta.name().orElse("unknown");
this.platformVersion = container.getVersion().get(); this.platformVersion = platformMeta.version().getQualifier();
this.onlineMode = Sponge.getServer().getOnlineMode(); this.onlineMode = Sponge.server().isOnlineModeEnabled();
this.serverIP = Sponge.getServer().getBoundAddress().get().getHostString(); Optional<InetSocketAddress> socketAddress = Sponge.server().boundAddress();
this.serverPort = Sponge.getServer().getBoundAddress().get().getPort(); this.serverIP = socketAddress.map(InetSocketAddress::getHostString).orElse("unknown");
this.serverPort = socketAddress.map(InetSocketAddress::getPort).orElse(-1);
this.plugins = new ArrayList<>(); this.plugins = new ArrayList<>();
for (PluginContainer plugin : Sponge.getPluginManager().getPlugins()) { for (PluginContainer plugin : Sponge.pluginManager().plugins()) {
String pluginClass = plugin.getInstance().map((pl) -> pl.getClass().getName()).orElse("unknown"); PluginMetadata meta = plugin.metadata();
this.plugins.add(new PluginInfo(true, plugin.getName(), plugin.getVersion().get(), pluginClass, plugin.getAuthors())); List<String> contributors = meta.contributors().stream().map(PluginContributor::name).collect(Collectors.toList());
this.plugins.add(new PluginInfo(true, meta.name().orElse("unknown"), meta.version().toString(), meta.entrypoint(), contributors));
} }
} }
} }

Datei anzeigen

@ -29,7 +29,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import org.slf4j.Logger; import org.apache.logging.log4j.Logger;
@AllArgsConstructor @AllArgsConstructor
public class GeyserSpongeLogger implements GeyserLogger { public class GeyserSpongeLogger implements GeyserLogger {

Datei anzeigen

@ -28,11 +28,12 @@ package org.geysermc.geyser.platform.sponge;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.ping.GeyserPingInfo; import org.geysermc.geyser.ping.GeyserPingInfo;
import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.spongepowered.api.MinecraftVersion; import org.spongepowered.api.MinecraftVersion;
import org.spongepowered.api.Sponge; import org.spongepowered.api.Sponge;
import org.spongepowered.api.event.Cause;
import org.spongepowered.api.event.EventContext;
import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.SpongeEventFactory;
import org.spongepowered.api.event.cause.Cause;
import org.spongepowered.api.event.cause.EventContext;
import org.spongepowered.api.event.server.ClientPingServerEvent; import org.spongepowered.api.event.server.ClientPingServerEvent;
import org.spongepowered.api.network.status.StatusClient; import org.spongepowered.api.network.status.StatusClient;
import org.spongepowered.api.profile.GameProfile; import org.spongepowered.api.profile.GameProfile;
@ -43,7 +44,7 @@ import java.util.Optional;
public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough { public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
private static final Cause CAUSE = Cause.of(EventContext.empty(), Sponge.getServer()); private static final Cause CAUSE = Cause.of(EventContext.empty(), Sponge.server());
private static Method SpongeStatusResponse_create; private static Method SpongeStatusResponse_create;
@ -59,50 +60,46 @@ public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
SpongeStatusResponse_create = SpongeStatusResponse.getDeclaredMethod("create", MinecraftServer); SpongeStatusResponse_create = SpongeStatusResponse.getDeclaredMethod("create", MinecraftServer);
} }
Object response = SpongeStatusResponse_create.invoke(null, Sponge.getServer()); Object response = SpongeStatusResponse_create.invoke(null, Sponge.server());
event = SpongeEventFactory.createClientPingServerEvent(CAUSE, new GeyserStatusClient(inetSocketAddress), (ClientPingServerEvent.Response) response); event = SpongeEventFactory.createClientPingServerEvent(CAUSE, new GeyserStatusClient(inetSocketAddress), (ClientPingServerEvent.Response) response);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
Sponge.getEventManager().post(event); Sponge.eventManager().post(event);
GeyserPingInfo geyserPingInfo = new GeyserPingInfo( GeyserPingInfo geyserPingInfo = new GeyserPingInfo(
event.getResponse().getDescription().toPlain(), MessageTranslator.convertMessage(event.response().description()),
new GeyserPingInfo.Players( new GeyserPingInfo.Players(
event.getResponse().getPlayers().orElseThrow(IllegalStateException::new).getMax(), event.response().players().orElseThrow(IllegalStateException::new).max(),
event.getResponse().getPlayers().orElseThrow(IllegalStateException::new).getOnline() event.response().players().orElseThrow(IllegalStateException::new).online()
), ),
new GeyserPingInfo.Version( new GeyserPingInfo.Version(
event.getResponse().getVersion().getName(), event.response().version().name(),
GameProtocol.getJavaProtocolVersion()) // thanks for also not exposing this sponge GameProtocol.getJavaProtocolVersion()) // thanks for also not exposing this sponge
); );
event.getResponse().getPlayers().get().getProfiles().stream() event.response().players().ifPresent(players -> players.profiles().stream()
.map(GameProfile::getName) .map(GameProfile::name)
.map(op -> op.orElseThrow(IllegalStateException::new)) .filter(Optional::isPresent)
.forEach(geyserPingInfo.getPlayerList()::add); .map(Optional::get)
.forEach(geyserPingInfo.getPlayerList()::add)
);
return geyserPingInfo; return geyserPingInfo;
} }
@SuppressWarnings("NullableProblems") private record GeyserStatusClient(InetSocketAddress remote) implements StatusClient {
private static class GeyserStatusClient implements StatusClient {
private final InetSocketAddress remote;
public GeyserStatusClient(InetSocketAddress remote) {
this.remote = remote;
}
@Override @Override
public InetSocketAddress getAddress() { public InetSocketAddress address() {
return this.remote; return this.remote;
} }
@Override @Override
public MinecraftVersion getVersion() { public MinecraftVersion version() {
return Sponge.getPlatform().getMinecraftVersion(); return Sponge.platform().minecraftVersion();
} }
@Override @Override
public Optional<InetSocketAddress> getVirtualHost() { public Optional<InetSocketAddress> virtualHost() {
return Optional.empty(); return Optional.empty();
} }
} }

Datei anzeigen

@ -26,81 +26,169 @@
package org.geysermc.geyser.platform.sponge; package org.geysermc.geyser.platform.sponge;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.apache.logging.log4j.Logger;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.sponge.command.GeyserSpongeCommandExecutor;
import org.geysermc.geyser.platform.sponge.command.GeyserSpongeCommandManager; import org.geysermc.geyser.platform.sponge.command.GeyserSpongeCommandManager;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.FileUtils;
import org.slf4j.Logger; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.platform.sponge.command.GeyserSpongeCommandExecutor;
import org.spongepowered.api.Server;
import org.spongepowered.api.Sponge; import org.spongepowered.api.Sponge;
import org.spongepowered.api.config.ConfigDir; import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GameStartedServerEvent; import org.spongepowered.api.event.lifecycle.ConstructPluginEvent;
import org.spongepowered.api.event.game.state.GameStoppedEvent; import org.spongepowered.api.event.lifecycle.RegisterCommandEvent;
import org.spongepowered.api.plugin.Plugin; import org.spongepowered.api.event.lifecycle.StartedEngineEvent;
import org.spongepowered.api.event.lifecycle.StoppingEngineEvent;
import org.spongepowered.plugin.PluginContainer;
import org.spongepowered.plugin.builtin.jvm.Plugin;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Plugin(id = "geyser", name = GeyserImpl.NAME + "-Sponge", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC") @Plugin(value = "geyser")
public class GeyserSpongePlugin implements GeyserBootstrap { public class GeyserSpongePlugin implements GeyserBootstrap {
/**
* True if the plugin should be in a disabled state.
* This exists because you can't unregister or disable plugins in Sponge
*/
private boolean enabled = true;
@Inject
private PluginContainer pluginContainer;
@Inject @Inject
private Logger logger; private Logger logger;
@Inject @Inject
@ConfigDir(sharedRoot = false) @ConfigDir(sharedRoot = false)
private File configDir; private Path configPath;
private GeyserSpongeCommandManager geyserCommandManager; // Available after construction lifecycle
private GeyserSpongeConfiguration geyserConfig; private GeyserSpongeConfiguration geyserConfig;
private GeyserSpongeLogger geyserLogger; private GeyserSpongeLogger geyserLogger;
private GeyserImpl geyser;
private GeyserSpongeCommandManager geyserCommandManager; // Commands are only registered after command registration lifecycle
// Available after StartedEngine lifecycle
private IGeyserPingPassthrough geyserSpongePingPassthrough; private IGeyserPingPassthrough geyserSpongePingPassthrough;
private GeyserImpl geyser;
/**
* Only to be used for reloading
*/
@Override @Override
public void onEnable() { public void onEnable() {
enabled = true;
onConstruction(null);
// new commands cannot be registered, and geyser's command manager does not need be reloaded
onStartedEngine(null);
}
@Override
public void onDisable() {
enabled = false;
if (geyser != null) {
geyser.shutdown();
geyser = null;
}
}
/**
* Construct the configuration, logger, and command manager. command manager will only be filled with commands once
* the connector is started, but it allows us to register events in sponge.
*
* @param event Not used.
*/
@Listener
public void onConstruction(@Nullable ConstructPluginEvent event) {
GeyserLocale.init(this); GeyserLocale.init(this);
if (!configDir.exists()) File configDir = configPath.toFile();
if (!configDir.exists()) {
configDir.mkdirs(); configDir.mkdirs();
}
File configFile; File configFile;
try { try {
configFile = FileUtils.fileOrCopiedFromResource(new File(configDir, "config.yml"), "config.yml", configFile = FileUtils.fileOrCopiedFromResource(new File(configDir, "config.yml"), "config.yml",
(file) -> file.replaceAll("generateduuid", UUID.randomUUID().toString()), this); (file) -> file.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpongeConfiguration.class);
} catch (IOException ex) { } catch (IOException ex) {
logger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed")); logger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"));
ex.printStackTrace(); ex.printStackTrace();
onDisable();
return; return;
} }
try { GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpongeConfiguration.class); this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode());
} catch (IOException ex) {
logger.warn(GeyserLocale.getLocaleStringLog("geyser.config.failed")); this.geyser = GeyserImpl.load(PlatformType.SPONGE, this);
ex.printStackTrace();
this.geyserCommandManager = new GeyserSpongeCommandManager(geyser);
this.geyserCommandManager.init();
}
/**
* Construct the {@link GeyserSpongeCommandManager} and register the commands
*
* @param event required to register the commands
*/
@Listener
public void onRegisterCommands(@Nonnull RegisterCommandEvent<org.spongepowered.api.command.Command.Raw> event) {
if (enabled) {
event.register(this.pluginContainer, new GeyserSpongeCommandExecutor(geyser, geyserCommandManager.getCommands()), "geyser");
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
Map<String, Command> commands = entry.getValue();
if (commands.isEmpty()) {
continue;
}
event.register(this.pluginContainer, new GeyserSpongeCommandExecutor(this.geyser, commands), entry.getKey().description().id());
}
}
}
/**
* Configure the config properly if remote address is auto. Start connector and ping passthrough, and register subcommands of /geyser
*
* @param event not required
*/
@Listener
public void onStartedEngine(@Nullable StartedEngineEvent<Server> event) {
if (!enabled) {
return; return;
} }
if (Sponge.getServer().getBoundAddress().isPresent()) { if (Sponge.server().boundAddress().isPresent()) {
InetSocketAddress javaAddr = Sponge.getServer().getBoundAddress().get(); InetSocketAddress javaAddr = Sponge.server().boundAddress().get();
// Don't change the ip if its listening on all interfaces
// By default this should be 127.0.0.1 but may need to be changed in some circumstances // By default this should be 127.0.0.1 but may need to be changed in some circumstances
if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) {
this.geyserConfig.setAutoconfiguredRemote(true); this.geyserConfig.setAutoconfiguredRemote(true);
// Don't change the ip if its listening on all interfaces
if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) {
this.geyserConfig.getRemote().setAddress(javaAddr.getHostString());
}
geyserConfig.getRemote().setPort(javaAddr.getPort()); geyserConfig.getRemote().setPort(javaAddr.getPort());
} }
} }
@ -109,25 +197,18 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
geyserConfig.getBedrock().setPort(geyserConfig.getRemote().port()); geyserConfig.getBedrock().setPort(geyserConfig.getRemote().port());
} }
this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode()); GeyserImpl.start();
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.start(PlatformType.SPONGE, this);
if (geyserConfig.isLegacyPingPassthrough()) { if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserSpongePingPassthrough = GeyserLegacyPingPassthrough.init(geyser); this.geyserSpongePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else { } else {
this.geyserSpongePingPassthrough = new GeyserSpongePingPassthrough(); this.geyserSpongePingPassthrough = new GeyserSpongePingPassthrough();
} }
this.geyserCommandManager = new GeyserSpongeCommandManager(Sponge.getCommandManager(), geyser);
this.geyserCommandManager.init();
Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser, geyserCommandManager.getCommands()), "geyser");
Sponge.getCommandManager().register(this, new GeyserSpongeCommandExecutor(geyser, geyserCommandManager.commands()), "geyserext");
} }
@Override @Listener
public void onDisable() { public void onEngineStopping(StoppingEngineEvent<Server> event) {
geyser.shutdown(); onDisable();
} }
@Override @Override
@ -142,7 +223,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
@Override @Override
public GeyserCommandManager getGeyserCommandManager() { public GeyserCommandManager getGeyserCommandManager() {
return this.geyserCommandManager; return geyserCommandManager;
} }
@Override @Override
@ -152,17 +233,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
@Override @Override
public Path getConfigFolder() { public Path getConfigFolder() {
return configDir.toPath(); return configPath;
}
@Listener
public void onServerStart(GameStartedServerEvent event) {
onEnable();
}
@Listener
public void onServerStop(GameStoppedEvent event) {
onDisable();
} }
@Override @Override
@ -172,6 +243,6 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
@Override @Override
public String getMinecraftServerVersion() { public String getMinecraftServerVersion() {
return Sponge.getPlatform().getMinecraftVersion().getName(); return Sponge.platform().minecraftVersion().name();
} }
} }

Datei anzeigen

@ -25,81 +25,88 @@
package org.geysermc.geyser.platform.sponge.command; package org.geysermc.geyser.platform.sponge.command;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandExecutor; import org.geysermc.geyser.command.GeyserCommandExecutor;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.spongepowered.api.command.CommandCallable; import org.jetbrains.annotations.NotNull;
import org.spongepowered.api.command.CommandCause;
import org.spongepowered.api.command.CommandCompletion;
import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.parameter.ArgumentReader;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;
import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
public class GeyserSpongeCommandExecutor extends GeyserCommandExecutor implements CommandCallable { public class GeyserSpongeCommandExecutor extends GeyserCommandExecutor implements org.spongepowered.api.command.Command.Raw {
public GeyserSpongeCommandExecutor(GeyserImpl geyser, Map<String, Command> commands) { public GeyserSpongeCommandExecutor(GeyserImpl geyser, Map<String, Command> commands) {
super(geyser, commands); super(geyser, commands);
} }
@Override @Override
public CommandResult process(CommandSource source, String arguments) { public CommandResult process(CommandCause cause, ArgumentReader.Mutable arguments) {
GeyserCommandSource commandSender = new SpongeCommandSource(source); GeyserCommandSource commandSource = new SpongeCommandSource(cause);
GeyserSession session = getGeyserSession(commandSender); GeyserSession session = getGeyserSession(commandSource);
String[] args = arguments.split(" "); String[] args = arguments.input().split(" ");
if (args.length > 0) { // This split operation results in an array of length 1, containing a zero length string, if the input string is empty
if (args.length > 0 && !args[0].isEmpty()) {
GeyserCommand command = getCommand(args[0]); GeyserCommand command = getCommand(args[0]);
if (command != null) { if (command != null) {
if (!source.hasPermission(command.permission())) { if (!cause.hasPermission(command.permission())) {
// Not ideal to use log here but we dont get a session cause.audience().sendMessage(Component.text(GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.permission_fail")).color(NamedTextColor.RED));
source.sendMessage(Text.of(ChatColor.RED + GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.permission_fail")));
return CommandResult.success(); return CommandResult.success();
} }
if (command.isBedrockOnly() && session == null) { if (command.isBedrockOnly() && session == null) {
source.sendMessage(Text.of(ChatColor.RED + GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.bedrock_only"))); cause.audience().sendMessage(Component.text(GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.bedrock_only")).color(NamedTextColor.RED));
return CommandResult.success(); return CommandResult.success();
} }
getCommand(args[0]).execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]); command.execute(session, commandSource, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
} else {
cause.audience().sendMessage(Component.text(GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.not_found")).color(NamedTextColor.RED));
} }
} else { } else {
getCommand("help").execute(session, commandSender, new String[0]); getCommand("help").execute(session, commandSource, new String[0]);
} }
return CommandResult.success(); return CommandResult.success();
} }
@Override @Override
public List<String> getSuggestions(CommandSource source, String arguments, @Nullable Location<World> targetPosition) { public List<CommandCompletion> complete(CommandCause cause, ArgumentReader.Mutable arguments) {
if (arguments.split(" ").length == 1) { if (arguments.input().split(" ").length == 1) {
return tabComplete(new SpongeCommandSource(source)); return tabComplete(new SpongeCommandSource(cause)).stream().map(CommandCompletion::of).collect(Collectors.toList());
} }
return Collections.emptyList(); return Collections.emptyList();
} }
@Override @Override
public boolean testPermission(CommandSource source) { public boolean canExecute(CommandCause cause) {
return true; return true;
} }
@Override @Override
public Optional<Text> getShortDescription(CommandSource source) { public Optional<Component> shortDescription(CommandCause cause) {
return Optional.of(Text.of("The main command for Geyser.")); return Optional.of(Component.text("The main command for Geyser."));
} }
@Override @Override
public Optional<Text> getHelp(CommandSource source) { public Optional<Component> extendedDescription(CommandCause cause) {
return Optional.of(Text.of("/geyser help")); return shortDescription(cause);
} }
@Override @Override
public Text getUsage(CommandSource source) { public Optional<Component> help(@NotNull CommandCause cause) {
return Text.of("/geyser help"); return Optional.of(Component.text("/geyser help"));
}
@Override
public Component usage(CommandCause cause) {
return Component.text("/geyser help");
} }
} }

Datei anzeigen

@ -25,25 +25,37 @@
package org.geysermc.geyser.platform.sponge.command; package org.geysermc.geyser.platform.sponge.command;
import net.kyori.adventure.text.Component;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.spongepowered.api.Sponge; import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandMapping; import org.spongepowered.api.command.CommandCause;
import org.spongepowered.api.text.Text; import org.spongepowered.api.command.manager.CommandMapping;
import java.util.Optional;
public class GeyserSpongeCommandManager extends GeyserCommandManager { public class GeyserSpongeCommandManager extends GeyserCommandManager {
private final org.spongepowered.api.command.CommandManager handle;
public GeyserSpongeCommandManager(org.spongepowered.api.command.CommandManager handle, GeyserImpl geyser) { public GeyserSpongeCommandManager(GeyserImpl geyser) {
super(geyser); super(geyser);
this.handle = handle;
} }
@Override @Override
public String description(String command) { public String description(String command) {
return handle.get(command).map(CommandMapping::getCallable) if (!Sponge.isServerAvailable()) {
.map(callable -> callable.getShortDescription(Sponge.getServer().getConsole()).orElse(Text.EMPTY)) return "";
.orElse(Text.EMPTY).toPlain(); }
// Note: The command manager may be replaced at any point during the game lifecycle
return Sponge.server().commandManager().commandMapping(command)
.map(this::description)
.map(Optional::get)
.map(MessageTranslator::convertMessage)
.orElse("");
}
public Optional<Component> description(CommandMapping mapping) {
return mapping.registrar().shortDescription(CommandCause.create(), mapping);
} }
} }

Datei anzeigen

@ -26,29 +26,30 @@
package org.geysermc.geyser.platform.sponge.command; package org.geysermc.geyser.platform.sponge.command;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
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.command.GeyserCommandSource;
import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.CommandCause;
import org.spongepowered.api.command.source.ConsoleSource; import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import org.spongepowered.api.text.Text;
@AllArgsConstructor @AllArgsConstructor
public class SpongeCommandSource implements GeyserCommandSource { public class SpongeCommandSource implements GeyserCommandSource {
private CommandSource handle; private final CommandCause handle;
@Override @Override
public String name() { public String name() {
return handle.getName(); return handle.friendlyIdentifier().orElse(handle.identifier());
} }
@Override @Override
public void sendMessage(String message) { public void sendMessage(@NonNull String message) {
handle.sendMessage(Text.of(message)); handle.audience().sendMessage(LegacyComponentSerializer.legacySection().deserialize(message));
} }
@Override @Override
public boolean isConsole() { public boolean isConsole() {
return handle instanceof ConsoleSource; return !(handle.cause().root() instanceof ServerPlayer);
} }
@Override @Override

Datei anzeigen

@ -0,0 +1,30 @@
{
"loader": {
"name": "java_plain",
"version": "1.0"
},
"license": "MIT",
"plugins": [
{
"id": "${id}",
"name": "${name}-Sponge",
"version": "${version}",
"entrypoint": "org.geysermc.geyser.platform.sponge.GeyserSpongePlugin",
"description": "${description}",
"links": {
"homepage": "${url}"
},
"contributors": [
{
"name": "${author}"
}
],
"dependencies": [
{
"id": "spongeapi",
"version": "8.0.0"
}
]
}
]
}

Datei anzeigen

@ -0,0 +1,6 @@
{
"pack": {
"description": "Geyser for Sponge",
"pack_format": 6
}
}

Datei anzeigen

@ -6,20 +6,16 @@ val jlineVersion = "3.21.0"
dependencies { dependencies {
api(projects.core) api(projects.core)
implementation("net.minecrell", "terminalconsoleappender", terminalConsoleVersion) { implementation(libs.terminalconsoleappender) {
exclude("org.apache.logging.log4j", "log4j-core") exclude("org.apache.logging.log4j", "log4j-core")
exclude("org.jline", "jline-reader") exclude("org.jline", "jline-reader")
exclude("org.jline", "jline-terminal") exclude("org.jline", "jline-terminal")
exclude("org.jline", "jline-terminal-jna") exclude("org.jline", "jline-terminal-jna")
} }
implementation("org.jline", "jline-terminal", jlineVersion) implementation(libs.bundles.jline)
implementation("org.jline", "jline-terminal-jna", jlineVersion)
implementation("org.jline", "jline-reader", jlineVersion)
implementation("org.apache.logging.log4j", "log4j-api", Versions.log4jVersion) implementation(libs.bundles.log4j)
implementation("org.apache.logging.log4j", "log4j-core", Versions.log4jVersion)
implementation("org.apache.logging.log4j", "log4j-slf4j18-impl", Versions.log4jVersion)
} }
application { application {
@ -27,7 +23,7 @@ application {
} }
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> { tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
archiveBaseName.set("Geyser") archiveBaseName.set("Geyser-Standalone")
transform(Log4j2PluginsCacheFileTransformer()) transform(Log4j2PluginsCacheFileTransformer())
} }

Datei anzeigen

@ -47,10 +47,10 @@ import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.standalone.command.GeyserStandaloneCommandManager;
import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI; import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.FileUtils;
import org.geysermc.geyser.util.LoopbackUtil;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -63,7 +63,7 @@ import java.util.stream.Collectors;
public class GeyserStandaloneBootstrap implements GeyserBootstrap { public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserStandaloneCommandManager geyserCommandManager; private GeyserCommandManager geyserCommandManager;
private GeyserStandaloneConfiguration geyserConfig; private GeyserStandaloneConfiguration geyserConfig;
private GeyserStandaloneLogger geyserLogger; private GeyserStandaloneLogger geyserLogger;
private IGeyserPingPassthrough geyserPingPassthrough; private IGeyserPingPassthrough geyserPingPassthrough;
@ -188,7 +188,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
geyserLogger = new GeyserStandaloneLogger(); geyserLogger = new GeyserStandaloneLogger();
LoopbackUtil.checkLoopback(geyserLogger); LoopbackUtil.checkAndApplyLoopback(geyserLogger);
try { try {
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml",
@ -216,8 +216,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
// Allow libraries like Protocol to have their debug information passthrough // Allow libraries like Protocol to have their debug information passthrough
logger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO); logger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO);
geyser = GeyserImpl.start(PlatformType.STANDALONE, this); geyser = GeyserImpl.load(PlatformType.STANDALONE, this);
geyserCommandManager = new GeyserStandaloneCommandManager(geyser); GeyserImpl.start();
geyserCommandManager = new GeyserCommandManager(geyser);
geyserCommandManager.init(); geyserCommandManager.init();
if (gui != null) { if (gui != null) {

Datei anzeigen

@ -95,24 +95,4 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
public boolean isDebug() { public boolean isDebug() {
return log.isDebugEnabled(); return log.isDebugEnabled();
} }
@Override
public String name() {
return "CONSOLE";
}
@Override
public void sendMessage(String message) {
info(message);
}
@Override
public boolean isConsole() {
return true;
}
@Override
public boolean hasPermission(String permission) {
return true;
}
} }

Datei anzeigen

@ -28,8 +28,8 @@ package org.geysermc.geyser.platform.standalone.gui;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.platform.standalone.GeyserStandaloneLogger; import org.geysermc.geyser.platform.standalone.GeyserStandaloneLogger;
import org.geysermc.geyser.platform.standalone.command.GeyserStandaloneCommandManager;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
@ -256,7 +256,7 @@ public class GeyserStandaloneGUI {
* @param geyserStandaloneLogger The current logger * @param geyserStandaloneLogger The current logger
* @param geyserCommandManager The commands manager * @param geyserCommandManager The commands manager
*/ */
public void setupInterface(GeyserStandaloneLogger geyserStandaloneLogger, GeyserStandaloneCommandManager geyserCommandManager) { public void setupInterface(GeyserStandaloneLogger geyserStandaloneLogger, GeyserCommandManager geyserCommandManager) {
commandsMenu.removeAll(); commandsMenu.removeAll();
optionsMenu.removeAll(); optionsMenu.removeAll();

Datei anzeigen

@ -1,7 +1,5 @@
val velocityVersion = "3.0.0"
dependencies { dependencies {
annotationProcessor("com.velocitypowered", "velocity-api", velocityVersion) annotationProcessor(libs.velocity.api)
api(projects.core) api(projects.core)
} }
@ -34,7 +32,7 @@ exclude("net.kyori:adventure-text-serializer-legacy:*")
exclude("net.kyori:adventure-nbt:*") exclude("net.kyori:adventure-nbt:*")
// These dependencies are already present on the platform // These dependencies are already present on the platform
provided("com.velocitypowered", "velocity-api", velocityVersion) provided(libs.velocity.api)
application { application {
mainClass.set("org.geysermc.geyser.platform.velocity.GeyserVelocityMain") mainClass.set("org.geysermc.geyser.platform.velocity.GeyserVelocityMain")

Datei anzeigen

@ -41,6 +41,8 @@ public class GeyserVelocityDumpInfo extends BootstrapDumpInfo {
private final String platformVersion; private final String platformVersion;
private final String platformVendor; private final String platformVendor;
private final boolean onlineMode; private final boolean onlineMode;
@AsteriskSerializer.Asterisk(isIp = true)
private final String serverIP; private final String serverIP;
private final int serverPort; private final int serverPort;
private final List<PluginInfo> plugins; private final List<PluginInfo> plugins;
@ -51,11 +53,7 @@ public class GeyserVelocityDumpInfo extends BootstrapDumpInfo {
this.platformVersion = proxy.getVersion().getVersion(); this.platformVersion = proxy.getVersion().getVersion();
this.platformVendor = proxy.getVersion().getVendor(); this.platformVendor = proxy.getVersion().getVendor();
this.onlineMode = proxy.getConfiguration().isOnlineMode(); this.onlineMode = proxy.getConfiguration().isOnlineMode();
if (AsteriskSerializer.showSensitive || (proxy.getBoundAddress().getHostString().equals("") || proxy.getBoundAddress().getHostString().equals("0.0.0.0"))) {
this.serverIP = proxy.getBoundAddress().getHostString(); this.serverIP = proxy.getBoundAddress().getHostString();
} else {
this.serverIP = "***";
}
this.serverPort = proxy.getBoundAddress().getPort(); this.serverPort = proxy.getBoundAddress().getPort();
this.plugins = new ArrayList<>(); this.plugins = new ArrayList<>();

Datei anzeigen

@ -35,9 +35,12 @@ import com.velocitypowered.api.network.ListenerType;
import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.util.Codec;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
@ -45,7 +48,6 @@ import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandExecutor; import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandExecutor;
import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandManager;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.FileUtils;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -57,6 +59,7 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC") @Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
@ -71,7 +74,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
@Inject @Inject
private CommandManager commandManager; private CommandManager commandManager;
private GeyserVelocityCommandManager geyserCommandManager; private GeyserCommandManager geyserCommandManager;
private GeyserVelocityConfiguration geyserConfig; private GeyserVelocityConfiguration geyserConfig;
private GeyserVelocityInjector geyserInjector; private GeyserVelocityInjector geyserInjector;
private GeyserVelocityLogger geyserLogger; private GeyserVelocityLogger geyserLogger;
@ -84,6 +87,15 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
@Override @Override
public void onEnable() { 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;
}
GeyserLocale.init(this); GeyserLocale.init(this);
try { try {
@ -118,6 +130,8 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode()); this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this);
// Remove this in like a year // Remove this in like a year
try { try {
// Should only exist on 1.0 // Should only exist on 1.0
@ -140,21 +154,34 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile()); geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile());
this.geyser = GeyserImpl.start(PlatformType.VELOCITY, this); }
private void postStartup() {
GeyserImpl.start();
this.geyserInjector = new GeyserVelocityInjector(proxyServer); this.geyserInjector = new GeyserVelocityInjector(proxyServer);
// Will be initialized after the proxy has been bound // Will be initialized after the proxy has been bound
this.geyserCommandManager = new GeyserVelocityCommandManager(geyser); this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init(); this.geyserCommandManager.init();
this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.getCommands())); this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.getCommands()));
this.commandManager.register("geyserext", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.commands())); for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
Map<String, Command> commands = entry.getValue();
if (commands.isEmpty()) {
continue;
}
this.commandManager.register(entry.getKey().description().id(), new GeyserVelocityCommandExecutor(this.geyser, commands));
}
if (geyserConfig.isLegacyPingPassthrough()) { if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else { } else {
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer); this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
} }
proxyServer.getEventManager().register(this, new GeyserVelocityUpdateListener());
} }
@Override @Override
@ -199,11 +226,16 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
@Subscribe @Subscribe
public void onProxyBound(ListenerBoundEvent event) { public void onProxyBound(ListenerBoundEvent event) {
if (event.getListenerType() == ListenerType.MINECRAFT && geyserInjector != null) { if (event.getListenerType() == ListenerType.MINECRAFT) {
// Once listener is bound, do our startup process
this.postStartup();
if (geyserInjector != null) {
// After this bound, we know that the channel initializer cannot change without it being ineffective for Velocity, too // After this bound, we know that the channel initializer cannot change without it being ineffective for Velocity, too
geyserInjector.initializeLocalChannel(this); geyserInjector.initializeLocalChannel(this);
} }
} }
}
@Override @Override
public BootstrapDumpInfo getDumpInfo() { public BootstrapDumpInfo getDumpInfo() {

Datei anzeigen

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.velocity;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.proxy.Player;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.platform.velocity.command.VelocityCommandSource;
import org.geysermc.geyser.util.VersionCheckUtils;
public final class GeyserVelocityUpdateListener {
@Subscribe
public void onPlayerJoin(PostLoginEvent event) {
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
final Player player = event.getPlayer();
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player));
}
}
}
}

Datei anzeigen

@ -63,6 +63,9 @@ public class GeyserVelocityCommandExecutor extends GeyserCommandExecutor impleme
return; return;
} }
command.execute(session, sender, invocation.arguments().length > 1 ? Arrays.copyOfRange(invocation.arguments(), 1, invocation.arguments().length) : new String[0]); command.execute(session, sender, invocation.arguments().length > 1 ? Arrays.copyOfRange(invocation.arguments(), 1, invocation.arguments().length) : new String[0]);
} else {
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.not_found", sender.locale());
sender.sendMessage(ChatColor.RED + message);
} }
} else { } else {
getCommand("help").execute(session, sender, new String[0]); getCommand("help").execute(session, sender, new String[0]);

Datei anzeigen

@ -28,6 +28,7 @@ package org.geysermc.geyser.platform.velocity.command;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ConsoleCommandSource; import com.velocitypowered.api.proxy.ConsoleCommandSource;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
@ -59,6 +60,12 @@ public class VelocityCommandSource implements GeyserCommandSource {
handle.sendMessage(LegacyComponentSerializer.legacy('§').deserialize(message)); handle.sendMessage(LegacyComponentSerializer.legacy('§').deserialize(message));
} }
@Override
public void sendMessage(Component message) {
// Be careful that we don't shade in Adventure!!
handle.sendMessage(message);
}
@Override @Override
public boolean isConsole() { public boolean isConsole() {
return handle instanceof ConsoleCommandSource; return handle instanceof ConsoleCommandSource;

Datei anzeigen

@ -6,12 +6,17 @@ plugins {
repositories { repositories {
gradlePluginPortal() gradlePluginPortal()
maven("https://repo.opencollab.dev/maven-snapshots")
} }
dependencies { dependencies {
implementation("net.kyori", "indra-common", "2.0.6") implementation("net.kyori", "indra-common", "2.0.6")
implementation("org.jfrog.buildinfo", "build-info-extractor-gradle", "4.26.1") implementation("org.jfrog.buildinfo", "build-info-extractor-gradle", "4.26.1")
implementation("gradle.plugin.com.github.johnrengelman", "shadow", "7.1.1") implementation("com.github.johnrengelman", "shadow", "7.1.3-SNAPSHOT")
// Within the gradle plugin classpath, there is a version conflict between loom and some other
// plugin for databind. This fixes it: minimum 2.13.2 is required by loom.
implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0")
} }
tasks.withType<KotlinCompile> { tasks.withType<KotlinCompile> {

Datei anzeigen

@ -1,46 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
object Versions {
const val jacksonVersion = "2.13.2"
const val fastutilVersion = "8.5.2"
const val nettyVersion = "4.1.66.Final"
const val guavaVersion = "29.0-jre"
const val nbtVersion = "2.1.0"
const val websocketVersion = "1.5.1"
const val protocolVersion = "a78a64b"
// Not pinned to specific version due to possible gradle bug
// See comment in settings.gradle.kts
const val raknetVersion = "1.6.28-SNAPSHOT"
const val mcauthlibVersion = "d9d773e"
const val mcprotocollibversion = "54fc9f0"
const val packetlibVersion = "3.0"
const val adventureVersion = "4.9.3"
const val eventVersion = "3.0.0"
const val junitVersion = "4.13.1"
const val checkerQualVersion = "3.19.0"
const val cumulusVersion = "1.1"
const val log4jVersion = "2.17.1"
}

Datei anzeigen

@ -25,7 +25,9 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.MinimalExternalModuleDependency
import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.named
fun Project.isSnapshot(): Boolean = fun Project.isSnapshot(): Boolean =
@ -43,9 +45,11 @@ fun Project.exclude(group: String) {
} }
} }
fun Project.platformRelocate(pattern: String) { fun Project.platformRelocate(pattern: String, exclusion: String = "") {
tasks.named<ShadowJar>("shadowJar") { tasks.named<ShadowJar>("shadowJar") {
relocate(pattern, "org.geysermc.geyser.platform.${project.name}.shaded.$pattern") relocate(pattern, "org.geysermc.geyser.platform.${project.name}.shaded.$pattern") {
exclude(exclusion)
}
} }
} }
@ -62,5 +66,11 @@ fun Project.provided(pattern: String, name: String, version: String, excludedOn:
fun Project.provided(dependency: ProjectDependency) = fun Project.provided(dependency: ProjectDependency) =
provided(dependency.group!!, dependency.name, dependency.version!!) provided(dependency.group!!, dependency.name, dependency.version!!)
fun Project.provided(dependency: MinimalExternalModuleDependency) =
provided(dependency.module.group, dependency.module.name, dependency.versionConstraint.requiredVersion)
fun Project.provided(provider: Provider<MinimalExternalModuleDependency>) =
provided(provider.get())
private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String = private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String =
if (excludedOn and bit > 0) section else "" if (excludedOn and bit > 0) section else ""

Datei anzeigen

@ -4,14 +4,15 @@ plugins {
} }
dependencies { dependencies {
compileOnly("org.checkerframework", "checker-qual", Versions.checkerQualVersion) compileOnly("org.checkerframework", "checker-qual", "3.19.0")
} }
tasks { tasks {
processResources { processResources {
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json")) { // Spigot, BungeeCord, Velocity, Sponge, Fabric
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "META-INF/sponge_plugins.json", "fabric.mod.json")) {
expand( expand(
"id" to "Geyser", "id" to "geyser",
"name" to "Geyser", "name" to "Geyser",
"version" to project.version, "version" to project.version,
"description" to project.description, "description" to project.description,

Datei anzeigen

@ -5,24 +5,26 @@ plugins {
} }
publishing { publishing {
publications.create<MavenPublication>("mavenJava") { publications {
create<MavenPublication>("mavenJava") {
groupId = project.group as String groupId = project.group as String
artifactId = project.name artifactId = project.name
version = project.version as String version = project.version as String
artifact(tasks["shadowJar"]) from(components["java"])
artifact(tasks["sourcesJar"]) }
} }
} }
artifactory { artifactory {
setContextUrl("https://repo.opencollab.dev/artifactory")
publish { publish {
repository { repository {
setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases") setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases")
setMavenCompatible(true) setMavenCompatible(true)
} }
defaults { defaults {
publishConfigs("archives") publications("mavenJava")
setPublishArtifacts(true) setPublishArtifacts(true)
setPublishPom(true) setPublishPom(true)
setPublishIvy(false) setPublishIvy(false)

Datei anzeigen

@ -15,6 +15,7 @@ allprojects {
} }
val platforms = setOf( val platforms = setOf(
projects.fabric,
projects.bungeecord, projects.bungeecord,
projects.spigot, projects.spigot,
projects.sponge, projects.sponge,

Datei anzeigen

@ -1,3 +1,8 @@
dependencies { plugins {
api("org.geysermc.cumulus", "cumulus", Versions.cumulusVersion) id("geyser.publish-conventions")
}
dependencies {
api(libs.cumulus)
api(libs.gson)
} }

Datei anzeigen

@ -25,7 +25,7 @@
package org.geysermc.floodgate.pluginmessage; package org.geysermc.floodgate.pluginmessage;
import com.google.common.base.Charsets; import java.nio.charset.StandardCharsets;
public final class PluginMessageChannels { public final class PluginMessageChannels {
public static final String SKIN = "floodgate:skin"; public static final String SKIN = "floodgate:skin";
@ -35,7 +35,7 @@ public final class PluginMessageChannels {
private static final byte[] FLOODGATE_REGISTER_DATA = private static final byte[] FLOODGATE_REGISTER_DATA =
String.join("\0", SKIN, FORM, TRANSFER, PACKET) String.join("\0", SKIN, FORM, TRANSFER, PACKET)
.getBytes(Charsets.UTF_8); .getBytes(StandardCharsets.UTF_8);
/** /**
* Get the prebuilt register data as a byte array * Get the prebuilt register data as a byte array

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen