Skip to content

Commit

Permalink
Merge pull request #5106 from GeyserMC/feature/1.21.2
Browse files Browse the repository at this point in the history
Java Edition 1.21.2 / 1.21.3 support
  • Loading branch information
Camotoy authored Nov 5, 2024
2 parents d61ad7b + 6e8a955 commit 6983095
Show file tree
Hide file tree
Showing 148 changed files with 9,849 additions and 4,179 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,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!

## Supported Versions
Geyser is currently supporting Minecraft Bedrock 1.20.80 - 1.21.44 and Minecraft Java 1.21/1.21.1. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
Geyser is currently supporting Minecraft Bedrock 1.20.80 - 1.21.44 and Minecraft Java 1.21.2/1.21.3. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).

## Setting Up
Take a look [here](https://geysermc.org/wiki/geyser/setup/) for how to set up Geyser.
Expand Down
1 change: 0 additions & 1 deletion bootstrap/mod/fabric/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ tasks.withType<Jar> {

relocate("org.cloudburstmc.netty")
relocate("org.cloudburstmc.protocol")
relocate("com.github.steveice10.mc.auth")

tasks {
remapJar {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import net.minecraft.server.level.ServerPlayer;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.CommandRegistry;
import org.geysermc.geyser.command.CommandSourceConverter;
Expand Down Expand Up @@ -80,7 +80,7 @@ public void onInitialize() {
var sourceConverter = CommandSourceConverter.layered(
CommandSourceStack.class,
id -> getServer().getPlayerList().getPlayer(id),
Player::createCommandSourceStack,
ServerPlayer::createCommandSourceStack,
() -> getServer().createCommandSourceStack(), // NPE if method reference is used, since server is not available yet
ModCommandSource::new
);
Expand Down
4 changes: 2 additions & 2 deletions bootstrap/mod/fabric/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
"geyser.mixins.json"
],
"depends": {
"fabricloader": ">=0.15.11",
"fabricloader": ">=0.16.7",
"fabric": "*",
"minecraft": ">=1.21"
"minecraft": ">=1.21.2"
}
}
14 changes: 14 additions & 0 deletions bootstrap/mod/neoforge/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ architectury {
provided("org.cloudburstmc.math", "api")
provided("com.google.errorprone", "error_prone_annotations")

// Jackson shipped by Minecraft is too old, so we shade & relocate our newer version
relocate("com.fasterxml.jackson")

val includeTransitive: Configuration = configurations.getByName("includeTransitive")

dependencies {
Expand All @@ -31,6 +34,12 @@ dependencies {
}
shadow(projects.core) { isTransitive = false }

// Minecraft (1.21.2+) includes jackson. But an old version!
shadow(libs.jackson.core) { isTransitive = false }
shadow(libs.jackson.databind) { isTransitive = false }
shadow(libs.jackson.dataformat.yaml) { isTransitive = false }
shadow(libs.jackson.annotations) { isTransitive = false }

// Let's shade in our own api
shadow(projects.api) { isTransitive = false }

Expand All @@ -56,6 +65,11 @@ tasks {
remapModrinthJar {
archiveBaseName.set("geyser-neoforge")
}

shadowJar {
// Without this, jackson's service files are not relocated
mergeServiceFiles()
}
}

modrinth {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
package org.geysermc.geyser.platform.neoforge;

import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.bus.api.EventPriority;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.common.Mod;
Expand Down Expand Up @@ -72,7 +72,7 @@ public GeyserNeoForgeBootstrap(ModContainer container) {
var sourceConverter = CommandSourceConverter.layered(
CommandSourceStack.class,
id -> getServer().getPlayerList().getPlayer(id),
Player::createCommandSourceStack,
ServerPlayer::createCommandSourceStack,
() -> getServer().createCommandSourceStack(),
ModCommandSource::new
);
Expand Down Expand Up @@ -104,7 +104,9 @@ private void onClientStopping(GameShuttingDownEvent ignored) {
}

private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
GeyserModUpdateListener.onPlayReady(event.getEntity());
if (event.getEntity() instanceof ServerPlayer player) {
GeyserModUpdateListener.onPlayReady(player);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import net.minecraft.network.protocol.login.ClientboundGameProfilePacket;
import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket;
import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket;

/**
* Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients
Expand All @@ -45,7 +45,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
Class<?> msgClass = msg.getClass();
// Don't let any compression packet get through
if (!ClientboundLoginCompressionPacket.class.isAssignableFrom(msgClass)) {
if (ClientboundGameProfilePacket.class.isAssignableFrom(msgClass)) {
if (ClientboundLoginFinishedPacket.class.isAssignableFrom(msgClass)) {

// We're past the point that a compression packet can be sent, so we can safely yeet ourselves away
ctx.channel().pipeline().remove(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@

package org.geysermc.geyser.platform.mod;

import net.minecraft.world.entity.player.Player;
import net.minecraft.server.level.ServerPlayer;
import org.geysermc.geyser.Permissions;
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
import org.geysermc.geyser.util.VersionCheckUtils;

public final class GeyserModUpdateListener {
public static void onPlayReady(Player player) {
public static void onPlayReady(ServerPlayer player) {
// Should be creating this in the supplier, but we need it for the permission check.
// Not a big deal currently because ModCommandSource doesn't load locale, so don't need to try to wait for it.
ModCommandSource source = new ModCommandSource(player.createCommandSourceStack());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BannerBlockEntity;
Expand All @@ -43,7 +45,6 @@
import net.minecraft.world.level.block.entity.DecoratedPotBlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.geyser.level.GeyserWorldManager;
Expand Down Expand Up @@ -84,16 +85,17 @@ public int getBlockAt(GeyserSession session, int x, int y, int z) {
}

Level level = player.level();
if (y < level.getMinBuildHeight()) {
if (y < level.getMinY()) {
return 0;
}

ChunkAccess chunk = level.getChunkSource().getChunk(x >> 4, z >> 4, ChunkStatus.FULL, false);
// Only loads active chunks, and doesn't delegate to main thread
ChunkAccess chunk = ((ServerChunkCache) level.getChunkSource()).chunkMap.getChunkToSend(ChunkPos.asLong(x >> 4, z >> 4));
if (chunk == null) {
return 0;
}

int worldOffset = level.getMinBuildHeight() >> 4;
int worldOffset = level.getMinY() >> 4;
int chunkOffset = (y >> 4) - worldOffset;
if (chunkOffset < chunk.getSections().length) {
LevelChunkSection section = chunk.getSections()[chunkOffset];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,24 @@
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.text.ChatColor;
import org.jline.reader.Candidate;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;

@Slf4j
public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, GeyserCommandSource {

@Override
protected LineReader buildReader(LineReaderBuilder builder) {
builder.completer((reader, line, candidates) -> {
var suggestions = GeyserImpl.getInstance().commandRegistry().suggestionsFor(this, line.line());
for (var suggestion : suggestions.list()) {
candidates.add(new Candidate(suggestion.suggestion()));
}
});
return super.buildReader(builder);
}

@Override
protected boolean isRunning() {
return !GeyserImpl.getInstance().isShuttingDown();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ tasks.modrinth.get().dependsOn(tasks.modrinthSyncBody)

modrinth {
token.set(System.getenv("MODRINTH_TOKEN") ?: "") // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
debugMode.set(System.getenv("MODRINTH_TOKEN") == null)
projectId.set("geyser")
versionName.set(versionName(project))
versionNumber.set(projectVersion(project))
versionType.set("beta")
changelog.set(System.getenv("CHANGELOG") ?: "")
gameVersions.addAll("1.21", libs.minecraft.get().version as String)
gameVersions.addAll("1.21.2", libs.minecraft.get().version as String)
failSilently.set(true)

syncBodyFrom.set(rootProject.file("README.md").readText())
Expand Down
110 changes: 109 additions & 1 deletion core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@

import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.command.CommandData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumConstraint;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandOverloadData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandParam;
import org.cloudburstmc.protocol.bedrock.data.command.CommandParamData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.event.EventRegistrar;
Expand All @@ -51,14 +58,29 @@
import org.geysermc.geyser.command.defaults.VersionCommand;
import org.geysermc.geyser.event.type.GeyserDefineCommandsEventImpl;
import org.geysermc.geyser.extension.command.GeyserExtensionCommand;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale;
import org.incendo.cloud.Command.Builder;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.execution.ExecutionCoordinator;

import org.incendo.cloud.internal.CommandNode;
import org.incendo.cloud.parser.standard.EnumParser;
import org.incendo.cloud.parser.standard.IntegerParser;
import org.incendo.cloud.parser.standard.LiteralParser;
import org.incendo.cloud.parser.standard.StringArrayParser;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.Suggestions;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import static org.geysermc.geyser.command.GeyserCommand.DEFAULT_ROOT_COMMAND;

Expand Down Expand Up @@ -299,4 +321,90 @@ public String description(@NonNull String command, @NonNull String locale) {
public void runCommand(@NonNull GeyserCommandSource source, @NonNull String command) {
cloud.commandExecutor().executeCommand(source, command);
}

public Suggestions<GeyserCommandSource, ? extends Suggestion> suggestionsFor(GeyserCommandSource source, String input) {
return cloud.suggestionFactory().suggestImmediately(source, input);
}

public void export(GeyserSession session, List<CommandData> bedrockCommands, Set<String> knownAliases) {
cloud.commandTree().rootNodes().forEach(commandTree -> {
var command = commandTree.command();
// Command null happens if you register an extension command with custom Cloud parameters...
if (command == null || session.hasPermission(command.commandPermission().permissionString())) {
var rootComponent = commandTree.component();
String name = rootComponent.name();
if (!knownAliases.add(name)) {
// If the server already defined the command, let's not crash.
return;
}

LinkedHashMap<String, Set<CommandEnumConstraint>> values = new LinkedHashMap<>();
for (String s : rootComponent.aliases()) {
values.put(s, EnumSet.of(CommandEnumConstraint.ALLOW_ALIASES));
}
CommandEnumData aliases = new CommandEnumData(name + "Aliases", values, false);

List<CommandOverloadData> data = new ArrayList<>();
for (var node : commandTree.children()) {
List<List<CommandParamData>> params = createParamData(session, node);
params.forEach(param -> data.add(new CommandOverloadData(false, param.toArray(CommandParamData[]::new))));
}

CommandData bedrockCommand = new CommandData(name, rootComponent.description().textDescription(),
Set.of(CommandData.Flag.NOT_CHEAT), CommandPermission.ANY, aliases,
Collections.emptyList(), data.toArray(new CommandOverloadData[0]));
bedrockCommands.add(bedrockCommand);
}
});
}

private List<List<CommandParamData>> createParamData(GeyserSession session, CommandNode<GeyserCommandSource> node) {
var command = node.command();
if (command != null && !session.hasPermission(command.commandPermission().permissionString())) {
// Triggers with subcommands like Geyser dump, stop, etc.
return Collections.emptyList();
}

CommandParamData data = new CommandParamData();
var component = node.component();
data.setName(component.name());
data.setOptional(component.optional());
var suggestionProvider = component.suggestionProvider();
if (suggestionProvider instanceof LiteralParser<GeyserCommandSource> parser) {
Map<String, Set<CommandEnumConstraint>> values = new LinkedHashMap<>();
for (String alias : parser.aliases()) {
values.put(alias, Set.of());
}

data.setEnumData(new CommandEnumData(component.name(), values, false));
} else if (suggestionProvider instanceof IntegerParser<GeyserCommandSource>) {
data.setType(CommandParam.INT);
} else if (suggestionProvider instanceof EnumParser<?,?> parser) {
LinkedHashMap<String, Set<CommandEnumConstraint>> map = new LinkedHashMap<>();
for (Enum<?> e : parser.acceptedValues()) {
map.put(e.name().toLowerCase(Locale.ROOT), Set.of());
}

data.setEnumData(new CommandEnumData(component.name().toLowerCase(Locale.ROOT), map, false));
} else if (component.parser() instanceof StringArrayParser<?>) {
data.setType(CommandParam.TEXT);
} else {
data.setType(CommandParam.STRING);
}

var children = node.children();
if (children.isEmpty()) {
List<CommandParamData> list = new ArrayList<>(); // Must be mutable; parents will be added to list.
list.add(data);
return Collections.singletonList(list); // Safe to do; will be consumed in an addAll call.
}
List<List<CommandParamData>> collectiveData = new ArrayList<>();
// If a node has multiple children, this will need to be represented
// by creating a new list/branch for each and cloning this node down each line.
for (var child : children) {
collectiveData.addAll(createParamData(session, child));
}
collectiveData.forEach(list -> list.add(0, data));
return collectiveData;
}
}
Loading

0 comments on commit 6983095

Please sign in to comment.