Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-20 06:50:09 +01:00
Merge remote-tracking branch 'upstream/master' into feature/protocol-3.0
# Conflicts: # core/src/main/java/org/geysermc/geyser/entity/type/living/monster/EndermanEntity.java # core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java # core/src/main/java/org/geysermc/geyser/network/ConnectorServerEventHandler.java # core/src/main/java/org/geysermc/geyser/network/GameProtocol.java # core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java # core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java # core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java # core/src/main/java/org/geysermc/geyser/session/GeyserSession.java # core/src/main/java/org/geysermc/geyser/text/ChatTypeEntry.java # core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java # core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java # core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaDisguisedChatTranslator.java # core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaPlayerChatTranslator.java # core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerInfoTranslator.java # core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaExplodeTranslator.java # core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java # core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java # gradle/libs.versions.toml
Dieser Commit ist enthalten in:
Commit
84248fa902
@ -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!
|
||||
|
||||
### Currently supporting Minecraft Bedrock 1.19.0 - 1.19.40 and Minecraft Java 1.19.1/1.19.2.
|
||||
### Currently supporting Minecraft Bedrock 1.19.20 - 1.19.51 and Minecraft Java 1.19.3.
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://wiki.geysermc.org/geyser/setup/) for how to set up Geyser.
|
||||
|
@ -29,7 +29,6 @@ import lombok.Getter;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.text.AsteriskSerializer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -52,15 +51,17 @@ public class GeyserBungeeDumpInfo extends BootstrapDumpInfo {
|
||||
this.plugins = new ArrayList<>();
|
||||
|
||||
for (net.md_5.bungee.api.config.ListenerInfo listener : proxy.getConfig().getListeners()) {
|
||||
String hostname = listener.getHost().getHostString();
|
||||
if (!AsteriskSerializer.showSensitive && !(hostname.equals("") || hostname.equals("0.0.0.0"))) {
|
||||
hostname = "***";
|
||||
}
|
||||
this.listeners.add(new ListenerInfo(hostname, listener.getHost().getPort()));
|
||||
this.listeners.add(new ListenerInfo(listener.getHost().getHostString(), listener.getHost().getPort()));
|
||||
}
|
||||
|
||||
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()))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
// 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_1");
|
||||
ProtocolConstants.class.getField("MINECRAFT_1_19_3");
|
||||
} catch (NoSuchFieldException e) {
|
||||
getLogger().warning(" / \\");
|
||||
getLogger().warning(" / \\");
|
||||
|
@ -1,8 +1,5 @@
|
||||
plugins {
|
||||
id("fabric-loom") version "1.0-SNAPSHOT"
|
||||
id("maven-publish")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
id("java")
|
||||
}
|
||||
|
||||
java {
|
||||
|
@ -25,65 +25,58 @@
|
||||
|
||||
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;
|
||||
|
||||
@SuppressWarnings("unused") // The way that the dump renders makes them used
|
||||
@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) {
|
||||
super();
|
||||
for (ModContainer modContainer : FabricLoader.getInstance().getAllMods()) {
|
||||
if (modContainer.getMetadata().getId().equals("fabricloader")) {
|
||||
this.platformVersion = modContainer.getMetadata().getVersion().getFriendlyString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
FabricLoader.getInstance().getModContainer("fabricloader").ifPresent(mod ->
|
||||
this.platformVersion = mod.getMetadata().getVersion().getFriendlyString());
|
||||
|
||||
this.environmentType = FabricLoader.getInstance().getEnvironmentType();
|
||||
if (AsteriskSerializer.showSensitive || (server.getLocalIp() == null || server.getLocalIp().equals("") || server.getLocalIp().equals("0.0.0.0"))) {
|
||||
this.serverIP = server.getLocalIp();
|
||||
} else {
|
||||
this.serverIP = "***";
|
||||
}
|
||||
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
|
||||
this.serverPort = server.getPort();
|
||||
this.mods = new ArrayList<>();
|
||||
|
||||
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {
|
||||
this.mods.add(new ModInfo(mod));
|
||||
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()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public String getPlatformVersion() {
|
||||
return platformVersion;
|
||||
}
|
||||
|
||||
public EnvType getEnvironmentType() {
|
||||
return environmentType;
|
||||
}
|
||||
|
||||
public String getServerIP() {
|
||||
return this.serverIP;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return this.serverPort;
|
||||
}
|
||||
|
||||
public List<ModInfo> getMods() {
|
||||
return this.mods;
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class ModInfo {
|
||||
public boolean enabled;
|
||||
public String name;
|
||||
public String version;
|
||||
public List<String> authors;
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +246,10 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||
@Override
|
||||
public InputStream getResourceOrNull(String resource) {
|
||||
// We need to handle this differently, because Fabric shares the classloader across multiple mods
|
||||
Path path = this.mod.getPath(resource);
|
||||
Path path = this.mod.findPath(resource).orElse(null);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return path.getFileSystem()
|
||||
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.fabric;
|
||||
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A wrapper for Fabric mod information to be presented in a Geyser dump
|
||||
*/
|
||||
public class ModInfo {
|
||||
|
||||
private final String name;
|
||||
private final String id;
|
||||
private final String version;
|
||||
private final List<String> authors;
|
||||
|
||||
public ModInfo(ModContainer mod) {
|
||||
this.name = mod.getMetadata().getName();
|
||||
this.id = mod.getMetadata().getId();
|
||||
this.authors = new ArrayList<>();
|
||||
mod.getMetadata().getAuthors().forEach((person) -> this.authors.add(person.getName()));
|
||||
this.version = mod.getMetadata().getVersion().getFriendlyString();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public List<String> getAuthors() {
|
||||
return this.authors;
|
||||
}
|
||||
}
|
@ -41,6 +41,8 @@ public class GeyserSpigotDumpInfo extends BootstrapDumpInfo {
|
||||
private final String platformVersion;
|
||||
private final String platformAPIVersion;
|
||||
private final boolean onlineMode;
|
||||
|
||||
@AsteriskSerializer.Asterisk(isIp = true)
|
||||
private final String serverIP;
|
||||
private final int serverPort;
|
||||
private final List<PluginInfo> plugins;
|
||||
@ -51,12 +53,7 @@ public class GeyserSpigotDumpInfo extends BootstrapDumpInfo {
|
||||
this.platformVersion = Bukkit.getVersion();
|
||||
this.platformAPIVersion = Bukkit.getBukkitVersion();
|
||||
this.onlineMode = Bukkit.getOnlineMode();
|
||||
String ip = Bukkit.getIp();
|
||||
if (AsteriskSerializer.showSensitive || (ip.equals("") || ip.equals("0.0.0.0"))) {
|
||||
this.serverIP = ip;
|
||||
} else {
|
||||
this.serverIP = "***";
|
||||
}
|
||||
this.serverIP = Bukkit.getIp();
|
||||
this.serverPort = Bukkit.getPort();
|
||||
this.plugins = new ArrayList<>();
|
||||
|
||||
|
@ -32,6 +32,7 @@ import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||
import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
||||
protected final SpigotWorldAdapter adapter;
|
||||
@ -49,4 +50,12 @@ public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
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.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
@ -51,7 +51,7 @@ import java.util.List;
|
||||
/**
|
||||
* The base world manager to use when there is no supported NMS revision
|
||||
*/
|
||||
public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||
public class GeyserSpigotWorldManager extends WorldManager {
|
||||
private final Plugin plugin;
|
||||
|
||||
public GeyserSpigotWorldManager(Plugin plugin) {
|
||||
@ -151,12 +151,12 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||
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());
|
||||
if (!value.isEmpty()) {
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
return (Boolean) gameRule.getDefaultValue();
|
||||
return gameRule.getDefaultBooleanValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -165,7 +165,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||
if (!value.isEmpty()) {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
return (int) gameRule.getDefaultValue();
|
||||
return gameRule.getDefaultIntValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,6 +45,8 @@ 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;
|
||||
@ -56,12 +58,7 @@ public class GeyserSpongeDumpInfo extends BootstrapDumpInfo {
|
||||
this.platformVersion = platformMeta.version().getQualifier();
|
||||
this.onlineMode = Sponge.server().isOnlineModeEnabled();
|
||||
Optional<InetSocketAddress> socketAddress = Sponge.server().boundAddress();
|
||||
String hostString = socketAddress.map(InetSocketAddress::getHostString).orElse("unknown");
|
||||
if (AsteriskSerializer.showSensitive || (hostString.equals("") || hostString.equals("0.0.0.0") || hostString.equals("unknown"))) {
|
||||
this.serverIP = hostString;
|
||||
} else {
|
||||
this.serverIP = "***";
|
||||
}
|
||||
this.serverIP = socketAddress.map(InetSocketAddress::getHostString).orElse("unknown");
|
||||
this.serverPort = socketAddress.map(InetSocketAddress::getPort).orElse(-1);
|
||||
this.plugins = new ArrayList<>();
|
||||
|
||||
|
@ -41,6 +41,8 @@ public class GeyserVelocityDumpInfo extends BootstrapDumpInfo {
|
||||
private final String platformVersion;
|
||||
private final String platformVendor;
|
||||
private final boolean onlineMode;
|
||||
|
||||
@AsteriskSerializer.Asterisk(isIp = true)
|
||||
private final String serverIP;
|
||||
private final int serverPort;
|
||||
private final List<PluginInfo> plugins;
|
||||
@ -51,12 +53,7 @@ public class GeyserVelocityDumpInfo extends BootstrapDumpInfo {
|
||||
this.platformVersion = proxy.getVersion().getVersion();
|
||||
this.platformVendor = proxy.getVersion().getVendor();
|
||||
this.onlineMode = proxy.getConfiguration().isOnlineMode();
|
||||
String hostString = proxy.getBoundAddress().getHostString();
|
||||
if (AsteriskSerializer.showSensitive || (hostString.equals("") || hostString.equals("0.0.0.0"))) {
|
||||
this.serverIP = hostString;
|
||||
} else {
|
||||
this.serverIP = "***";
|
||||
}
|
||||
this.serverIP = proxy.getBoundAddress().getHostString();
|
||||
this.serverPort = proxy.getBoundAddress().getPort();
|
||||
this.plugins = new ArrayList<>();
|
||||
|
||||
|
@ -16,7 +16,7 @@ dependencies {
|
||||
|
||||
// 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.13.3")
|
||||
implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
|
@ -111,6 +111,8 @@ public interface GeyserConfiguration {
|
||||
|
||||
boolean isNotifyOnNewBedrockUpdate();
|
||||
|
||||
String getUnusableSpaceBlock();
|
||||
|
||||
IMetricsInfo getMetrics();
|
||||
|
||||
int getPendingAuthenticationTimeout();
|
||||
|
@ -154,6 +154,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
@JsonProperty("notify-on-new-bedrock-update")
|
||||
private boolean notifyOnNewBedrockUpdate = true;
|
||||
|
||||
@JsonProperty("unusable-space-block")
|
||||
private String unusableSpaceBlock = "minecraft:barrier";
|
||||
|
||||
private MetricsInfo metrics = new MetricsInfo();
|
||||
|
||||
@JsonProperty("pending-authentication-timeout")
|
||||
|
@ -29,6 +29,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.text.AsteriskSerializer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -53,6 +54,8 @@ public class BootstrapDumpInfo {
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class ListenerInfo {
|
||||
|
||||
@AsteriskSerializer.Asterisk(isIp = true)
|
||||
public String ip;
|
||||
public int port;
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ public final class EntityDefinitions {
|
||||
public static final EntityDefinition<BeeEntity> BEE;
|
||||
public static final EntityDefinition<BlazeEntity> BLAZE;
|
||||
public static final EntityDefinition<BoatEntity> BOAT;
|
||||
public static final EntityDefinition<CamelEntity> CAMEL;
|
||||
public static final EntityDefinition<CatEntity> CAT;
|
||||
public static final EntityDefinition<SpiderEntity> CAVE_SPIDER;
|
||||
public static final EntityDefinition<MinecartEntity> CHEST_MINECART;
|
||||
@ -859,6 +860,13 @@ public final class EntityDefinitions {
|
||||
.addTranslator(MetadataType.BYTE, AbstractHorseEntity::setHorseFlags)
|
||||
.addTranslator(null) // UUID of owner
|
||||
.build();
|
||||
CAMEL = EntityDefinition.inherited(CamelEntity::new, abstractHorseEntityBase)
|
||||
.type(EntityType.CAMEL)
|
||||
.identifier("minecraft:llama") // todo 1.20
|
||||
.height(2.375f).width(1.7f)
|
||||
.addTranslator(MetadataType.BOOLEAN, CamelEntity::setDashing)
|
||||
.addTranslator(null) // Last pose change tick
|
||||
.build();
|
||||
HORSE = EntityDefinition.inherited(HorseEntity::new, abstractHorseEntityBase)
|
||||
.type(EntityType.HORSE)
|
||||
.height(1.6f).width(1.3965f)
|
||||
|
@ -48,6 +48,8 @@ import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.GeyserDirtyMetadata;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
@ -360,6 +362,7 @@ public class Entity {
|
||||
setFlag(EntityFlag.ON_FIRE, ((xd & 0x01) == 0x01) && !getFlag(EntityFlag.FIRE_IMMUNE)); // Otherwise immune entities sometimes flicker onfire
|
||||
setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02);
|
||||
setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08);
|
||||
|
||||
// Swimming is ignored here and instead we rely on the pose
|
||||
setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
|
||||
|
||||
|
@ -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.entity.type.living.animal.horse;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class CamelEntity extends AbstractHorseEntity {
|
||||
|
||||
private static final float SITTING_HEIGHT_DIFFERENCE = 1.43F;
|
||||
|
||||
public CamelEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeMetadata() {
|
||||
super.initializeMetadata();
|
||||
this.dirtyMetadata.put(EntityData.VARIANT, 2); // Closest llama colour to camel
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEat(String javaIdentifierStripped, ItemMapping mapping) {
|
||||
return "cactus".equals(javaIdentifierStripped);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDimensions(Pose pose) {
|
||||
if (pose == Pose.SITTING) {
|
||||
setBoundingBoxWidth(definition.height() - SITTING_HEIGHT_DIFFERENCE);
|
||||
setBoundingBoxWidth(definition.width());
|
||||
} else {
|
||||
super.setDimensions(pose);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDashing(BooleanEntityMetadata entityMetadata) {
|
||||
|
||||
}
|
||||
}
|
@ -25,6 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.entity.type.living.monster;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.OptionalIntMetadataType;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
@ -35,6 +37,7 @@ import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
import java.util.UUID;
|
||||
|
||||
public class EndermanEntity extends MonsterEntity {
|
||||
@ -43,8 +46,15 @@ public class EndermanEntity extends MonsterEntity {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
public void setCarriedBlock(IntEntityMetadata entityMetadata) {
|
||||
dirtyMetadata.put(EntityDataTypes.BLOCK, session.getBlockMappings().getBedrockBlock(entityMetadata.getPrimitiveValue()));
|
||||
public void setCarriedBlock(EntityMetadata<OptionalInt, OptionalIntMetadataType> entityMetadata) {
|
||||
int bedrockBlockId;
|
||||
if (entityMetadata.getValue().isPresent()) {
|
||||
bedrockBlockId = session.getBlockMappings().getBedrockBlockId(entityMetadata.getValue().getAsInt());
|
||||
} else {
|
||||
bedrockBlockId = session.getBlockMappings().getBedrockAirId();
|
||||
}
|
||||
|
||||
dirtyMetadata.put(EntityData.BLOCK, bedrockBlockId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,6 +120,16 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
refreshSpeed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since 1.19.40, the client must be re-informed of its bounding box on respawn
|
||||
* See https://github.com/GeyserMC/Geyser/issues/3370
|
||||
*/
|
||||
public void updateBoundingBox() {
|
||||
dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, getBoundingBoxHeight());
|
||||
dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, getBoundingBoxWidth());
|
||||
updateBedrockMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBoundingBoxHeight(float height) {
|
||||
if (super.setBoundingBoxHeight(height)) {
|
||||
|
@ -76,7 +76,7 @@ public class AnvilContainer extends Container {
|
||||
String originalName = ItemUtils.getCustomName(getInput().getNbt());
|
||||
|
||||
String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale());
|
||||
String plainNewName = MessageTranslator.convertToPlainText(rename, session.locale());
|
||||
String plainNewName = MessageTranslator.convertToPlainText(rename);
|
||||
if (!plainOriginalName.equals(plainNewName)) {
|
||||
// Strip out formatting since Java Edition does not allow it
|
||||
correctRename = plainNewName;
|
||||
|
@ -39,6 +39,7 @@ import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.util.BlockUtils;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -63,14 +64,14 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||
Set<String> validBlocksTemp = new HashSet<>(validBlocks.length + 1);
|
||||
Collections.addAll(validBlocksTemp, validBlocks);
|
||||
validBlocksTemp.add(BlockUtils.getCleanIdentifier(javaBlockIdentifier));
|
||||
this.validBlocks = ImmutableSet.copyOf(validBlocksTemp);
|
||||
this.validBlocks = Set.copyOf(validBlocksTemp);
|
||||
} else {
|
||||
this.validBlocks = Collections.singleton(BlockUtils.getCleanIdentifier(javaBlockIdentifier));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||
public boolean prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
|
||||
// Check to see if there is an existing block we can use that the player just selected.
|
||||
// First, verify that the player's position has not changed, so we don't try to select a block wildly out of range.
|
||||
// (This could be a virtual inventory that the player is opening)
|
||||
@ -83,13 +84,16 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||
inventory.setHolderPosition(session.getLastInteractionBlockPosition());
|
||||
((Container) inventory).setUsingRealBlock(true, javaBlockString[0]);
|
||||
setCustomName(session, session.getLastInteractionBlockPosition(), inventory, javaBlockId);
|
||||
return;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, time to conjure up a fake block!
|
||||
Vector3i position = session.getPlayerEntity().getPosition().toInt();
|
||||
position = position.add(Vector3i.UP);
|
||||
Vector3i position = InventoryUtils.findAvailableWorldSpace(session);
|
||||
if (position == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
||||
blockPacket.setDataLayer(0);
|
||||
blockPacket.setBlockPosition(position);
|
||||
@ -99,6 +103,8 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||
inventory.setHolderPosition(position);
|
||||
|
||||
setCustomName(session, position, inventory, defaultJavaBlockState);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,7 +30,7 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
|
||||
public abstract class InventoryHolder {
|
||||
public abstract void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
|
||||
public abstract boolean prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
|
||||
public abstract void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
|
||||
public abstract void closeInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory);
|
||||
}
|
||||
|
@ -32,43 +32,41 @@ import lombok.Getter;
|
||||
* It is used to construct the list for the settings menu
|
||||
*/
|
||||
public enum GameRule {
|
||||
ANNOUNCEADVANCEMENTS("announceAdvancements", Boolean.class, true), // JE only
|
||||
COMMANDBLOCKOUTPUT("commandBlockOutput", Boolean.class, true),
|
||||
DISABLEELYTRAMOVEMENTCHECK("disableElytraMovementCheck", Boolean.class, false), // JE only
|
||||
DISABLERAIDS("disableRaids", Boolean.class, false), // JE only
|
||||
DODAYLIGHTCYCLE("doDaylightCycle", Boolean.class, true),
|
||||
DOENTITYDROPS("doEntityDrops", Boolean.class, true),
|
||||
DOFIRETICK("doFireTick", Boolean.class, true),
|
||||
DOIMMEDIATERESPAWN("doImmediateRespawn", Boolean.class, false),
|
||||
DOINSOMNIA("doInsomnia", Boolean.class, true),
|
||||
DOLIMITEDCRAFTING("doLimitedCrafting", Boolean.class, false), // JE only
|
||||
DOMOBLOOT("doMobLoot", Boolean.class, true),
|
||||
DOMOBSPAWNING("doMobSpawning", Boolean.class, true),
|
||||
DOPATROLSPAWNING("doPatrolSpawning", Boolean.class, true), // JE only
|
||||
DOTILEDROPS("doTileDrops", Boolean.class, true),
|
||||
DOTRADERSPAWNING("doTraderSpawning", Boolean.class, true), // JE only
|
||||
DOWEATHERCYCLE("doWeatherCycle", Boolean.class, true),
|
||||
DROWNINGDAMAGE("drowningDamage", Boolean.class, true),
|
||||
FALLDAMAGE("fallDamage", Boolean.class, true),
|
||||
FIREDAMAGE("fireDamage", Boolean.class, true),
|
||||
FREEZEDAMAGE("freezeDamage", Boolean.class, true),
|
||||
FORGIVEDEADPLAYERS("forgiveDeadPlayers", Boolean.class, true), // JE only
|
||||
KEEPINVENTORY("keepInventory", Boolean.class, false),
|
||||
LOGADMINCOMMANDS("logAdminCommands", Boolean.class, true), // JE only
|
||||
MAXCOMMANDCHAINLENGTH("maxCommandChainLength", Integer.class, 65536),
|
||||
MAXENTITYCRAMMING("maxEntityCramming", Integer.class, 24), // JE only
|
||||
MOBGRIEFING("mobGriefing", Boolean.class, true),
|
||||
NATURALREGENERATION("naturalRegeneration", Boolean.class, true),
|
||||
PLAYERSSLEEPINGPERCENTAGE("playersSleepingPercentage", Integer.class, 100), // JE only
|
||||
RANDOMTICKSPEED("randomTickSpeed", Integer.class, 3),
|
||||
REDUCEDDEBUGINFO("reducedDebugInfo", Boolean.class, false), // JE only
|
||||
SENDCOMMANDFEEDBACK("sendCommandFeedback", Boolean.class, true),
|
||||
SHOWDEATHMESSAGES("showDeathMessages", Boolean.class, true),
|
||||
SPAWNRADIUS("spawnRadius", Integer.class, 10),
|
||||
SPECTATORSGENERATECHUNKS("spectatorsGenerateChunks", Boolean.class, true), // JE only
|
||||
UNIVERSALANGER("universalAnger", Boolean.class, false), // JE only
|
||||
|
||||
UNKNOWN("unknown", Object.class);
|
||||
ANNOUNCEADVANCEMENTS("announceAdvancements", true), // JE only
|
||||
COMMANDBLOCKOUTPUT("commandBlockOutput", true),
|
||||
DISABLEELYTRAMOVEMENTCHECK("disableElytraMovementCheck", false), // JE only
|
||||
DISABLERAIDS("disableRaids", false), // JE only
|
||||
DODAYLIGHTCYCLE("doDaylightCycle", true),
|
||||
DOENTITYDROPS("doEntityDrops", true),
|
||||
DOFIRETICK("doFireTick", true),
|
||||
DOIMMEDIATERESPAWN("doImmediateRespawn", false),
|
||||
DOINSOMNIA("doInsomnia", true),
|
||||
DOLIMITEDCRAFTING("doLimitedCrafting", false), // JE only
|
||||
DOMOBLOOT("doMobLoot", true),
|
||||
DOMOBSPAWNING("doMobSpawning", true),
|
||||
DOPATROLSPAWNING("doPatrolSpawning", true), // JE only
|
||||
DOTILEDROPS("doTileDrops", true),
|
||||
DOTRADERSPAWNING("doTraderSpawning", true), // JE only
|
||||
DOWEATHERCYCLE("doWeatherCycle", true),
|
||||
DROWNINGDAMAGE("drowningDamage", true),
|
||||
FALLDAMAGE("fallDamage", true),
|
||||
FIREDAMAGE("fireDamage", true),
|
||||
FREEZEDAMAGE("freezeDamage", true),
|
||||
FORGIVEDEADPLAYERS("forgiveDeadPlayers", true), // JE only
|
||||
KEEPINVENTORY("keepInventory", false),
|
||||
LOGADMINCOMMANDS("logAdminCommands", true), // JE only
|
||||
MAXCOMMANDCHAINLENGTH("maxCommandChainLength", 65536),
|
||||
MAXENTITYCRAMMING("maxEntityCramming", 24), // JE only
|
||||
MOBGRIEFING("mobGriefing", true),
|
||||
NATURALREGENERATION("naturalRegeneration", true),
|
||||
PLAYERSSLEEPINGPERCENTAGE("playersSleepingPercentage", 100), // JE only
|
||||
RANDOMTICKSPEED("randomTickSpeed", 3),
|
||||
REDUCEDDEBUGINFO("reducedDebugInfo", false), // JE only
|
||||
SENDCOMMANDFEEDBACK("sendCommandFeedback", true),
|
||||
SHOWDEATHMESSAGES("showDeathMessages", true),
|
||||
SPAWNRADIUS("spawnRadius", 10),
|
||||
SPECTATORSGENERATECHUNKS("spectatorsGenerateChunks", true), // JE only
|
||||
UNIVERSALANGER("universalAnger", false); // JE only
|
||||
|
||||
public static final GameRule[] VALUES = values();
|
||||
|
||||
@ -78,48 +76,25 @@ public enum GameRule {
|
||||
@Getter
|
||||
private final Class<?> type;
|
||||
|
||||
@Getter
|
||||
private final Object defaultValue;
|
||||
private final int defaultValue;
|
||||
|
||||
GameRule(String javaID, Class<?> type) {
|
||||
this(javaID, type, null);
|
||||
GameRule(String javaID, boolean defaultValue) {
|
||||
this.javaID = javaID;
|
||||
this.type = Boolean.class;
|
||||
this.defaultValue = defaultValue ? 1 : 0;
|
||||
}
|
||||
|
||||
GameRule(String javaID, Class<?> type, Object defaultValue) {
|
||||
GameRule(String javaID, int defaultValue) {
|
||||
this.javaID = javaID;
|
||||
this.type = type;
|
||||
this.type = Integer.class;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to an object of the correct type for the current gamerule
|
||||
*
|
||||
* @param value The string value to convert
|
||||
* @return The converted and formatted value
|
||||
*/
|
||||
public Object convertValue(String value) {
|
||||
if (type.equals(Boolean.class)) {
|
||||
return Boolean.parseBoolean(value);
|
||||
} else if (type.equals(Integer.class)) {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
return null;
|
||||
public boolean getDefaultBooleanValue() {
|
||||
return defaultValue != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a game rule by the given Java ID
|
||||
*
|
||||
* @param id The ID of the gamerule
|
||||
* @return A {@link GameRule} object representing the requested ID or {@link GameRule#UNKNOWN}
|
||||
*/
|
||||
public static GameRule fromJavaID(String id) {
|
||||
for (GameRule gamerule : VALUES) {
|
||||
if (gamerule.javaID.equals(id)) {
|
||||
return gamerule;
|
||||
}
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
public int getDefaultIntValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
package org.geysermc.geyser.level;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
|
||||
import com.nukkitx.nbt.NbtMap;
|
||||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
@ -36,11 +34,8 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.ChunkCache;
|
||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class GeyserWorldManager extends WorldManager {
|
||||
|
||||
private static final Object2ObjectMap<String, String> gameruleCache = new Object2ObjectOpenHashMap<>();
|
||||
private final Object2ObjectMap<String, String> gameruleCache = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
@Override
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
@ -82,18 +77,18 @@ public class GeyserWorldManager extends WorldManager {
|
||||
|
||||
@Override
|
||||
public void setGameRule(GeyserSession session, String name, Object value) {
|
||||
session.sendCommand("gamerule " + name + " " + value);
|
||||
super.setGameRule(session, name, value);
|
||||
gameruleCache.put(name, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
||||
public boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
|
||||
String value = gameruleCache.get(gameRule.getJavaID());
|
||||
if (value != null) {
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
return gameRule.getDefaultValue() != null ? (Boolean) gameRule.getDefaultValue() : false;
|
||||
return gameRule.getDefaultBooleanValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,17 +98,7 @@ public class GeyserWorldManager extends WorldManager {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
return gameRule.getDefaultValue() != null ? (int) gameRule.getDefaultValue() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerGameMode(GeyserSession session, GameMode gameMode) {
|
||||
session.sendCommand("gamemode " + gameMode.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDifficulty(GeyserSession session, Difficulty difficulty) {
|
||||
session.sendCommand("difficulty " + difficulty.name().toLowerCase(Locale.ROOT));
|
||||
return gameRule.getDefaultIntValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,6 +31,9 @@ import org.cloudburstmc.math.vector.Vector3i;
|
||||
import com.nukkitx.nbt.NbtMap;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Class that manages or retrieves various information
|
||||
* from the world. Everything in this class should be
|
||||
@ -105,7 +108,9 @@ public abstract class WorldManager {
|
||||
* @param name The gamerule to change
|
||||
* @param value The new value for the gamerule
|
||||
*/
|
||||
public abstract void setGameRule(GeyserSession session, String name, Object value);
|
||||
public void setGameRule(GeyserSession session, String name, Object value) {
|
||||
session.sendCommand("gamerule " + name + " " + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a gamerule value as a boolean
|
||||
@ -114,7 +119,7 @@ public abstract class WorldManager {
|
||||
* @param gameRule The gamerule to fetch the value of
|
||||
* @return The boolean representation of the value
|
||||
*/
|
||||
public abstract Boolean getGameRuleBool(GeyserSession session, GameRule gameRule);
|
||||
public abstract boolean getGameRuleBool(GeyserSession session, GameRule gameRule);
|
||||
|
||||
/**
|
||||
* Get a gamerule value as an integer
|
||||
@ -131,7 +136,9 @@ public abstract class WorldManager {
|
||||
* @param session The session of the player to change the game mode of
|
||||
* @param gameMode The game mode to change the player to
|
||||
*/
|
||||
public abstract void setPlayerGameMode(GeyserSession session, GameMode gameMode);
|
||||
public void setPlayerGameMode(GeyserSession session, GameMode gameMode) {
|
||||
session.sendCommand("gamemode " + gameMode.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the difficulty of the Java server
|
||||
@ -139,7 +146,9 @@ public abstract class WorldManager {
|
||||
* @param session The session of the user that requested the change
|
||||
* @param difficulty The difficulty to change to
|
||||
*/
|
||||
public abstract void setDifficulty(GeyserSession session, Difficulty difficulty);
|
||||
public void setDifficulty(GeyserSession session, Difficulty difficulty) {
|
||||
session.sendCommand("difficulty " + difficulty.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given session's player has a permission
|
||||
@ -149,4 +158,12 @@ public abstract class WorldManager {
|
||||
* @return True if the player has the requested permission, false if not
|
||||
*/
|
||||
public abstract boolean hasPermission(GeyserSession session, String permission);
|
||||
|
||||
/**
|
||||
* Returns a list of biome identifiers available on the server.
|
||||
*/
|
||||
@Nullable
|
||||
public String[] getBiomeIdentifiers(boolean withTags) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public final class GameProtocol {
|
||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||
* release of the game that Geyser supports.
|
||||
*/
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v557.CODEC;
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = Bedrock_v560.CODEC;
|
||||
/**
|
||||
* A list of all supported Bedrock versions that can join Geyser
|
||||
*/
|
||||
@ -62,12 +62,6 @@ public final class GameProtocol {
|
||||
private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC;
|
||||
|
||||
static {
|
||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v527.CODEC.toBuilder()
|
||||
.minecraftVersion("1.19.0/1.19.2")
|
||||
.build());
|
||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v534.CODEC.toBuilder()
|
||||
.minecraftVersion("1.19.10/1.19.11")
|
||||
.build());
|
||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v544.CODEC);
|
||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v545.CODEC.toBuilder()
|
||||
.minecraftVersion("1.19.21/1.19.22")
|
||||
@ -75,7 +69,12 @@ public final class GameProtocol {
|
||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v554.CODEC.toBuilder()
|
||||
.minecraftVersion("1.19.30/1.19.31")
|
||||
.build());
|
||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v557.CODEC.toBuilder()
|
||||
.minecraftVersion("1.19.40/1.19.41")
|
||||
.build());
|
||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
|
||||
.minecraftVersion("1.19.50/1.19.51")
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,14 +93,14 @@ public final class GameProtocol {
|
||||
|
||||
/* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */
|
||||
|
||||
public static boolean supports1_19_10(GeyserSession session) {
|
||||
return session.getUpstream().getProtocolVersion() >= Bedrock_v534.CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
public static boolean supports1_19_30(GeyserSession session) {
|
||||
return session.getUpstream().getProtocolVersion() >= Bedrock_v554.CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
public static boolean supports1_19_50(GeyserSession session) {
|
||||
return session.getUpstream().getProtocolVersion() >= Bedrock_v560.V560_CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link PacketCodec} for Minecraft: Java Edition.
|
||||
*
|
||||
@ -117,7 +116,7 @@ public final class GameProtocol {
|
||||
* @return the supported Minecraft: Java Edition version names
|
||||
*/
|
||||
public static List<String> getJavaVersions() {
|
||||
return List.of(DEFAULT_JAVA_CODEC.getMinecraftVersion(), "1.19.2");
|
||||
return List.of(DEFAULT_JAVA_CODEC.getMinecraftVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,9 +61,13 @@ import org.geysermc.geyser.util.MathUtils;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
|
||||
private Deque<String> packsToSent = new ArrayDeque<>();
|
||||
|
||||
public UpstreamPacketHandler(GeyserImpl geyser, GeyserSession session) {
|
||||
super(geyser, session);
|
||||
}
|
||||
@ -186,24 +190,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
break;
|
||||
|
||||
case SEND_PACKS:
|
||||
for(String id : packet.getPackIds()) {
|
||||
ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket();
|
||||
String[] packID = id.split("_");
|
||||
ResourcePack pack = ResourcePack.PACKS.get(packID[0]);
|
||||
ResourcePackManifest.Header header = pack.getManifest().getHeader();
|
||||
|
||||
data.setPackId(header.getUuid());
|
||||
int chunkCount = (int) Math.ceil((int) pack.getFile().length() / (double) ResourcePack.CHUNK_SIZE);
|
||||
data.setChunkCount(chunkCount);
|
||||
data.setCompressedPackSize(pack.getFile().length());
|
||||
data.setMaxChunkSize(ResourcePack.CHUNK_SIZE);
|
||||
data.setHash(pack.getSha256());
|
||||
data.setPackVersion(packID[1]);
|
||||
data.setPremium(false);
|
||||
data.setType(ResourcePackType.RESOURCES);
|
||||
|
||||
session.sendUpstreamPacket(data);
|
||||
}
|
||||
packsToSent.addAll(packet.getPackIds());
|
||||
sendPackDataInfo(packsToSent.pop());
|
||||
break;
|
||||
|
||||
case HAVE_ALL_PACKS:
|
||||
@ -296,7 +284,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
data.setPackId(packet.getPackId());
|
||||
|
||||
int offset = packet.getChunkIndex() * ResourcePack.CHUNK_SIZE;
|
||||
byte[] packData = new byte[(int) MathUtils.constrain(pack.getFile().length() - offset, 0, ResourcePack.CHUNK_SIZE)];
|
||||
long remainingSize = pack.getFile().length() - offset;
|
||||
byte[] packData = new byte[(int) MathUtils.constrain(remainingSize, 0, ResourcePack.CHUNK_SIZE)];
|
||||
|
||||
try (InputStream inputStream = new FileInputStream(pack.getFile())) {
|
||||
inputStream.skip(offset);
|
||||
@ -308,6 +297,31 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
data.setData(Unpooled.wrappedBuffer(packData));
|
||||
|
||||
session.sendUpstreamPacket(data);
|
||||
|
||||
// Check if it is the last chunk and send next pack in queue when available.
|
||||
if (remainingSize <= ResourcePack.CHUNK_SIZE && !packsToSent.isEmpty()) {
|
||||
sendPackDataInfo(packsToSent.pop());
|
||||
}
|
||||
|
||||
return PacketSignal.HANDLED;
|
||||
}
|
||||
|
||||
private void sendPackDataInfo(String id) {
|
||||
ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket();
|
||||
String[] packID = id.split("_");
|
||||
ResourcePack pack = ResourcePack.PACKS.get(packID[0]);
|
||||
ResourcePackManifest.Header header = pack.getManifest().getHeader();
|
||||
|
||||
data.setPackId(header.getUuid());
|
||||
int chunkCount = (int) Math.ceil((int) pack.getFile().length() / (double) ResourcePack.CHUNK_SIZE);
|
||||
data.setChunkCount(chunkCount);
|
||||
data.setCompressedPackSize(pack.getFile().length());
|
||||
data.setMaxChunkSize(ResourcePack.CHUNK_SIZE);
|
||||
data.setHash(pack.getSha256());
|
||||
data.setPackVersion(packID[1]);
|
||||
data.setPremium(false);
|
||||
data.setType(ResourcePackType.RESOURCES);
|
||||
|
||||
session.sendUpstreamPacket(data);
|
||||
}
|
||||
}
|
||||
|
@ -181,12 +181,15 @@ public final class Registries {
|
||||
POTION_MIXES = SimpleRegistry.create(PotionMixRegistryLoader::new);
|
||||
ENCHANTMENTS = SimpleMappedRegistry.create("mappings/enchantments.json", EnchantmentRegistryLoader::new);
|
||||
|
||||
// TEMPORARY FIX TO MAKE OLD BIOMES NBT WORK WITH 1.19.30
|
||||
// Remove unneeded client generation data from NbtMapBuilder
|
||||
NbtMapBuilder biomesNbt = NbtMap.builder();
|
||||
for (Map.Entry<String, Object> entry : BIOMES_NBT.get().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
NbtMapBuilder value = ((NbtMap) entry.getValue()).toBuilder();
|
||||
value.put("name_hash", key);
|
||||
value.remove("minecraft:consolidated_features");
|
||||
value.remove("minecraft:multinoise_generation_rules");
|
||||
value.remove("minecraft:surface_material_adjustments");
|
||||
value.remove( "minecraft:surface_parameters");
|
||||
biomesNbt.put(key, value.build());
|
||||
}
|
||||
BIOMES_NBT.set(biomesNbt.build());
|
||||
|
@ -84,13 +84,9 @@ public final class BlockRegistryPopulator {
|
||||
private static void registerBedrockBlocks() {
|
||||
BiFunction<String, NbtMapBuilder, String> emptyMapper = (bedrockIdentifier, statesBuilder) -> null;
|
||||
ImmutableMap<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>> blockMappers = ImmutableMap.<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>>builder()
|
||||
.put(ObjectIntPair.of("1_19_0", Bedrock_v527.CODEC.getProtocolVersion()), (bedrockIdentifier, statesBuilder) -> {
|
||||
if (bedrockIdentifier.equals("minecraft:muddy_mangrove_roots")) {
|
||||
statesBuilder.remove("pillar_axis");
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.put(ObjectIntPair.of("1_19_20", Bedrock_v544.CODEC.getProtocolVersion()), emptyMapper).build();
|
||||
.put(ObjectIntPair.of("1_19_20", Bedrock_v544.CODEC.getProtocolVersion()), emptyMapper)
|
||||
.put(ObjectIntPair.of("1_19_50", Bedrock_v560.CODEC.getProtocolVersion()), emptyMapper)
|
||||
.build();
|
||||
|
||||
for (Map.Entry<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>> palette : blockMappers.entrySet()) {
|
||||
NbtList<NbtMap> blocksTag;
|
||||
|
@ -174,6 +174,33 @@ public class CustomItemRegistryPopulator {
|
||||
computeArmorProperties(mapping.getArmorType(), mapping.getProtectionValue(), componentBuilder);
|
||||
}
|
||||
|
||||
if (mapping.getFirstBlockRuntimeId() != null) {
|
||||
computeBlockItemProperties(mapping.getBedrockIdentifier(), componentBuilder);
|
||||
}
|
||||
|
||||
if (mapping.isEdible()) {
|
||||
computeConsumableProperties(itemProperties, componentBuilder, 1, false);
|
||||
}
|
||||
|
||||
if (mapping.isEntityPlacer()) {
|
||||
computeEntityPlacerProperties(componentBuilder);
|
||||
}
|
||||
|
||||
switch (mapping.getBedrockIdentifier()) {
|
||||
case "minecraft:fire_charge", "minecraft:flint_and_steel" -> {
|
||||
computeBlockItemProperties("minecraft:fire", componentBuilder);
|
||||
}
|
||||
case "minecraft:bow", "minecraft:crossbow", "minecraft:trident" -> {
|
||||
computeChargeableProperties(itemProperties, componentBuilder);
|
||||
}
|
||||
case "minecraft:honey_bottle", "minecraft:milk_bucket", "minecraft:potion" -> {
|
||||
computeConsumableProperties(itemProperties, componentBuilder, 2, true);
|
||||
}
|
||||
case "minecraft:experience_bottle", "minecraft:egg", "minecraft:ender_pearl", "minecraft:ender_eye", "minecraft:lingering_potion", "minecraft:snowball", "minecraft:splash_potion" -> {
|
||||
computeThrowableProperties(componentBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
computeRenderOffsets(false, customItemData, componentBuilder);
|
||||
|
||||
componentBuilder.putCompound("item_properties", itemProperties.build());
|
||||
@ -310,6 +337,48 @@ public class CustomItemRegistryPopulator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void computeBlockItemProperties(String blockItem, NbtMapBuilder componentBuilder) {
|
||||
// carved pumpkin should be able to be worn and for that we would need to add wearable and armor with protection 0 here
|
||||
// however this would have the side effect of preventing carved pumpkins from working as an attachable on the RP side outside the head slot
|
||||
// it also causes the item to glitch when right clicked to "equip" so this should only be added here later if these issues can be overcome
|
||||
|
||||
// all block items registered should be given this component to prevent double placement
|
||||
componentBuilder.putCompound("minecraft:block_placer", NbtMap.builder().putString("block", blockItem).build());
|
||||
}
|
||||
|
||||
private static void computeChargeableProperties(NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) {
|
||||
// setting high use_duration prevents the consume animation from playing
|
||||
itemProperties.putInt("use_duration", Integer.MAX_VALUE);
|
||||
// display item as tool (mainly for crossbow and bow)
|
||||
itemProperties.putBoolean("hand_equipped", true);
|
||||
// ensure client moves at slow speed while charging (note: this was calculated by hand as the movement modifer value does not seem to scale linearly)
|
||||
componentBuilder.putCompound("minecraft:chargeable", NbtMap.builder().putFloat("movement_modifier", 0.35F).build());
|
||||
}
|
||||
|
||||
private static void computeConsumableProperties(NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder, int useAnimation, boolean canAlwaysEat) {
|
||||
// this is the duration of the use animation in ticks; note that in behavior packs this is set as a float in seconds, but over the network it is an int in ticks
|
||||
itemProperties.putInt("use_duration", 32);
|
||||
// this dictates that the item will use the eat or drink animation (in the first person) and play eat or drink sounds
|
||||
// note that in behavior packs this is set as the string "eat" or "drink", but over the network it as an int, with these values being 1 and 2 respectively
|
||||
itemProperties.putInt("use_animation", useAnimation);
|
||||
// this component is required to allow the eat animation to play
|
||||
componentBuilder.putCompound("minecraft:food", NbtMap.builder().putBoolean("can_always_eat", canAlwaysEat).build());
|
||||
}
|
||||
|
||||
private static void computeEntityPlacerProperties(NbtMapBuilder componentBuilder) {
|
||||
// all items registered that place entities should be given this component to prevent double placement
|
||||
// it is okay that the entity here does not match the actual one since we control what entity actually spawns
|
||||
componentBuilder.putCompound("minecraft:entity_placer", NbtMap.builder().putString("entity", "minecraft:minecart").build());
|
||||
}
|
||||
|
||||
private static void computeThrowableProperties(NbtMapBuilder componentBuilder) {
|
||||
// allows item to be thrown when holding down right click (individual presses are required w/o this component)
|
||||
componentBuilder.putCompound("minecraft:throwable", NbtMap.builder().putBoolean("do_swing_animation", true).build());
|
||||
// this must be set to something for the swing animation to play
|
||||
// it is okay that the projectile here does not match the actual one since we control what entity actually spawns
|
||||
componentBuilder.putCompound("minecraft:projectile", NbtMap.builder().putString("projectile_entity", "minecraft:snowball").build());
|
||||
}
|
||||
|
||||
private static void computeRenderOffsets(boolean isHat, CustomItemData customItemData, NbtMapBuilder componentBuilder) {
|
||||
if (isHat) {
|
||||
componentBuilder.remove("minecraft:render_offsets");
|
||||
|
@ -90,10 +90,8 @@ public class ItemRegistryPopulator {
|
||||
|
||||
public static void populate() {
|
||||
Map<String, PaletteVersion> paletteVersions = new Object2ObjectOpenHashMap<>();
|
||||
paletteVersions.put("1_19_0", new PaletteVersion(Bedrock_v527.CODEC.getProtocolVersion(),
|
||||
Collections.singletonMap("minecraft:trader_llama_spawn_egg", "minecraft:llama_spawn_egg")));
|
||||
paletteVersions.put("1_19_10", new PaletteVersion(Bedrock_v534.CODEC.getProtocolVersion(), Collections.emptyMap()));
|
||||
paletteVersions.put("1_19_20", new PaletteVersion(Bedrock_v544.CODEC.getProtocolVersion(), Collections.emptyMap()));
|
||||
paletteVersions.put("1_19_50", new PaletteVersion(Bedrock_v560.CODEC.getProtocolVersion(), Collections.emptyMap()));
|
||||
|
||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||
|
||||
|
@ -90,8 +90,6 @@ public class RecipeRegistryPopulator {
|
||||
Collections.singletonList(CraftingData.fromMulti(UUID.fromString("d392b075-4ba1-40ae-8789-af868d56f6ce"), ++LAST_RECIPE_NET_ID)));
|
||||
craftingData.put(RecipeType.CRAFTING_SPECIAL_MAPCLONING,
|
||||
Collections.singletonList(CraftingData.fromMulti(UUID.fromString("85939755-ba10-4d9d-a4cc-efb7a8e943c4"), ++LAST_RECIPE_NET_ID)));
|
||||
craftingData.put(RecipeType.CRAFTING_SPECIAL_BANNERADDPATTERN,
|
||||
Collections.singletonList(CraftingData.fromMulti(UUID.fromString("b5c5d105-75a2-4076-af2b-923ea2bf4bf0"), ++LAST_RECIPE_NET_ID)));
|
||||
|
||||
// https://github.com/pmmp/PocketMine-MP/blob/stable/src/pocketmine/inventory/MultiRecipe.php
|
||||
|
||||
|
@ -48,4 +48,6 @@ public class GeyserMappingItem {
|
||||
@JsonProperty("repair_materials") List<String> repairMaterials;
|
||||
@JsonProperty("has_suspicious_stew_effect") boolean hasSuspiciousStewEffect = false;
|
||||
@JsonProperty("dye_color") int dyeColor = -1;
|
||||
@JsonProperty("is_edible") boolean edible = false;
|
||||
@JsonProperty("is_entity_placer") boolean entityPlacer = false;
|
||||
}
|
||||
|
@ -70,7 +70,6 @@ import com.nimbusds.jwt.SignedJWT;
|
||||
import com.nukkitx.nbt.NbtMap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoop;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrays;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
@ -137,7 +136,6 @@ import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
|
||||
import org.geysermc.geyser.level.JavaDimension;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.level.physics.CollisionManager;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.network.netty.LocalSession;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||
@ -336,6 +334,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
*/
|
||||
@Setter
|
||||
private String worldName = null;
|
||||
/**
|
||||
* As of Java 1.19.3, the client only uses these for commands.
|
||||
*/
|
||||
@Setter
|
||||
private String[] levels;
|
||||
|
||||
private boolean sneaking;
|
||||
|
||||
@ -656,6 +659,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
creativePacket.setContents(this.itemMappings.getCreativeItems());
|
||||
upstream.sendPacket(creativePacket);
|
||||
|
||||
// Potion mixes are registered by default, as they are needed to be able to put ingredients into the brewing stand.
|
||||
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
|
||||
craftingDataPacket.setCleanRecipes(true);
|
||||
craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.get());
|
||||
upstream.sendPacket(craftingDataPacket);
|
||||
|
||||
PlayStatusPacket playStatusPacket = new PlayStatusPacket();
|
||||
playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
|
||||
upstream.sendPacket(playStatusPacket);
|
||||
@ -1389,14 +1398,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
* Sends a chat message to the Java server.
|
||||
*/
|
||||
public void sendChat(String message) {
|
||||
sendDownstreamPacket(new ServerboundChatPacket(message, Instant.now().toEpochMilli(), 0L, ByteArrays.EMPTY_ARRAY, false, Collections.emptyList(), null));
|
||||
sendDownstreamPacket(new ServerboundChatPacket(message, Instant.now().toEpochMilli(), 0L, null, 0, new BitSet()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a command to the Java server.
|
||||
*/
|
||||
public void sendCommand(String command) {
|
||||
sendDownstreamPacket(new ServerboundChatCommandPacket(command, Instant.now().toEpochMilli(), 0L, Collections.emptyList(), false, Collections.emptyList(), null));
|
||||
sendDownstreamPacket(new ServerboundChatCommandPacket(command, Instant.now().toEpochMilli(), 0L, Collections.emptyList(), 0, new BitSet()));
|
||||
}
|
||||
|
||||
public void setServerRenderDistance(int renderDistance) {
|
||||
@ -1454,7 +1463,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
startGamePacket.setRotation(Vector2f.from(1, 1));
|
||||
|
||||
startGamePacket.setSeed(-1L);
|
||||
startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(dimension));
|
||||
startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(chunkCache.getBedrockDimension()));
|
||||
startGamePacket.setGeneratorId(1);
|
||||
startGamePacket.setLevelGameType(GameType.SURVIVAL);
|
||||
startGamePacket.setDifficulty(1);
|
||||
@ -1655,76 +1664,40 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
boolean spectator = gameMode == GameMode.SPECTATOR;
|
||||
boolean worldImmutable = gameMode == GameMode.ADVENTURE || spectator;
|
||||
|
||||
if (GameProtocol.supports1_19_10(this)) {
|
||||
UpdateAdventureSettingsPacket adventureSettingsPacket = new UpdateAdventureSettingsPacket();
|
||||
adventureSettingsPacket.setNoMvP(false);
|
||||
adventureSettingsPacket.setNoPvM(false);
|
||||
adventureSettingsPacket.setImmutableWorld(worldImmutable);
|
||||
adventureSettingsPacket.setShowNameTags(false);
|
||||
adventureSettingsPacket.setAutoJump(true);
|
||||
sendUpstreamPacket(adventureSettingsPacket);
|
||||
UpdateAdventureSettingsPacket adventureSettingsPacket = new UpdateAdventureSettingsPacket();
|
||||
adventureSettingsPacket.setNoMvP(false);
|
||||
adventureSettingsPacket.setNoPvM(false);
|
||||
adventureSettingsPacket.setImmutableWorld(worldImmutable);
|
||||
adventureSettingsPacket.setShowNameTags(false);
|
||||
adventureSettingsPacket.setAutoJump(true);
|
||||
sendUpstreamPacket(adventureSettingsPacket);
|
||||
|
||||
UpdateAbilitiesPacket updateAbilitiesPacket = new UpdateAbilitiesPacket();
|
||||
updateAbilitiesPacket.setUniqueEntityId(bedrockId);
|
||||
updateAbilitiesPacket.setCommandPermission(commandPermission);
|
||||
updateAbilitiesPacket.setPlayerPermission(playerPermission);
|
||||
UpdateAbilitiesPacket updateAbilitiesPacket = new UpdateAbilitiesPacket();
|
||||
updateAbilitiesPacket.setUniqueEntityId(bedrockId);
|
||||
updateAbilitiesPacket.setCommandPermission(commandPermission);
|
||||
updateAbilitiesPacket.setPlayerPermission(playerPermission);
|
||||
|
||||
AbilityLayer abilityLayer = new AbilityLayer();
|
||||
Set<Ability> abilities = abilityLayer.getAbilityValues();
|
||||
if (canFly || spectator) {
|
||||
abilities.add(Ability.MAY_FLY);
|
||||
}
|
||||
|
||||
// Default stuff we have to fill in
|
||||
abilities.add(Ability.BUILD);
|
||||
abilities.add(Ability.MINE);
|
||||
// Needed so you can drop items
|
||||
abilities.add(Ability.DOORS_AND_SWITCHES);
|
||||
if (gameMode == GameMode.CREATIVE) {
|
||||
// Needed so the client doesn't attempt to take away items
|
||||
abilities.add(Ability.INSTABUILD);
|
||||
}
|
||||
|
||||
if (commandPermission == CommandPermission.GAME_DIRECTORS) {
|
||||
// Fixes a bug? since 1.19.11 where the player can change their gamemode in Bedrock settings and
|
||||
// a packet is not sent to the server.
|
||||
// https://github.com/GeyserMC/Geyser/issues/3191
|
||||
abilities.add(Ability.OPERATOR_COMMANDS);
|
||||
}
|
||||
|
||||
if (flying || spectator) {
|
||||
if (spectator && !flying) {
|
||||
// We're "flying locked" in this gamemode
|
||||
flying = true;
|
||||
ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(true);
|
||||
sendDownstreamPacket(abilitiesPacket);
|
||||
}
|
||||
abilities.add(Ability.FLYING);
|
||||
}
|
||||
|
||||
if (spectator) {
|
||||
abilities.add(Ability.NO_CLIP);
|
||||
}
|
||||
|
||||
abilityLayer.setLayerType(AbilityLayer.Type.BASE);
|
||||
abilityLayer.setFlySpeed(flySpeed);
|
||||
// https://github.com/GeyserMC/Geyser/issues/3139 as of 1.19.10
|
||||
abilityLayer.setWalkSpeed(walkSpeed == 0f ? 0.01f : walkSpeed);
|
||||
Collections.addAll(abilityLayer.getAbilitiesSet(), USED_ABILITIES);
|
||||
|
||||
updateAbilitiesPacket.getAbilityLayers().add(abilityLayer);
|
||||
sendUpstreamPacket(updateAbilitiesPacket);
|
||||
return;
|
||||
AbilityLayer abilityLayer = new AbilityLayer();
|
||||
Set<Ability> abilities = abilityLayer.getAbilityValues();
|
||||
if (canFly || spectator) {
|
||||
abilities.add(Ability.MAY_FLY);
|
||||
}
|
||||
|
||||
AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket();
|
||||
adventureSettingsPacket.setUniqueEntityId(bedrockId);
|
||||
adventureSettingsPacket.setCommandPermission(commandPermission);
|
||||
adventureSettingsPacket.setPlayerPermission(playerPermission);
|
||||
// Default stuff we have to fill in
|
||||
abilities.add(Ability.BUILD);
|
||||
abilities.add(Ability.MINE);
|
||||
// Needed so you can drop items
|
||||
abilities.add(Ability.DOORS_AND_SWITCHES);
|
||||
if (gameMode == GameMode.CREATIVE) {
|
||||
// Needed so the client doesn't attempt to take away items
|
||||
abilities.add(Ability.INSTABUILD);
|
||||
}
|
||||
|
||||
Set<AdventureSetting> flags = adventureSettingsPacket.getSettings();
|
||||
if (canFly || spectator) {
|
||||
flags.add(AdventureSetting.MAY_FLY);
|
||||
if (commandPermission == CommandPermission.GAME_DIRECTORS) {
|
||||
// Fixes a bug? since 1.19.11 where the player can change their gamemode in Bedrock settings and
|
||||
// a packet is not sent to the server.
|
||||
// https://github.com/GeyserMC/Geyser/issues/3191
|
||||
abilities.add(Ability.OPERATOR_COMMANDS);
|
||||
}
|
||||
|
||||
if (flying || spectator) {
|
||||
@ -1734,20 +1707,21 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(true);
|
||||
sendDownstreamPacket(abilitiesPacket);
|
||||
}
|
||||
flags.add(AdventureSetting.FLYING);
|
||||
}
|
||||
|
||||
if (worldImmutable) {
|
||||
flags.add(AdventureSetting.WORLD_IMMUTABLE);
|
||||
abilities.add(Ability.FLYING);
|
||||
}
|
||||
|
||||
if (spectator) {
|
||||
flags.add(AdventureSetting.NO_CLIP);
|
||||
abilities.add(Ability.NO_CLIP);
|
||||
}
|
||||
|
||||
flags.add(AdventureSetting.AUTO_JUMP);
|
||||
abilityLayer.setLayerType(AbilityLayer.Type.BASE);
|
||||
abilityLayer.setFlySpeed(flySpeed);
|
||||
// https://github.com/GeyserMC/Geyser/issues/3139 as of 1.19.10
|
||||
abilityLayer.setWalkSpeed(walkSpeed == 0f ? 0.01f : walkSpeed);
|
||||
Collections.addAll(abilityLayer.getAbilitiesSet(), USED_ABILITIES);
|
||||
|
||||
sendUpstreamPacket(adventureSettingsPacket);
|
||||
updateAbilitiesPacket.getAbilityLayers().add(abilityLayer);
|
||||
sendUpstreamPacket(updateAbilitiesPacket);
|
||||
}
|
||||
|
||||
private int getRenderDistance() {
|
||||
|
@ -43,6 +43,8 @@ import java.util.Optional;
|
||||
|
||||
public class AsteriskSerializer extends StdSerializer<Object> implements ContextualSerializer {
|
||||
|
||||
public static final String[] NON_SENSITIVE_ADDRESSES = {"", "0.0.0.0", "localhost", "127.0.0.1", "auto", "unknown"};
|
||||
|
||||
public static boolean showSensitive = false;
|
||||
|
||||
@Target({ElementType.FIELD})
|
||||
@ -91,11 +93,11 @@ public class AsteriskSerializer extends StdSerializer<Object> implements Context
|
||||
}
|
||||
|
||||
private boolean isSensitiveIp(String ip) {
|
||||
if (ip.equalsIgnoreCase("localhost") || ip.equalsIgnoreCase("auto")) {
|
||||
// `auto` should not be shown unless there is an obscure issue with setting the localhost address
|
||||
return false;
|
||||
for (String address : NON_SENSITIVE_ADDRESSES) {
|
||||
if (address.equalsIgnoreCase(ip)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !ip.isEmpty() && !ip.equals("0.0.0.0") && !ip.equals("127.0.0.1");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +65,8 @@ public abstract class AbstractBlockInventoryTranslator extends BaseInventoryTran
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
holder.prepareInventory(this, session, inventory);
|
||||
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
return holder.prepareInventory(this, session, inventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,10 +59,12 @@ public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||
CraftRecipeOptionalStackRequestActionData data = (CraftRecipeOptionalStackRequestActionData) request.getActions()[0];
|
||||
AnvilContainer container = (AnvilContainer) inventory;
|
||||
|
||||
// Required as of 1.18.30 - FilterTextPackets no longer appear to be sent
|
||||
String name = request.getFilterStrings()[data.getFilteredStringIndex()];
|
||||
if (!Objects.equals(name, container.getNewName())) {
|
||||
container.checkForRename(session, name);
|
||||
if (request.getFilterStrings().length != 0) {
|
||||
// Required as of 1.18.30 - FilterTextPackets no longer appear to be sent
|
||||
String name = request.getFilterStrings()[data.getFilteredStringIndex()];
|
||||
if (!Objects.equals(name, container.getNewName())) { // TODO is this still necessary after pre-1.19.50 support is dropped?
|
||||
container.checkForRename(session, name);
|
||||
}
|
||||
}
|
||||
|
||||
return super.translateRequest(session, inventory, request);
|
||||
|
@ -105,7 +105,7 @@ public abstract class InventoryTranslator {
|
||||
|
||||
public final int size;
|
||||
|
||||
public abstract void prepareInventory(GeyserSession session, Inventory inventory);
|
||||
public abstract boolean prepareInventory(GeyserSession session, Inventory inventory);
|
||||
public abstract void openInventory(GeyserSession session, Inventory inventory);
|
||||
public abstract void closeInventory(GeyserSession session, Inventory inventory);
|
||||
public abstract void updateProperty(GeyserSession session, Inventory inventory, int key, int value);
|
||||
|
@ -55,7 +55,8 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -98,7 +98,7 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
MerchantContainer merchantInventory = (MerchantContainer) inventory;
|
||||
if (merchantInventory.getVillager() == null) {
|
||||
long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet();
|
||||
@ -121,6 +121,8 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator {
|
||||
|
||||
merchantInventory.setVillager(villager);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -527,7 +527,8 @@ public class PlayerInventoryTranslator extends InventoryTranslator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,6 +41,7 @@ import org.geysermc.geyser.level.block.DoubleChestValue;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.DoubleChestBlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
|
||||
public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
||||
private final int defaultJavaBlockState;
|
||||
@ -51,7 +52,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
// See BlockInventoryHolder - same concept there except we're also dealing with a specific block state
|
||||
if (session.getLastInteractionPlayerPosition().equals(session.getPlayerEntity().getPosition())) {
|
||||
int javaBlockId = session.getGeyser().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition());
|
||||
@ -77,11 +78,16 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
||||
dataPacket.setData(tag.build());
|
||||
dataPacket.setBlockPosition(session.getLastInteractionBlockPosition());
|
||||
session.sendUpstreamPacket(dataPacket);
|
||||
return;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP);
|
||||
Vector3i position = InventoryUtils.findAvailableWorldSpace(session);
|
||||
if (position == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3i pairPosition = position.add(Vector3i.UNIT_X);
|
||||
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(defaultJavaBlockState);
|
||||
|
||||
@ -126,6 +132,8 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
||||
session.sendUpstreamPacket(dataPacket);
|
||||
|
||||
inventory.setHolderPosition(position);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,8 +52,8 @@ public class SingleChestInventoryTranslator extends ChestInventoryTranslator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
holder.prepareInventory(this, session, inventory);
|
||||
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
return holder.prepareInventory(this, session, inventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,7 +40,8 @@ public abstract class AbstractHorseInventoryTranslator extends BaseInventoryTran
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,7 +32,7 @@ import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.SignUtils;
|
||||
|
||||
@BlockEntity(type = BlockEntityType.SIGN)
|
||||
@BlockEntity(type = {BlockEntityType.SIGN, BlockEntityType.HANGING_SIGN})
|
||||
public class SignBlockEntityTranslator extends BlockEntityTranslator {
|
||||
/**
|
||||
* Maps a color stored in a sign's Color tag to its ARGB value.
|
||||
@ -88,6 +88,7 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
|
||||
signWidth += SignUtils.getCharacterWidth(c);
|
||||
}
|
||||
|
||||
// todo 1.20: update for hanging signs (smaller width). Currently OK because bedrock sees hanging signs as normal signs
|
||||
if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) {
|
||||
finalSignLine.append(c);
|
||||
} else {
|
||||
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.level.block.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
@ -68,16 +69,18 @@ public class SpawnerBlockEntityTranslator extends BlockEntityTranslator {
|
||||
|
||||
CompoundTag spawnData = tag.get("SpawnData");
|
||||
if (spawnData != null) {
|
||||
String entityID = (String) ((CompoundTag) spawnData.get("entity"))
|
||||
.get("id")
|
||||
.getValue();
|
||||
builder.put("EntityIdentifier", entityID);
|
||||
StringTag idTag = ((CompoundTag) spawnData.get("entity")).get("id");
|
||||
if (idTag != null) {
|
||||
// As of 1.19.3, spawners can be empty
|
||||
String entityId = idTag.getValue();
|
||||
builder.put("EntityIdentifier", entityId);
|
||||
|
||||
EntityDefinition<?> definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityID);
|
||||
if (definition != null) {
|
||||
builder.put("DisplayEntityWidth", definition.width());
|
||||
builder.put("DisplayEntityHeight", definition.height());
|
||||
builder.put("DisplayEntityScale", 1.0f);
|
||||
EntityDefinition<?> definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityId);
|
||||
if (definition != null) {
|
||||
builder.put("DisplayEntityWidth", definition.width());
|
||||
builder.put("DisplayEntityHeight", definition.height());
|
||||
builder.put("DisplayEntityScale", 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,10 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
|
||||
// This converts the message into the array'd message Java wants
|
||||
for (char character : text.toCharArray()) {
|
||||
widthCount += SignUtils.getCharacterWidth(character);
|
||||
|
||||
// todo 1.20: update for hanging signs (smaller width). Currently bedrock thinks hanging signs are normal,
|
||||
// so it thinks hanging signs have more width than they actually do. Seems like JE just truncates it.
|
||||
|
||||
// If we get a return in Bedrock, or go over the character width max, that signals to use the next line.
|
||||
if (character == '\n' || widthCount > SignUtils.JAVA_CHARACTER_WIDTH_MAX) {
|
||||
// We need to apply some more logic if we went over the character width max
|
||||
|
@ -29,6 +29,7 @@ import org.cloudburstmc.protocol.bedrock.packet.CommandRequestPacket;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
@ -38,16 +39,14 @@ public class BedrockCommandRequestTranslator extends PacketTranslator<CommandReq
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, CommandRequestPacket packet) {
|
||||
String command = packet.getCommand().replace("/", "");
|
||||
String command = MessageTranslator.convertToPlainText(packet.getCommand());
|
||||
if (!(session.getGeyser().getPlatformType() == PlatformType.STANDALONE
|
||||
&& GeyserImpl.getInstance().commandManager().runCommand(session, command))) {
|
||||
String message = packet.getCommand().trim();
|
||||
|
||||
if (MessageTranslator.isTooLong(message, session)) {
|
||||
&& GeyserImpl.getInstance().commandManager().runCommand(session, command.substring(1)))) {
|
||||
if (MessageTranslator.isTooLong(command, session)) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.sendCommand(message.substring(1));
|
||||
session.sendCommand(command.substring(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,6 @@ import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||
import org.geysermc.geyser.inventory.click.Click;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
@ -478,10 +477,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||
InteractAction.ATTACK, session.isSneaking());
|
||||
session.sendDownstreamPacket(attackPacket);
|
||||
|
||||
if (GameProtocol.supports1_19_10(session)) {
|
||||
// Since 1.19.10, LevelSoundEventPackets are no longer sent by the client when attacking entities
|
||||
CooldownUtils.sendCooldown(session);
|
||||
}
|
||||
// Since 1.19.10, LevelSoundEventPackets are no longer sent by the client when attacking entities
|
||||
CooldownUtils.sendCooldown(session);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -37,21 +37,7 @@ public class BedrockTextTranslator extends PacketTranslator<TextPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, TextPacket packet) {
|
||||
String message = packet.getMessage();
|
||||
|
||||
// The order here is important - strip out illegal characters first, then check if it's blank
|
||||
// (in case the message is blank after removing)
|
||||
if (message.indexOf(ChatColor.ESCAPE) != -1) {
|
||||
// Filter out all escape characters - Java doesn't let you type these
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < message.length(); i++) {
|
||||
char c = message.charAt(i);
|
||||
if (c != ChatColor.ESCAPE) {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
message = builder.toString();
|
||||
}
|
||||
String message = MessageTranslator.convertToPlainText(packet.getMessage());
|
||||
|
||||
if (message.isBlank()) {
|
||||
// Java Edition (as of 1.17.1) just doesn't pass on these messages, so... we won't either!
|
||||
|
@ -83,6 +83,9 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||
attributesPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
attributesPacket.getAttributes().addAll(entity.getAttributes().values());
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
|
||||
// Bounding box must be sent after a player dies and respawns since 1.19.40
|
||||
entity.updateBoundingBox();
|
||||
break;
|
||||
case START_SWIMMING:
|
||||
if (!entity.getFlag(EntityFlag.SWIMMING)) {
|
||||
|
@ -45,6 +45,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.event.downstream.ServerDefineCommandsEvent;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
@ -138,7 +139,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
||||
// Get and update the commandArgs list with the found arguments
|
||||
if (node.getChildIndices().length >= 1) {
|
||||
for (int childIndex : node.getChildIndices()) {
|
||||
commandArgs.computeIfAbsent(nodeIndex, ArrayList::new).add(nodes[childIndex]);
|
||||
commandArgs.computeIfAbsent(nodeIndex, ($) -> new ArrayList<>()).add(nodes[childIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,7 +206,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
||||
if (commandNode.getChildIndices().length >= 1) {
|
||||
// Create the root param node and build all the children
|
||||
ParamInfo rootParam = new ParamInfo(commandNode, null);
|
||||
rootParam.buildChildren(session, allNodes);
|
||||
rootParam.buildChildren(new CommandBuilderContext(session), allNodes);
|
||||
|
||||
List<CommandParamData[]> treeData = rootParam.getTree();
|
||||
|
||||
@ -218,11 +219,11 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
||||
/**
|
||||
* Convert Java edition command types to Bedrock edition
|
||||
*
|
||||
* @param session the session
|
||||
* @param context the session's command context
|
||||
* @param node Command type to convert
|
||||
* @return Bedrock parameter data type
|
||||
*/
|
||||
private static Object mapCommandType(GeyserSession session, CommandNode node) {
|
||||
private static Object mapCommandType(CommandBuilderContext context, CommandNode node) {
|
||||
CommandParser parser = node.getParser();
|
||||
if (parser == null) {
|
||||
return CommandParam.STRING;
|
||||
@ -239,21 +240,24 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
||||
case RESOURCE_LOCATION, FUNCTION -> CommandParam.FILE_PATH;
|
||||
case BOOL -> ENUM_BOOLEAN;
|
||||
case OPERATION -> CommandParam.OPERATOR; // ">=", "==", etc
|
||||
case BLOCK_STATE -> BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.get().keySet().toArray(new String[0]);
|
||||
case ITEM_STACK -> session.getItemMappings().getItemNames();
|
||||
case ITEM_ENCHANTMENT -> Enchantment.JavaEnchantment.ALL_JAVA_IDENTIFIERS;
|
||||
case ENTITY_SUMMON -> Registries.JAVA_ENTITY_IDENTIFIERS.get().keySet().toArray(new String[0]);
|
||||
case BLOCK_STATE -> context.getBlockStates();
|
||||
case ITEM_STACK -> context.session.getItemMappings().getItemNames();
|
||||
case COLOR -> VALID_COLORS;
|
||||
case SCOREBOARD_SLOT -> VALID_SCOREBOARD_SLOTS;
|
||||
case MOB_EFFECT -> ALL_EFFECT_IDENTIFIERS;
|
||||
case RESOURCE, RESOURCE_OR_TAG -> {
|
||||
String resource = ((ResourceProperties) node.getProperties()).getRegistryKey();
|
||||
if (resource.equals("minecraft:attribute")) {
|
||||
yield ATTRIBUTES;
|
||||
} else {
|
||||
yield CommandParam.STRING;
|
||||
}
|
||||
}
|
||||
case RESOURCE -> handleResource(context, ((ResourceProperties) node.getProperties()).getRegistryKey(), false);
|
||||
case RESOURCE_OR_TAG -> handleResource(context, ((ResourceProperties) node.getProperties()).getRegistryKey(), true);
|
||||
case DIMENSION -> context.session.getLevels();
|
||||
default -> CommandParam.STRING;
|
||||
};
|
||||
}
|
||||
|
||||
private static Object handleResource(CommandBuilderContext context, String resource, boolean tags) {
|
||||
return switch (resource) {
|
||||
case "minecraft:attribute" -> ATTRIBUTES;
|
||||
case "minecraft:enchantment" -> Enchantment.JavaEnchantment.ALL_JAVA_IDENTIFIERS;
|
||||
case "minecraft:entity_type" -> context.getEntityTypes();
|
||||
case "minecraft:mob_effect" -> ALL_EFFECT_IDENTIFIERS;
|
||||
case "minecraft:worldgen/biome" -> tags ? context.getBiomesWithTags() : context.getBiomes();
|
||||
default -> CommandParam.STRING;
|
||||
};
|
||||
}
|
||||
@ -261,7 +265,55 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
||||
/**
|
||||
* Stores the command description and parameter data for best optimizing the Bedrock commands packet.
|
||||
*/
|
||||
private static record BedrockCommandInfo(String name, String description, CommandParamData[][] paramData) implements ServerDefineCommandsEvent.CommandInfo {
|
||||
private record BedrockCommandInfo(String name, String description, CommandParamData[][] paramData) implements ServerDefineCommandsEvent.CommandInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores command completions so we don't have to rebuild the same values multiple times.
|
||||
*/
|
||||
@MonotonicNonNull
|
||||
private static class CommandBuilderContext {
|
||||
private final GeyserSession session;
|
||||
private Object biomesWithTags;
|
||||
private Object biomesNoTags;
|
||||
private String[] blockStates;
|
||||
private String[] entityTypes;
|
||||
|
||||
CommandBuilderContext(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
private Object getBiomes() {
|
||||
if (biomesNoTags != null) {
|
||||
return biomesNoTags;
|
||||
}
|
||||
|
||||
String[] identifiers = session.getGeyser().getWorldManager().getBiomeIdentifiers(false);
|
||||
return (biomesNoTags = identifiers != null ? identifiers : CommandParam.STRING);
|
||||
}
|
||||
|
||||
private Object getBiomesWithTags() {
|
||||
if (biomesWithTags != null) {
|
||||
return biomesWithTags;
|
||||
}
|
||||
|
||||
String[] identifiers = session.getGeyser().getWorldManager().getBiomeIdentifiers(true);
|
||||
return (biomesWithTags = identifiers != null ? identifiers : CommandParam.STRING);
|
||||
}
|
||||
|
||||
private String[] getBlockStates() {
|
||||
if (blockStates != null) {
|
||||
return blockStates;
|
||||
}
|
||||
return (blockStates = BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.get().keySet().toArray(new String[0]));
|
||||
}
|
||||
|
||||
private String[] getEntityTypes() {
|
||||
if (entityTypes != null) {
|
||||
return entityTypes;
|
||||
}
|
||||
return (entityTypes = Registries.JAVA_ENTITY_IDENTIFIERS.get().keySet().toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ -286,10 +338,10 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
||||
/**
|
||||
* Build the array of all the child parameters (recursive)
|
||||
*
|
||||
* @param session the session
|
||||
* @param context the session's command builder context
|
||||
* @param allNodes Every command node
|
||||
*/
|
||||
public void buildChildren(GeyserSession session, CommandNode[] allNodes) {
|
||||
public void buildChildren(CommandBuilderContext context, CommandNode[] allNodes) {
|
||||
for (int paramID : paramNode.getChildIndices()) {
|
||||
CommandNode paramNode = allNodes[paramID];
|
||||
|
||||
@ -343,7 +395,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
||||
}
|
||||
} else {
|
||||
// Put the non-enum param into the list
|
||||
Object mappedType = mapCommandType(session, paramNode);
|
||||
Object mappedType = mapCommandType(context, paramNode);
|
||||
CommandEnumData enumData = null;
|
||||
CommandParam type = null;
|
||||
boolean optional = this.paramNode.isExecutable();
|
||||
@ -353,7 +405,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
||||
map.put(s, Set.of());
|
||||
}
|
||||
|
||||
enumData = new CommandEnumData(paramNode.getParser().name().toLowerCase(Locale.ROOT), map, false);
|
||||
enumData = new CommandEnumData(getEnumDataName(paramNode).toLowerCase(Locale.ROOT), map, false);
|
||||
} else {
|
||||
type = (CommandParam) mappedType;
|
||||
// Bedrock throws a fit if an optional message comes after a string or target
|
||||
@ -377,10 +429,25 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
|
||||
|
||||
// Recursively build all child options
|
||||
for (ParamInfo child : children) {
|
||||
child.buildChildren(session, allNodes);
|
||||
child.buildChildren(context, allNodes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mitigates https://github.com/GeyserMC/Geyser/issues/3411. Not a perfect solution.
|
||||
*/
|
||||
private static String getEnumDataName(CommandNode node) {
|
||||
if (node.getProperties() instanceof ResourceProperties properties) {
|
||||
String registryKey = properties.getRegistryKey();
|
||||
int identifierSplit = registryKey.indexOf(':');
|
||||
if (identifierSplit != -1) {
|
||||
return registryKey.substring(identifierSplit);
|
||||
}
|
||||
return registryKey;
|
||||
}
|
||||
return node.getParser().name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparing CommandNode type a and b, determine if they are in the same overload.
|
||||
* <p>
|
||||
|
@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.translator.protocol.java.level;
|
||||
package org.geysermc.geyser.translator.protocol.java;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundCustomSoundPacket;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
@ -31,19 +31,13 @@ import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket;
|
||||
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.SoundUtils;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
|
||||
@Translator(packet = ClientboundCustomSoundPacket.class)
|
||||
public class JavaCustomSoundTranslator extends PacketTranslator<ClientboundCustomSoundPacket> {
|
||||
@Translator(packet = ClientboundDisguisedChatPacket.class)
|
||||
public class JavaDisguisedChatTranslator extends PacketTranslator<ClientboundDisguisedChatPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundCustomSoundPacket packet) {
|
||||
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
|
||||
playSoundPacket.setSound(SoundUtils.translatePlaySound(packet.getSound()));
|
||||
playSoundPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
playSoundPacket.setVolume(packet.getVolume());
|
||||
playSoundPacket.setPitch(packet.getPitch());
|
||||
|
||||
session.sendUpstreamPacket(playSoundPacket);
|
||||
public void translate(GeyserSession session, ClientboundDisguisedChatPacket packet) {
|
||||
MessageTranslator.handleChatPacket(session, packet.getMessage(), packet.getChatType(), packet.getTargetName(), packet.getName());
|
||||
}
|
||||
}
|
@ -87,6 +87,7 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
||||
session.getWorldCache().removeScoreboard();
|
||||
}
|
||||
session.setWorldName(packet.getWorldName());
|
||||
session.setLevels(packet.getWorldNames());
|
||||
|
||||
BiomeTranslator.loadServerBiomes(session, packet.getRegistry());
|
||||
session.getTagCache().clear();
|
||||
@ -99,16 +100,11 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
||||
if (needsSpawnPacket) {
|
||||
// The player has yet to spawn so let's do that using some of the information in this Java packet
|
||||
session.setDimension(newDimension);
|
||||
session.setDimensionType(dimensions.get(newDimension));
|
||||
ChunkUtils.loadDimension(session);
|
||||
DimensionUtils.setBedrockDimension(session, newDimension);
|
||||
session.connect();
|
||||
|
||||
// It is now safe to send these packets
|
||||
session.getUpstream().sendPostStartGamePackets();
|
||||
} else if (!session.isSpawned()) {
|
||||
// Called for online mode, being presented with a GUI before logging ing
|
||||
session.setDimensionType(dimensions.get(newDimension));
|
||||
ChunkUtils.loadDimension(session);
|
||||
}
|
||||
|
||||
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();
|
||||
@ -151,5 +147,7 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
||||
// If the player is spawning into the "fake" nether, send them some fog
|
||||
session.sendFog("minecraft:fog_hell");
|
||||
}
|
||||
|
||||
ChunkUtils.loadDimension(session);
|
||||
}
|
||||
}
|
||||
|
@ -28,55 +28,17 @@ package org.geysermc.geyser.translator.protocol.java;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerChatPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TranslatableComponent;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.TextDecoration;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Translator(packet = ClientboundPlayerChatPacket.class)
|
||||
public class JavaPlayerChatTranslator extends PacketTranslator<ClientboundPlayerChatPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundPlayerChatPacket packet) {
|
||||
TextPacket textPacket = new TextPacket();
|
||||
textPacket.setPlatformChatId("");
|
||||
textPacket.setSourceName("");
|
||||
textPacket.setXuid(session.getAuthData().xuid());
|
||||
textPacket.setType(TextPacket.Type.CHAT);
|
||||
|
||||
textPacket.setNeedsTranslation(false);
|
||||
Component message = packet.getUnsignedContent() == null ? packet.getMessageDecorated() : packet.getUnsignedContent();
|
||||
|
||||
TextDecoration decoration = session.getChatTypes().get(packet.getChatType());
|
||||
if (decoration != null) {
|
||||
// As of 1.19 - do this to apply all the styling for signed messages
|
||||
// Though, Bedrock cannot care about the signed stuff.
|
||||
TranslatableComponent.Builder withDecoration = Component.translatable()
|
||||
.key(decoration.translationKey())
|
||||
.style(decoration.style());
|
||||
Set<TextDecoration.Parameter> parameters = decoration.parameters();
|
||||
List<Component> args = new ArrayList<>(3);
|
||||
if (parameters.contains(TextDecoration.Parameter.TARGET)) {
|
||||
args.add(packet.getTargetName());
|
||||
}
|
||||
if (parameters.contains(TextDecoration.Parameter.SENDER)) {
|
||||
args.add(packet.getName());
|
||||
}
|
||||
if (parameters.contains(TextDecoration.Parameter.CONTENT)) {
|
||||
args.add(message);
|
||||
}
|
||||
withDecoration.args(args);
|
||||
textPacket.setMessage(MessageTranslator.convertMessage(withDecoration.build(), session.locale()));
|
||||
} else {
|
||||
textPacket.setMessage(MessageTranslator.convertMessage(message, session.locale()));
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(textPacket);
|
||||
Component message = packet.getUnsignedContent() == null ? Component.text(packet.getContent()) : packet.getUnsignedContent();
|
||||
MessageTranslator.handleChatPacket(session, message, packet.getChatType(), packet.getTargetName(), packet.getName());
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.util.ChunkUtils;
|
||||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
|
||||
@Translator(packet = ClientboundRespawnPacket.class)
|
||||
@ -83,16 +84,15 @@ public class JavaRespawnTranslator extends PacketTranslator<ClientboundRespawnPa
|
||||
|
||||
String newDimension = packet.getDimension();
|
||||
if (!session.getDimension().equals(newDimension) || !packet.getWorldName().equals(session.getWorldName())) {
|
||||
// Switching to a new world (based off the world name change); send a fake dimension change
|
||||
if (!packet.getWorldName().equals(session.getWorldName()) && (session.getDimension().equals(newDimension)
|
||||
// Ensure that the player never ever dimension switches to the same dimension - BAD
|
||||
// Can likely be removed if the Above Bedrock Nether Building option can be removed
|
||||
|| DimensionUtils.javaToBedrock(session.getDimension()) == DimensionUtils.javaToBedrock(newDimension))) {
|
||||
// Switching to a new world (based off the world name change or new dimension); send a fake dimension change
|
||||
if (DimensionUtils.javaToBedrock(session.getDimension()) == DimensionUtils.javaToBedrock(newDimension)) {
|
||||
String fakeDim = DimensionUtils.getTemporaryDimension(session.getDimension(), newDimension);
|
||||
DimensionUtils.switchDimension(session, fakeDim);
|
||||
}
|
||||
session.setWorldName(packet.getWorldName());
|
||||
DimensionUtils.switchDimension(session, newDimension);
|
||||
|
||||
ChunkUtils.loadDimension(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,6 @@ public class JavaSoundEntityTranslator extends PacketTranslator<ClientboundSound
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
SoundUtils.playBuiltinSound(session, packet.getSound(), entity.getPosition(), packet.getVolume(), packet.getPitch());
|
||||
SoundUtils.playSound(session, packet.getSound(), entity.getPosition(), packet.getVolume(), packet.getPitch());
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public class JavaPlayerCombatKillTranslator extends PacketTranslator<Clientbound
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundPlayerCombatKillPacket packet) {
|
||||
if (packet.getPlayerId() == session.getPlayerEntity().getEntityId() && GameProtocol.supports1_19_10(session)) {
|
||||
if (packet.getPlayerId() == session.getPlayerEntity().getEntityId()) {
|
||||
Component deathMessage = packet.getMessage();
|
||||
// TODO - could inject score in, but as of 1.19.10 newlines don't center and start at the left of the first text
|
||||
DeathInfoPacket deathInfoPacket = new DeathInfoPacket();
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.translator.protocol.java.entity.player;
|
||||
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerInfoRemovePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.PlayerListPacket;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Translator(packet = ClientboundPlayerInfoRemovePacket.class)
|
||||
public class JavaPlayerInfoRemoveTranslator extends PacketTranslator<ClientboundPlayerInfoRemovePacket> {
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundPlayerInfoRemovePacket packet) {
|
||||
PlayerListPacket translate = new PlayerListPacket();
|
||||
translate.setAction(PlayerListPacket.Action.REMOVE);
|
||||
|
||||
for (UUID id : packet.getProfileIds()) {
|
||||
// As the player entity is no longer present, we can remove the entry
|
||||
PlayerEntity entity = session.getEntityCache().removePlayerEntity(id);
|
||||
if (entity != null) {
|
||||
// Just remove the entity's player list status
|
||||
// Don't despawn the entity - the Java server will also take care of that.
|
||||
entity.setPlayerList(false);
|
||||
}
|
||||
if (entity == session.getPlayerEntity()) {
|
||||
// If removing ourself we use our AuthData UUID
|
||||
translate.getEntries().add(new PlayerListPacket.Entry(session.getAuthData().uuid()));
|
||||
} else {
|
||||
translate.getEntries().add(new PlayerListPacket.Entry(id));
|
||||
}
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(translate);
|
||||
}
|
||||
}
|
@ -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.translator.protocol.java.entity.player;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.protocol.data.game.PlayerListEntry;
|
||||
import com.github.steveice10.mc.protocol.data.game.PlayerListEntryAction;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerInfoPacket;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.skin.SkinManager;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
||||
@Translator(packet = ClientboundPlayerInfoPacket.class)
|
||||
public class JavaPlayerInfoTranslator extends PacketTranslator<ClientboundPlayerInfoPacket> {
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundPlayerInfoPacket packet) {
|
||||
if (packet.getAction() != PlayerListEntryAction.ADD_PLAYER && packet.getAction() != PlayerListEntryAction.REMOVE_PLAYER)
|
||||
return;
|
||||
|
||||
PlayerListPacket translate = new PlayerListPacket();
|
||||
translate.setAction(packet.getAction() == PlayerListEntryAction.ADD_PLAYER ? PlayerListPacket.Action.ADD : PlayerListPacket.Action.REMOVE);
|
||||
|
||||
for (PlayerListEntry entry : packet.getEntries()) {
|
||||
switch (packet.getAction()) {
|
||||
case ADD_PLAYER -> {
|
||||
GameProfile profile = entry.getProfile();
|
||||
PlayerEntity playerEntity;
|
||||
boolean self = profile.getId().equals(session.getPlayerEntity().getUuid());
|
||||
|
||||
if (self) {
|
||||
// Entity is ourself
|
||||
playerEntity = session.getPlayerEntity();
|
||||
} else {
|
||||
playerEntity = session.getEntityCache().getPlayerEntity(profile.getId());
|
||||
}
|
||||
|
||||
GameProfile.Property textures = profile.getProperty("textures");
|
||||
String texturesProperty = textures == null ? null : textures.getValue();
|
||||
|
||||
if (playerEntity == null) {
|
||||
// It's a new player
|
||||
playerEntity = new PlayerEntity(
|
||||
session,
|
||||
-1,
|
||||
session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
profile.getId(),
|
||||
Vector3f.ZERO,
|
||||
Vector3f.ZERO,
|
||||
0, 0, 0,
|
||||
profile.getName(),
|
||||
texturesProperty
|
||||
);
|
||||
|
||||
session.getEntityCache().addPlayerEntity(playerEntity);
|
||||
} else {
|
||||
playerEntity.setUsername(profile.getName());
|
||||
playerEntity.setTexturesProperty(texturesProperty);
|
||||
}
|
||||
|
||||
playerEntity.setPlayerList(true);
|
||||
|
||||
// We'll send our own PlayerListEntry in requestAndHandleSkinAndCape
|
||||
// But we need to send other player's entries so they show up in the player list
|
||||
// without processing their skin information - that'll be processed when they spawn in
|
||||
if (self) {
|
||||
SkinManager.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape ->
|
||||
GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername()));
|
||||
} else {
|
||||
playerEntity.setValid(true);
|
||||
PlayerListPacket.Entry playerListEntry = SkinManager.buildCachedEntry(session, playerEntity);
|
||||
|
||||
translate.getEntries().add(playerListEntry);
|
||||
}
|
||||
}
|
||||
case REMOVE_PLAYER -> {
|
||||
// As the player entity is no longer present, we can remove the entry
|
||||
PlayerEntity entity = session.getEntityCache().removePlayerEntity(entry.getProfile().getId());
|
||||
if (entity != null) {
|
||||
// Just remove the entity's player list status
|
||||
// Don't despawn the entity - the Java server will also take care of that.
|
||||
entity.setPlayerList(false);
|
||||
}
|
||||
if (entity == session.getPlayerEntity()) {
|
||||
// If removing ourself we use our AuthData UUID
|
||||
translate.getEntries().add(new PlayerListPacket.Entry(session.getAuthData().uuid()));
|
||||
} else {
|
||||
translate.getEntries().add(new PlayerListPacket.Entry(entry.getProfile().getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!translate.getEntries().isEmpty()) {
|
||||
session.sendUpstreamPacket(translate);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.translator.protocol.java.entity.player;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.protocol.data.game.PlayerListEntry;
|
||||
import com.github.steveice10.mc.protocol.data.game.PlayerListEntryAction;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerInfoUpdatePacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.PlayerListPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.skin.SkinManager;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
||||
@Translator(packet = ClientboundPlayerInfoUpdatePacket.class)
|
||||
public class JavaPlayerInfoUpdateTranslator extends PacketTranslator<ClientboundPlayerInfoUpdatePacket> {
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundPlayerInfoUpdatePacket packet) {
|
||||
if (!packet.getActions().contains(PlayerListEntryAction.ADD_PLAYER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerListPacket translate = new PlayerListPacket();
|
||||
translate.setAction(PlayerListPacket.Action.ADD);
|
||||
|
||||
for (PlayerListEntry entry : packet.getEntries()) {
|
||||
GameProfile profile = entry.getProfile();
|
||||
PlayerEntity playerEntity;
|
||||
boolean self = profile.getId().equals(session.getPlayerEntity().getUuid());
|
||||
|
||||
if (self) {
|
||||
// Entity is ourself
|
||||
playerEntity = session.getPlayerEntity();
|
||||
} else {
|
||||
playerEntity = session.getEntityCache().getPlayerEntity(profile.getId());
|
||||
}
|
||||
|
||||
GameProfile.Property textures = profile.getProperty("textures");
|
||||
String texturesProperty = textures == null ? null : textures.getValue();
|
||||
|
||||
if (playerEntity == null) {
|
||||
// It's a new player
|
||||
playerEntity = new PlayerEntity(
|
||||
session,
|
||||
-1,
|
||||
session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
profile.getId(),
|
||||
Vector3f.ZERO,
|
||||
Vector3f.ZERO,
|
||||
0, 0, 0,
|
||||
profile.getName(),
|
||||
texturesProperty
|
||||
);
|
||||
|
||||
session.getEntityCache().addPlayerEntity(playerEntity);
|
||||
} else {
|
||||
playerEntity.setUsername(profile.getName());
|
||||
playerEntity.setTexturesProperty(texturesProperty);
|
||||
}
|
||||
|
||||
playerEntity.setPlayerList(true);
|
||||
|
||||
// We'll send our own PlayerListEntry in requestAndHandleSkinAndCape
|
||||
// But we need to send other player's entries so they show up in the player list
|
||||
// without processing their skin information - that'll be processed when they spawn in
|
||||
if (self) {
|
||||
SkinManager.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape ->
|
||||
GeyserImpl.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data for " + session.getClientData().getUsername()));
|
||||
} else {
|
||||
playerEntity.setValid(true);
|
||||
PlayerListPacket.Entry playerListEntry = SkinManager.buildCachedEntry(session, playerEntity);
|
||||
|
||||
translate.getEntries().add(playerListEntry);
|
||||
}
|
||||
}
|
||||
|
||||
if (!translate.getEntries().isEmpty()) {
|
||||
session.sendUpstreamPacket(translate);
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.translator.inventory.PlayerInventoryTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
@ -46,7 +47,7 @@ public class JavaContainerSetContentTranslator extends PacketTranslator<Clientbo
|
||||
|
||||
int inventorySize = inventory.getSize();
|
||||
for (int i = 0; i < packet.getItems().length; i++) {
|
||||
if (i > inventorySize) {
|
||||
if (i >= inventorySize) {
|
||||
GeyserImpl geyser = session.getGeyser();
|
||||
geyser.getLogger().warning("ClientboundContainerSetContentPacket sent to " + session.bedrockUsername()
|
||||
+ " that exceeds inventory size!");
|
||||
@ -54,10 +55,7 @@ public class JavaContainerSetContentTranslator extends PacketTranslator<Clientbo
|
||||
geyser.getLogger().debug(packet);
|
||||
geyser.getLogger().debug(inventory);
|
||||
}
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null) {
|
||||
translator.updateInventory(session, inventory);
|
||||
}
|
||||
updateInventory(session, inventory, packet.getContainerId());
|
||||
// 1.18.1 behavior: the previous items will be correctly set, but the state ID and carried item will not
|
||||
// as this produces a stack trace on the client.
|
||||
// If Java processes this correctly in the future, we can revert this behavior
|
||||
@ -68,10 +66,7 @@ public class JavaContainerSetContentTranslator extends PacketTranslator<Clientbo
|
||||
inventory.setItem(i, newItem, session);
|
||||
}
|
||||
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null) {
|
||||
translator.updateInventory(session, inventory);
|
||||
}
|
||||
updateInventory(session, inventory, packet.getContainerId());
|
||||
|
||||
int stateId = packet.getStateId();
|
||||
session.setEmulatePost1_16Logic(stateId > 0 || stateId != inventory.getStateId());
|
||||
@ -80,4 +75,14 @@ public class JavaContainerSetContentTranslator extends PacketTranslator<Clientbo
|
||||
session.getPlayerInventory().setCursor(GeyserItemStack.from(packet.getCarriedItem()), session);
|
||||
InventoryUtils.updateCursor(session);
|
||||
}
|
||||
|
||||
private void updateInventory(GeyserSession session, Inventory inventory, int containerId) {
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (containerId == 0 && !(translator instanceof PlayerInventoryTranslator)) {
|
||||
// In rare cases, the window ID can still be 0 but Java treats it as valid
|
||||
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateInventory(session, inventory);
|
||||
} else if (translator != null) {
|
||||
translator.updateInventory(session, inventory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,9 +49,9 @@ public class JavaExplodeTranslator extends PacketTranslator<ClientboundExplodePa
|
||||
LevelEventGenericPacket levelEventPacket = new LevelEventGenericPacket();
|
||||
levelEventPacket.setEventId(2026/*LevelEvent.PARTICLE_BLOCK_EXPLOSION*/);
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
builder.putFloat("originX", packet.getX());
|
||||
builder.putFloat("originY", packet.getY());
|
||||
builder.putFloat("originZ", packet.getZ());
|
||||
builder.putFloat("originX", (float) packet.getX());
|
||||
builder.putFloat("originY", (float) packet.getY());
|
||||
builder.putFloat("originZ", (float) packet.getZ());
|
||||
builder.putFloat("radius", packet.getRadius());
|
||||
builder.putInt("size", packet.getExploded().size());
|
||||
int i = 0;
|
||||
|
@ -51,6 +51,8 @@ public class JavaMapItemDataTranslator extends PacketTranslator<ClientboundMapIt
|
||||
mapItemDataPacket.setLocked(packet.isLocked());
|
||||
mapItemDataPacket.setOrigin(Vector3i.ZERO); // Required since 1.19.20
|
||||
mapItemDataPacket.setScale(packet.getScale());
|
||||
// Required as of 1.19.50
|
||||
mapItemDataPacket.getTrackedEntityIds().add(packet.getMapId());
|
||||
|
||||
MapData data = packet.getData();
|
||||
if (data != null) {
|
||||
|
@ -38,6 +38,6 @@ public class JavaSoundTranslator extends PacketTranslator<ClientboundSoundPacket
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundSoundPacket packet) {
|
||||
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
|
||||
SoundUtils.playBuiltinSound(session, packet.getSound(), position, packet.getVolume(), packet.getPitch());
|
||||
SoundUtils.playSound(session, packet.getSound(), position, packet.getVolume(), packet.getPitch());
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ package org.geysermc.geyser.translator.text;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
|
||||
import com.nukkitx.protocol.bedrock.packet.TextPacket;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TranslatableComponent;
|
||||
import net.kyori.adventure.text.renderer.TranslatableComponentRenderer;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
@ -36,8 +38,7 @@ import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.*;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class MessageTranslator {
|
||||
// These are used for handling the translations of the messages
|
||||
@ -201,6 +202,28 @@ public class MessageTranslator {
|
||||
return GSON_SERIALIZER.serialize(component);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert legacy format message to plain text
|
||||
*
|
||||
* @param message Message to convert
|
||||
* @return The plain text of the message
|
||||
*/
|
||||
public static String convertToPlainText(String message) {
|
||||
char[] input = message.toCharArray();
|
||||
char[] output = new char[input.length];
|
||||
int outputSize = 0;
|
||||
for (int i = 0, inputLength = input.length; i < inputLength; i++) {
|
||||
char c = input[i];
|
||||
if (c == ChatColor.ESCAPE) {
|
||||
i++;
|
||||
} else {
|
||||
output[outputSize++] = c;
|
||||
}
|
||||
}
|
||||
return new String(output, 0, outputSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert JSON and legacy format message to plain text
|
||||
*
|
||||
@ -228,6 +251,46 @@ public class MessageTranslator {
|
||||
return PlainTextComponentSerializer.plainText().serialize(messageComponent);
|
||||
}
|
||||
|
||||
public static void handleChatPacket(GeyserSession session, Component message, int chatType, Component targetName, Component sender) {
|
||||
TextPacket textPacket = new TextPacket();
|
||||
textPacket.setPlatformChatId("");
|
||||
textPacket.setSourceName("");
|
||||
textPacket.setXuid(session.getAuthData().xuid());
|
||||
textPacket.setType(TextPacket.Type.CHAT);
|
||||
|
||||
textPacket.setNeedsTranslation(false);
|
||||
|
||||
TextDecoration decoration = session.getChatTypes().get(chatType);
|
||||
if (decoration != null) {
|
||||
// As of 1.19 - do this to apply all the styling for signed messages
|
||||
// Though, Bedrock cannot care about the signed stuff.
|
||||
TranslatableComponent.Builder withDecoration = Component.translatable()
|
||||
.key(decoration.translationKey())
|
||||
.style(decoration.style());
|
||||
Set<TextDecoration.Parameter> parameters = decoration.parameters();
|
||||
List<Component> args = new ArrayList<>(3);
|
||||
if (parameters.contains(TextDecoration.Parameter.TARGET)) {
|
||||
args.add(targetName);
|
||||
}
|
||||
if (parameters.contains(TextDecoration.Parameter.SENDER)) {
|
||||
args.add(sender);
|
||||
}
|
||||
if (parameters.contains(TextDecoration.Parameter.CONTENT)) {
|
||||
args.add(message);
|
||||
}
|
||||
withDecoration.args(args);
|
||||
textPacket.setMessage(MessageTranslator.convertMessage(withDecoration.build(), session.locale()));
|
||||
} else {
|
||||
session.getGeyser().getLogger().debug("Likely illegal chat type detection found.");
|
||||
if (session.getGeyser().getConfig().isDebugMode()) {
|
||||
Thread.dumpStack();
|
||||
}
|
||||
textPacket.setMessage(MessageTranslator.convertMessage(message, session.locale()));
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(textPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a team color to a chat color
|
||||
*
|
||||
|
@ -218,7 +218,8 @@ public class ChunkUtils {
|
||||
* This must be done after the player has switched dimensions so we know what their dimension is
|
||||
*/
|
||||
public static void loadDimension(GeyserSession session) {
|
||||
JavaDimension dimension = session.getDimensionType();
|
||||
JavaDimension dimension = session.getDimensions().get(session.getDimension());
|
||||
session.setDimensionType(dimension);
|
||||
int minY = dimension.minY();
|
||||
int maxY = dimension.maxY();
|
||||
|
||||
@ -229,13 +230,7 @@ public class ChunkUtils {
|
||||
throw new RuntimeException("Maximum Y must be a multiple of 16!");
|
||||
}
|
||||
|
||||
BedrockDimension bedrockDimension = switch (session.getDimension()) {
|
||||
case DimensionUtils.THE_END -> BedrockDimension.THE_END;
|
||||
case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER;
|
||||
default -> BedrockDimension.OVERWORLD;
|
||||
};
|
||||
session.getChunkCache().setBedrockDimension(bedrockDimension);
|
||||
|
||||
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
|
||||
// Yell in the console if the world height is too height in the current scenario
|
||||
// The constraints change depending on if the player is in the overworld or not, and if experimental height is enabled
|
||||
// (Ignore this for the Nether. We can't change that at the moment without the workaround. :/ )
|
||||
|
@ -32,6 +32,8 @@ import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MobEffectPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.StopSoundPacket;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.Set;
|
||||
@ -93,9 +95,10 @@ public class DimensionUtils {
|
||||
changeDimensionPacket.setRespawn(true);
|
||||
changeDimensionPacket.setPosition(pos);
|
||||
session.sendUpstreamPacket(changeDimensionPacket);
|
||||
|
||||
session.setDimension(javaDimension);
|
||||
session.setDimensionType(session.getDimensions().get(javaDimension));
|
||||
ChunkUtils.loadDimension(session);
|
||||
setBedrockDimension(session, javaDimension);
|
||||
|
||||
player.setPosition(pos);
|
||||
session.setSpawned(false);
|
||||
session.setLastChunkPosition(null);
|
||||
@ -117,6 +120,19 @@ public class DimensionUtils {
|
||||
stopSoundPacket.setSoundName("");
|
||||
session.sendUpstreamPacket(stopSoundPacket);
|
||||
|
||||
// Kind of silly but Bedrock 1.19.50 requires an acknowledgement after the
|
||||
// initial chunks are sent, prior to the client acknowledgement
|
||||
if (GameProtocol.supports1_19_50(session)) {
|
||||
// Note: send this before chunks are sent. Fixed https://github.com/GeyserMC/Geyser/issues/3421
|
||||
PlayerActionPacket ackPacket = new PlayerActionPacket();
|
||||
ackPacket.setRuntimeEntityId(player.getGeyserId());
|
||||
ackPacket.setAction(PlayerActionType.DIMENSION_CHANGE_SUCCESS);
|
||||
ackPacket.setBlockPosition(Vector3i.ZERO);
|
||||
ackPacket.setResultPosition(Vector3i.ZERO);
|
||||
ackPacket.setFace(0);
|
||||
session.sendUpstreamPacket(ackPacket);
|
||||
}
|
||||
|
||||
// TODO - fix this hack of a fix by sending the final dimension switching logic after sections have been sent.
|
||||
// The client wants sections sent to it before it can successfully respawn.
|
||||
ChunkUtils.sendEmptyChunks(session, player.getPosition().toInt(), 3, true);
|
||||
@ -133,6 +149,24 @@ public class DimensionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setBedrockDimension(GeyserSession session, String javaDimension) {
|
||||
session.getChunkCache().setBedrockDimension(switch (javaDimension) {
|
||||
case DimensionUtils.THE_END -> BedrockDimension.THE_END;
|
||||
case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER;
|
||||
default -> BedrockDimension.OVERWORLD;
|
||||
});
|
||||
}
|
||||
|
||||
public static int javaToBedrock(BedrockDimension dimension) {
|
||||
if (dimension == BedrockDimension.THE_NETHER) {
|
||||
return BEDROCK_NETHER_ID;
|
||||
} else if (dimension == BedrockDimension.THE_END) {
|
||||
return 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the Java edition dimension IDs to Bedrock edition
|
||||
*
|
||||
@ -171,7 +205,9 @@ public class DimensionUtils {
|
||||
// Prevents rare instances of Bedrock locking up
|
||||
return javaToBedrock(newDimension) == 2 ? OVERWORLD : NETHER;
|
||||
}
|
||||
return currentDimension.equals(OVERWORLD) ? NETHER : OVERWORLD;
|
||||
// Check current Bedrock dimension and not just the Java dimension.
|
||||
// Fixes rare instances like https://github.com/GeyserMC/Geyser/issues/3161
|
||||
return javaToBedrock(currentDimension) == 0 ? NETHER : OVERWORLD;
|
||||
}
|
||||
|
||||
public static boolean isCustomBedrockNetherId() {
|
||||
|
@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundPickItemPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.nbt.NbtMap;
|
||||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import com.nukkitx.nbt.NbtType;
|
||||
@ -46,6 +47,7 @@ import org.geysermc.geyser.inventory.click.Click;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
@ -85,8 +87,7 @@ public class InventoryUtils {
|
||||
|
||||
public static void displayInventory(GeyserSession session, Inventory inventory) {
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null) {
|
||||
translator.prepareInventory(session, inventory);
|
||||
if (translator != null && translator.prepareInventory(session, inventory)) {
|
||||
if (translator instanceof DoubleChestInventoryTranslator && !((Container) inventory).isUsingRealBlock()) {
|
||||
session.scheduleInEventLoop(() -> {
|
||||
Inventory openInv = session.getOpenInventory();
|
||||
@ -103,7 +104,6 @@ public class InventoryUtils {
|
||||
translator.updateInventory(session, inventory);
|
||||
}
|
||||
} else {
|
||||
// Precaution - as of 1.16 every inventory should be translated so this shouldn't happen
|
||||
session.setOpenInventory(null);
|
||||
}
|
||||
}
|
||||
@ -136,6 +136,28 @@ public class InventoryUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a usable block space in the world to place a fake inventory block, and returns the position.
|
||||
*/
|
||||
@Nullable
|
||||
public static Vector3i findAvailableWorldSpace(GeyserSession session) {
|
||||
// Check if a fake block can be placed, either above the player or beneath.
|
||||
BedrockDimension dimension = session.getChunkCache().getBedrockDimension();
|
||||
int minY = dimension.minY(), maxY = minY + dimension.height();
|
||||
Vector3i flatPlayerPosition = session.getPlayerEntity().getPosition().toInt();
|
||||
Vector3i position = flatPlayerPosition.add(Vector3i.UP);
|
||||
if (position.getY() < minY) {
|
||||
return null;
|
||||
}
|
||||
if (position.getY() >= maxY) {
|
||||
position = flatPlayerPosition.sub(0, 4, 0);
|
||||
if (position.getY() >= maxY) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
public static void updateCursor(GeyserSession session) {
|
||||
InventorySlotPacket cursorPacket = new InventorySlotPacket();
|
||||
cursorPacket.setContainerId(ContainerId.UI);
|
||||
@ -150,18 +172,6 @@ public class InventoryUtils {
|
||||
return item1.getJavaId() == item2.getJavaId() && Objects.equals(item1.getNbt(), item2.getNbt());
|
||||
}
|
||||
|
||||
public static boolean canStack(ItemStack item1, ItemStack item2) {
|
||||
if (item1 == null || item2 == null)
|
||||
return false;
|
||||
return item1.getId() == item2.getId() && Objects.equals(item1.getNbt(), item2.getNbt());
|
||||
}
|
||||
|
||||
public static boolean canStack(ItemData item1, ItemData item2) {
|
||||
if (item1 == null || item2 == null)
|
||||
return false;
|
||||
return item1.equals(item2, false, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if an item stack represents air or has no count.
|
||||
*/
|
||||
@ -186,11 +196,22 @@ public class InventoryUtils {
|
||||
|
||||
root.put("display", display.build());
|
||||
return protocolVersion -> ItemData.builder()
|
||||
.definition(Registries.ITEMS.forVersion(protocolVersion).getStoredItems().barrier().getBedrockDefinition())
|
||||
.definition(getUnusableSpaceBlockID(protocolVersion))
|
||||
.count(1)
|
||||
.tag(root.build()).build();
|
||||
}
|
||||
|
||||
private static int getUnusableSpaceBlockID(int protocolVersion) {
|
||||
String unusableSpaceBlock = GeyserImpl.getInstance().getConfig().getUnusableSpaceBlock();
|
||||
ItemMapping unusableSpaceBlockID = Registries.ITEMS.forVersion(protocolVersion).getMapping(unusableSpaceBlock);
|
||||
if (unusableSpaceBlockID != null) {
|
||||
return unusableSpaceBlockID.getBedrockId();
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().error("Invalid value" + unusableSpaceBlock + ". Resorting to barrier block.");
|
||||
return Registries.ITEMS.forVersion(protocolVersion).getStoredItems().barrier().getBedrockId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #findOrCreateItem(GeyserSession, String)}. This is for finding a specified {@link ItemStack}.
|
||||
*
|
||||
|
@ -100,11 +100,7 @@ public class SettingsUtils {
|
||||
.translator(MinecraftLocale::getLocaleString); // we need translate gamerules next
|
||||
|
||||
WorldManager worldManager = GeyserImpl.getInstance().getWorldManager();
|
||||
for (GameRule gamerule : GameRule.values()) {
|
||||
if (gamerule.equals(GameRule.UNKNOWN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (GameRule gamerule : GameRule.VALUES) {
|
||||
// Add the relevant form item based on the gamerule type
|
||||
if (Boolean.class.equals(gamerule.getType())) {
|
||||
builder.toggle("gamerule." + gamerule.getJavaID(), worldManager.getGameRuleBool(session, gamerule));
|
||||
@ -146,10 +142,6 @@ public class SettingsUtils {
|
||||
|
||||
if (showGamerules) {
|
||||
for (GameRule gamerule : GameRule.VALUES) {
|
||||
if (gamerule.equals(GameRule.UNKNOWN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Boolean.class.equals(gamerule.getType())) {
|
||||
boolean value = response.next();
|
||||
if (value != session.getGeyser().getWorldManager().getGameRuleBool(session, gamerule)) {
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
package org.geysermc.geyser.util;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.level.sound.BuiltinSound;
|
||||
import com.github.steveice10.mc.protocol.data.game.level.sound.CustomSound;
|
||||
import com.github.steveice10.mc.protocol.data.game.level.sound.Sound;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
@ -63,30 +61,20 @@ public final class SoundUtils {
|
||||
/**
|
||||
* Translates a Java Custom or Builtin Sound to its Bedrock equivalent
|
||||
*
|
||||
* @param sound the sound to translate
|
||||
* @param javaIdentifier the sound to translate
|
||||
* @return a Bedrock sound
|
||||
*/
|
||||
public static String translatePlaySound(Sound sound) {
|
||||
String packetSound;
|
||||
if (sound instanceof BuiltinSound builtinSound) {
|
||||
packetSound = builtinSound.getName();
|
||||
} else if (sound instanceof CustomSound customSound) {
|
||||
packetSound = customSound.getName();
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().debug("Unknown sound, we were unable to map this. " + sound);
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String translatePlaySound(String javaIdentifier) {
|
||||
// Drop the Minecraft namespace if applicable
|
||||
if (packetSound.startsWith("minecraft:")) {
|
||||
packetSound = packetSound.substring("minecraft:".length());
|
||||
if (javaIdentifier.startsWith("minecraft:")) {
|
||||
javaIdentifier = javaIdentifier.substring("minecraft:".length());
|
||||
}
|
||||
|
||||
SoundMapping soundMapping = Registries.SOUNDS.get(packetSound);
|
||||
SoundMapping soundMapping = Registries.SOUNDS.get(javaIdentifier);
|
||||
if (soundMapping == null || soundMapping.getPlaysound() == null) {
|
||||
// no mapping
|
||||
GeyserImpl.getInstance().getLogger().debug("[PlaySound] Defaulting to sound server gave us for " + sound);
|
||||
return packetSound;
|
||||
GeyserImpl.getInstance().getLogger().debug("[PlaySound] Defaulting to sound server gave us for " + javaIdentifier);
|
||||
return javaIdentifier;
|
||||
}
|
||||
return soundMapping.getPlaysound();
|
||||
}
|
||||
@ -99,7 +87,7 @@ public final class SoundUtils {
|
||||
* @param position the position
|
||||
* @param pitch the pitch
|
||||
*/
|
||||
public static void playBuiltinSound(GeyserSession session, BuiltinSound javaSound, Vector3f position, float volume, float pitch) {
|
||||
public static void playSound(GeyserSession session, Sound javaSound, Vector3f position, float volume, float pitch) {
|
||||
String packetSound = javaSound.getName();
|
||||
|
||||
SoundMapping soundMapping = Registries.SOUNDS.get(packetSound);
|
||||
|
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
BIN
core/src/main/resources/bedrock/block_palette.1_19_50.nbt
Normale Datei
BIN
core/src/main/resources/bedrock/block_palette.1_19_50.nbt
Normale Datei
Binäre Datei nicht angezeigt.
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Binäre Datei nicht angezeigt.
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -9,7 +9,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:acacia_chest_boat",
|
||||
"id" : 642
|
||||
"id" : 645
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:acacia_door",
|
||||
@ -19,6 +19,10 @@
|
||||
"name" : "minecraft:acacia_fence_gate",
|
||||
"id" : 187
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:acacia_hanging_sign",
|
||||
"id" : -504
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:acacia_pressure_plate",
|
||||
"id" : -150
|
||||
@ -131,17 +135,97 @@
|
||||
"name" : "minecraft:bamboo",
|
||||
"id" : -163
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_button",
|
||||
"id" : -511
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_chest_raft",
|
||||
"id" : 648
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_door",
|
||||
"id" : -517
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_double_slab",
|
||||
"id" : -521
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_fence",
|
||||
"id" : -515
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_fence_gate",
|
||||
"id" : -516
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_hanging_sign",
|
||||
"id" : -522
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_mosaic",
|
||||
"id" : -509
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_mosaic_double_slab",
|
||||
"id" : -525
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_mosaic_slab",
|
||||
"id" : -524
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_mosaic_stairs",
|
||||
"id" : -523
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_planks",
|
||||
"id" : -510
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_pressure_plate",
|
||||
"id" : -514
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_raft",
|
||||
"id" : 638
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_sapling",
|
||||
"id" : -164
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_sign",
|
||||
"id" : 637
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_slab",
|
||||
"id" : -513
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_stairs",
|
||||
"id" : -512
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_standing_sign",
|
||||
"id" : -518
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_trapdoor",
|
||||
"id" : -520
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bamboo_wall_sign",
|
||||
"id" : -519
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:banner",
|
||||
"id" : 567
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:banner_pattern",
|
||||
"id" : 651
|
||||
"id" : 655
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:barrel",
|
||||
@ -217,7 +301,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:birch_chest_boat",
|
||||
"id" : 639
|
||||
"id" : 642
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:birch_door",
|
||||
@ -227,6 +311,10 @@
|
||||
"name" : "minecraft:birch_fence_gate",
|
||||
"id" : 184
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:birch_hanging_sign",
|
||||
"id" : -502
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:birch_pressure_plate",
|
||||
"id" : -151
|
||||
@ -329,7 +417,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:boat",
|
||||
"id" : 649
|
||||
"id" : 653
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:bone",
|
||||
@ -435,6 +523,10 @@
|
||||
"name" : "minecraft:calcite",
|
||||
"id" : -326
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:camel_spawn_egg",
|
||||
"id" : 633
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:camera",
|
||||
"id" : 593
|
||||
@ -541,7 +633,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:chest_boat",
|
||||
"id" : 645
|
||||
"id" : 649
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:chest_minecart",
|
||||
@ -555,6 +647,10 @@
|
||||
"name" : "minecraft:chicken_spawn_egg",
|
||||
"id" : 435
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:chiseled_bookshelf",
|
||||
"id" : -526
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:chiseled_deepslate",
|
||||
"id" : -395
|
||||
@ -827,6 +923,10 @@
|
||||
"name" : "minecraft:crimson_fungus",
|
||||
"id" : -228
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:crimson_hanging_sign",
|
||||
"id" : -506
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:crimson_hyphae",
|
||||
"id" : -299
|
||||
@ -921,7 +1021,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:dark_oak_chest_boat",
|
||||
"id" : 643
|
||||
"id" : 646
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:dark_oak_door",
|
||||
@ -931,6 +1031,10 @@
|
||||
"name" : "minecraft:dark_oak_fence_gate",
|
||||
"id" : 186
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:dark_oak_hanging_sign",
|
||||
"id" : -505
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:dark_oak_pressure_plate",
|
||||
"id" : -152
|
||||
@ -1121,7 +1225,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:disc_fragment_5",
|
||||
"id" : 637
|
||||
"id" : 640
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:dispenser",
|
||||
@ -1193,11 +1297,11 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:dye",
|
||||
"id" : 650
|
||||
"id" : 654
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:echo_shard",
|
||||
"id" : 647
|
||||
"id" : 651
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:egg",
|
||||
@ -1725,7 +1829,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:end_crystal",
|
||||
"id" : 653
|
||||
"id" : 657
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:end_gateway",
|
||||
@ -1933,7 +2037,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:glow_berries",
|
||||
"id" : 654
|
||||
"id" : 658
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:glow_frame",
|
||||
@ -2405,7 +2509,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:jungle_chest_boat",
|
||||
"id" : 640
|
||||
"id" : 643
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:jungle_door",
|
||||
@ -2415,6 +2519,10 @@
|
||||
"name" : "minecraft:jungle_fence_gate",
|
||||
"id" : 185
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:jungle_hanging_sign",
|
||||
"id" : -503
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:jungle_pressure_plate",
|
||||
"id" : -153
|
||||
@ -2665,7 +2773,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:mangrove_boat",
|
||||
"id" : 635
|
||||
"id" : 636
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:mangrove_button",
|
||||
@ -2673,11 +2781,11 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:mangrove_chest_boat",
|
||||
"id" : 644
|
||||
"id" : 647
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:mangrove_door",
|
||||
"id" : 633
|
||||
"id" : 634
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:mangrove_double_slab",
|
||||
@ -2691,6 +2799,10 @@
|
||||
"name" : "minecraft:mangrove_fence_gate",
|
||||
"id" : -492
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:mangrove_hanging_sign",
|
||||
"id" : -508
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:mangrove_leaves",
|
||||
"id" : -472
|
||||
@ -2717,7 +2829,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:mangrove_sign",
|
||||
"id" : 634
|
||||
"id" : 635
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:mangrove_slab",
|
||||
@ -2861,7 +2973,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:music_disc_5",
|
||||
"id" : 636
|
||||
"id" : 639
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:music_disc_blocks",
|
||||
@ -3037,7 +3149,11 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:oak_chest_boat",
|
||||
"id" : 638
|
||||
"id" : 641
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:oak_hanging_sign",
|
||||
"id" : -500
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:oak_sign",
|
||||
@ -3473,7 +3589,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:recovery_compass",
|
||||
"id" : 646
|
||||
"id" : 650
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:red_candle",
|
||||
@ -3781,7 +3897,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:spawn_egg",
|
||||
"id" : 652
|
||||
"id" : 656
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:spider_eye",
|
||||
@ -3813,7 +3929,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:spruce_chest_boat",
|
||||
"id" : 641
|
||||
"id" : 644
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:spruce_door",
|
||||
@ -3823,6 +3939,10 @@
|
||||
"name" : "minecraft:spruce_fence_gate",
|
||||
"id" : 183
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:spruce_hanging_sign",
|
||||
"id" : -501
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:spruce_pressure_plate",
|
||||
"id" : -154
|
||||
@ -4081,7 +4201,7 @@
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:trader_llama_spawn_egg",
|
||||
"id" : 648
|
||||
"id" : 652
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:trapdoor",
|
||||
@ -4223,6 +4343,10 @@
|
||||
"name" : "minecraft:warped_fungus_on_a_stick",
|
||||
"id" : 618
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:warped_hanging_sign",
|
||||
"id" : -507
|
||||
},
|
||||
{
|
||||
"name" : "minecraft:warped_hyphae",
|
||||
"id" : -298
|
@ -183,6 +183,10 @@ log-player-ip-addresses: true
|
||||
# auto-update.
|
||||
notify-on-new-bedrock-update: true
|
||||
|
||||
# Which item to use to mark unavailable slots in a Bedrock player inventory. Examples of this are the 2x2 crafting grid while in creative,
|
||||
# or custom inventory menus with sizes different from the usual 3x9. A barrier block is the default item.
|
||||
unusable-space-block: minecraft:barrier
|
||||
|
||||
# bStats is a stat tracker that is entirely anonymous and tracks only basic information
|
||||
# about Geyser, such as how many people are online, how many servers are using Geyser,
|
||||
# what OS is being used, etc. You can learn more about bStats here: https://bstats.org/.
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit f1c9c2fbba0e102dc4f8c96dd9485f7ec9768174
|
||||
Subproject commit f9d62b3f73db270bd4e0c833b7728b30d29e1369
|
@ -85,6 +85,7 @@ public class MessageTranslatorTest {
|
||||
@Test
|
||||
public void convertToPlainText() {
|
||||
Assert.assertEquals("JSON message is not handled properly", "Many colors here", MessageTranslator.convertToPlainText("{\"extra\":[{\"color\":\"red\",\"text\":\"M\"},{\"color\":\"gold\",\"text\":\"a\"},{\"color\":\"yellow\",\"text\":\"n\"},{\"color\":\"green\",\"text\":\"y \"},{\"color\":\"aqua\",\"text\":\"c\"},{\"color\":\"dark_purple\",\"text\":\"o\"},{\"color\":\"red\",\"text\":\"l\"},{\"color\":\"gold\",\"text\":\"o\"},{\"color\":\"yellow\",\"text\":\"r\"},{\"color\":\"green\",\"text\":\"s \"},{\"color\":\"aqua\",\"text\":\"h\"},{\"color\":\"dark_purple\",\"text\":\"e\"},{\"color\":\"red\",\"text\":\"r\"},{\"color\":\"gold\",\"text\":\"e\"}],\"text\":\"\"}", "en_US"));
|
||||
Assert.assertEquals("Legacy formatted message is not handled properly (Colors)", "Many colors here", MessageTranslator.convertToPlainText("§cM§6a§en§ay §bc§5o§cl§6o§er§as §bh§5e§cr§6e"));
|
||||
Assert.assertEquals("Legacy formatted message is not handled properly (Colors)", "Many colors here", MessageTranslator.convertToPlainText("§cM§6a§en§ay §bc§5o§cl§6o§er§as §bh§5e§cr§6e", "en_US"));
|
||||
Assert.assertEquals("Legacy formatted message is not handled properly (Style)", "Obf Bold Strikethrough Underline Italic Reset", MessageTranslator.convertToPlainText("§kObf §lBold §mStrikethrough §nUnderline §oItalic §rReset", "en_US"));
|
||||
Assert.assertEquals("Valid lenient JSON is not handled properly", "Strange", MessageTranslator.convertToPlainText("§rStrange", "en_US"));
|
||||
|
@ -1,14 +1,14 @@
|
||||
[versions]
|
||||
jackson = "2.13.4"
|
||||
jackson = "2.14.0"
|
||||
fastutil = "8.5.2"
|
||||
netty = "4.1.80.Final"
|
||||
guava = "29.0-jre"
|
||||
gson = "2.3.1" # Provided by Spigot 1.8.8
|
||||
websocket = "1.5.1"
|
||||
protocol = "3.0.0.Beta2-SNAPSHOT"
|
||||
protocol = "3.0.0.Beta1-SNAPSHOT"
|
||||
raknet = "0.0.1.Final-SNAPSHOT"
|
||||
mcauthlib = "d9d773e"
|
||||
mcprotocollib = "1.19.2-SNAPSHOT"
|
||||
mcprotocollib = "1.19.3-SNAPSHOT"
|
||||
packetlib = "3.0"
|
||||
adventure = "4.12.0-20220629.025215-9"
|
||||
adventure-platform = "4.1.2"
|
||||
@ -21,7 +21,7 @@ jline = "3.21.0"
|
||||
terminalconsoleappender = "1.2.0"
|
||||
paper = "1.19-R0.1-SNAPSHOT"
|
||||
viaversion = "4.0.0"
|
||||
adapters = "1.5-SNAPSHOT"
|
||||
adapters = "1.6-SNAPSHOT"
|
||||
commodore = "2.2"
|
||||
bungeecord = "a7c6ede"
|
||||
velocity = "3.0.0"
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren