mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-03-21 06:28:53 +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
This commit is contained in:
commit
84248fa902
85 changed files with 1947 additions and 11441 deletions
README.md
bootstrap
bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord
fabric
build.gradle.kts
src/main/java/org/geysermc/geyser/platform/fabric
spigot/src/main/java/org/geysermc/geyser/platform/spigot
GeyserSpigotDumpInfo.java
world/manager
sponge/src/main/java/org/geysermc/geyser/platform/sponge
velocity/src/main/java/org/geysermc/geyser/platform/velocity
build-logic
core/src
main
java/org/geysermc/geyser
configuration
dump
entity
inventory
level
network
registry
Registries.java
populator
BlockRegistryPopulator.javaCustomItemRegistryPopulator.javaItemRegistryPopulator.javaRecipeRegistryPopulator.java
type
session
text
translator
inventory
AbstractBlockInventoryTranslator.javaAnvilInventoryTranslator.javaInventoryTranslator.javaLecternInventoryTranslator.javaMerchantInventoryTranslator.javaPlayerInventoryTranslator.java
chest
horse
level/block/entity
protocol
bedrock
BedrockBlockEntityDataTranslator.javaBedrockCommandRequestTranslator.javaBedrockInventoryTransactionTranslator.javaBedrockTextTranslator.java
entity/player
java
text
util
resources
test/java/org/geysermc/geyser/network/translators/chat
gradle
|
@ -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,11 +16,11 @@ 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> {
|
||||
kotlinOptions {
|
||||
jvmTarget = "16"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Binary file not shown.
Binary file not shown.
BIN
core/src/main/resources/bedrock/block_palette.1_19_50.nbt
Normal file
BIN
core/src/main/resources/bedrock/block_palette.1_19_50.nbt
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue