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

# Conflicts:
#	bootstrap/fabric/src/main/java/org/geysermc/geyser/platform/fabric/world/GeyserFabricWorldManager.java
#	bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/manager/GeyserSpigotWorldManager.java
#	bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/GeyserSpongePlugin.java
#	bootstrap/sponge/src/main/java/org/geysermc/geyser/platform/sponge/command/SpongeCommandSource.java
#	core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java
This commit is contained in:
Konicai 2023-09-11 14:02:05 -04:00
commit 6e4f718069
35 changed files with 224 additions and 807 deletions

View File

@ -65,13 +65,6 @@ jobs:
name: Geyser BungeeCord
path: bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Sponge)
uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser Sponge
path: bootstrap/sponge/build/libs/Geyser-Sponge.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Velocity)
uses: actions/upload-artifact@v3
if: success()

View File

@ -80,13 +80,6 @@ jobs:
name: Geyser BungeeCord
path: geyser/bootstrap/bungeecord/build/libs/Geyser-BungeeCord.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Sponge)
uses: actions/upload-artifact@v3
if: success()
with:
name: Geyser Sponge
path: geyser/bootstrap/sponge/build/libs/Geyser-Sponge.jar
if-no-files-found: error
- name: Archive artifacts (Geyser Velocity)
uses: actions/upload-artifact@v3
if: success()

1
.gitignore vendored
View File

@ -251,3 +251,4 @@ locales/
/saved-refresh-tokens.json
/custom_mappings/
/languages/
/custom-skulls.yml

View File

@ -29,10 +29,14 @@ package org.geysermc.geyser.api.util;
* Represents the platform Geyser is running on.
*/
public record PlatformType(String platformName) {
@Deprecated
public static final PlatformType ANDROID = new PlatformType("Android");
public static final PlatformType BUNGEECORD = new PlatformType("BungeeCord");
public static final PlatformType FABRIC = new PlatformType("Fabric");
public static final PlatformType SPIGOT = new PlatformType("Spigot");
@Deprecated
public static final PlatformType SPONGE = new PlatformType("Sponge");
public static final PlatformType STANDALONE = new PlatformType("Standalone");
public static final PlatformType VELOCITY = new PlatformType("Velocity");

View File

@ -25,6 +25,7 @@
package org.geysermc.geyser.platform.fabric.world;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.core.BlockPos;
@ -147,6 +148,11 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
}
@Override
public GameMode getDefaultGameMode(GeyserSession session) {
return GameMode.byId(server.getDefaultGameType().getId());
}
@Nonnull
@Override
public CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {

View File

@ -25,6 +25,7 @@
package org.geysermc.geyser.platform.spigot.world.manager;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.bukkit.Bukkit;
@ -171,6 +172,11 @@ public class GeyserSpigotWorldManager extends WorldManager {
return gameRule.getDefaultIntValue();
}
@Override
public GameMode getDefaultGameMode(GeyserSession session) {
return GameMode.byId(Bukkit.getDefaultGameMode().ordinal());
}
@Nonnull
@Override
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {

View File

@ -1,38 +0,0 @@
dependencies {
api(projects.core)
}
platformRelocate("com.fasterxml.jackson")
platformRelocate("io.netty")
platformRelocate("it.unimi.dsi.fastutil")
platformRelocate("com.google.common")
platformRelocate("com.google.guava")
platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl")
platformRelocate("net.kyori.adventure.nbt")
// These dependencies are already present on the platform
provided(libs.sponge.api)
application {
mainClass.set("org.geysermc.geyser.platform.sponge.GeyserSpongeMain")
}
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
archiveBaseName.set("Geyser-Sponge")
dependencies {
exclude(dependency("com.google.code.gson:.*"))
exclude(dependency("org.yaml:.*"))
exclude(dependency("org.slf4j:.*"))
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:.*"))
}
}

View File

@ -1,37 +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.sponge;
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
import java.nio.file.Path;
public final class GeyserSpongeConfiguration extends GeyserJacksonConfiguration {
@Override
public Path getFloodgateKeyPath() {
return null; //floodgate isn't available for Sponge
}
}

View File

@ -1,71 +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.sponge;
import lombok.Getter;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.text.AsteriskSerializer;
import org.spongepowered.api.Platform;
import org.spongepowered.api.Sponge;
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.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Getter
public class GeyserSpongeDumpInfo extends BootstrapDumpInfo {
private final String platformName;
private final String platformVersion;
private final boolean onlineMode;
@AsteriskSerializer.Asterisk(isIp = true)
private final String serverIP;
private final int serverPort;
private final List<PluginInfo> plugins;
GeyserSpongeDumpInfo() {
PluginContainer container = Sponge.platform().container(Platform.Component.IMPLEMENTATION);
PluginMetadata platformMeta = container.metadata();
this.platformName = platformMeta.name().orElse("unknown");
this.platformVersion = platformMeta.version().getQualifier();
this.onlineMode = Sponge.server().isOnlineModeEnabled();
Optional<InetSocketAddress> socketAddress = Sponge.server().boundAddress();
this.serverIP = socketAddress.map(InetSocketAddress::getHostString).orElse("unknown");
this.serverPort = socketAddress.map(InetSocketAddress::getPort).orElse(-1);
this.plugins = new ArrayList<>();
for (PluginContainer plugin : Sponge.pluginManager().plugins()) {
PluginMetadata meta = plugin.metadata();
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));
}
}
}

View File

@ -1,43 +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.sponge;
import org.geysermc.geyser.GeyserMain;
public class GeyserSpongeMain extends GeyserMain {
public static void main(String[] args) {
new GeyserSpongeMain().displayMessage();
}
public String getPluginType() {
return "Sponge";
}
public String getPluginFolder() {
return "mods";
}
}

View File

@ -1,106 +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.sponge;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.ping.GeyserPingInfo;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.spongepowered.api.MinecraftVersion;
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.server.ClientPingServerEvent;
import org.spongepowered.api.network.status.StatusClient;
import org.spongepowered.api.profile.GameProfile;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.util.Optional;
public class GeyserSpongePingPassthrough implements IGeyserPingPassthrough {
private static final Cause CAUSE = Cause.of(EventContext.empty(), Sponge.server());
private static Method SpongeStatusResponse_create;
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) {
// come on Sponge, this is in commons, why not expose it :(
ClientPingServerEvent event;
try {
if (SpongeStatusResponse_create == null) {
Class SpongeStatusResponse = Class.forName("org.spongepowered.common.network.status.SpongeStatusResponse");
Class MinecraftServer = Class.forName("net.minecraft.server.MinecraftServer");
SpongeStatusResponse_create = SpongeStatusResponse.getDeclaredMethod("create", MinecraftServer);
}
Object response = SpongeStatusResponse_create.invoke(null, Sponge.server());
event = SpongeEventFactory.createClientPingServerEvent(CAUSE, new GeyserStatusClient(inetSocketAddress), (ClientPingServerEvent.Response) response);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
Sponge.eventManager().post(event);
GeyserPingInfo geyserPingInfo = new GeyserPingInfo(
MessageTranslator.convertMessage(event.response().description()),
new GeyserPingInfo.Players(
event.response().players().orElseThrow(IllegalStateException::new).max(),
event.response().players().orElseThrow(IllegalStateException::new).online()
),
new GeyserPingInfo.Version(
event.response().version().name(),
GameProtocol.getJavaProtocolVersion()) // thanks for also not exposing this sponge
);
event.response().players().ifPresent(players -> players.profiles().stream()
.map(GameProfile::name)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(geyserPingInfo.getPlayerList()::add)
);
return geyserPingInfo;
}
private record GeyserStatusClient(InetSocketAddress remote) implements StatusClient {
@Override
public InetSocketAddress address() {
return this.remote;
}
@Override
public MinecraftVersion version() {
return Sponge.platform().minecraftVersion();
}
@Override
public Optional<InetSocketAddress> virtualHost() {
return Optional.empty();
}
}
}

View File

@ -1,227 +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.sponge;
import com.google.inject.Inject;
import org.apache.logging.log4j.Logger;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.CommandRegistry;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.util.FileUtils;
import org.geysermc.geyser.text.GeyserLocale;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.api.Server;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.lifecycle.ConstructPluginEvent;
import org.spongepowered.api.event.lifecycle.RegisterCommandEvent;
import org.spongepowered.api.event.lifecycle.StartedEngineEvent;
import org.spongepowered.api.event.lifecycle.StoppingEngineEvent;
import org.spongepowered.plugin.builtin.jvm.Plugin;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.util.Objects;
import java.util.UUID;
@Plugin(value = "geyser")
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 Logger logger;
@Inject
@ConfigDir(sharedRoot = false)
private Path configPath;
// Available after construction lifecycle
private GeyserSpongeConfiguration geyserConfig;
private GeyserSpongeLogger geyserLogger;
private GeyserImpl geyser;
private CommandRegistry commandRegistry;
// Available after StartedEngine lifecycle
private IGeyserPingPassthrough geyserSpongePingPassthrough;
/**
* Only to be used for reloading
*/
@Override
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);
File configDir = configPath.toFile();
if (!configDir.exists()) {
configDir.mkdirs();
}
File configFile;
try {
configFile = FileUtils.fileOrCopiedFromResource(new File(configDir, "config.yml"), "config.yml",
(file) -> file.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpongeConfiguration.class);
} catch (IOException ex) {
logger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"));
ex.printStackTrace();
onDisable();
return;
}
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode());
this.geyser = GeyserImpl.load(PlatformType.SPONGE, this);
this.commandRegistry = new CommandRegistry(geyser, Objects.requireNonNull(null)); // todo: commands
}
/**
* Construct the {@link CommandRegistry} and register the commands
*
* @param event required to register the commands
*/
@Listener
public void onRegisterCommands(@Nonnull RegisterCommandEvent<org.spongepowered.api.command.Command.Raw> event) {
// todo: commands. sponge-cloud probably hooks into this without us needing to
}
/**
* 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;
}
GeyserImpl.start();
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserSpongePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
this.geyserSpongePingPassthrough = new GeyserSpongePingPassthrough();
}
}
@Listener
public void onEngineStopping(StoppingEngineEvent<Server> event) {
onDisable();
}
@Override
public GeyserSpongeConfiguration getGeyserConfig() {
return geyserConfig;
}
@Override
public GeyserSpongeLogger getGeyserLogger() {
return geyserLogger;
}
@Override
public CommandRegistry getCommandRegistry() {
return commandRegistry;
}
@Override
public IGeyserPingPassthrough getGeyserPingPassthrough() {
return geyserSpongePingPassthrough;
}
@Override
public Path getConfigFolder() {
return configPath;
}
@Override
public BootstrapDumpInfo getDumpInfo() {
return new GeyserSpongeDumpInfo();
}
@Override
public String getMinecraftServerVersion() {
return Sponge.platform().minecraftVersion().name();
}
@NotNull
@Override
public String getServerBindAddress() {
return Sponge.server().boundAddress().map(InetSocketAddress::getHostString).orElse("");
}
@Override
public int getServerPort() {
return Sponge.server().boundAddress().stream().mapToInt(InetSocketAddress::getPort).findFirst().orElse(-1);
}
@Override
public boolean testFloodgatePluginPresent() {
return false;
}
}

View File

@ -1,75 +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.sponge.command;
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.spongepowered.api.command.CommandCause;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import java.util.Optional;
import java.util.UUID;
@AllArgsConstructor
public class SpongeCommandSource implements GeyserCommandSource {
private final CommandCause handle;
@Override
public String name() {
return handle.friendlyIdentifier().orElse(handle.identifier());
}
@Override
public void sendMessage(@NonNull String message) {
handle.audience().sendMessage(LegacyComponentSerializer.legacySection().deserialize(message));
}
@Override
public boolean isConsole() {
return !(handle.cause().root() instanceof ServerPlayer);
}
@Override
public Optional<UUID> playerUuid() {
if (handle.cause().root() instanceof ServerPlayer player) {
return Optional.of(player.uniqueId());
}
return Optional.empty();
}
@Override
public boolean hasPermission(String permission) {
return handle.hasPermission(permission);
}
@Override
public Object handle() {
return handle;
}
}

View File

@ -1,30 +0,0 @@
{
"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"
}
]
}
]
}

View File

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

View File

@ -22,8 +22,8 @@ indra {
tasks {
processResources {
// Spigot, BungeeCord, Velocity, Sponge, Fabric
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "META-INF/sponge_plugins.json", "fabric.mod.json")) {
// Spigot, BungeeCord, Velocity, Fabric
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json")) {
expand(
"id" to "geyser",
"name" to "Geyser",

View File

@ -20,7 +20,6 @@ val platforms = setOf(
projects.fabric,
projects.bungeecord,
projects.spigot,
projects.sponge,
projects.standalone,
projects.velocity
).map { it.dependencyProject }

View File

@ -25,6 +25,7 @@
package org.geysermc.geyser.level;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
@ -160,6 +161,11 @@ public class GeyserWorldManager extends WorldManager {
return gameRule.getDefaultIntValue();
}
@Override
public GameMode getDefaultGameMode(GeyserSession session) {
return GameMode.SURVIVAL;
}
@Nonnull
@Override
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {

View File

@ -170,6 +170,24 @@ public abstract class WorldManager {
session.sendCommand("gamemode " + gameMode.name().toLowerCase(Locale.ROOT));
}
/**
* Get the default game mode of the server
*
* @param session the player requesting the default game mode
* @return the default game mode of the server, or Survival if unknown.
*/
public abstract GameMode getDefaultGameMode(GeyserSession session);
/**
* Change the default game mode of the session's server
*
* @param session the player making the change
* @param gameMode the new default game mode
*/
public void setDefaultGameMode(GeyserSession session, GameMode gameMode) {
session.sendCommand("defaultgamemode " + gameMode.name().toLowerCase(Locale.ROOT));
}
/**
* Change the difficulty of the Java server
*

View File

@ -255,6 +255,11 @@ public class LoggingPacketHandler implements BedrockPacketHandler {
return defaultHandler(packet);
}
@Override
public PacketSignal handle(RequestPermissionsPacket packet) {
return defaultHandler(packet);
}
@Override
public PacketSignal handle(ResourcePackChunkRequestPacket packet) {
return defaultHandler(packet);

View File

@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDe
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundTabListPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundLightUpdatePacket;
import io.netty.channel.EventLoop;
import org.cloudburstmc.protocol.bedrock.packet.RequestPermissionsPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.registry.loader.RegistryLoaders;
import org.geysermc.geyser.session.GeyserSession;
@ -46,6 +47,7 @@ public class PacketTranslatorRegistry<T> extends AbstractMappedRegistry<Class<?
IGNORED_PACKETS.add(ClientboundLightUpdatePacket.class); // Light is handled on Bedrock for us
IGNORED_PACKETS.add(ClientboundTabListPacket.class); // Cant be implemented in Bedrock
IGNORED_PACKETS.add(ClientboundDelimiterPacket.class); // Not implemented, spams logs
IGNORED_PACKETS.add(RequestPermissionsPacket.class); // Bedrock client asks permission to switch default game mode, but we handle this ourselves
}
protected PacketTranslatorRegistry() {

View File

@ -34,7 +34,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.*;
import org.cloudburstmc.nbt.*;
import org.cloudburstmc.protocol.bedrock.codec.v582.Bedrock_v582;
import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
import org.cloudburstmc.protocol.bedrock.codec.v589.Bedrock_v589;
import org.cloudburstmc.protocol.bedrock.codec.v594.Bedrock_v594;
@ -74,10 +73,9 @@ public final class BlockRegistryPopulator {
public static void populate(Stage stage) {
switch (stage) {
case PRE_INIT -> { nullifyBlocksNode(); }
case PRE_INIT, POST_INIT -> { nullifyBlocksNode(); }
case INIT_JAVA -> { registerJavaBlocks(); }
case INIT_BEDROCK -> { registerBedrockBlocks(); }
case POST_INIT -> { nullifyBlocksNode(); }
default -> { throw new IllegalArgumentException("Unknown stage: " + stage); }
}
}
@ -86,9 +84,8 @@ public final class BlockRegistryPopulator {
* Stores the raw blocks JSON until it is no longer needed.
*/
private static JsonNode BLOCKS_JSON;
private static int minCustomRuntimeID = -1;
private static int maxCustomRuntimeID = -1;
private static int javaBlocksSize = -1;
private static int MIN_CUSTOM_RUNTIME_ID = -1;
private static int JAVA_BLOCKS_SIZE = -1;
private static void nullifyBlocksNode() {
BLOCKS_JSON = null;
@ -224,8 +221,8 @@ public final class BlockRegistryPopulator {
BiFunction<String, NbtMapBuilder, String> stateMapper = blockMappers.getOrDefault(palette.getKey(), emptyMapper);
GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[javaBlocksSize];
GeyserBedrockBlock[] javaToVanillaBedrockBlocks = new GeyserBedrockBlock[javaBlocksSize];
GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
GeyserBedrockBlock[] javaToVanillaBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
Map<String, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
Map<NbtMap, BlockDefinition> itemFrames = new Object2ObjectOpenHashMap<>();
@ -309,8 +306,8 @@ public final class BlockRegistryPopulator {
Map<JavaBlockState, CustomBlockState> nonVanillaStateOverrides = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get();
if (nonVanillaStateOverrides.size() > 0) {
// First ensure all non vanilla runtime IDs at minimum are air in case they aren't consecutive
Arrays.fill(javaToVanillaBedrockBlocks, minCustomRuntimeID, javaToVanillaBedrockBlocks.length, airDefinition);
Arrays.fill(javaToBedrockBlocks, minCustomRuntimeID, javaToBedrockBlocks.length, airDefinition);
Arrays.fill(javaToVanillaBedrockBlocks, MIN_CUSTOM_RUNTIME_ID, javaToVanillaBedrockBlocks.length, airDefinition);
Arrays.fill(javaToBedrockBlocks, MIN_CUSTOM_RUNTIME_ID, javaToBedrockBlocks.length, airDefinition);
for (Map.Entry<JavaBlockState, CustomBlockState> entry : nonVanillaStateOverrides.entrySet()) {
GeyserBedrockBlock bedrockDefinition = customBlockStateDefinitions.get(entry.getValue());
@ -364,20 +361,20 @@ public final class BlockRegistryPopulator {
throw new AssertionError("Unable to load Java block mappings", e);
}
javaBlocksSize = blocksJson.size();
JAVA_BLOCKS_SIZE = blocksJson.size();
if (BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().size() > 0) {
minCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().min(Comparator.comparing(JavaBlockState::javaId)).get().javaId();
maxCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().max(Comparator.comparing(JavaBlockState::javaId)).get().javaId();
MIN_CUSTOM_RUNTIME_ID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().min(Comparator.comparing(JavaBlockState::javaId)).get().javaId();
int maxCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().max(Comparator.comparing(JavaBlockState::javaId)).get().javaId();
if (minCustomRuntimeID < blocksJson.size()) {
throw new RuntimeException("Non vanilla custom block state overrides runtime ID must start after the last vanilla block state (" + javaBlocksSize + ")");
if (MIN_CUSTOM_RUNTIME_ID < blocksJson.size()) {
throw new RuntimeException("Non vanilla custom block state overrides runtime ID must start after the last vanilla block state (" + JAVA_BLOCKS_SIZE + ")");
}
javaBlocksSize = maxCustomRuntimeID + 1; // Runtime ids start at 0, so we need to add 1
JAVA_BLOCKS_SIZE = maxCustomRuntimeID + 1; // Runtime ids start at 0, so we need to add 1
}
BlockRegistries.JAVA_BLOCKS.set(new BlockMapping[javaBlocksSize]); // Set array size to number of blockstates
BlockRegistries.JAVA_BLOCKS.set(new BlockMapping[JAVA_BLOCKS_SIZE]); // Set array size to number of blockstates
Deque<String> cleanIdentifiers = new ArrayDeque<>();

View File

@ -26,8 +26,6 @@
package org.geysermc.geyser.registry.populator;
import com.fasterxml.jackson.databind.JsonNode;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtUtils;
@ -124,6 +122,7 @@ public class CreativeItemRegistryPopulator {
NbtMapBuilder builder = stateTag.toBuilder();
builder.remove("name_hash");
builder.remove("network_id");
builder.remove("version");
blockDefinition = blockMappings.getDefinition(builder.build());
} catch (IOException e) {

View File

@ -70,21 +70,21 @@ public class CustomBlockRegistryPopulator {
}
}
private static Set<CustomBlockData> customBlocks;
private static Set<String> customBlockNames;
private static Int2ObjectMap<CustomBlockState> blockStateOverrides;
private static Map<String, CustomBlockData> customBlockItemOverrides;
private static Map<JavaBlockState, CustomBlockState> nonVanillaBlockStateOverrides;
private static Set<CustomBlockData> CUSTOM_BLOCKS;
private static Set<String> CUSTOM_BLOCK_NAMES;
private static Int2ObjectMap<CustomBlockState> BLOCK_STATE_OVERRIDES;
private static Map<String, CustomBlockData> CUSTOM_BLOCK_ITEM_OVERRIDES;
private static Map<JavaBlockState, CustomBlockState> NON_VANILLA_BLOCK_STATE_OVERRIDES;
/**
* Initializes custom blocks defined by API
*/
private static void populateBedrock() {
customBlocks = new ObjectOpenHashSet<>();
customBlockNames = new ObjectOpenHashSet<>();
blockStateOverrides = new Int2ObjectOpenHashMap<>();
customBlockItemOverrides = new HashMap<>();
nonVanillaBlockStateOverrides = new HashMap<>();
CUSTOM_BLOCKS = new ObjectOpenHashSet<>();
CUSTOM_BLOCK_NAMES = new ObjectOpenHashSet<>();
BLOCK_STATE_OVERRIDES = new Int2ObjectOpenHashMap<>();
CUSTOM_BLOCK_ITEM_OVERRIDES = new HashMap<>();
NON_VANILLA_BLOCK_STATE_OVERRIDES = new HashMap<>();
GeyserImpl.getInstance().getEventBus().fire(new GeyserDefineCustomBlocksEvent() {
@Override
@ -92,13 +92,13 @@ public class CustomBlockRegistryPopulator {
if (customBlockData.name().length() == 0) {
throw new IllegalArgumentException("Custom block name must have at least 1 character.");
}
if (!customBlockNames.add(customBlockData.name())) {
if (!CUSTOM_BLOCK_NAMES.add(customBlockData.name())) {
throw new IllegalArgumentException("Another custom block was already registered under the name: " + customBlockData.name());
}
if (Character.isDigit(customBlockData.name().charAt(0))) {
throw new IllegalArgumentException("Custom block can not start with a digit. Name: " + customBlockData.name());
}
customBlocks.add(customBlockData);
CUSTOM_BLOCKS.add(customBlockData);
}
@Override
@ -107,10 +107,10 @@ public class CustomBlockRegistryPopulator {
if (id == -1) {
throw new IllegalArgumentException("Unknown Java block state. Identifier: " + javaIdentifier);
}
if (!customBlocks.contains(customBlockState.block())) {
if (!CUSTOM_BLOCKS.contains(customBlockState.block())) {
throw new IllegalArgumentException("Custom block is unregistered. Name: " + customBlockState.name());
}
CustomBlockState oldBlockState = blockStateOverrides.put(id, customBlockState);
CustomBlockState oldBlockState = BLOCK_STATE_OVERRIDES.put(id, customBlockState);
if (oldBlockState != null) {
GeyserImpl.getInstance().getLogger().debug("Duplicate block state override for Java Identifier: " +
javaIdentifier + " Old override: " + oldBlockState.name() + " New override: " + customBlockState.name());
@ -119,18 +119,18 @@ public class CustomBlockRegistryPopulator {
@Override
public void registerItemOverride(@NonNull String javaIdentifier, @NonNull CustomBlockData customBlockData) {
if (!customBlocks.contains(customBlockData)) {
if (!CUSTOM_BLOCKS.contains(customBlockData)) {
throw new IllegalArgumentException("Custom block is unregistered. Name: " + customBlockData.name());
}
customBlockItemOverrides.put(javaIdentifier, customBlockData);
CUSTOM_BLOCK_ITEM_OVERRIDES.put(javaIdentifier, customBlockData);
}
@Override
public void registerOverride(@NonNull JavaBlockState javaBlockState, @NonNull CustomBlockState customBlockState) {
if (!customBlocks.contains(customBlockState.block())) {
if (!CUSTOM_BLOCKS.contains(customBlockState.block())) {
throw new IllegalArgumentException("Custom block is unregistered. Name: " + customBlockState.name());
}
nonVanillaBlockStateOverrides.put(javaBlockState, customBlockState);
NON_VANILLA_BLOCK_STATE_OVERRIDES.put(javaBlockState, customBlockState);
}
});
}
@ -140,25 +140,25 @@ public class CustomBlockRegistryPopulator {
*/
private static void populateVanilla() {
for (CustomSkull customSkull : BlockRegistries.CUSTOM_SKULLS.get().values()) {
customBlocks.add(customSkull.getCustomBlockData());
CUSTOM_BLOCKS.add(customSkull.getCustomBlockData());
}
Map<CustomBlockData, Set<Integer>> extendedCollisionBoxes = new HashMap<>();
Map<BoxComponent, CustomBlockData> extendedCollisionBoxSet = new HashMap<>();
MappingsConfigReader mappingsConfigReader = new MappingsConfigReader();
mappingsConfigReader.loadBlockMappingsFromJson((key, block) -> {
customBlocks.add(block.data());
CUSTOM_BLOCKS.add(block.data());
if (block.overrideItem()) {
customBlockItemOverrides.put(block.javaIdentifier(), block.data());
CUSTOM_BLOCK_ITEM_OVERRIDES.put(block.javaIdentifier(), block.data());
}
block.states().forEach((javaIdentifier, customBlockState) -> {
int id = BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(javaIdentifier, -1);
blockStateOverrides.put(id, customBlockState.state());
BLOCK_STATE_OVERRIDES.put(id, customBlockState.state());
BoxComponent extendedCollisionBox = customBlockState.extendedCollisionBox();
if (extendedCollisionBox != null) {
CustomBlockData extendedCollisionBlock = extendedCollisionBoxSet.computeIfAbsent(extendedCollisionBox, box -> {
CustomBlockData collisionBlock = createExtendedCollisionBlock(box, extendedCollisionBoxSet.size());
customBlocks.add(collisionBlock);
CUSTOM_BLOCKS.add(collisionBlock);
return collisionBlock;
});
extendedCollisionBoxes.computeIfAbsent(extendedCollisionBlock, k -> new HashSet<>())
@ -167,30 +167,40 @@ public class CustomBlockRegistryPopulator {
});
});
BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.set(blockStateOverrides);
GeyserImpl.getInstance().getLogger().info("Registered " + blockStateOverrides.size() + " custom block overrides.");
BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.set(BLOCK_STATE_OVERRIDES);
if (BLOCK_STATE_OVERRIDES.size() != 0) {
GeyserImpl.getInstance().getLogger().info("Registered " + BLOCK_STATE_OVERRIDES.size() + " custom block overrides.");
}
BlockRegistries.CUSTOM_BLOCK_ITEM_OVERRIDES.set(customBlockItemOverrides);
GeyserImpl.getInstance().getLogger().info("Registered " + customBlockItemOverrides.size() + " custom block item overrides.");
BlockRegistries.CUSTOM_BLOCK_ITEM_OVERRIDES.set(CUSTOM_BLOCK_ITEM_OVERRIDES);
if (CUSTOM_BLOCK_ITEM_OVERRIDES.size() != 0) {
GeyserImpl.getInstance().getLogger().info("Registered " + CUSTOM_BLOCK_ITEM_OVERRIDES.size() + " custom block item overrides.");
}
BlockRegistries.EXTENDED_COLLISION_BOXES.set(extendedCollisionBoxes);
GeyserImpl.getInstance().getLogger().info("Registered " + extendedCollisionBoxes.size() + " custom block extended collision boxes.");
if (extendedCollisionBoxes.size() != 0) {
GeyserImpl.getInstance().getLogger().info("Registered " + extendedCollisionBoxes.size() + " custom block extended collision boxes.");
}
}
/**
* Registers all non-vanilla custom blocks defined by API
*/
private static void populateNonVanilla() {
BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.set(nonVanillaBlockStateOverrides);
GeyserImpl.getInstance().getLogger().info("Registered " + nonVanillaBlockStateOverrides.size() + " non-vanilla block overrides.");
BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.set(NON_VANILLA_BLOCK_STATE_OVERRIDES);
if (NON_VANILLA_BLOCK_STATE_OVERRIDES.size() != 0) {
GeyserImpl.getInstance().getLogger().info("Registered " + NON_VANILLA_BLOCK_STATE_OVERRIDES.size() + " non-vanilla block overrides.");
}
}
/**
* Registers all bedrock custom blocks defined in previous stages
*/
private static void registration() {
BlockRegistries.CUSTOM_BLOCKS.set(customBlocks.toArray(new CustomBlockData[0]));
GeyserImpl.getInstance().getLogger().info("Registered " + customBlocks.size() + " custom blocks.");
BlockRegistries.CUSTOM_BLOCKS.set(CUSTOM_BLOCKS.toArray(new CustomBlockData[0]));
if (CUSTOM_BLOCKS.size() != 0) {
GeyserImpl.getInstance().getLogger().info("Registered " + CUSTOM_BLOCKS.size() + " custom blocks.");
}
}
/**

View File

@ -129,7 +129,9 @@ public class CustomSkullRegistryPopulator {
}
});
GeyserImpl.getInstance().getLogger().info("Registered " + BlockRegistries.CUSTOM_SKULLS.get().size() + " custom skulls as custom blocks.");
if (BlockRegistries.CUSTOM_SKULLS.get().size() != 0) {
GeyserImpl.getInstance().getLogger().info("Registered " + BlockRegistries.CUSTOM_SKULLS.get().size() + " custom skulls as custom blocks.");
}
}
/**

View File

@ -34,12 +34,10 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.*;
import org.geysermc.geyser.Constants;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.codec.v582.Bedrock_v582;
import org.cloudburstmc.protocol.bedrock.codec.v589.Bedrock_v589;
import org.cloudburstmc.protocol.bedrock.codec.v594.Bedrock_v594;
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
@ -48,6 +46,7 @@ import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ComponentItemData;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.block.custom.CustomBlockData;
@ -58,10 +57,10 @@ import org.geysermc.geyser.api.item.custom.CustomItemOptions;
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
import org.geysermc.geyser.inventory.item.StoredItemMappings;
import org.geysermc.geyser.item.GeyserCustomMappingData;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.*;
import java.io.InputStream;
@ -356,7 +355,7 @@ public class ItemRegistryPopulator {
boolean valid = true;
for (Map.Entry<String, Object> nbtEntry : requiredBlockStates.entrySet()) {
if (states.getOrDefault(nbtEntry.getKey(), null) == null || !states.get(nbtEntry.getKey()).equals(nbtEntry.getValue())) {
if (!Objects.equals(states.get(nbtEntry.getKey()), nbtEntry.getValue())) {
// A required block state doesn't match - this one is not valid
valid = false;
break;

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import org.cloudburstmc.protocol.bedrock.packet.SetDefaultGameTypePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetPlayerGameTypePacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.EntityUtils;
@Translator(packet = SetDefaultGameTypePacket.class)
public class BedrockSetDefaultGameTypeTranslator extends PacketTranslator<SetDefaultGameTypePacket> {
/**
* Sets the default game mode for the server via the Bedrock client's "world" menu (given sufficient permissions).
*/
@Override
public void translate(GeyserSession session, SetDefaultGameTypePacket packet) {
if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) {
session.getGeyser().getWorldManager().setDefaultGameMode(session, GameMode.byId(packet.getGamemode()));
}
// Stop the client from updating their own Gamemode without telling the server
// Can occur when client Gamemode is set to "default", and default game mode is changed.
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(EntityUtils.toBedrockGamemode(session.getGameMode()).ordinal());
session.sendUpstreamPacket(playerGameTypePacket);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -23,54 +23,26 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.sponge;
package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.geyser.GeyserLogger;
import org.apache.logging.log4j.Logger;
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import org.cloudburstmc.protocol.bedrock.packet.SetDifficultyPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@AllArgsConstructor
public class GeyserSpongeLogger implements GeyserLogger {
private final Logger logger;
@Getter @Setter
private boolean debug;
@Translator(packet = SetDifficultyPacket.class)
public class BedrockSetDifficultyTranslator extends PacketTranslator<SetDifficultyPacket> {
/**
* Sets the Java server's difficulty via the Bedrock client's "world" menu (given sufficient permissions).
*/
@Override
public void severe(String message) {
logger.error(message);
}
@Override
public void severe(String message, Throwable error) {
logger.error(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 debug(String message) {
if (debug) {
info(message);
public void translate(GeyserSession session, SetDifficultyPacket packet) {
if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) {
if (packet.getDifficulty() != session.getWorldCache().getDifficulty().ordinal()) {
session.getGeyser().getWorldManager().setDifficulty(session, Difficulty.from(packet.getDifficulty()));
}
}
}
}

View File

@ -25,23 +25,43 @@
package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import org.cloudburstmc.protocol.bedrock.packet.SetPlayerGameTypePacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.EntityUtils;
/**
* In vanilla Bedrock, if you have operator status, this sets the player's gamemode without confirmation from the server.
* Since we have a custom server option to request the gamemode, we just reset the gamemode and ignore this.
* With operator status, the Gamemode change is sent to the Java server, if it is not present, the gamemode is not changed.
*/
@Translator(packet = SetPlayerGameTypePacket.class)
public class BedrockSetPlayerGameTypeTranslator extends PacketTranslator<SetPlayerGameTypePacket> {
/**
* Sets client game mode for the server via the Bedrock client's "world" menu (given sufficient permissions).
*/
@Override
public void translate(GeyserSession session, SetPlayerGameTypePacket packet) {
// no
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(session.getGameMode().ordinal());
session.sendUpstreamPacket(playerGameTypePacket);
// yes, if you are OP
if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) {
if (packet.getGamemode() != session.getGameMode().ordinal()) {
// Bedrock has more Gamemodes than Java, leading to cases 5 (for "default") and 6 (for "spectator") being sent
// https://github.com/CloudburstMC/Protocol/blob/3.0/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/data/GameType.java
GameMode gameMode = switch (packet.getGamemode()) {
case 1 -> GameMode.CREATIVE;
case 2 -> GameMode.ADVENTURE;
case 5 -> session.getGeyser().getWorldManager().getDefaultGameMode(session);
case 6 -> GameMode.SPECTATOR;
default -> GameMode.SURVIVAL;
};
session.getGeyser().getWorldManager().setPlayerGameMode(session, gameMode);
}
} else {
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(EntityUtils.toBedrockGamemode(session.getGameMode()).ordinal());
session.sendUpstreamPacket(playerGameTypePacket);
}
}
}

View File

@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundSy
import net.kyori.adventure.text.TranslatableComponent;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@ -55,7 +56,11 @@ public class JavaSystemChatTranslator extends PacketTranslator<ClientboundSystem
textPacket.setType(packet.isOverlay() ? TextPacket.Type.JUKEBOX_POPUP : TextPacket.Type.SYSTEM);
textPacket.setNeedsTranslation(false);
textPacket.setMessage(MessageTranslator.convertMessage(packet.getContent(), session.locale()));
if (packet.isOverlay()) {
textPacket.setMessage(ChatColor.WHITE + MessageTranslator.convertMessage(packet.getContent(), session.locale()));
} else {
textPacket.setMessage(MessageTranslator.convertMessage(packet.getContent(), session.locale()));
}
if (session.isSentSpawnPacket()) {
session.sendUpstreamPacket(textPacket);

View File

@ -73,7 +73,8 @@ public class JavaOpenScreenTranslator extends PacketTranslator<ClientboundOpenSc
if (openInventory != null) {
// If the window type is the same, don't close.
// In rare cases, inventories can do funny things where it keeps the same window type up but change the contents.
if (openInventory.getContainerType() != packet.getType()) {
// Or, inventory names can change (useful for JsonUI). In these cases, we need to close the old inventory.
if (openInventory.getContainerType() != packet.getType() || !openInventory.getTitle().equals(name)) {
// Sometimes the server can double-open an inventory with the same ID - don't confirm in that instance.
InventoryUtils.closeInventory(session, openInventory.getJavaId(), openInventory.getJavaId() != packet.getContainerId());
}

View File

@ -26,7 +26,7 @@
package org.geysermc.geyser.translator.protocol.java.title;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.title.ClientboundSetActionBarTextPacket;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
@ -44,14 +44,11 @@ public class JavaSetActionBarTextTranslator extends PacketTranslator<Clientbound
text = MessageTranslator.convertMessage(packet.getText(), session.locale());
}
// Type seems wrong, but is intentional to avoid collisions with armor/remaining air bars
TextPacket textPacket = new TextPacket();
textPacket.setType(TextPacket.Type.JUKEBOX_POPUP);
textPacket.setNeedsTranslation(false);
textPacket.setSourceName("");
textPacket.setMessage(text);
textPacket.setXuid("");
textPacket.setPlatformChatId("");
session.sendUpstreamPacket(textPacket);
SetTitlePacket titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
titlePacket.setText(text);
titlePacket.setXuid("");
titlePacket.setPlatformOnlineId("");
session.sendUpstreamPacket(titlePacket);
}
}

View File

@ -25,8 +25,6 @@
package org.geysermc.geyser.util;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import org.geysermc.cumulus.component.DropdownComponent;
import org.geysermc.cumulus.form.CustomForm;
import org.geysermc.geyser.GeyserImpl;
@ -77,23 +75,6 @@ public class SettingsUtils {
}
}
boolean canModifyServer = session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server");
if (canModifyServer) {
builder.label("geyser.settings.title.server");
DropdownComponent.Builder gamemodeDropdown = DropdownComponent.builder("%createWorldScreen.gameMode.personal");
for (GameMode gamemode : GameMode.values()) {
gamemodeDropdown.option("selectWorld.gameMode." + gamemode.name().toLowerCase(), session.getGameMode() == gamemode);
}
builder.dropdown(gamemodeDropdown);
DropdownComponent.Builder difficultyDropdown = DropdownComponent.builder("%options.difficulty");
for (Difficulty difficulty : Difficulty.values()) {
difficultyDropdown.option("%options.difficulty." + difficulty.name().toLowerCase(), session.getWorldCache().getDifficulty() == difficulty);
}
builder.dropdown(difficultyDropdown);
}
boolean showGamerules = session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules");
if (showGamerules) {
builder.label("geyser.settings.title.game_rules")
@ -128,18 +109,6 @@ public class SettingsUtils {
}
}
if (canModifyServer) {
GameMode gameMode = GameMode.values()[(int) response.next()];
if (gameMode != null && gameMode != session.getGameMode()) {
session.getGeyser().getWorldManager().setPlayerGameMode(session, gameMode);
}
Difficulty difficulty = Difficulty.values()[(int) response.next()];
if (difficulty != null && difficulty != session.getWorldCache().getDifficulty()) {
session.getGeyser().getWorldManager().setDifficulty(session, difficulty);
}
}
if (showGamerules) {
for (GameRule gamerule : GameRule.VALUES) {
if (Boolean.class.equals(gamerule.getType())) {

View File

@ -28,7 +28,6 @@ cloud = "1.8.3"
commodore = "2.2"
bungeecord = "a7c6ede"
velocity = "3.1.1"
sponge = "8.0.0"
fabric-minecraft = "1.20"
fabric-loader = "0.14.21"
fabric-api = "0.83.0+1.20"
@ -95,7 +94,6 @@ junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "ju
mcauthlib = { group = "com.github.GeyserMC", name = "MCAuthLib", version.ref = "mcauthlib" }
mcprotocollib = { group = "com.github.steveice10", name = "mcprotocollib", version.ref = "mcprotocollib" }
raknet = { group = "org.cloudburstmc.netty", name = "netty-transport-raknet", version.ref = "raknet" }
sponge-api = { group = "org.spongepowered", name = "spongeapi", version.ref = "sponge" }
terminalconsoleappender = { group = "net.minecrell", name = "terminalconsoleappender", version.ref = "terminalconsoleappender" }
velocity-api = { group = "com.velocitypowered", name = "velocity-api", version.ref = "velocity" }
viaversion = { group = "com.viaversion", name = "viaversion", version.ref = "viaversion" }

View File

@ -32,9 +32,6 @@ dependencyResolutionManagement {
name = "viaversion"
}
// Sponge
maven("https://repo.spongepowered.org/repository/maven-public/")
maven("https://jitpack.io") {
content { includeGroupByRegex("com\\.github\\..*") }
}
@ -65,7 +62,6 @@ include(":api")
include(":bungeecord")
include(":fabric")
include(":spigot")
include(":sponge")
include(":standalone")
include(":velocity")
include(":common")
@ -75,6 +71,5 @@ include(":core")
project(":bungeecord").projectDir = file("bootstrap/bungeecord")
project(":fabric").projectDir = file("bootstrap/fabric")
project(":spigot").projectDir = file("bootstrap/spigot")
project(":sponge").projectDir = file("bootstrap/sponge")
project(":standalone").projectDir = file("bootstrap/standalone")
project(":velocity").projectDir = file("bootstrap/velocity")