forked from GeyserMC/Geyser
Merge branch 'master' of https://github.com/GeyserMC/Geyser into feature/addons
This commit is contained in:
commit
269e72c943
194 changed files with 5867 additions and 1975 deletions
|
@ -25,17 +25,19 @@
|
|||
|
||||
package org.geysermc.connector;
|
||||
|
||||
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FloodgateKeyLoader {
|
||||
public static Path getKey(GeyserLogger logger, GeyserConfiguration config, Path floodgateKey, Object floodgate, Path floodgateFolder) {
|
||||
public static Path getKeyPath(GeyserJacksonConfiguration config, Object floodgate, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) {
|
||||
Path floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile());
|
||||
|
||||
if (!Files.exists(floodgateKey) && config.getRemote().getAuthType().equals("floodgate")) {
|
||||
if (floodgate != null) {
|
||||
Path autoKey = floodgateFolder.resolve("public-key.pem");
|
||||
Path autoKey = floodgateDataFolder.resolve("public-key.pem");
|
||||
if (Files.exists(autoKey)) {
|
||||
logger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.auto_loaded"));
|
||||
floodgateKey = autoKey;
|
||||
|
|
|
@ -25,12 +25,11 @@
|
|||
|
||||
package org.geysermc.connector;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.nukkitx.network.raknet.RakNetConstants;
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||
import com.nukkitx.protocol.bedrock.BedrockServer;
|
||||
import com.nukkitx.protocol.bedrock.v407.Bedrock_v407;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
|
@ -50,22 +49,27 @@ import org.geysermc.connector.network.translators.effect.EffectRegistry;
|
|||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.network.translators.item.PotionMixRegistry;
|
||||
import org.geysermc.connector.network.translators.item.RecipeRegistry;
|
||||
import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry;
|
||||
import org.geysermc.connector.network.translators.sound.SoundRegistry;
|
||||
import org.geysermc.connector.network.translators.world.WorldManager;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
import org.geysermc.connector.utils.DockerCheck;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
import org.geysermc.connector.utils.ResourcePack;
|
||||
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
@ -74,9 +78,7 @@ import java.util.concurrent.TimeUnit;
|
|||
@Getter
|
||||
public class GeyserConnector {
|
||||
|
||||
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
|
||||
|
||||
public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v407.V407_CODEC;
|
||||
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().enable(JsonParser.Feature.IGNORE_UNDEFINED).enable(JsonParser.Feature.ALLOW_COMMENTS).disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
|
||||
public static final String NAME = "Geyser";
|
||||
public static final String VERSION = "DEV"; // A fallback for running in IDEs
|
||||
|
@ -135,12 +137,23 @@ public class GeyserConnector {
|
|||
ItemTranslator.init();
|
||||
LocaleUtils.init();
|
||||
PotionMixRegistry.init();
|
||||
RecipeRegistry.init();
|
||||
SoundRegistry.init();
|
||||
SoundHandlerRegistry.init();
|
||||
AddonListenerRegistry.init();
|
||||
|
||||
if (platformType != PlatformType.STANDALONE) {
|
||||
DockerCheck.check(bootstrap);
|
||||
ResourcePack.loadPacks();
|
||||
|
||||
if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) {
|
||||
// Set the remote address to localhost since that is where we are always connecting
|
||||
try {
|
||||
config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress());
|
||||
} catch (UnknownHostException ex) {
|
||||
logger.debug("Unknown host when trying to find localhost.");
|
||||
if (config.isDebugMode()) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
String remoteAddress = config.getRemote().getAddress();
|
||||
int remotePort = config.getRemote().getPort();
|
||||
|
@ -158,7 +171,7 @@ public class GeyserConnector {
|
|||
config.getRemote().setPort(remotePort = Integer.parseInt(record[2]));
|
||||
logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\"");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
} catch (Exception | NoClassDefFoundError ex) { // Check for a NoClassDefFoundError to prevent Android crashes
|
||||
logger.debug("Exception while trying to find an SRV record for the remote host.");
|
||||
if (config.isDebugMode())
|
||||
ex.printStackTrace(); // Otherwise we can get a stack trace for any domain that doesn't have an SRV record
|
||||
|
@ -181,7 +194,7 @@ public class GeyserConnector {
|
|||
if (throwable == null) {
|
||||
logger.info(LanguageUtils.getLocaleStringLog("geyser.core.start", config.getBedrock().getAddress(), String.valueOf(config.getBedrock().getPort())));
|
||||
} else {
|
||||
logger.severe(LanguageUtils.getLocaleStringLog("geyser.core.fail", config.getBedrock().getAddress(), config.getBedrock().getPort()));
|
||||
logger.severe(LanguageUtils.getLocaleStringLog("geyser.core.fail", config.getBedrock().getAddress(), String.valueOf(config.getBedrock().getPort())));
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}).join();
|
||||
|
@ -190,8 +203,39 @@ public class GeyserConnector {
|
|||
metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger(""));
|
||||
metrics.addCustomChart(new Metrics.SingleLineChart("servers", () -> 1));
|
||||
metrics.addCustomChart(new Metrics.SingleLineChart("players", players::size));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("authMode", authType.name()::toLowerCase));
|
||||
// Prevent unwanted words best we can
|
||||
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> AuthType.getByName(config.getRemote().getAuthType()).toString().toLowerCase()));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", LanguageUtils::getDefaultLocale));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserConnector.VERSION));
|
||||
metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> {
|
||||
Map<String, Integer> valueMap = new HashMap<>();
|
||||
for (GeyserSession session : players) {
|
||||
if (session == null) continue;
|
||||
if (session.getClientData() == null) continue;
|
||||
String os = session.getClientData().getDeviceOS().toString();
|
||||
if (!valueMap.containsKey(os)) {
|
||||
valueMap.put(os, 1);
|
||||
} else {
|
||||
valueMap.put(os, valueMap.get(os) + 1);
|
||||
}
|
||||
}
|
||||
return valueMap;
|
||||
}));
|
||||
metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> {
|
||||
Map<String, Integer> valueMap = new HashMap<>();
|
||||
for (GeyserSession session : players) {
|
||||
if (session == null) continue;
|
||||
if (session.getClientData() == null) continue;
|
||||
String version = session.getClientData().getGameVersion();
|
||||
if (!valueMap.containsKey(version)) {
|
||||
valueMap.put(version, 1);
|
||||
} else {
|
||||
valueMap.put(version, valueMap.get(version) + 1);
|
||||
}
|
||||
}
|
||||
return valueMap;
|
||||
}));
|
||||
}
|
||||
|
||||
boolean isGui = false;
|
||||
|
@ -213,6 +257,10 @@ public class GeyserConnector {
|
|||
message += LanguageUtils.getLocaleStringLog("geyser.core.finish.console");
|
||||
}
|
||||
logger.info(message);
|
||||
|
||||
if (platformType == PlatformType.STANDALONE) {
|
||||
logger.warning(LanguageUtils.getLocaleStringLog("geyser.core.movement_warn"));
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
|
@ -298,6 +346,19 @@ public class GeyserConnector {
|
|||
return bootstrap.getWorldManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use XML reflections in the jar or manually find the reflections.
|
||||
* Will return true if the version number is not 'DEV' and the platform is not Fabric.
|
||||
* On Fabric - it complains about being unable to create a default XMLReader.
|
||||
* On other platforms this should only be true in compiled jars.
|
||||
*
|
||||
* @return whether to use XML reflections
|
||||
*/
|
||||
public boolean useXmlReflections() {
|
||||
//noinspection ConstantConditions
|
||||
return !this.getPlatformType().equals(PlatformType.FABRIC) && !"DEV".equals(GeyserConnector.VERSION);
|
||||
}
|
||||
|
||||
public static GeyserConnector getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ public interface GeyserLogger {
|
|||
|
||||
/**
|
||||
* Logs a severe message and an exception to console
|
||||
*
|
||||
* @param message the message to log
|
||||
* @param error the error to throw
|
||||
*/
|
||||
void severe(String message, Throwable error);
|
||||
|
||||
|
@ -48,6 +51,9 @@ public interface GeyserLogger {
|
|||
|
||||
/**
|
||||
* Logs an error message and an exception to console
|
||||
*
|
||||
* @param message the message to log
|
||||
* @param error the error to throw
|
||||
*/
|
||||
void error(String message, Throwable error);
|
||||
|
||||
|
@ -78,4 +84,9 @@ public interface GeyserLogger {
|
|||
* @param debug if the logger should print debug messages
|
||||
*/
|
||||
void setDebug(boolean debug);
|
||||
|
||||
/**
|
||||
* If debug is enabled for this logger
|
||||
*/
|
||||
boolean isDebug();
|
||||
}
|
||||
|
|
|
@ -30,14 +30,14 @@ import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
|||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
import org.geysermc.connector.command.CommandManager;
|
||||
import org.geysermc.connector.network.translators.world.CachedChunkManager;
|
||||
import org.geysermc.connector.network.translators.world.GeyserWorldManager;
|
||||
import org.geysermc.connector.network.translators.world.WorldManager;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface GeyserBootstrap {
|
||||
|
||||
CachedChunkManager DEFAULT_CHUNK_MANAGER = new CachedChunkManager();
|
||||
GeyserWorldManager DEFAULT_CHUNK_MANAGER = new GeyserWorldManager();
|
||||
|
||||
/**
|
||||
* Called when the GeyserBootstrap is enabled
|
||||
|
|
|
@ -33,10 +33,12 @@ import org.geysermc.connector.common.ChatColor;
|
|||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.command.CommandSender;
|
||||
import org.geysermc.connector.command.GeyserCommand;
|
||||
import org.geysermc.connector.common.serializer.AsteriskSerializer;
|
||||
import org.geysermc.connector.dump.DumpInfo;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.WebUtils;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DumpCommand extends GeyserCommand {
|
||||
|
@ -49,43 +51,80 @@ public class DumpCommand extends GeyserCommand {
|
|||
super(name, description, permission);
|
||||
|
||||
this.connector = connector;
|
||||
|
||||
final SimpleFilterProvider filter = new SimpleFilterProvider();
|
||||
filter.addFilter("dump_user_auth", SimpleBeanPropertyFilter.serializeAllExcept(new String[] {"password"}));
|
||||
|
||||
MAPPER.setFilterProvider(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
boolean showSensitive = false;
|
||||
boolean offlineDump = false;
|
||||
if (args.length >= 1) {
|
||||
for (String arg : args) {
|
||||
switch (arg) {
|
||||
case "full":
|
||||
showSensitive = true;
|
||||
break;
|
||||
case "offline":
|
||||
offlineDump = true;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsteriskSerializer.showSensitive = showSensitive;
|
||||
|
||||
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.collecting"));
|
||||
String dumpData = "";
|
||||
try {
|
||||
dumpData = MAPPER.writeValueAsString(new DumpInfo());
|
||||
if (offlineDump) {
|
||||
dumpData = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(new DumpInfo());
|
||||
} else {
|
||||
dumpData = MAPPER.writeValueAsString(new DumpInfo());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.collect_error"));
|
||||
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e);
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.uploading"));
|
||||
String response;
|
||||
JsonNode responseNode;
|
||||
try {
|
||||
response = WebUtils.post(DUMP_URL + "documents", dumpData);
|
||||
responseNode = MAPPER.readTree(response);
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error"));
|
||||
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e);
|
||||
return;
|
||||
String uploadedDumpUrl = "";
|
||||
|
||||
if (offlineDump) {
|
||||
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.writing"));
|
||||
|
||||
try {
|
||||
FileOutputStream outputStream = new FileOutputStream(GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("dump.json").toFile());
|
||||
outputStream.write(dumpData.getBytes());
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.write_error"));
|
||||
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.write_error_short"), e);
|
||||
return;
|
||||
}
|
||||
|
||||
uploadedDumpUrl = "dump.json";
|
||||
} else {
|
||||
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.uploading"));
|
||||
|
||||
String response;
|
||||
JsonNode responseNode;
|
||||
try {
|
||||
response = WebUtils.post(DUMP_URL + "documents", dumpData);
|
||||
responseNode = MAPPER.readTree(response);
|
||||
} catch (IOException e) {
|
||||
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error"));
|
||||
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!responseNode.has("key")) {
|
||||
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short") + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response));
|
||||
return;
|
||||
}
|
||||
|
||||
uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText();
|
||||
}
|
||||
|
||||
if (!responseNode.has("key")) {
|
||||
sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short") + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response));
|
||||
return;
|
||||
}
|
||||
|
||||
String uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText();
|
||||
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.message") + " " + ChatColor.DARK_AQUA + uploadedDumpUrl);
|
||||
if (!sender.isConsole()) {
|
||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.commands.dump.created", sender.getName(), uploadedDumpUrl));
|
||||
|
|
|
@ -49,10 +49,6 @@ public class StopCommand extends GeyserCommand {
|
|||
return;
|
||||
}
|
||||
|
||||
connector.shutdown();
|
||||
|
||||
if (connector.getPlatformType() == PlatformType.STANDALONE) {
|
||||
System.exit(0);
|
||||
}
|
||||
connector.getBootstrap().onDisable();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,10 +26,12 @@
|
|||
package org.geysermc.connector.command.defaults;
|
||||
|
||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.command.CommandSender;
|
||||
import org.geysermc.connector.command.GeyserCommand;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
import org.geysermc.connector.network.BedrockProtocol;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.WebUtils;
|
||||
|
@ -37,6 +39,7 @@ import org.geysermc.connector.utils.WebUtils;
|
|||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
public class VersionCommand extends GeyserCommand {
|
||||
|
@ -50,7 +53,15 @@ public class VersionCommand extends GeyserCommand {
|
|||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.version", GeyserConnector.NAME, GeyserConnector.VERSION, MinecraftConstants.GAME_VERSION, GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion()));
|
||||
String bedrockVersions;
|
||||
List<BedrockPacketCodec> supportedCodecs = BedrockProtocol.SUPPORTED_BEDROCK_CODECS;
|
||||
if (supportedCodecs.size() > 1) {
|
||||
bedrockVersions = supportedCodecs.get(0).getMinecraftVersion() + " - " + supportedCodecs.get(supportedCodecs.size() - 1).getMinecraftVersion();
|
||||
} else {
|
||||
bedrockVersions = BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion();
|
||||
}
|
||||
|
||||
sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.version", GeyserConnector.NAME, GeyserConnector.VERSION, MinecraftConstants.GAME_VERSION, bedrockVersions));
|
||||
|
||||
// Disable update checking in dev mode
|
||||
//noinspection ConstantConditions - changes in production
|
||||
|
|
|
@ -32,11 +32,13 @@ import lombok.Getter;
|
|||
@AllArgsConstructor
|
||||
public enum PlatformType {
|
||||
|
||||
ANDROID("Android"),
|
||||
BUNGEECORD("BungeeCord"),
|
||||
FABRIC("Fabric"),
|
||||
SPIGOT("Spigot"),
|
||||
SPONGE("Sponge"),
|
||||
STANDALONE("Standalone"),
|
||||
VELOCITY("Velocity");
|
||||
|
||||
private String platformName;
|
||||
private final String platformName;
|
||||
}
|
||||
|
|
|
@ -42,34 +42,46 @@ import java.lang.annotation.Target;
|
|||
import java.util.Optional;
|
||||
|
||||
public class AsteriskSerializer extends StdSerializer<Object> implements ContextualSerializer {
|
||||
|
||||
public static boolean showSensitive = false;
|
||||
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@JacksonAnnotationsInside
|
||||
@JsonSerialize(using = AsteriskSerializer.class)
|
||||
public @interface Asterisk {
|
||||
String value() default "***";
|
||||
boolean sensitive() default false;
|
||||
}
|
||||
|
||||
String asterisk;
|
||||
boolean sensitive;
|
||||
|
||||
public AsteriskSerializer() {
|
||||
super(Object.class);
|
||||
}
|
||||
|
||||
public AsteriskSerializer(String asterisk) {
|
||||
public AsteriskSerializer(String asterisk, boolean sensitive) {
|
||||
super(Object.class);
|
||||
this.asterisk = asterisk;
|
||||
this.sensitive = sensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty property) {
|
||||
Optional<Asterisk> anno = Optional.ofNullable(property)
|
||||
.map(prop -> prop.getAnnotation(Asterisk.class));
|
||||
return new AsteriskSerializer(anno.map(Asterisk::value).orElse(null));
|
||||
|
||||
return new AsteriskSerializer(anno.map(Asterisk::value).orElse(null), anno.map(Asterisk::sensitive).orElse(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Object obj, JsonGenerator gen, SerializerProvider prov) throws IOException {
|
||||
if (sensitive && showSensitive) {
|
||||
gen.writeObject(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
gen.writeString(asterisk);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ package org.geysermc.connector.configuration;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
@ -36,7 +35,7 @@ import java.util.Map;
|
|||
public interface GeyserConfiguration {
|
||||
|
||||
// Modify this when you update the config
|
||||
int CURRENT_CONFIG_VERSION = 3;
|
||||
int CURRENT_CONFIG_VERSION = 4;
|
||||
|
||||
IBedrockConfiguration getBedrock();
|
||||
|
||||
|
@ -49,6 +48,9 @@ public interface GeyserConfiguration {
|
|||
@JsonIgnore
|
||||
boolean isPassthroughMotd();
|
||||
|
||||
@JsonIgnore
|
||||
boolean isPassthroughProtocolName();
|
||||
|
||||
@JsonIgnore
|
||||
boolean isPassthroughPlayerCounts();
|
||||
|
||||
|
@ -71,12 +73,14 @@ public interface GeyserConfiguration {
|
|||
|
||||
String getDefaultLocale();
|
||||
|
||||
Path getFloodgateKeyFile();
|
||||
Path getFloodgateKeyPath();
|
||||
|
||||
boolean isAboveBedrockNetherBuilding();
|
||||
|
||||
boolean isCacheChunks();
|
||||
|
||||
boolean isForceResourcePacks();
|
||||
|
||||
int getCacheImages();
|
||||
|
||||
IMetricsInfo getMetrics();
|
||||
|
@ -92,6 +96,8 @@ public interface GeyserConfiguration {
|
|||
String getMotd1();
|
||||
|
||||
String getMotd2();
|
||||
|
||||
String getServerName();
|
||||
}
|
||||
|
||||
interface IRemoteConfiguration {
|
||||
|
@ -120,6 +126,11 @@ public interface GeyserConfiguration {
|
|||
String getUniqueId();
|
||||
}
|
||||
|
||||
int getScoreboardPacketThreshold();
|
||||
|
||||
// if u have offline mode enabled pls be safe
|
||||
boolean isEnableProxyConnections();
|
||||
|
||||
int getMtu();
|
||||
|
||||
int getConfigVersion();
|
||||
|
|
|
@ -29,98 +29,116 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.common.serializer.AsteriskSerializer;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public abstract class GeyserJacksonConfiguration implements GeyserConfiguration {
|
||||
|
||||
private BedrockConfiguration bedrock;
|
||||
private RemoteConfiguration remote;
|
||||
/**
|
||||
* If the config was originally 'auto' before the values changed
|
||||
*/
|
||||
@Setter
|
||||
private boolean autoconfiguredRemote = false;
|
||||
|
||||
private BedrockConfiguration bedrock = new BedrockConfiguration();
|
||||
private RemoteConfiguration remote = new RemoteConfiguration();
|
||||
|
||||
@JsonProperty("floodgate-key-file")
|
||||
private String floodgateKeyFile;
|
||||
private String floodgateKeyFile = "public-key.pem";
|
||||
|
||||
public abstract Path getFloodgateKeyFile();
|
||||
public abstract Path getFloodgateKeyPath();
|
||||
|
||||
private Map<String, UserAuthenticationInfo> userAuths;
|
||||
|
||||
@JsonProperty("command-suggestions")
|
||||
private boolean commandSuggestions;
|
||||
private boolean commandSuggestions = true;
|
||||
|
||||
@JsonProperty("passthrough-motd")
|
||||
private boolean isPassthroughMotd;
|
||||
private boolean isPassthroughMotd = false;
|
||||
|
||||
@JsonProperty("passthrough-player-counts")
|
||||
private boolean isPassthroughPlayerCounts;
|
||||
private boolean isPassthroughPlayerCounts = false;
|
||||
|
||||
@JsonProperty("passthrough-protocol-name")
|
||||
private boolean isPassthroughProtocolName = false;
|
||||
|
||||
@JsonProperty("legacy-ping-passthrough")
|
||||
private boolean isLegacyPingPassthrough;
|
||||
private boolean isLegacyPingPassthrough = false;
|
||||
|
||||
@JsonProperty("ping-passthrough-interval")
|
||||
private int pingPassthroughInterval;
|
||||
private int pingPassthroughInterval = 3;
|
||||
|
||||
@JsonProperty("max-players")
|
||||
private int maxPlayers;
|
||||
private int maxPlayers = 100;
|
||||
|
||||
@JsonProperty("debug-mode")
|
||||
private boolean debugMode;
|
||||
private boolean debugMode = false;
|
||||
|
||||
@JsonProperty("general-thread-pool")
|
||||
private int generalThreadPool;
|
||||
private int generalThreadPool = 32;
|
||||
|
||||
@JsonProperty("allow-third-party-capes")
|
||||
private boolean allowThirdPartyCapes;
|
||||
private boolean allowThirdPartyCapes = true;
|
||||
|
||||
@JsonProperty("show-cooldown")
|
||||
private boolean showCooldown = true;
|
||||
|
||||
@JsonProperty("allow-third-party-ears")
|
||||
private boolean allowThirdPartyEars;
|
||||
private boolean allowThirdPartyEars = false;
|
||||
|
||||
@JsonProperty("default-locale")
|
||||
private String defaultLocale;
|
||||
private String defaultLocale = null; // is null by default so system language takes priority
|
||||
|
||||
@JsonProperty("cache-chunks")
|
||||
private boolean cacheChunks;
|
||||
private boolean cacheChunks = false;
|
||||
|
||||
@JsonProperty("cache-images")
|
||||
private int cacheImages = 0;
|
||||
|
||||
@JsonProperty("above-bedrock-nether-building")
|
||||
private boolean aboveBedrockNetherBuilding;
|
||||
private boolean aboveBedrockNetherBuilding = false;
|
||||
|
||||
private MetricsInfo metrics;
|
||||
@JsonProperty("force-resource-packs")
|
||||
private boolean forceResourcePacks = true;
|
||||
|
||||
private MetricsInfo metrics = new MetricsInfo();
|
||||
|
||||
@Getter
|
||||
public static class BedrockConfiguration implements IBedrockConfiguration {
|
||||
|
||||
private String address;
|
||||
@AsteriskSerializer.Asterisk(sensitive = true)
|
||||
private String address = "0.0.0.0";
|
||||
|
||||
@Setter
|
||||
private int port;
|
||||
private int port = 19132;
|
||||
|
||||
@JsonProperty("clone-remote-port")
|
||||
private boolean cloneRemotePort;
|
||||
private boolean cloneRemotePort = false;
|
||||
|
||||
private String motd1;
|
||||
private String motd2;
|
||||
private String motd1 = "GeyserMC";
|
||||
private String motd2 = "Geyser";
|
||||
|
||||
@JsonProperty("server-name")
|
||||
private String serverName = GeyserConnector.NAME;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class RemoteConfiguration implements IRemoteConfiguration {
|
||||
@Setter
|
||||
@AsteriskSerializer.Asterisk(sensitive = true)
|
||||
private String address = "auto";
|
||||
|
||||
@Setter
|
||||
private String address;
|
||||
private int port = 25565;
|
||||
|
||||
@Setter
|
||||
private int port;
|
||||
|
||||
@JsonProperty("auth-type")
|
||||
private String authType;
|
||||
private String authType = "online";
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
@ -134,16 +152,21 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
|
||||
@Getter
|
||||
public static class MetricsInfo implements IMetricsInfo {
|
||||
|
||||
private boolean enabled;
|
||||
private boolean enabled = true;
|
||||
|
||||
@JsonProperty("uuid")
|
||||
private String uniqueId;
|
||||
private String uniqueId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
@JsonProperty("scoreboard-packet-threshold")
|
||||
private int scoreboardPacketThreshold = 10;
|
||||
|
||||
@JsonProperty("enable-proxy-connections")
|
||||
private boolean enableProxyConnections = false;
|
||||
|
||||
@JsonProperty("mtu")
|
||||
private int mtu = 1400;
|
||||
|
||||
@JsonProperty("config-version")
|
||||
private int configVersion;
|
||||
private int configVersion = 0;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,9 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.common.serializer.AsteriskSerializer;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.network.BedrockProtocol;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.DockerCheck;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
|
@ -111,16 +113,21 @@ public class DumpInfo {
|
|||
private final boolean dockerCheck;
|
||||
|
||||
NetworkInfo() {
|
||||
try {
|
||||
// This is the most reliable for getting the main local IP
|
||||
Socket socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("geysermc.org", 80));
|
||||
this.internalIP = socket.getLocalAddress().getHostAddress();
|
||||
} catch (IOException e1) {
|
||||
if (AsteriskSerializer.showSensitive) {
|
||||
try {
|
||||
// Fallback to the normal way of getting the local IP
|
||||
this.internalIP = InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException ignored) { }
|
||||
// This is the most reliable for getting the main local IP
|
||||
Socket socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("geysermc.org", 80));
|
||||
this.internalIP = socket.getLocalAddress().getHostAddress();
|
||||
} catch (IOException e1) {
|
||||
try {
|
||||
// Fallback to the normal way of getting the local IP
|
||||
this.internalIP = InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException ignored) { }
|
||||
}
|
||||
} else {
|
||||
// Sometimes the internal IP is the external IP...
|
||||
this.internalIP = "***";
|
||||
}
|
||||
|
||||
this.dockerCheck = DockerCheck.checkBasic();
|
||||
|
@ -136,8 +143,8 @@ public class DumpInfo {
|
|||
private final int javaProtocol;
|
||||
|
||||
MCInfo() {
|
||||
this.bedrockVersion = GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion();
|
||||
this.bedrockProtocol = GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion();
|
||||
this.bedrockVersion = BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion();
|
||||
this.bedrockProtocol = BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion();
|
||||
this.javaVersion = MinecraftConstants.GAME_VERSION;
|
||||
this.javaProtocol = MinecraftConstants.PROTOCOL_VERSION;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ public class AreaEffectCloudEntity extends Entity {
|
|||
|
||||
// This disabled client side shrink of the cloud
|
||||
metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, 0.0f);
|
||||
metadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_RATE, -0.005f);
|
||||
metadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_ON_PICKUP, -0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,11 +52,14 @@ public class AreaEffectCloudEntity extends Entity {
|
|||
if (entityMetadata.getId() == 7) {
|
||||
metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, entityMetadata.getValue());
|
||||
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * (float) entityMetadata.getValue());
|
||||
} else if (entityMetadata.getId() == 8) {
|
||||
metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue());
|
||||
} else if (entityMetadata.getId() == 10) {
|
||||
Particle particle = (Particle) entityMetadata.getValue();
|
||||
metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, EffectRegistry.getParticleString(particle.getType()));
|
||||
} else if (entityMetadata.getId() == 8) {
|
||||
metadata.put(EntityData.POTION_AUX_VALUE, entityMetadata.getValue());
|
||||
int particleId = EffectRegistry.getParticleId(particle.getType());
|
||||
if (particleId != -1) {
|
||||
metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, particleId);
|
||||
}
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.message.Message;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
|
||||
|
||||
public CommandBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
// Required, or else the GUI will not open
|
||||
metadata.put(EntityData.CONTAINER_TYPE, (byte) 16);
|
||||
metadata.put(EntityData.CONTAINER_BASE_SIZE, 1);
|
||||
// Required, or else the client does not bother to send a packet back with the new information
|
||||
metadata.put(EntityData.COMMAND_BLOCK_ENABLED, (byte) 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
if (entityMetadata.getId() == 13) {
|
||||
metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue());
|
||||
}
|
||||
if (entityMetadata.getId() == 14) {
|
||||
metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageUtils.getBedrockMessage((Message) entityMetadata.getValue()));
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, the command block shown is purple on Bedrock, which does not match Java Edition's orange.
|
||||
*/
|
||||
@Override
|
||||
public void updateDefaultBlockMetadata() {
|
||||
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.BEDROCK_RUNTIME_COMMAND_BLOCK_ID);
|
||||
metadata.put(EntityData.DISPLAY_OFFSET, 6);
|
||||
}
|
||||
}
|
|
@ -32,8 +32,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
||||
import com.github.steveice10.mc.protocol.data.message.TextMessage;
|
||||
import com.github.steveice10.mc.protocol.data.message.TranslationMessage;
|
||||
import com.github.steveice10.mc.protocol.data.message.Message;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
|
@ -318,13 +317,10 @@ public class Entity {
|
|||
}
|
||||
break;
|
||||
case 2: // custom name
|
||||
if (entityMetadata.getValue() instanceof TextMessage) {
|
||||
TextMessage name = (TextMessage) entityMetadata.getValue();
|
||||
if (name != null)
|
||||
metadata.put(EntityData.NAMETAG, MessageUtils.getBedrockMessage(name));
|
||||
} else if (entityMetadata.getValue() instanceof TranslationMessage) {
|
||||
TranslationMessage message = (TranslationMessage) entityMetadata.getValue();
|
||||
if (entityMetadata.getValue() instanceof Message) {
|
||||
Message message = (Message) entityMetadata.getValue();
|
||||
if (message != null)
|
||||
// Always translate even if it's a TextMessage since there could be translatable parameters
|
||||
metadata.put(EntityData.NAMETAG, MessageUtils.getTranslatedBedrockMessage(message, session.getClientData().getLanguageCode(), true));
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.geysermc.connector.entity.type.EntityType;
|
|||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.FireworkColor;
|
||||
import org.geysermc.connector.utils.MathUtils;
|
||||
import org.geysermc.floodgate.util.DeviceOS;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -55,13 +56,26 @@ public class FireworkEntity extends Entity {
|
|||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
if (entityMetadata.getId() == 7) {
|
||||
ItemStack item = (ItemStack) entityMetadata.getValue();
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
CompoundTag tag = item.getNbt();
|
||||
|
||||
if (tag == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Remove once Mojang fixes bugs with fireworks crashing clients on these specific devices.
|
||||
// https://bugs.mojang.com/browse/MCPE-89115
|
||||
if (session.getClientData().getDeviceOS() == DeviceOS.XBOX_ONE || session.getClientData().getDeviceOS() == DeviceOS.ORBIS) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompoundTag fireworks = tag.get("Fireworks");
|
||||
if (fireworks == null) {
|
||||
// Thank you Mineplex very cool
|
||||
return;
|
||||
}
|
||||
|
||||
NbtMapBuilder fireworksBuilder = NbtMap.builder();
|
||||
if (fireworks.get("Flight") != null) {
|
||||
|
|
|
@ -34,7 +34,6 @@ import com.nukkitx.nbt.NbtMap;
|
|||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
@ -102,13 +101,7 @@ public class ItemFrameEntity extends Entity {
|
|||
ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue());
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
|
||||
String blockName = "";
|
||||
for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) {
|
||||
if (startGamePacketItemEntry.getId() == (short) itemEntry.getBedrockId()) {
|
||||
blockName = startGamePacketItemEntry.getIdentifier();
|
||||
break;
|
||||
}
|
||||
}
|
||||
String blockName = ItemRegistry.getBedrockIdentifer(itemEntry);
|
||||
|
||||
builder.putByte("Count", (byte) itemData.getCount());
|
||||
if (itemData.getTag() != null) {
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.connector.entity;
|
|||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.NameTagVisibility;
|
||||
import com.github.steveice10.mc.protocol.data.message.TextMessage;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
|
@ -35,11 +36,15 @@ import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
|||
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.entity.attribute.Attribute;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.EntityEffectCache;
|
||||
|
@ -62,8 +67,14 @@ public class PlayerEntity extends LivingEntity {
|
|||
private boolean playerList = true; // Player is in the player list
|
||||
private final EntityEffectCache effectCache;
|
||||
|
||||
private Entity leftParrot;
|
||||
private Entity rightParrot;
|
||||
/**
|
||||
* Saves the parrot currently on the player's left shoulder; otherwise null
|
||||
*/
|
||||
private ParrotEntity leftParrot;
|
||||
/**
|
||||
* Saves the parrot currently on the player's right shoulder; otherwise null
|
||||
*/
|
||||
private ParrotEntity rightParrot;
|
||||
|
||||
public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation);
|
||||
|
@ -75,12 +86,6 @@ public class PlayerEntity extends LivingEntity {
|
|||
if (geyserId == 1) valid = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean despawnEntity(GeyserSession session) {
|
||||
super.despawnEntity(session);
|
||||
return !playerList; // don't remove from cache when still on playerlist
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
if (geyserId == 1) return;
|
||||
|
@ -95,7 +100,7 @@ public class PlayerEntity extends LivingEntity {
|
|||
addPlayerPacket.setMotion(motion);
|
||||
addPlayerPacket.setHand(hand);
|
||||
addPlayerPacket.getAdventureSettings().setCommandPermission(CommandPermission.NORMAL);
|
||||
addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.VISITOR);
|
||||
addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER);
|
||||
addPlayerPacket.setDeviceId("");
|
||||
addPlayerPacket.setPlatformChatId("");
|
||||
addPlayerPacket.getMetadata().putAll(metadata);
|
||||
|
@ -113,7 +118,7 @@ public class PlayerEntity extends LivingEntity {
|
|||
}
|
||||
|
||||
public void sendPlayer(GeyserSession session) {
|
||||
if(session.getEntityCache().getPlayerEntity(uuid) == null)
|
||||
if (session.getEntityCache().getPlayerEntity(uuid) == null)
|
||||
return;
|
||||
|
||||
if (session.getUpstream().isInitialized() && session.getEntityCache().getEntityByGeyserId(geyserId) == null) {
|
||||
|
@ -186,6 +191,12 @@ public class PlayerEntity extends LivingEntity {
|
|||
@Override
|
||||
public void updatePositionAndRotation(GeyserSession session, double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) {
|
||||
moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround);
|
||||
if (leftParrot != null) {
|
||||
leftParrot.moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround);
|
||||
}
|
||||
if (rightParrot != null) {
|
||||
rightParrot.moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -199,6 +210,12 @@ public class PlayerEntity extends LivingEntity {
|
|||
movePlayerPacket.setOnGround(isOnGround);
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.HEAD_ROTATION);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
if (leftParrot != null) {
|
||||
leftParrot.updateRotation(session, yaw, pitch, isOnGround);
|
||||
}
|
||||
if (rightParrot != null) {
|
||||
rightParrot.updateRotation(session, yaw, pitch, isOnGround);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -211,20 +228,25 @@ public class PlayerEntity extends LivingEntity {
|
|||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
|
||||
if (entityMetadata.getId() == 2) {
|
||||
// System.out.println(session.getScoreboardCache().getScoreboard().getObjectives().keySet());
|
||||
for (Team team : session.getScoreboardCache().getScoreboard().getTeams().values()) {
|
||||
// session.getConnector().getLogger().info("team name " + team.getName());
|
||||
// session.getConnector().getLogger().info("team entities " + team.getEntities());
|
||||
}
|
||||
String username = this.username;
|
||||
TextMessage name = (TextMessage) entityMetadata.getValue();
|
||||
if (name != null) {
|
||||
username = MessageUtils.getBedrockMessage(name);
|
||||
}
|
||||
Team team = session.getScoreboardCache().getScoreboard().getTeamFor(username);
|
||||
Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
|
||||
if (team != null) {
|
||||
// session.getConnector().getLogger().info("team name es " + team.getName() + " with prefix " + team.getPrefix() + " and suffix " + team.getSuffix());
|
||||
metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix());
|
||||
// Cover different visibility settings
|
||||
if (team.getNameTagVisibility() == NameTagVisibility.NEVER) {
|
||||
metadata.put(EntityData.NAMETAG, "");
|
||||
} else if (team.getNameTagVisibility() == NameTagVisibility.HIDE_FOR_OTHER_TEAMS &&
|
||||
!team.getEntities().contains(session.getPlayerEntity().getUsername())) {
|
||||
metadata.put(EntityData.NAMETAG, "");
|
||||
} else if (team.getNameTagVisibility() == NameTagVisibility.HIDE_FOR_OWN_TEAM &&
|
||||
team.getEntities().contains(session.getPlayerEntity().getUsername())) {
|
||||
metadata.put(EntityData.NAMETAG, "");
|
||||
} else {
|
||||
metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,11 +262,15 @@ public class PlayerEntity extends LivingEntity {
|
|||
}
|
||||
|
||||
// Parrot occupying shoulder
|
||||
if ((entityMetadata.getId() == 18 && leftParrot == null) || (entityMetadata.getId() == 19 && rightParrot == null)) { // null check since this code just creates the parrot
|
||||
if (entityMetadata.getId() == 18 || entityMetadata.getId() == 19) {
|
||||
CompoundTag tag = (CompoundTag) entityMetadata.getValue();
|
||||
if (tag != null && !tag.isEmpty()) {
|
||||
if ((entityMetadata.getId() == 18 && leftParrot != null) || (entityMetadata.getId() == 19 && rightParrot != null)) {
|
||||
// No need to update a parrot's data when it already exists
|
||||
return;
|
||||
}
|
||||
// The parrot is a separate entity in Bedrock, but part of the player entity in Java
|
||||
Entity parrot = new Entity(0, session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
ParrotEntity parrot = new ParrotEntity(0, session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
EntityType.PARROT, position, motion, rotation);
|
||||
parrot.spawnEntity(session);
|
||||
parrot.getMetadata().put(EntityData.VARIANT, tag.get("Variant").getValue());
|
||||
|
|
|
@ -25,12 +25,39 @@
|
|||
|
||||
package org.geysermc.connector.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.item.TippedArrowPotion;
|
||||
|
||||
/**
|
||||
* Internally this is known as TippedArrowEntity but is used with tipped arrows and normal arrows
|
||||
*/
|
||||
public class TippedArrowEntity extends AbstractArrowEntity {
|
||||
|
||||
public TippedArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
// Arrow potion effect color
|
||||
if (entityMetadata.getId() == 9) {
|
||||
int potionColor = (int) entityMetadata.getValue();
|
||||
// -1 means no color
|
||||
if (potionColor == -1) {
|
||||
metadata.remove(EntityData.CUSTOM_DISPLAY);
|
||||
} else {
|
||||
TippedArrowPotion potion = TippedArrowPotion.getByJavaColor(potionColor);
|
||||
if (potion != null && potion.getJavaColor() != -1) {
|
||||
metadata.put(EntityData.CUSTOM_DISPLAY, (byte) potion.getBedrockId());
|
||||
} else {
|
||||
metadata.remove(EntityData.CUSTOM_DISPLAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ import org.geysermc.connector.network.session.GeyserSession;
|
|||
|
||||
public class WolfEntity extends TameableEntity {
|
||||
|
||||
private byte collarColor;
|
||||
|
||||
public WolfEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
@ -57,12 +59,13 @@ public class WolfEntity extends TameableEntity {
|
|||
// Wolf collar color
|
||||
// Relies on EntityData.OWNER_EID being set in TameableEntity.java
|
||||
if (entityMetadata.getId() == 19 && !metadata.getFlags().getFlag(EntityFlag.ANGRY)) {
|
||||
metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
|
||||
metadata.put(EntityData.COLOR, collarColor = (byte) (int) entityMetadata.getValue());
|
||||
}
|
||||
|
||||
// Wolf anger (1.16+)
|
||||
if (entityMetadata.getId() == 20) {
|
||||
metadata.getFlags().setFlag(EntityFlag.ANGRY, (int) entityMetadata.getValue() != 0);
|
||||
metadata.put(EntityData.COLOR, (int) entityMetadata.getValue() != 0 ? (byte) 0 : collarColor);
|
||||
}
|
||||
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package org.geysermc.connector.entity.living.monster;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
|
||||
public class BasePiglinEntity extends MonsterEntity {
|
||||
|
||||
public BasePiglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ import org.geysermc.connector.entity.type.EntityType;
|
|||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
|
||||
public class PiglinEntity extends MonsterEntity {
|
||||
public class PiglinEntity extends BasePiglinEntity {
|
||||
|
||||
public PiglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
|
|
|
@ -53,14 +53,21 @@ public class ShulkerEntity extends GolemEntity {
|
|||
metadata.put(EntityData.SHULKER_ATTACH_POS, Vector3i.from(position.getX(), position.getY(), position.getZ()));
|
||||
}
|
||||
}
|
||||
//TODO Outdated metadata flag SHULKER_PEAK_HEIGHT
|
||||
// if (entityMetadata.getId() == 17) {
|
||||
// int height = (byte) entityMetadata.getValue();
|
||||
// metadata.put(EntityData.SHULKER_PEAK_HEIGHT, height);
|
||||
// }
|
||||
|
||||
if (entityMetadata.getId() == 17) {
|
||||
int height = (byte) entityMetadata.getValue();
|
||||
metadata.put(EntityData.SHULKER_PEEK_ID, height);
|
||||
}
|
||||
|
||||
if (entityMetadata.getId() == 18) {
|
||||
int color = Math.abs((byte) entityMetadata.getValue() - 15);
|
||||
metadata.put(EntityData.VARIANT, color);
|
||||
byte color = (byte) entityMetadata.getValue();
|
||||
if (color == 16) {
|
||||
// 16 is default on both editions
|
||||
metadata.put(EntityData.VARIANT, 16);
|
||||
} else {
|
||||
// Every other shulker color is offset 15 in bedrock edition
|
||||
metadata.put(EntityData.VARIANT, Math.abs(color - 15));
|
||||
}
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
|
|
@ -23,31 +23,27 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.world.chunk;
|
||||
package org.geysermc.connector.entity.living.monster.raid;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
public class ChunkPosition {
|
||||
public class PillagerEntity extends AbstractIllagerEntity {
|
||||
|
||||
private int x;
|
||||
private int z;
|
||||
|
||||
public Position getBlock(int x, int y, int z) {
|
||||
return new Position((this.x << 4) + x, y, (this.z << 4) + z);
|
||||
public PillagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
public Position getChunkBlock(int x, int y, int z) {
|
||||
int chunkX = x & 15;
|
||||
int chunkY = y & 15;
|
||||
int chunkZ = z & 15;
|
||||
return new Position(chunkX, chunkY, chunkZ);
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
if (entityMetadata.getId() == 16) {
|
||||
// Java Edition always has the Pillager entity as positioning the crossbow
|
||||
metadata.getFlags().setFlag(EntityFlag.USING_ITEM, true);
|
||||
metadata.getFlags().setFlag(EntityFlag.CHARGED, true);
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ import org.geysermc.connector.entity.living.animal.tameable.*;
|
|||
import org.geysermc.connector.entity.living.merchant.*;
|
||||
import org.geysermc.connector.entity.living.monster.*;
|
||||
import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity;
|
||||
import org.geysermc.connector.entity.living.monster.raid.PillagerEntity;
|
||||
import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity;
|
||||
import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity;
|
||||
|
||||
|
@ -90,7 +91,7 @@ public enum EntityType {
|
|||
ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f),
|
||||
AGENT(Entity.class, 56, 0f),
|
||||
VINDICATOR(AbstractIllagerEntity.class, 57, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
PILLAGER(AbstractIllagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
PILLAGER(PillagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
WANDERING_TRADER(AbstractMerchantEntity.class, 118, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
PHANTOM(FlyingEntity.class, 58, 0.5f, 0.9f, 0.9f, 0.6f),
|
||||
RAVAGER(RaidParticipantEntity.class, 59, 1.9f, 1.2f),
|
||||
|
@ -135,7 +136,7 @@ public enum EntityType {
|
|||
MINECART_CHEST(MinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:chest_minecart"),
|
||||
MINECART_FURNACE(FurnaceMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
|
||||
MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
|
||||
MINECART_COMMAND_BLOCK(MinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"),
|
||||
MINECART_COMMAND_BLOCK(CommandBlockMinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"),
|
||||
LINGERING_POTION(ThrowableEntity.class, 101, 0f),
|
||||
LLAMA_SPIT(Entity.class, 102, 0.25f),
|
||||
EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"),
|
||||
|
@ -155,6 +156,7 @@ public enum EntityType {
|
|||
HOGLIN(AnimalEntity.class, 124, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:hoglin"),
|
||||
ZOGLIN(ZoglinEntity.class, 126, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:zoglin"),
|
||||
PIGLIN(PiglinEntity.class, 123, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin"),
|
||||
PIGLIN_BRUTE(BasePiglinEntity.class, 127, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin_brute"),
|
||||
|
||||
/**
|
||||
* Item frames are handled differently since they are a block in Bedrock.
|
||||
|
|
|
@ -31,6 +31,10 @@ import lombok.Setter;
|
|||
|
||||
public class PlayerInventory extends Inventory {
|
||||
|
||||
/**
|
||||
* Stores the held item slot, starting at index 0.
|
||||
* Add 36 in order to get the network item slot.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private int heldItemSlot;
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||
import com.nukkitx.protocol.bedrock.v407.Bedrock_v407;
|
||||
import com.nukkitx.protocol.bedrock.v408.Bedrock_v408;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Contains information about the supported Bedrock protocols in Geyser.
|
||||
*/
|
||||
public class BedrockProtocol {
|
||||
/**
|
||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||
* release of the game that Geyser supports.
|
||||
*/
|
||||
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v408.V408_CODEC;
|
||||
/**
|
||||
* A list of all supported Bedrock versions that can join Geyser
|
||||
*/
|
||||
public static final List<BedrockPacketCodec> SUPPORTED_BEDROCK_CODECS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v407.V407_CODEC);
|
||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link BedrockPacketCodec} of the given protocol version.
|
||||
* @param protocolVersion The protocol version to attempt to find
|
||||
* @return The packet codec, or null if the client's protocol is unsupported
|
||||
*/
|
||||
public static BedrockPacketCodec getBedrockCodec(int protocolVersion) {
|
||||
for (BedrockPacketCodec packetCodec : SUPPORTED_BEDROCK_CODECS) {
|
||||
if (packetCodec.getProtocolVersion() == protocolVersion) {
|
||||
return packetCodec;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -71,7 +71,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
|||
pong.setEdition("MCPE");
|
||||
pong.setGameType("Default");
|
||||
pong.setNintendoLimited(false);
|
||||
pong.setProtocolVersion(GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion());
|
||||
pong.setProtocolVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion());
|
||||
pong.setVersion(null); // Server tries to connect either way and it looks better
|
||||
pong.setIpv4Port(config.getBedrock().getPort());
|
||||
|
||||
|
@ -108,7 +108,8 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
|||
public void onSessionCreation(BedrockServerSession bedrockServerSession) {
|
||||
bedrockServerSession.setLogging(true);
|
||||
bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(connector, new GeyserSession(connector, bedrockServerSession)));
|
||||
bedrockServerSession.setPacketCodec(GeyserConnector.BEDROCK_PACKET_CODEC);
|
||||
// Set the packet codec to default just in case we need to send disconnect packets.
|
||||
bedrockServerSession.setPacketCodec(BedrockProtocol.DEFAULT_BEDROCK_CODEC);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -141,6 +141,7 @@ public class QueryPacketHandler {
|
|||
String motd;
|
||||
String currentPlayerCount;
|
||||
String maxPlayerCount;
|
||||
String map;
|
||||
|
||||
if (connector.getConfig().isPassthroughMotd() || connector.getConfig().isPassthroughPlayerCounts()) {
|
||||
pingInfo = connector.getBootstrap().getGeyserPingPassthrough().getPingInformation();
|
||||
|
@ -162,14 +163,21 @@ public class QueryPacketHandler {
|
|||
maxPlayerCount = String.valueOf(connector.getConfig().getMaxPlayers());
|
||||
}
|
||||
|
||||
// If passthrough protocol name is enabled let's get the protocol name from the ping response.
|
||||
if (connector.getConfig().isPassthroughProtocolName() && pingInfo != null) {
|
||||
map = String.valueOf((pingInfo.getVersion().getName()));
|
||||
} else {
|
||||
map = GeyserConnector.NAME;
|
||||
}
|
||||
|
||||
// Create a hashmap of all game data needed in the query
|
||||
Map<String, String> gameData = new HashMap<String, String>();
|
||||
gameData.put("hostname", motd);
|
||||
gameData.put("gametype", "SMP");
|
||||
gameData.put("game_id", "MINECRAFT");
|
||||
gameData.put("version", GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion());
|
||||
gameData.put("version", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion());
|
||||
gameData.put("plugins", "");
|
||||
gameData.put("map", GeyserConnector.NAME);
|
||||
gameData.put("map", map);
|
||||
gameData.put("numplayers", currentPlayerCount);
|
||||
gameData.put("maxplayers", maxPlayerCount);
|
||||
gameData.put("hostport", String.valueOf(connector.getConfig().getBedrock().getPort()));
|
||||
|
|
|
@ -26,15 +26,24 @@
|
|||
package org.geysermc.connector.network;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||
import com.nukkitx.protocol.bedrock.data.ResourcePackType;
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.common.AuthType;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.addon.FormAddonListener;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
||||
import org.geysermc.connector.utils.MathUtils;
|
||||
import org.geysermc.connector.utils.ResourcePack;
|
||||
import org.geysermc.connector.utils.ResourcePackManifest;
|
||||
import org.geysermc.connector.utils.SettingsUtils;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
|
||||
|
@ -48,15 +57,20 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
|
||||
@Override
|
||||
public boolean handle(LoginPacket loginPacket) {
|
||||
if (loginPacket.getProtocolVersion() > GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()) {
|
||||
// Too early to determine session locale
|
||||
session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.server", GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion()));
|
||||
return true;
|
||||
} else if (loginPacket.getProtocolVersion() < GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()) {
|
||||
session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.client", GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion()));
|
||||
return true;
|
||||
BedrockPacketCodec packetCodec = BedrockProtocol.getBedrockCodec(loginPacket.getProtocolVersion());
|
||||
if (packetCodec == null) {
|
||||
if (loginPacket.getProtocolVersion() > BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) {
|
||||
// Too early to determine session locale
|
||||
session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.server", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()));
|
||||
return true;
|
||||
} else if (loginPacket.getProtocolVersion() < BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) {
|
||||
session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.client", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
session.getUpstream().getSession().setPacketCodec(packetCodec);
|
||||
|
||||
LoginEncryptionUtils.encryptPlayerConnection(connector, session, loginPacket);
|
||||
|
||||
PlayStatusPacket playStatus = new PlayStatusPacket();
|
||||
|
@ -64,6 +78,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
session.sendUpstreamPacket(playStatus);
|
||||
|
||||
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
||||
for(ResourcePack resourcePack : ResourcePack.PACKS.values()) {
|
||||
ResourcePackManifest.Header header = resourcePack.getManifest().getHeader();
|
||||
resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(header.getUuid().toString(), header.getVersionString(), resourcePack.getFile().length(), "", "", "", false));
|
||||
}
|
||||
resourcePacksInfo.setForcedToAccept(GeyserConnector.getInstance().getConfig().isForceResourcePacks());
|
||||
session.sendUpstreamPacket(resourcePacksInfo);
|
||||
return true;
|
||||
}
|
||||
|
@ -75,13 +94,42 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
session.connect(connector.getRemoteServer());
|
||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.connect", session.getAuthData().getName()));
|
||||
break;
|
||||
case HAVE_ALL_PACKS:
|
||||
ResourcePackStackPacket stack = new ResourcePackStackPacket();
|
||||
stack.setExperimental(false);
|
||||
stack.setForcedToAccept(false);
|
||||
stack.setGameVersion("*");
|
||||
session.sendUpstreamPacket(stack);
|
||||
|
||||
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.RESOURCE);
|
||||
|
||||
session.sendUpstreamPacket(data);
|
||||
}
|
||||
break;
|
||||
|
||||
case HAVE_ALL_PACKS:
|
||||
ResourcePackStackPacket stackPacket = new ResourcePackStackPacket();
|
||||
stackPacket.setExperimentsPreviouslyToggled(false);
|
||||
stackPacket.setForcedToAccept(false); // Leaving this as false allows the player to choose to download or not
|
||||
stackPacket.setGameVersion(session.getClientData().getGameVersion());
|
||||
|
||||
for (ResourcePack pack : ResourcePack.PACKS.values()) {
|
||||
ResourcePackManifest.Header header = pack.getManifest().getHeader();
|
||||
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), ""));
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(stackPacket);
|
||||
break;
|
||||
|
||||
default:
|
||||
session.disconnect("disconnectionScreen.resourcePack");
|
||||
break;
|
||||
|
@ -95,6 +143,9 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
if (packet.getFormId() == LoginEncryptionUtils.AUTH_FORM_ID || packet.getFormId() == LoginEncryptionUtils.AUTH_DETAILS_FORM_ID) {
|
||||
return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormId(), packet.getFormData());
|
||||
}
|
||||
if (packet.getFormId() == SettingsUtils.SETTINGS_FORM_ID) {
|
||||
return SettingsUtils.handleSettingsForm(session, packet.getFormData());
|
||||
}
|
||||
FormAddonListener.get().handleResponse(this.session, packet);
|
||||
return true;
|
||||
}
|
||||
|
@ -143,4 +194,30 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
boolean defaultHandler(BedrockPacket packet) {
|
||||
return translateAndDefault(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(ResourcePackChunkRequestPacket packet) {
|
||||
ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket();
|
||||
ResourcePack pack = ResourcePack.PACKS.get(packet.getPackId().toString());
|
||||
|
||||
data.setChunkIndex(packet.getChunkIndex());
|
||||
data.setProgress(packet.getChunkIndex() * ResourcePack.CHUNK_SIZE);
|
||||
data.setPackVersion(packet.getPackVersion());
|
||||
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)];
|
||||
|
||||
try (InputStream inputStream = new FileInputStream(pack.getFile())) {
|
||||
inputStream.skip(offset);
|
||||
inputStream.read(packData, 0, packData.length);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
data.setData(packData);
|
||||
|
||||
session.sendUpstreamPacket(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.connector.network.session;
|
|||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsException;
|
||||
import com.github.steveice10.mc.auth.exception.request.RequestException;
|
||||
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
||||
import com.github.steveice10.mc.protocol.data.SubProtocol;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
|
@ -46,6 +47,7 @@ import com.nukkitx.math.vector.*;
|
|||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||
import com.nukkitx.protocol.bedrock.BedrockServerSession;
|
||||
import com.nukkitx.protocol.bedrock.data.*;
|
||||
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
||||
|
@ -54,6 +56,7 @@ import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
|||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.common.window.CustomFormWindow;
|
||||
import org.geysermc.common.window.FormWindow;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.command.CommandSender;
|
||||
|
@ -68,6 +71,7 @@ import org.geysermc.connector.network.session.cache.*;
|
|||
import org.geysermc.connector.network.translators.BiomeTranslator;
|
||||
import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||
import org.geysermc.connector.network.translators.inventory.EnchantmentInventoryTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.*;
|
||||
|
@ -79,9 +83,8 @@ import java.net.InetSocketAddress;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Getter
|
||||
|
@ -102,7 +105,7 @@ public class GeyserSession implements CommandSender {
|
|||
private ChunkCache chunkCache;
|
||||
private EntityCache entityCache;
|
||||
private InventoryCache inventoryCache;
|
||||
private ScoreboardCache scoreboardCache;
|
||||
private WorldCache worldCache;
|
||||
private WindowCache windowCache;
|
||||
@Setter
|
||||
private TeleportCache teleportCache;
|
||||
|
@ -116,8 +119,6 @@ public class GeyserSession implements CommandSender {
|
|||
*/
|
||||
private final Object2LongMap<Vector3i> itemFrameCache = new Object2LongOpenHashMap<>();
|
||||
|
||||
private DataCache<Packet> javaPacketCache;
|
||||
|
||||
@Setter
|
||||
private Vector2i lastChunkPosition = null;
|
||||
private int renderDistance;
|
||||
|
@ -155,11 +156,14 @@ public class GeyserSession implements CommandSender {
|
|||
@Setter
|
||||
private boolean interacting;
|
||||
|
||||
/**
|
||||
* Stores the last position of the block the player interacted with. This can either be a block that the client
|
||||
* placed or an existing block the player interacted with (for example, a chest). <br>
|
||||
* Initialized as (0, 0, 0) so it is always not-null.
|
||||
*/
|
||||
@Setter
|
||||
private Vector3i lastInteractionPosition;
|
||||
private Vector3i lastInteractionPosition = Vector3i.ZERO;
|
||||
|
||||
@Setter
|
||||
private boolean switchingDimension = false;
|
||||
private boolean manyDimPackets = false;
|
||||
private ServerRespawnPacket lastDimPacket = null;
|
||||
|
||||
|
@ -172,16 +176,28 @@ public class GeyserSession implements CommandSender {
|
|||
@Setter
|
||||
private long lastWindowCloseTime = 0;
|
||||
|
||||
/**
|
||||
* Saves the timestamp of the last keep alive packet
|
||||
*/
|
||||
@Setter
|
||||
private long lastKeepAliveTimestamp = 0;
|
||||
|
||||
@Setter
|
||||
private VillagerTrade[] villagerTrades;
|
||||
@Setter
|
||||
private long lastInteractedVillagerEid;
|
||||
|
||||
/**
|
||||
* Stores the enchantment information the client has received if they are in an enchantment table GUI
|
||||
*/
|
||||
private final EnchantmentInventoryTranslator.EnchantmentSlotData[] enchantmentSlotData = new EnchantmentInventoryTranslator.EnchantmentSlotData[3];
|
||||
|
||||
/**
|
||||
* The current attack speed of the player. Used for sending proper cooldown timings.
|
||||
* Setting a default fixes cooldowns not showing up on a fresh world.
|
||||
*/
|
||||
@Setter
|
||||
private double attackSpeed;
|
||||
private double attackSpeed = 4.0d;
|
||||
/**
|
||||
* The time of the last hit. Used to gauge how long the cooldown is taking.
|
||||
* This is a session variable in order to prevent more scheduled threads than necessary.
|
||||
|
@ -189,6 +205,76 @@ public class GeyserSession implements CommandSender {
|
|||
@Setter
|
||||
private long lastHitTime;
|
||||
|
||||
/**
|
||||
* Store the last time the player interacted. Used to fix a right-click spam bug.
|
||||
* See https://github.com/GeyserMC/Geyser/issues/503 for context.
|
||||
*/
|
||||
@Setter
|
||||
private long lastInteractionTime;
|
||||
|
||||
/**
|
||||
* Stores a future interaction to place a bucket. Will be cancelled if the client instead intended to
|
||||
* interact with a block.
|
||||
*/
|
||||
@Setter
|
||||
private ScheduledFuture<?> bucketScheduledFuture;
|
||||
|
||||
private boolean reducedDebugInfo = false;
|
||||
|
||||
@Setter
|
||||
private CustomFormWindow settingsForm;
|
||||
|
||||
/**
|
||||
* The op permission level set by the server
|
||||
*/
|
||||
@Setter
|
||||
private int opPermissionLevel = 0;
|
||||
|
||||
/**
|
||||
* If the current player can fly
|
||||
*/
|
||||
@Setter
|
||||
private boolean canFly = false;
|
||||
|
||||
/**
|
||||
* If the current player is flying
|
||||
*/
|
||||
@Setter
|
||||
private boolean flying = false;
|
||||
|
||||
/**
|
||||
* If the current player is in noclip
|
||||
*/
|
||||
@Setter
|
||||
private boolean noClip = false;
|
||||
|
||||
/**
|
||||
* If the current player can not interact with the world
|
||||
*/
|
||||
@Setter
|
||||
private boolean worldImmutable = false;
|
||||
|
||||
/**
|
||||
* Caches current rain status.
|
||||
*/
|
||||
@Setter
|
||||
private boolean raining = false;
|
||||
|
||||
/**
|
||||
* Caches current thunder status.
|
||||
*/
|
||||
@Setter
|
||||
private boolean thunder = false;
|
||||
|
||||
/**
|
||||
* Stores the last text inputted into a sign.
|
||||
*
|
||||
* Bedrock sends packets every time you update the sign, Java only wants the final packet.
|
||||
* Until we determine that the user has finished editing, we save the sign's current status.
|
||||
*/
|
||||
@Setter
|
||||
private String lastSignMessage;
|
||||
|
||||
private MinecraftProtocol protocol;
|
||||
|
||||
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
|
||||
|
@ -198,14 +284,12 @@ public class GeyserSession implements CommandSender {
|
|||
this.chunkCache = new ChunkCache(this);
|
||||
this.entityCache = new EntityCache(this);
|
||||
this.inventoryCache = new InventoryCache(this);
|
||||
this.scoreboardCache = new ScoreboardCache(this);
|
||||
this.worldCache = new WorldCache(this);
|
||||
this.windowCache = new WindowCache(this);
|
||||
|
||||
this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO);
|
||||
this.inventory = new PlayerInventory();
|
||||
|
||||
this.javaPacketCache = new DataCache<>();
|
||||
|
||||
this.spawned = false;
|
||||
this.loggedIn = false;
|
||||
|
||||
|
@ -249,6 +333,15 @@ public class GeyserSession implements CommandSender {
|
|||
attributes.add(new AttributeData("minecraft:movement", 0.0f, 1024f, 0.1f, 0.1f));
|
||||
attributesPacket.setAttributes(attributes);
|
||||
upstream.sendPacket(attributesPacket);
|
||||
|
||||
GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket();
|
||||
// Only allow the server to send health information
|
||||
// Setting this to false allows natural regeneration to work false but doesn't break it being true
|
||||
gamerulePacket.getGameRules().add(new GameRuleData<>("naturalregeneration", false));
|
||||
// Don't let the client modify the inventory on death
|
||||
// Setting this to true allows keep inventory to work if enabled but doesn't break functionality being false
|
||||
gamerulePacket.getGameRules().add(new GameRuleData<>("keepinventory", true));
|
||||
upstream.sendPacket(gamerulePacket);
|
||||
}
|
||||
|
||||
public void login() {
|
||||
|
@ -289,7 +382,7 @@ public class GeyserSession implements CommandSender {
|
|||
PublicKey key = null;
|
||||
try {
|
||||
key = EncryptionUtil.getKeyFromFile(
|
||||
connector.getConfig().getFloodgateKeyFile(),
|
||||
connector.getConfig().getFloodgateKeyPath(),
|
||||
PublicKey.class
|
||||
);
|
||||
} catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
|
@ -303,6 +396,8 @@ public class GeyserSession implements CommandSender {
|
|||
}
|
||||
|
||||
downstream = new Client(remoteServer.getAddress(), remoteServer.getPort(), protocol, new TcpSessionFactory());
|
||||
// Let Geyser handle sending the keep alive
|
||||
downstream.getSession().setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false);
|
||||
downstream.getSession().addListener(new SessionAdapter() {
|
||||
@Override
|
||||
public void packetSending(PacketSendingEvent event) {
|
||||
|
@ -434,7 +529,7 @@ public class GeyserSession implements CommandSender {
|
|||
|
||||
this.chunkCache = null;
|
||||
this.entityCache = null;
|
||||
this.scoreboardCache = null;
|
||||
this.worldCache = null;
|
||||
this.inventoryCache = null;
|
||||
this.windowCache = null;
|
||||
|
||||
|
@ -533,8 +628,10 @@ public class GeyserSession implements CommandSender {
|
|||
startGamePacket.setFromWorldTemplate(false);
|
||||
startGamePacket.setWorldTemplateOptionLocked(false);
|
||||
|
||||
startGamePacket.setLevelId("world");
|
||||
startGamePacket.setLevelName("world");
|
||||
String serverName = connector.getConfig().getBedrock().getServerName();
|
||||
startGamePacket.setLevelId(serverName);
|
||||
startGamePacket.setLevelName(serverName);
|
||||
|
||||
startGamePacket.setPremiumWorldTemplateId("00000000-0000-0000-0000-000000000000");
|
||||
// startGamePacket.setCurrentTick(0);
|
||||
startGamePacket.setEnchantmentSeed(0);
|
||||
|
@ -542,7 +639,7 @@ public class GeyserSession implements CommandSender {
|
|||
startGamePacket.setBlockPalette(BlockTranslator.BLOCKS);
|
||||
startGamePacket.setItemEntries(ItemRegistry.ITEMS);
|
||||
startGamePacket.setVanillaVersion("*");
|
||||
// startGamePacket.setMovementServerAuthoritative(true);
|
||||
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT);
|
||||
upstream.sendPacket(startGamePacket);
|
||||
}
|
||||
|
||||
|
@ -567,7 +664,7 @@ public class GeyserSession implements CommandSender {
|
|||
* @param packet the bedrock packet from the NukkitX protocol lib
|
||||
*/
|
||||
public void sendUpstreamPacket(BedrockPacket packet) {
|
||||
if (upstream != null && !upstream.isClosed()) {
|
||||
if (upstream != null) {
|
||||
upstream.sendPacket(packet);
|
||||
} else {
|
||||
connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " but the session was null");
|
||||
|
@ -580,7 +677,7 @@ public class GeyserSession implements CommandSender {
|
|||
* @param packet the bedrock packet from the NukkitX protocol lib
|
||||
*/
|
||||
public void sendUpstreamPacketImmediately(BedrockPacket packet) {
|
||||
if (upstream != null && !upstream.isClosed()) {
|
||||
if (upstream != null) {
|
||||
upstream.sendPacketImmediately(packet);
|
||||
} else {
|
||||
connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " immediately but the session was null");
|
||||
|
@ -599,4 +696,77 @@ public class GeyserSession implements CommandSender {
|
|||
connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cached value for the reduced debug info gamerule.
|
||||
* This also toggles the coordinates display
|
||||
*
|
||||
* @param value The new value for reducedDebugInfo
|
||||
*/
|
||||
public void setReducedDebugInfo(boolean value) {
|
||||
worldCache.setShowCoordinates(!value);
|
||||
reducedDebugInfo = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a gamerule value to the client
|
||||
*
|
||||
* @param gameRule The gamerule to send
|
||||
* @param value The value of the gamerule
|
||||
*/
|
||||
public void sendGameRule(String gameRule, Object value) {
|
||||
GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket();
|
||||
gameRulesChangedPacket.getGameRules().add(new GameRuleData<>(gameRule, value));
|
||||
upstream.sendPacket(gameRulesChangedPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given session's player has a permission
|
||||
*
|
||||
* @param permission The permission node to check
|
||||
* @return true if the player has the requested permission, false if not
|
||||
*/
|
||||
public Boolean hasPermission(String permission) {
|
||||
return connector.getWorldManager().hasPermission(this, permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an AdventureSettingsPacket to the client with the latest flags
|
||||
*/
|
||||
public void sendAdventureSettings() {
|
||||
AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket();
|
||||
adventureSettingsPacket.setUniqueEntityId(playerEntity.getGeyserId());
|
||||
// Set command permission if OP permission level is high enough
|
||||
// This allows mobile players access to a GUI for doing commands. The commands there do not change above OPERATOR
|
||||
// and all commands there are accessible with OP permission level 2
|
||||
adventureSettingsPacket.setCommandPermission(opPermissionLevel >= 2 ? CommandPermission.OPERATOR : CommandPermission.NORMAL);
|
||||
// Required to make command blocks destroyable
|
||||
adventureSettingsPacket.setPlayerPermission(opPermissionLevel >= 2 ? PlayerPermission.OPERATOR : PlayerPermission.MEMBER);
|
||||
|
||||
// Update the noClip and worldImmutable values based on the current gamemode
|
||||
noClip = gameMode == GameMode.SPECTATOR;
|
||||
worldImmutable = gameMode == GameMode.ADVENTURE || gameMode == GameMode.SPECTATOR;
|
||||
|
||||
Set<AdventureSetting> flags = new HashSet<>();
|
||||
if (canFly) {
|
||||
flags.add(AdventureSetting.MAY_FLY);
|
||||
}
|
||||
|
||||
if (flying) {
|
||||
flags.add(AdventureSetting.FLYING);
|
||||
}
|
||||
|
||||
if (worldImmutable) {
|
||||
flags.add(AdventureSetting.WORLD_IMMUTABLE);
|
||||
}
|
||||
|
||||
if (noClip) {
|
||||
flags.add(AdventureSetting.NO_CLIP);
|
||||
}
|
||||
|
||||
flags.add(AdventureSetting.AUTO_JUMP);
|
||||
|
||||
adventureSettingsPacket.getSettings().addAll(flags);
|
||||
sendUpstreamPacket(adventureSettingsPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,17 +41,15 @@ public class UpstreamSession {
|
|||
private boolean initialized = false;
|
||||
|
||||
public void sendPacket(@NonNull BedrockPacket packet) {
|
||||
if (isClosed())
|
||||
return;
|
||||
|
||||
session.sendPacket(packet);
|
||||
if (!isClosed()) {
|
||||
session.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacketImmediately(@NonNull BedrockPacket packet) {
|
||||
if (isClosed())
|
||||
return;
|
||||
|
||||
session.sendPacketImmediately(packet);
|
||||
if (!isClosed()) {
|
||||
session.sendPacketImmediately(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect(String reason) {
|
||||
|
|
|
@ -27,22 +27,18 @@ package org.geysermc.connector.network.session.cache;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
||||
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import lombok.Getter;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.network.translators.world.chunk.ChunkPosition;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.geysermc.connector.utils.MathUtils;
|
||||
|
||||
public class ChunkCache {
|
||||
|
||||
private final boolean cache;
|
||||
|
||||
@Getter
|
||||
private Map<ChunkPosition, Column> chunks = new HashMap<>();
|
||||
private final Long2ObjectMap<Column> chunks = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
public ChunkCache(GeyserSession session) {
|
||||
if (session.getConnector().getWorldManager().getClass() == GeyserBootstrap.DEFAULT_CHUNK_MANAGER.getClass()) {
|
||||
|
@ -52,52 +48,74 @@ public class ChunkCache {
|
|||
}
|
||||
}
|
||||
|
||||
public void addToCache(Column chunk) {
|
||||
public Column addToCache(Column chunk) {
|
||||
if (!cache) {
|
||||
return;
|
||||
return chunk;
|
||||
}
|
||||
ChunkPosition position = new ChunkPosition(chunk.getX(), chunk.getZ());
|
||||
chunks.put(position, chunk);
|
||||
}
|
||||
|
||||
public void updateBlock(Position position, int block) {
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
ChunkPosition chunkPosition = new ChunkPosition(position.getX() >> 4, position.getZ() >> 4);
|
||||
if (!chunks.containsKey(chunkPosition))
|
||||
return;
|
||||
|
||||
Column column = chunks.get(chunkPosition);
|
||||
Chunk chunk = column.getChunks()[position.getY() >> 4];
|
||||
Position blockPosition = chunkPosition.getChunkBlock(position.getX(), position.getY(), position.getZ());
|
||||
if (chunk != null) {
|
||||
chunk.set(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), block);
|
||||
long chunkPosition = MathUtils.chunkPositionToLong(chunk.getX(), chunk.getZ());
|
||||
Column existingChunk;
|
||||
if (chunk.getBiomeData() == null // Only consider merging columns if the new chunk isn't a full chunk
|
||||
&& (existingChunk = chunks.getOrDefault(chunkPosition, null)) != null) { // Column is already present in cache, we can merge with existing
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < chunk.getChunks().length; i++) { // The chunks member is final, so chunk.getChunks() will probably be inlined and then completely optimized away
|
||||
if (chunk.getChunks()[i] != null) {
|
||||
existingChunk.getChunks()[i] = chunk.getChunks()[i];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed ? existingChunk : null;
|
||||
} else {
|
||||
chunks.put(chunkPosition, chunk);
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
public int getBlockAt(Position position) {
|
||||
public Column getChunk(int chunkX, int chunkZ) {
|
||||
long chunkPosition = MathUtils.chunkPositionToLong(chunkX, chunkZ);
|
||||
return chunks.getOrDefault(chunkPosition, null);
|
||||
}
|
||||
|
||||
public void updateBlock(int x, int y, int z, int block) {
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
Column column = this.getChunk(x >> 4, z >> 4);
|
||||
if (column == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Chunk chunk = column.getChunks()[y >> 4];
|
||||
if (chunk != null) {
|
||||
chunk.set(x & 0xF, y & 0xF, z & 0xF, block);
|
||||
}
|
||||
}
|
||||
|
||||
public int getBlockAt(int x, int y, int z) {
|
||||
if (!cache) {
|
||||
return BlockTranslator.AIR;
|
||||
}
|
||||
ChunkPosition chunkPosition = new ChunkPosition(position.getX() >> 4, position.getZ() >> 4);
|
||||
if (!chunks.containsKey(chunkPosition))
|
||||
return BlockTranslator.AIR;
|
||||
|
||||
Column column = chunks.get(chunkPosition);
|
||||
Chunk chunk = column.getChunks()[position.getY() >> 4];
|
||||
Position blockPosition = chunkPosition.getChunkBlock(position.getX(), position.getY(), position.getZ());
|
||||
Column column = this.getChunk(x >> 4, z >> 4);
|
||||
if (column == null) {
|
||||
return BlockTranslator.AIR;
|
||||
}
|
||||
|
||||
Chunk chunk = column.getChunks()[y >> 4];
|
||||
if (chunk != null) {
|
||||
return chunk.get(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
|
||||
return chunk.get(x & 0xF, y & 0xF, z & 0xF);
|
||||
}
|
||||
|
||||
return BlockTranslator.AIR;
|
||||
}
|
||||
|
||||
public void removeChunk(ChunkPosition position) {
|
||||
public void removeChunk(int chunkX, int chunkZ) {
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
chunks.remove(position);
|
||||
|
||||
long chunkPosition = MathUtils.chunkPositionToLong(chunkX, chunkZ);
|
||||
chunks.remove(chunkPosition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,9 +76,6 @@ public class EntityCache {
|
|||
if (entity != null && entity.isValid() && (force || entity.despawnEntity(session))) {
|
||||
long geyserId = entityIdTranslations.remove(entity.getEntityId());
|
||||
entities.remove(geyserId);
|
||||
if (entity.is(PlayerEntity.class)) {
|
||||
playerEntities.remove(entity.as(PlayerEntity.class).getUuid());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -25,31 +25,53 @@
|
|||
|
||||
package org.geysermc.connector.network.session.cache;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.scoreboard.Objective;
|
||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
|
||||
@Getter
|
||||
public class ScoreboardCache {
|
||||
private GeyserSession session;
|
||||
private Scoreboard scoreboard;
|
||||
public class WorldCache {
|
||||
private final GeyserSession session;
|
||||
@Setter
|
||||
private Difficulty difficulty = Difficulty.EASY;
|
||||
private boolean showCoordinates = true;
|
||||
|
||||
public ScoreboardCache(GeyserSession session) {
|
||||
private Scoreboard scoreboard;
|
||||
private final ScoreboardUpdater scoreboardUpdater;
|
||||
|
||||
public WorldCache(GeyserSession session) {
|
||||
this.session = session;
|
||||
this.scoreboard = new Scoreboard(session);
|
||||
scoreboardUpdater = new ScoreboardUpdater(this);
|
||||
scoreboardUpdater.start();
|
||||
}
|
||||
|
||||
public void removeScoreboard() {
|
||||
if (scoreboard != null) {
|
||||
Collection<Objective> objectives = scoreboard.getObjectives().values();
|
||||
scoreboard = new Scoreboard(session);
|
||||
|
||||
for (Objective objective : objectives) {
|
||||
for (Objective objective : scoreboard.getObjectives().values()) {
|
||||
scoreboard.despawnObjective(objective);
|
||||
}
|
||||
scoreboard = new Scoreboard(session);
|
||||
}
|
||||
}
|
||||
|
||||
public int increaseAndGetScoreboardPacketsPerSecond() {
|
||||
int pendingPps = scoreboardUpdater.incrementAndGetPacketsPerSecond();
|
||||
int pps = scoreboardUpdater.getPacketsPerSecond();
|
||||
return Math.max(pps, pendingPps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the client to hide or show the coordinates
|
||||
*
|
||||
* @param value True to show, false to hide
|
||||
*/
|
||||
public void setShowCoordinates(boolean value) {
|
||||
showCoordinates = value;
|
||||
session.sendGameRule("showcoordinates", value);
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
package org.geysermc.connector.network.translators;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerKeepAlivePacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPlayerListDataPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateLightPacket;
|
||||
import com.github.steveice10.packetlib.packet.Packet;
|
||||
|
@ -33,6 +32,7 @@ import com.nukkitx.protocol.bedrock.BedrockPacket;
|
|||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
|
@ -48,7 +48,7 @@ public class PacketTranslatorRegistry<T> {
|
|||
private static final ObjectArrayList<Class<?>> IGNORED_PACKETS = new ObjectArrayList<>();
|
||||
|
||||
static {
|
||||
Reflections ref = new Reflections("org.geysermc.connector.network.translators");
|
||||
Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators") : new Reflections("org.geysermc.connector.network.translators");
|
||||
|
||||
for (Class<?> clazz : ref.getTypesAnnotatedWith(Translator.class)) {
|
||||
Class<?> packet = clazz.getAnnotation(Translator.class).packet();
|
||||
|
@ -74,7 +74,6 @@ public class PacketTranslatorRegistry<T> {
|
|||
}
|
||||
}
|
||||
|
||||
IGNORED_PACKETS.add(ServerKeepAlivePacket.class); // Handled by MCProtocolLib
|
||||
IGNORED_PACKETS.add(ServerUpdateLightPacket.class); // Light is handled on Bedrock for us
|
||||
IGNORED_PACKETS.add(ServerPlayerListDataPacket.class); // Cant be implemented in bedrock
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket;
|
||||
import com.nukkitx.protocol.bedrock.data.AdventureSetting;
|
||||
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
|
||||
|
@ -38,14 +37,8 @@ public class BedrockAdventureSettingsTranslator extends PacketTranslator<Adventu
|
|||
|
||||
@Override
|
||||
public void translate(AdventureSettingsPacket packet, GeyserSession session) {
|
||||
// Only canFly and flying are used by the server
|
||||
// https://wiki.vg/Protocol#Player_Abilities_.28serverbound.29
|
||||
boolean canFly = packet.getSettings().contains(AdventureSetting.MAY_FLY);
|
||||
boolean flying = packet.getSettings().contains(AdventureSetting.FLYING);
|
||||
boolean creative = session.getGameMode() == GameMode.CREATIVE;
|
||||
ClientPlayerAbilitiesPacket abilitiesPacket = new ClientPlayerAbilitiesPacket(
|
||||
false, canFly, flying, creative
|
||||
);
|
||||
ClientPlayerAbilitiesPacket abilitiesPacket =
|
||||
new ClientPlayerAbilitiesPacket(packet.getSettings().contains(AdventureSetting.FLYING));
|
||||
session.sendDownstreamPacket(abilitiesPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,23 +26,18 @@
|
|||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientUpdateJigsawBlockPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientUpdateSignPacket;
|
||||
import com.nukkitx.nbt.NbtMap;
|
||||
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.geysermc.connector.utils.SignUtils;
|
||||
|
||||
@Translator(packet = BlockEntityDataPacket.class)
|
||||
public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEntityDataPacket> {
|
||||
|
||||
// In case two people are editing signs at the same time this array holds the temporary messages to be sent
|
||||
// Position -> Message being held
|
||||
protected static Map<Position, String> lastMessages = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void translate(BlockEntityDataPacket packet, GeyserSession session) {
|
||||
NbtMap tag = packet.getData();
|
||||
|
@ -50,9 +45,8 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
|
|||
// This is the reason why this all works - Bedrock sends packets every time you update the sign, Java only wants the final packet
|
||||
// But Bedrock sends one final packet when you're done editing the sign, which should be equal to the last message since there's no edits
|
||||
// So if the latest update does not match the last cached update then it's still being edited
|
||||
Position pos = new Position(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
|
||||
if (!tag.getString("Text").equals(lastMessages.get(pos))) {
|
||||
lastMessages.put(pos, tag.getString("Text"));
|
||||
if (!tag.getString("Text").equals(session.getLastSignMessage())) {
|
||||
session.setLastSignMessage(tag.getString("Text"));
|
||||
return;
|
||||
}
|
||||
// Otherwise the two messages are identical and we can get to work deconstructing
|
||||
|
@ -61,29 +55,73 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
|
|||
// (Initialized all with empty strings because it complains about null)
|
||||
String[] lines = new String[] {"", "", "", ""};
|
||||
int iterator = 0;
|
||||
// Keep track of the width of each character
|
||||
// If it goes over the maximum, we need to start a new line to match Java
|
||||
int widthCount = 0;
|
||||
// This converts the message into the array'd message Java wants
|
||||
for (char character : tag.getString("Text").toCharArray()) {
|
||||
// If we get a return in Bedrock, that signals to use the next line.
|
||||
if (character == '\n') {
|
||||
widthCount += SignUtils.getCharacterWidth(character);
|
||||
// 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
|
||||
boolean wentOverMax = widthCount > SignUtils.JAVA_CHARACTER_WIDTH_MAX && character != '\n';
|
||||
widthCount = 0;
|
||||
// Saves if we're moving a word to the next line
|
||||
String word = null;
|
||||
if (wentOverMax && iterator < lines.length - 1) {
|
||||
// If we went over the max, we want to try to wrap properly like Bedrock does.
|
||||
// So we look for a space in the Bedrock user's text to imply a word.
|
||||
int index = newMessage.lastIndexOf(" ");
|
||||
if (index != -1) {
|
||||
// There is indeed a space in this line; let's get it
|
||||
word = newMessage.substring(index + 1);
|
||||
// 'Delete' that word from the string builder
|
||||
newMessage.delete(index, newMessage.length());
|
||||
}
|
||||
}
|
||||
lines[iterator] = newMessage.toString();
|
||||
iterator++;
|
||||
// Bedrock, for whatever reason, can hold a message out of bounds
|
||||
// Bedrock, for whatever reason, can hold a message out of the bounds of the four lines
|
||||
// We don't care about that so we discard that
|
||||
if (iterator > lines.length - 1) {
|
||||
break;
|
||||
}
|
||||
newMessage = new StringBuilder();
|
||||
if (wentOverMax) {
|
||||
// Apply the wrapped word to the new line
|
||||
if (word != null) {
|
||||
newMessage.append(word);
|
||||
// And apply the width count
|
||||
for (char wordCharacter : word.toCharArray()) {
|
||||
widthCount += SignUtils.getCharacterWidth(wordCharacter);
|
||||
}
|
||||
}
|
||||
// If we went over the max, we want to append the character to the new line.
|
||||
newMessage.append(character);
|
||||
widthCount += SignUtils.getCharacterWidth(character);
|
||||
}
|
||||
} else newMessage.append(character);
|
||||
}
|
||||
// Put the final line on since it isn't done in the for loop
|
||||
if (iterator < lines.length) lines[iterator] = newMessage.toString();
|
||||
Position pos = new Position(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
|
||||
ClientUpdateSignPacket clientUpdateSignPacket = new ClientUpdateSignPacket(pos, lines);
|
||||
session.sendDownstreamPacket(clientUpdateSignPacket);
|
||||
//TODO (potentially): originally I was going to update the sign blocks so Bedrock and Java users would match visually
|
||||
// However Java can still store a lot per-line and visuals are still messed up so that doesn't work
|
||||
|
||||
// We remove the sign position from map to indicate there is no work-in-progress sign
|
||||
lastMessages.remove(pos);
|
||||
// We set the sign text cached in the session to null to indicate there is no work-in-progress sign
|
||||
session.setLastSignMessage(null);
|
||||
|
||||
} else if (tag.getString("id").equals("JigsawBlock")) {
|
||||
// Client has just sent a jigsaw block update
|
||||
Position pos = new Position(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
|
||||
String name = tag.getString("name");
|
||||
String target = tag.getString("target");
|
||||
String pool = tag.getString("target_pool");
|
||||
String finalState = tag.getString("final_state");
|
||||
String joint = tag.getString("joint");
|
||||
ClientUpdateJigsawBlockPacket jigsawPacket = new ClientUpdateJigsawBlockPacket(pos, name, target, pool,
|
||||
finalState, joint);
|
||||
session.sendDownstreamPacket(jigsawPacket);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.bedrock;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientMoveItemToHotbarPacket;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.packet.BlockPickRequestPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
|
||||
@Translator(packet = BlockPickRequestPacket.class)
|
||||
public class BedrockBlockPickRequestPacketTranslator extends PacketTranslator<BlockPickRequestPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(BlockPickRequestPacket packet, GeyserSession session) {
|
||||
Vector3i vector = packet.getBlockPosition();
|
||||
int blockToPick = session.getConnector().getWorldManager().getBlockAt(session, vector.getX(), vector.getY(), vector.getZ());
|
||||
|
||||
// Block is air - chunk caching is probably off
|
||||
if (blockToPick == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the inventory to choose a slot to pick
|
||||
Inventory inventory = session.getInventoryCache().getOpenInventory();
|
||||
if (inventory == null) {
|
||||
inventory = session.getInventory();
|
||||
}
|
||||
|
||||
String targetIdentifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockToPick).split("\\[")[0];
|
||||
|
||||
// Check hotbar for item
|
||||
for (int i = 36; i < 45; i++) {
|
||||
if (inventory.getItem(i) == null) {
|
||||
continue;
|
||||
}
|
||||
ItemEntry item = ItemRegistry.getItem(inventory.getItem(i));
|
||||
// If this isn't the item we're looking for
|
||||
if (!item.getJavaIdentifier().equals(targetIdentifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
|
||||
hotbarPacket.setContainerId(0);
|
||||
// Java inventory slot to hotbar slot ID
|
||||
hotbarPacket.setSelectedHotbarSlot(i - 36);
|
||||
hotbarPacket.setSelectHotbarSlot(true);
|
||||
session.sendUpstreamPacket(hotbarPacket);
|
||||
session.getInventory().setHeldItemSlot(i - 36);
|
||||
// Don't check inventory if item was in hotbar
|
||||
return;
|
||||
}
|
||||
|
||||
// Check inventory for item
|
||||
for (int i = 9; i < 36; i++) {
|
||||
if (inventory.getItem(i) == null) {
|
||||
continue;
|
||||
}
|
||||
ItemEntry item = ItemRegistry.getItem(inventory.getItem(i));
|
||||
// If this isn't the item we're looking for
|
||||
if (!item.getJavaIdentifier().equals(targetIdentifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ClientMoveItemToHotbarPacket packetToSend = new ClientMoveItemToHotbarPacket(i); // https://wiki.vg/Protocol#Pick_Item
|
||||
session.sendDownstreamPacket(packetToSend);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.bedrock;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.packet.BlockPickRequestPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
|
||||
@Translator(packet = BlockPickRequestPacket.class)
|
||||
public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPickRequestPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(BlockPickRequestPacket packet, GeyserSession session) {
|
||||
Vector3i vector = packet.getBlockPosition();
|
||||
int blockToPick = session.getConnector().getWorldManager().getBlockAt(session, vector.getX(), vector.getY(), vector.getZ());
|
||||
|
||||
// Block is air - chunk caching is probably off
|
||||
if (blockToPick == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
String targetIdentifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockToPick).split("\\[")[0];
|
||||
InventoryUtils.findOrCreatePickedBlock(session, targetIdentifier);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.bedrock;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.CommandBlockMode;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientUpdateCommandBlockMinecartPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientUpdateCommandBlockPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.CommandBlockUpdatePacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
@Translator(packet = CommandBlockUpdatePacket.class)
|
||||
public class BedrockCommandBlockUpdateTranslator extends PacketTranslator<CommandBlockUpdatePacket> {
|
||||
|
||||
@Override
|
||||
public void translate(CommandBlockUpdatePacket packet, GeyserSession session) {
|
||||
String command = packet.getCommand();
|
||||
boolean outputTracked = packet.isOutputTracked();
|
||||
if (packet.isBlock()) {
|
||||
CommandBlockMode mode;
|
||||
switch (packet.getMode()) {
|
||||
case CHAIN: // The green one
|
||||
mode = CommandBlockMode.SEQUENCE;
|
||||
break;
|
||||
case REPEATING: // The purple one
|
||||
mode = CommandBlockMode.AUTO;
|
||||
break;
|
||||
default: // NORMAL, the orange one
|
||||
mode = CommandBlockMode.REDSTONE;
|
||||
break;
|
||||
}
|
||||
boolean isConditional = packet.isConditional();
|
||||
boolean automatic = !packet.isRedstoneMode(); // Automatic = Always Active option in Java
|
||||
ClientUpdateCommandBlockPacket commandBlockPacket = new ClientUpdateCommandBlockPacket(
|
||||
new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()),
|
||||
command, mode, outputTracked, isConditional, automatic);
|
||||
session.sendDownstreamPacket(commandBlockPacket);
|
||||
} else {
|
||||
ClientUpdateCommandBlockMinecartPacket commandMinecartPacket = new ClientUpdateCommandBlockMinecartPacket(
|
||||
(int) session.getEntityCache().getEntityByGeyserId(packet.getMinecartRuntimeEntityId()).getEntityId(),
|
||||
command, outputTracked
|
||||
);
|
||||
session.sendDownstreamPacket(commandMinecartPacket);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.bedrock;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.packet.EntityPickRequestPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
|
||||
/**
|
||||
* Called when the Bedrock user uses the pick block button on an entity
|
||||
*/
|
||||
@Translator(packet = EntityPickRequestPacket.class)
|
||||
public class BedrockEntityPickRequestTranslator extends PacketTranslator<EntityPickRequestPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(EntityPickRequestPacket packet, GeyserSession session) {
|
||||
if (session.getGameMode() != GameMode.CREATIVE) return; // Apparently Java behavior
|
||||
Entity entity = session.getEntityCache().getEntityByGeyserId(packet.getRuntimeEntityId());
|
||||
if (entity == null) return;
|
||||
|
||||
// Get the corresponding item
|
||||
String itemName;
|
||||
switch (entity.getEntityType()) {
|
||||
case BOAT:
|
||||
// Include type of boat in the name
|
||||
int variant = entity.getMetadata().getInt(EntityData.VARIANT);
|
||||
String typeOfBoat;
|
||||
switch (variant) {
|
||||
case 1:
|
||||
typeOfBoat = "spruce";
|
||||
break;
|
||||
case 2:
|
||||
typeOfBoat = "birch";
|
||||
break;
|
||||
case 3:
|
||||
typeOfBoat = "jungle";
|
||||
break;
|
||||
case 4:
|
||||
typeOfBoat = "acacia";
|
||||
break;
|
||||
case 5:
|
||||
typeOfBoat = "dark_oak";
|
||||
break;
|
||||
default:
|
||||
typeOfBoat = "oak";
|
||||
break;
|
||||
}
|
||||
itemName = typeOfBoat + "_boat";
|
||||
break;
|
||||
case LEASH_KNOT:
|
||||
itemName = "lead";
|
||||
break;
|
||||
case MINECART_CHEST:
|
||||
case MINECART_COMMAND_BLOCK:
|
||||
case MINECART_FURNACE:
|
||||
case MINECART_HOPPER:
|
||||
case MINECART_TNT:
|
||||
// Move MINECART to the end of the name
|
||||
itemName = entity.getEntityType().toString().toLowerCase().replace("minecart_", "") + "_minecart";
|
||||
break;
|
||||
case MINECART_SPAWNER:
|
||||
// Turns into a normal minecart
|
||||
itemName = "minecart";
|
||||
break;
|
||||
case ARMOR_STAND:
|
||||
case END_CRYSTAL:
|
||||
case ITEM_FRAME:
|
||||
case MINECART:
|
||||
case PAINTING:
|
||||
// No spawn egg, just an item
|
||||
itemName = entity.getEntityType().toString().toLowerCase();
|
||||
break;
|
||||
default:
|
||||
itemName = entity.getEntityType().toString().toLowerCase() + "_spawn_egg";
|
||||
break;
|
||||
}
|
||||
|
||||
String fullItemName = "minecraft:" + itemName;
|
||||
ItemEntry entry = ItemRegistry.getItemEntry(fullItemName);
|
||||
// Verify it is, indeed, an item
|
||||
if (entry == null) return;
|
||||
|
||||
InventoryUtils.findOrCreatePickedBlock(session, fullItemName);
|
||||
}
|
||||
}
|
|
@ -39,8 +39,13 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye
|
|||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.ItemFrameEntity;
|
||||
import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity;
|
||||
|
@ -53,8 +58,11 @@ import org.geysermc.connector.network.translators.item.ItemEntry;
|
|||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.BlockUtils;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Translator(packet = InventoryTransactionPacket.class)
|
||||
public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> {
|
||||
|
||||
|
@ -75,6 +83,17 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
case ITEM_USE:
|
||||
switch (packet.getActionType()) {
|
||||
case 0:
|
||||
// Check to make sure the client isn't spamming interaction
|
||||
// Based on Nukkit 1.0, with changes to ensure holding down still works
|
||||
boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 &&
|
||||
packet.getBlockPosition().distanceSquared(session.getLastInteractionPosition()) < 0.00001;
|
||||
session.setLastInteractionPosition(packet.getBlockPosition());
|
||||
if (hasAlreadyClicked) {
|
||||
break;
|
||||
} else {
|
||||
// Only update the interaction time if it's valid - that way holding down still works.
|
||||
session.setLastInteractionTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
// Bedrock sends block interact code for a Java entity so we send entity code back to Java
|
||||
if (BlockTranslator.isItemFrame(packet.getBlockRuntimeId()) &&
|
||||
|
@ -98,39 +117,50 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
session.sendDownstreamPacket(blockPacket);
|
||||
|
||||
// Otherwise boats will not be able to be placed in survival and buckets wont work on mobile
|
||||
if (packet.getItemInHand() != null && (packet.getItemInHand().getId() == ItemRegistry.BOAT.getBedrockId() || packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId())) {
|
||||
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||
session.sendDownstreamPacket(itemPacket);
|
||||
if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BOAT.getBedrockId()) {
|
||||
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||
session.sendDownstreamPacket(itemPacket);
|
||||
}
|
||||
// Check actions, otherwise buckets may be activated when block inventories are accessed
|
||||
else if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId()) {
|
||||
// Let the server decide if the bucket item should change, not the client, and revert the changes the client made
|
||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||
slotPacket.setContainerId(ContainerId.INVENTORY);
|
||||
slotPacket.setSlot(packet.getHotbarSlot());
|
||||
slotPacket.setItem(packet.getItemInHand());
|
||||
session.sendUpstreamPacket(slotPacket);
|
||||
// Delay the interaction in case the client doesn't intend to actually use the bucket
|
||||
// See BedrockActionTranslator.java
|
||||
session.setBucketScheduledFuture(session.getConnector().getGeneralThreadPool().schedule(() -> {
|
||||
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||
session.sendDownstreamPacket(itemPacket);
|
||||
}, 5, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
Vector3i blockPos = packet.getBlockPosition();
|
||||
// TODO: Find a better way to do this?
|
||||
switch (packet.getBlockFace()) {
|
||||
case 0:
|
||||
blockPos = blockPos.sub(0, 1, 0);
|
||||
break;
|
||||
case 1:
|
||||
blockPos = blockPos.add(0, 1, 0);
|
||||
break;
|
||||
case 2:
|
||||
blockPos = blockPos.sub(0, 0, 1);
|
||||
break;
|
||||
case 3:
|
||||
blockPos = blockPos.add(0, 0, 1);
|
||||
break;
|
||||
case 4:
|
||||
blockPos = blockPos.sub(1, 0, 0);
|
||||
break;
|
||||
case 5:
|
||||
blockPos = blockPos.add(1, 0, 0);
|
||||
break;
|
||||
if (packet.getActions().isEmpty()) {
|
||||
if (session.getOpPermissionLevel() >= 2 && session.getGameMode() == GameMode.CREATIVE) {
|
||||
// Otherwise insufficient permissions
|
||||
int blockState = BlockTranslator.getJavaBlockState(packet.getBlockRuntimeId());
|
||||
String blockName = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(blockState, "");
|
||||
// In the future this can be used for structure blocks too, however not all elements
|
||||
// are available in each GUI
|
||||
if (blockName.contains("jigsaw")) {
|
||||
ContainerOpenPacket openPacket = new ContainerOpenPacket();
|
||||
openPacket.setBlockPosition(packet.getBlockPosition());
|
||||
openPacket.setId((byte) 1);
|
||||
openPacket.setType(ContainerType.JIGSAW_EDITOR);
|
||||
openPacket.setUniqueEntityId(-1);
|
||||
session.sendUpstreamPacket(openPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3i blockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getBlockFace());
|
||||
ItemEntry handItem = ItemRegistry.getItem(packet.getItemInHand());
|
||||
if (handItem.isBlock()) {
|
||||
session.setLastBlockPlacePosition(blockPos);
|
||||
session.setLastBlockPlacedId(handItem.getJavaIdentifier());
|
||||
}
|
||||
session.setLastInteractionPosition(packet.getBlockPosition());
|
||||
session.setInteracting(true);
|
||||
break;
|
||||
case 1:
|
||||
|
@ -140,15 +170,14 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
break;
|
||||
}
|
||||
|
||||
// Handled in ITEM_USE
|
||||
if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId()) {
|
||||
// Handled in ITEM_USE if the item is not milk
|
||||
if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId() &&
|
||||
packet.getItemInHand().getDamage() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||
session.sendDownstreamPacket(useItemPacket);
|
||||
// Used for sleeping in beds
|
||||
session.setLastInteractionPosition(packet.getBlockPosition());
|
||||
break;
|
||||
case 2:
|
||||
int blockState = session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ());
|
||||
|
@ -195,6 +224,18 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
//https://wiki.vg/Protocol#Interact_Entity
|
||||
switch (packet.getActionType()) {
|
||||
case 0: //Interact
|
||||
if (entity instanceof CommandBlockMinecartEntity) {
|
||||
// The UI is handled client-side on Java Edition
|
||||
// Ensure OP permission level and gamemode is appropriate
|
||||
if (session.getOpPermissionLevel() < 2 || session.getGameMode() != GameMode.CREATIVE) return;
|
||||
ContainerOpenPacket openPacket = new ContainerOpenPacket();
|
||||
openPacket.setBlockPosition(Vector3i.ZERO);
|
||||
openPacket.setId((byte) 1);
|
||||
openPacket.setType(ContainerType.COMMAND_BLOCK);
|
||||
openPacket.setUniqueEntityId(entity.getGeyserId());
|
||||
session.sendUpstreamPacket(openPacket);
|
||||
break;
|
||||
}
|
||||
Vector3f vector = packet.getClickPosition();
|
||||
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
||||
InteractAction.INTERACT, Hand.MAIN_HAND, session.isSneaking());
|
||||
|
|
|
@ -40,7 +40,8 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||
@Override
|
||||
public void translate(MobEquipmentPacket packet, GeyserSession session) {
|
||||
if (!session.isSpawned() || packet.getHotbarSlot() > 8 ||
|
||||
packet.getContainerId() != ContainerId.INVENTORY) {
|
||||
packet.getContainerId() != ContainerId.INVENTORY || session.getInventory().getHeldItemSlot() == packet.getHotbarSlot()) {
|
||||
// For the last condition - Don't update the slot if the slot is the same - not Java Edition behavior and messes with plugins such as Grief Prevention
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.bedrock;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientKeepAlivePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.NetworkStackLatencyPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
/**
|
||||
* Used to send the keep alive packet back to the server
|
||||
*/
|
||||
@Translator(packet = NetworkStackLatencyPacket.class)
|
||||
public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<NetworkStackLatencyPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(NetworkStackLatencyPacket packet, GeyserSession session) {
|
||||
// The client sends a timestamp back but it's rounded and therefore unreliable when we need the exact number
|
||||
ClientKeepAlivePacket keepAlivePacket = new ClientKeepAlivePacket(session.getLastKeepAliveTimestamp());
|
||||
session.sendDownstreamPacket(keepAlivePacket);
|
||||
}
|
||||
}
|
|
@ -28,7 +28,10 @@ package org.geysermc.connector.network.translators.bedrock;
|
|||
import com.github.steveice10.mc.protocol.data.game.ClientRequest;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientRequestPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.RespawnPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
@ -39,12 +42,30 @@ public class BedrockRespawnTranslator extends PacketTranslator<RespawnPacket> {
|
|||
@Override
|
||||
public void translate(RespawnPacket packet, GeyserSession session) {
|
||||
if (packet.getState() == RespawnPacket.State.CLIENT_READY) {
|
||||
if (!session.isSpawned()) { // Otherwise when immediate respawn is on the client never loads
|
||||
RespawnPacket respawnPacket = new RespawnPacket();
|
||||
respawnPacket.setRuntimeEntityId(0);
|
||||
respawnPacket.setPosition(Vector3f.ZERO);
|
||||
respawnPacket.setState(RespawnPacket.State.SERVER_SEARCHING);
|
||||
session.sendUpstreamPacket(respawnPacket);
|
||||
// Previously we only sent the respawn packet before the server finished loading
|
||||
// The message included was 'Otherwise when immediate respawn is on the client never loads'
|
||||
// But I assume the new if statement below fixes that problem
|
||||
RespawnPacket respawnPacket = new RespawnPacket();
|
||||
respawnPacket.setRuntimeEntityId(0);
|
||||
respawnPacket.setPosition(Vector3f.ZERO);
|
||||
respawnPacket.setState(RespawnPacket.State.SERVER_READY);
|
||||
session.sendUpstreamPacket(respawnPacket);
|
||||
|
||||
if (session.isSpawned()) {
|
||||
// Client might be stuck; resend spawn information
|
||||
PlayerEntity entity = session.getPlayerEntity();
|
||||
if (entity == null) return;
|
||||
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
||||
entityDataPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
entityDataPacket.getMetadata().putAll(entity.getMetadata());
|
||||
session.sendUpstreamPacket(entityDataPacket);
|
||||
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
movePlayerPacket.setPosition(entity.getPosition());
|
||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
}
|
||||
|
||||
ClientRequestPacket javaRespawnPacket = new ClientRequestPacket(ClientRequest.RESPAWN);
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.bedrock;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.packet.ServerSettingsRequestPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.ServerSettingsResponsePacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.SettingsUtils;
|
||||
|
||||
@Translator(packet = ServerSettingsRequestPacket.class)
|
||||
public class BedrockServerSettingsRequestTranslator extends PacketTranslator<ServerSettingsRequestPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerSettingsRequestPacket packet, GeyserSession session) {
|
||||
SettingsUtils.buildForm(session);
|
||||
|
||||
ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket();
|
||||
serverSettingsResponsePacket.setFormData(session.getSettingsForm().getJSONData());
|
||||
serverSettingsResponsePacket.setFormId(SettingsUtils.SETTINGS_FORM_ID);
|
||||
session.sendUpstreamPacket(serverSettingsResponsePacket);
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
package org.geysermc.connector.network.translators.bedrock.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
|
||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
|
@ -23,10 +23,9 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
package org.geysermc.connector.network.translators.bedrock.entity.player;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
||||
|
@ -45,6 +44,7 @@ import org.geysermc.connector.network.session.GeyserSession;
|
|||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.BlockUtils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -79,9 +79,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
|||
break;
|
||||
case START_GLIDE:
|
||||
// Otherwise gliding will not work in creative
|
||||
ClientPlayerAbilitiesPacket playerAbilitiesPacket = new ClientPlayerAbilitiesPacket(
|
||||
false, false, false, session.getGameMode() == GameMode.CREATIVE
|
||||
);
|
||||
ClientPlayerAbilitiesPacket playerAbilitiesPacket = new ClientPlayerAbilitiesPacket(false);
|
||||
session.sendDownstreamPacket(playerAbilitiesPacket);
|
||||
case STOP_GLIDE:
|
||||
ClientPlayerStatePacket glidePacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_ELYTRA_FLYING);
|
||||
|
@ -116,9 +114,26 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
|||
session.sendDownstreamPacket(stopSleepingPacket);
|
||||
break;
|
||||
case BLOCK_INTERACT:
|
||||
// Handled in BedrockInventoryTransactionTranslator
|
||||
// Client means to interact with a block; cancel bucket interaction, if any
|
||||
if (session.getBucketScheduledFuture() != null) {
|
||||
session.getBucketScheduledFuture().cancel(true);
|
||||
session.setBucketScheduledFuture(null);
|
||||
}
|
||||
// Otherwise handled in BedrockInventoryTransactionTranslator
|
||||
break;
|
||||
case START_BREAK:
|
||||
if (session.getConnector().getConfig().isCacheChunks()) {
|
||||
// Account for fire - the client likes to hit the block behind.
|
||||
Vector3i fireBlockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getFace());
|
||||
int blockUp = session.getConnector().getWorldManager().getBlockAt(session, fireBlockPos);
|
||||
String identifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockUp);
|
||||
if (identifier.startsWith("minecraft:fire") || identifier.startsWith("minecraft:soul_fire")) {
|
||||
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(fireBlockPos.getX(),
|
||||
fireBlockPos.getY(), fireBlockPos.getZ()), BlockFace.values()[packet.getFace()]);
|
||||
session.sendDownstreamPacket(startBreakingPacket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(packet.getBlockPosition().getX(),
|
||||
packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), BlockFace.values()[packet.getFace()]);
|
||||
session.sendDownstreamPacket(startBreakingPacket);
|
|
@ -23,10 +23,10 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
package org.geysermc.connector.network.translators.bedrock.entity.player;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.packet.EmotePacket;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
@ -37,9 +37,12 @@ public class BedrockEmoteTranslator extends PacketTranslator<EmotePacket> {
|
|||
@Override
|
||||
public void translate(EmotePacket packet, GeyserSession session) {
|
||||
long javaId = session.getPlayerEntity().getEntityId();
|
||||
for (GeyserSession otherSession : GeyserConnector.getInstance().getPlayers()) {
|
||||
for (GeyserSession otherSession : session.getConnector().getPlayers()) {
|
||||
if (otherSession != session) {
|
||||
packet.setRuntimeEntityId(otherSession.getEntityCache().getEntityByJavaId(javaId).getGeyserId());
|
||||
if (otherSession.isClosed()) continue;
|
||||
Entity otherEntity = otherSession.getEntityCache().getEntityByJavaId(javaId);
|
||||
if (otherEntity == null) continue;
|
||||
packet.setRuntimeEntityId(otherEntity.getGeyserId());
|
||||
otherSession.sendUpstreamPacket(packet);
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
package org.geysermc.connector.network.translators.bedrock.entity.player;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
|
|
@ -23,7 +23,7 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
package org.geysermc.connector.network.translators.bedrock.entity.player;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3d;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
|
@ -86,6 +86,13 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
|||
entity.setPosition(packet.getPosition().sub(0, EntityType.PLAYER.getOffset(), 0));
|
||||
entity.setRotation(rotation);
|
||||
entity.setOnGround(packet.isOnGround());
|
||||
// Move parrots to match if applicable
|
||||
if (entity.getLeftParrot() != null) {
|
||||
entity.getLeftParrot().moveAbsolute(session, entity.getPosition(), entity.getRotation(), true, false);
|
||||
}
|
||||
if (entity.getRightParrot() != null) {
|
||||
entity.getRightParrot().moveAbsolute(session, entity.getPosition(), entity.getRotation(), true, false);
|
||||
}
|
||||
|
||||
/*
|
||||
boolean colliding = false;
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.bedrock.entity.player;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
/**
|
||||
* In vanilla Bedrock, if you have operator status, this sets the player's gamemode without confirmation from the server.
|
||||
* Since we have a custom server option to request the gamemode, we just reset the gamemode and ignore this.
|
||||
*/
|
||||
@Translator(packet = SetPlayerGameTypePacket.class)
|
||||
public class BedrockSetPlayerGameTypeTranslator extends PacketTranslator<SetPlayerGameTypePacket> {
|
||||
|
||||
@Override
|
||||
public void translate(SetPlayerGameTypePacket packet, GeyserSession session) {
|
||||
// no
|
||||
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
||||
playerGameTypePacket.setGamemode(session.getGameMode().ordinal());
|
||||
session.sendUpstreamPacket(playerGameTypePacket);
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
package org.geysermc.connector.network.translators.bedrock.world;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.data.SoundEvent;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
|
|
@ -32,10 +32,11 @@ import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
|||
import com.nukkitx.protocol.bedrock.data.SoundEvent;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import lombok.NonNull;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
|
@ -50,8 +51,19 @@ public class EffectRegistry {
|
|||
public static final Map<SoundEffect, Effect> SOUND_EFFECTS = new HashMap<>();
|
||||
public static final Int2ObjectMap<SoundEvent> RECORDS = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static Map<ParticleType, LevelEventType> particleTypeMap = new HashMap<>();
|
||||
private static Map<ParticleType, String> particleStringMap = new HashMap<>();
|
||||
/**
|
||||
* Java particle type to Bedrock particle ID
|
||||
* Used for area effect clouds.
|
||||
*/
|
||||
private static final Object2IntMap<ParticleType> PARTICLE_TO_ID = new Object2IntOpenHashMap<>();
|
||||
/**
|
||||
* Java particle type to Bedrock level event
|
||||
*/
|
||||
private static final Map<ParticleType, LevelEventType> PARTICLE_TO_LEVEL_EVENT = new HashMap<>();
|
||||
/**
|
||||
* Java particle type to Bedrock namespaced string ID
|
||||
*/
|
||||
private static final Map<ParticleType, String> PARTICLE_TO_STRING = new HashMap<>();
|
||||
|
||||
public static void init() {
|
||||
// no-op
|
||||
|
@ -68,22 +80,24 @@ public class EffectRegistry {
|
|||
}
|
||||
|
||||
Iterator<Map.Entry<String, JsonNode>> particlesIterator = particleEntries.fields();
|
||||
while (particlesIterator.hasNext()) {
|
||||
Map.Entry<String, JsonNode> entry = particlesIterator.next();
|
||||
try {
|
||||
particleTypeMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase()));
|
||||
} catch (IllegalArgumentException e1) {
|
||||
try {
|
||||
particleStringMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText());
|
||||
GeyserConnector.getInstance().getLogger().debug("Force to map particle "
|
||||
+ entry.getKey()
|
||||
+ "=>"
|
||||
+ entry.getValue().asText()
|
||||
+ ", it will take effect.");
|
||||
} catch (IllegalArgumentException e2){
|
||||
GeyserConnector.getInstance().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.particle.failed_map", entry.getKey(), entry.getValue().asText()));
|
||||
try {
|
||||
while (particlesIterator.hasNext()) {
|
||||
Map.Entry<String, JsonNode> entry = particlesIterator.next();
|
||||
JsonNode bedrockId = entry.getValue().get("bedrockId");
|
||||
JsonNode bedrockIdNumeric = entry.getValue().get("bedrockNumericId");
|
||||
JsonNode eventType = entry.getValue().get("eventType");
|
||||
if (bedrockIdNumeric != null) {
|
||||
PARTICLE_TO_ID.put(ParticleType.valueOf(entry.getKey().toUpperCase()), bedrockIdNumeric.asInt());
|
||||
}
|
||||
if (bedrockId != null) {
|
||||
PARTICLE_TO_STRING.put(ParticleType.valueOf(entry.getKey().toUpperCase()), bedrockId.asText());
|
||||
}
|
||||
if (eventType != null) {
|
||||
PARTICLE_TO_LEVEL_EVENT.put(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(eventType.asText().toUpperCase()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
/* Load effects */
|
||||
|
@ -149,11 +163,27 @@ public class EffectRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) {
|
||||
return particleTypeMap.getOrDefault(type, null);
|
||||
/**
|
||||
* @param type the Java particle to search for
|
||||
* @return the Bedrock integer ID of the particle, or -1 if it does not exist
|
||||
*/
|
||||
public static int getParticleId(@NonNull ParticleType type) {
|
||||
return PARTICLE_TO_ID.getOrDefault(type, -1);
|
||||
}
|
||||
|
||||
public static String getParticleString(@NonNull ParticleType type){
|
||||
return particleStringMap.getOrDefault(type, null);
|
||||
/**
|
||||
* @param type the Java particle to search for
|
||||
* @return the level event equivalent Bedrock particle
|
||||
*/
|
||||
public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) {
|
||||
return PARTICLE_TO_LEVEL_EVENT.getOrDefault(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type the Java particle to search for
|
||||
* @return the namespaced ID equivalent for Bedrock
|
||||
*/
|
||||
public static String getParticleString(@NonNull ParticleType type) {
|
||||
return PARTICLE_TO_STRING.getOrDefault(type, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,12 +30,9 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
|||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.InventorySource;
|
||||
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;
|
||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -48,7 +45,7 @@ public class CraftingInventoryTranslator extends BlockInventoryTranslator {
|
|||
@Override
|
||||
public int bedrockSlotToJava(InventoryActionData action) {
|
||||
if (action.getSlot() == 50) {
|
||||
GeyserConnector.getInstance().getLogger().warning("Slot 50 found, please report: " + action);
|
||||
// Slot 50 is used for crafting with a controller.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,18 +25,243 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.inventory;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket;
|
||||
import com.nukkitx.nbt.NbtMap;
|
||||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import com.nukkitx.nbt.NbtType;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||
import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater;
|
||||
import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater;
|
||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A temporary reconstruction of the enchantment table UI until our inventory rewrite is complete.
|
||||
* The enchantment table on Bedrock without server authoritative inventories doesn't tell us which button is pressed
|
||||
* when selecting an enchantment.
|
||||
*/
|
||||
public class EnchantmentInventoryTranslator extends BlockInventoryTranslator {
|
||||
public EnchantmentInventoryTranslator() {
|
||||
super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, new ContainerInventoryUpdater());
|
||||
|
||||
private static final int DYE_ID = 351;
|
||||
private static final short LAPIS_DAMAGE = 4;
|
||||
private static final int ENCHANTED_BOOK_ID = 403;
|
||||
|
||||
public EnchantmentInventoryTranslator(InventoryUpdater updater) {
|
||||
super(2, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, updater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
|
||||
for (InventoryActionData action : actions) {
|
||||
if (action.getSource().getContainerId() == inventory.getId()) {
|
||||
// This is the hopper UI
|
||||
switch (action.getSlot()) {
|
||||
case 1:
|
||||
// Don't allow the slot to be put through if the item isn't lapis
|
||||
if ((action.getToItem().getId() != DYE_ID
|
||||
&& action.getToItem().getDamage() != LAPIS_DAMAGE) && action.getToItem() != ItemData.AIR) {
|
||||
updateInventory(session, inventory);
|
||||
InventoryUtils.updateCursor(session);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
// The books here act as buttons
|
||||
ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), action.getSlot() - 2);
|
||||
session.sendDownstreamPacket(packet);
|
||||
updateInventory(session, inventory);
|
||||
InventoryUtils.updateCursor(session);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.translateActions(session, inventory, actions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateInventory(GeyserSession session, Inventory inventory) {
|
||||
super.updateInventory(session, inventory);
|
||||
ItemData[] items = new ItemData[5];
|
||||
items[0] = ItemTranslator.translateToBedrock(session, inventory.getItem(0));
|
||||
items[1] = ItemTranslator.translateToBedrock(session, inventory.getItem(1));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
items[i + 2] = session.getEnchantmentSlotData()[i].getItem() != null ? session.getEnchantmentSlotData()[i].getItem() : createEnchantmentBook();
|
||||
}
|
||||
|
||||
InventoryContentPacket contentPacket = new InventoryContentPacket();
|
||||
contentPacket.setContainerId(inventory.getId());
|
||||
contentPacket.setContents(items);
|
||||
session.sendUpstreamPacket(contentPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) {
|
||||
int bookSlotToUpdate;
|
||||
switch (key) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
// Experience required
|
||||
bookSlotToUpdate = key;
|
||||
session.getEnchantmentSlotData()[bookSlotToUpdate].setExperienceRequired(value);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
// Enchantment name
|
||||
bookSlotToUpdate = key - 4;
|
||||
if (value != -1) {
|
||||
session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentType(EnchantmentTableEnchantments.values()[value - 1]);
|
||||
} else {
|
||||
// -1 means no enchantment specified
|
||||
session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentType(null);
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
// Enchantment level
|
||||
bookSlotToUpdate = key - 7;
|
||||
session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentLevel(value);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
updateEnchantmentBook(session, inventory, bookSlotToUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openInventory(GeyserSession session, Inventory inventory) {
|
||||
super.openInventory(session, inventory);
|
||||
for (int i = 0; i < session.getEnchantmentSlotData().length; i++) {
|
||||
session.getEnchantmentSlotData()[i] = new EnchantmentSlotData();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeInventory(GeyserSession session, Inventory inventory) {
|
||||
super.closeInventory(session, inventory);
|
||||
Arrays.fill(session.getEnchantmentSlotData(), null);
|
||||
}
|
||||
|
||||
private ItemData createEnchantmentBook() {
|
||||
NbtMapBuilder root = NbtMap.builder();
|
||||
NbtMapBuilder display = NbtMap.builder();
|
||||
|
||||
display.putString("Name", ChatColor.RESET + "No Enchantment");
|
||||
|
||||
root.put("display", display.build());
|
||||
return ItemData.of(ENCHANTED_BOOK_ID, (short) 0, 1, root.build());
|
||||
}
|
||||
|
||||
private void updateEnchantmentBook(GeyserSession session, Inventory inventory, int slot) {
|
||||
NbtMapBuilder root = NbtMap.builder();
|
||||
NbtMapBuilder display = NbtMap.builder();
|
||||
EnchantmentSlotData data = session.getEnchantmentSlotData()[slot];
|
||||
if (data.getEnchantmentType() != null) {
|
||||
display.putString("Name", ChatColor.ITALIC + data.getEnchantmentType().toString(session) +
|
||||
(data.getEnchantmentLevel() != -1 ? " " + toRomanNumeral(session, data.getEnchantmentLevel()) : "") + "?");
|
||||
} else {
|
||||
display.putString("Name", ChatColor.RESET + "No Enchantment");
|
||||
}
|
||||
|
||||
display.putList("Lore", NbtType.STRING, Collections.singletonList(ChatColor.DARK_GRAY + data.getExperienceRequired() + "xp"));
|
||||
root.put("display", display.build());
|
||||
ItemData book = ItemData.of(ENCHANTED_BOOK_ID, (short) 0, 1, root.build());
|
||||
|
||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||
slotPacket.setContainerId(inventory.getId());
|
||||
slotPacket.setSlot(slot + 2);
|
||||
slotPacket.setItem(book);
|
||||
session.sendUpstreamPacket(slotPacket);
|
||||
data.setItem(book);
|
||||
}
|
||||
|
||||
private String toRomanNumeral(GeyserSession session, int level) {
|
||||
return LocaleUtils.getLocaleString("enchantment.level." + level,
|
||||
session.getClientData().getLanguageCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the data of each slot in an enchantment table
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public static class EnchantmentSlotData {
|
||||
private EnchantmentTableEnchantments enchantmentType = null;
|
||||
private int enchantmentLevel = 0;
|
||||
private int experienceRequired = 0;
|
||||
private ItemData item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classifies enchantments by Java order
|
||||
*/
|
||||
public enum EnchantmentTableEnchantments {
|
||||
PROTECTION,
|
||||
FIRE_PROTECTION,
|
||||
FEATHER_FALLING,
|
||||
BLAST_PROTECTION,
|
||||
PROJECTILE_PROTECTION,
|
||||
RESPIRATION,
|
||||
AQUA_AFFINITY,
|
||||
THORNS,
|
||||
DEPTH_STRIDER,
|
||||
FROST_WALKER,
|
||||
BINDING_CURSE,
|
||||
SHARPNESS,
|
||||
SMITE,
|
||||
BANE_OF_ARTHROPODS,
|
||||
KNOCKBACK,
|
||||
FIRE_ASPECT,
|
||||
LOOTING,
|
||||
SWEEPING,
|
||||
EFFICIENCY,
|
||||
SILK_TOUCH,
|
||||
UNBREAKING,
|
||||
FORTUNE,
|
||||
POWER,
|
||||
PUNCH,
|
||||
FLAME,
|
||||
INFINITY,
|
||||
LUCK_OF_THE_SEA,
|
||||
LURE,
|
||||
LOYALTY,
|
||||
IMPALING,
|
||||
RIPTIDE,
|
||||
CHANNELING,
|
||||
MENDING,
|
||||
VANISHING_CURSE, // After this is not documented
|
||||
MULTISHOT,
|
||||
PIERCING,
|
||||
QUICK_CHARGE,
|
||||
SOUL_SPEED;
|
||||
|
||||
public String toString(GeyserSession session) {
|
||||
return LocaleUtils.getLocaleString("enchantment.minecraft." + this.toString().toLowerCase(),
|
||||
session.getClientData().getLanguageCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ public abstract class InventoryTranslator {
|
|||
put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator());
|
||||
put(WindowType.MERCHANT, new MerchantInventoryTranslator());
|
||||
put(WindowType.SMITHING, new SmithingInventoryTranslator());
|
||||
//put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO
|
||||
|
||||
InventoryTranslator furnace = new FurnaceInventoryTranslator();
|
||||
put(WindowType.FURNACE, furnace);
|
||||
|
@ -64,6 +63,7 @@ public abstract class InventoryTranslator {
|
|||
put(WindowType.SMOKER, furnace);
|
||||
|
||||
InventoryUpdater containerUpdater = new ContainerInventoryUpdater();
|
||||
put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator(containerUpdater)); //TODO
|
||||
put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater));
|
||||
put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater));
|
||||
put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater));
|
||||
|
|
|
@ -35,9 +35,8 @@ import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
|||
import lombok.AllArgsConstructor;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class BlockInventoryHolder extends InventoryHolder {
|
||||
|
@ -60,7 +59,7 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||
.putInt("x", position.getX())
|
||||
.putInt("y", position.getY())
|
||||
.putInt("z", position.getZ())
|
||||
.putString("CustomName", LocaleUtils.getLocaleString(inventory.getTitle(), session.getClientData().getLanguageCode())).build();
|
||||
.putString("CustomName", inventory.getTitle()).build();
|
||||
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
|
||||
dataPacket.setData(tag);
|
||||
dataPacket.setBlockPosition(position);
|
||||
|
|
|
@ -173,21 +173,8 @@ public class ItemRegistry {
|
|||
int netId = 1;
|
||||
List<ItemData> creativeItems = new ArrayList<>();
|
||||
for (JsonNode itemNode : creativeItemEntries) {
|
||||
try {
|
||||
short damage = 0;
|
||||
NbtMap tag = null;
|
||||
if (itemNode.has("damage")) {
|
||||
damage = itemNode.get("damage").numberValue().shortValue();
|
||||
}
|
||||
if (itemNode.has("nbt_b64")) {
|
||||
byte[] bytes = Base64.getDecoder().decode(itemNode.get("nbt_b64").asText());
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||
tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag();
|
||||
}
|
||||
creativeItems.add(ItemData.fromNet(netId++, itemNode.get("id").asInt(), damage, 1, tag));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
ItemData item = getBedrockItemFromJson(itemNode);
|
||||
creativeItems.add(ItemData.fromNet(netId++, item.getId(), item.getDamage(), item.getCount(), item.getTag()));
|
||||
}
|
||||
CREATIVE_ITEMS = creativeItems.toArray(new ItemData[0]);
|
||||
}
|
||||
|
@ -210,14 +197,9 @@ public class ItemRegistry {
|
|||
*/
|
||||
public static ItemEntry getItem(ItemData data) {
|
||||
for (ItemEntry itemEntry : ITEM_ENTRIES.values()) {
|
||||
if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() || itemEntry.getJavaIdentifier().endsWith("potion"))) {
|
||||
return itemEntry;
|
||||
}
|
||||
}
|
||||
// If item find was unsuccessful first time, we try again while ignoring damage
|
||||
// Fixes piston, sticky pistons, dispensers and droppers turning into air from creative inventory
|
||||
for (ItemEntry itemEntry : ITEM_ENTRIES.values()) {
|
||||
if (itemEntry.getBedrockId() == data.getId()) {
|
||||
if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() ||
|
||||
// Make exceptions for potions and tipped arrows, whose damage values can vary
|
||||
(itemEntry.getJavaIdentifier().endsWith("potion") || itemEntry.getJavaIdentifier().equals("minecraft:arrow")))) {
|
||||
return itemEntry;
|
||||
}
|
||||
}
|
||||
|
@ -240,4 +222,48 @@ public class ItemRegistry {
|
|||
return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> ITEM_ENTRIES.values()
|
||||
.stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the Bedrock string identifier of an ItemEntry
|
||||
*
|
||||
* @param entry the ItemEntry to search for
|
||||
* @return the Bedrock identifier
|
||||
*/
|
||||
public static String getBedrockIdentifer(ItemEntry entry) {
|
||||
String blockName = "";
|
||||
for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) {
|
||||
if (startGamePacketItemEntry.getId() == (short) entry.getBedrockId()) {
|
||||
blockName = startGamePacketItemEntry.getIdentifier(); // Find the Bedrock string name
|
||||
break;
|
||||
}
|
||||
}
|
||||
return blockName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Bedrock {@link ItemData} from a {@link JsonNode}
|
||||
* @param itemNode the JSON node that contains ProxyPass-compatible Bedrock item data
|
||||
* @return
|
||||
*/
|
||||
public static ItemData getBedrockItemFromJson(JsonNode itemNode) {
|
||||
int count = 1;
|
||||
short damage = 0;
|
||||
NbtMap tag = null;
|
||||
if (itemNode.has("damage")) {
|
||||
damage = itemNode.get("damage").numberValue().shortValue();
|
||||
}
|
||||
if (itemNode.has("count")) {
|
||||
count = itemNode.get("count").asInt();
|
||||
}
|
||||
if (itemNode.has("nbt_b64")) {
|
||||
byte[] bytes = Base64.getDecoder().decode(itemNode.get("nbt_b64").asText());
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||
try {
|
||||
tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return ItemData.of(itemNode.get("id").asInt(), damage, count, tag);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,12 +35,14 @@ import com.nukkitx.nbt.NbtType;
|
|||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.reflections.Reflections;
|
||||
|
@ -62,7 +64,7 @@ public abstract class ItemTranslator {
|
|||
|
||||
static {
|
||||
/* Load item translators */
|
||||
Reflections ref = new Reflections("org.geysermc.connector.network.translators.item");
|
||||
Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.item") : new Reflections("org.geysermc.connector.network.translators.item");
|
||||
|
||||
Map<NbtItemStackTranslator, Integer> loadedNbtItemTranslators = new HashMap<>();
|
||||
for (Class<?> clazz : ref.getTypesAnnotatedWith(ItemRemapper.class)) {
|
||||
|
@ -138,11 +140,13 @@ public abstract class ItemTranslator {
|
|||
if (nbt != null) {
|
||||
for (NbtItemStackTranslator translator : NBT_TRANSLATORS) {
|
||||
if (translator.acceptItem(bedrockItem)) {
|
||||
translator.translateToBedrock(nbt, bedrockItem);
|
||||
translator.translateToBedrock(session, nbt, bedrockItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
translateDisplayProperties(session, nbt);
|
||||
|
||||
ItemData itemData;
|
||||
ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId());
|
||||
if (itemStackTranslator != null) {
|
||||
|
@ -151,42 +155,43 @@ public abstract class ItemTranslator {
|
|||
itemData = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem);
|
||||
}
|
||||
|
||||
|
||||
// Get the display name of the item
|
||||
NbtMap tag = itemData.getTag();
|
||||
if (tag != null) {
|
||||
NbtMap display = tag.getCompound("display");
|
||||
if (display != null && !display.isEmpty() && display.containsKey("Name")) {
|
||||
String name = display.getString("Name");
|
||||
|
||||
// If its not a message convert it
|
||||
if (!MessageUtils.isMessage(name)) {
|
||||
TextComponent component = LegacyComponentSerializer.legacySection().deserialize(name);
|
||||
name = GsonComponentSerializer.gson().serialize(component);
|
||||
}
|
||||
|
||||
// Check if its a message to translate
|
||||
if (MessageUtils.isMessage(name)) {
|
||||
// Get the translated name
|
||||
name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(name), session.getClientData().getLanguageCode());
|
||||
|
||||
// Build the new display tag
|
||||
NbtMapBuilder displayBuilder = display.toBuilder();
|
||||
displayBuilder.putString("Name", name);
|
||||
|
||||
// Build the new root tag
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.put("display", displayBuilder.build());
|
||||
|
||||
// Create a new item with the original data + updated name
|
||||
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build());
|
||||
}
|
||||
}
|
||||
if (nbt != null) {
|
||||
// Translate the canDestroy and canPlaceOn Java NBT
|
||||
ListTag canDestroy = nbt.get("CanDestroy");
|
||||
String[] canBreak = new String[0];
|
||||
ListTag canPlaceOn = nbt.get("CanPlaceOn");
|
||||
String[] canPlace = new String[0];
|
||||
canBreak = getCanModify(canDestroy, canBreak);
|
||||
canPlace = getCanModify(canPlaceOn, canPlace);
|
||||
itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), itemData.getTag(), canPlace, canBreak);
|
||||
}
|
||||
|
||||
return itemData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the Java NBT of canDestroy and canPlaceOn to its Bedrock counterparts.
|
||||
* In Java, this is treated as normal NBT, but in Bedrock, these arguments are extra parts of the item data itself.
|
||||
* @param canModifyJava the list of items in Java
|
||||
* @param canModifyBedrock the empty list of items in Bedrock
|
||||
* @return the new list of items in Bedrock
|
||||
*/
|
||||
private static String[] getCanModify(ListTag canModifyJava, String[] canModifyBedrock) {
|
||||
if (canModifyJava != null && canModifyJava.size() > 0) {
|
||||
canModifyBedrock = new String[canModifyJava.size()];
|
||||
for (int i = 0; i < canModifyBedrock.length; i++) {
|
||||
// Get the Java identifier of the block that can be placed
|
||||
String block = ((StringTag) canModifyJava.get(i)).getValue();
|
||||
// Sometimes this is done but it's still valid
|
||||
if (!block.startsWith("minecraft:")) block = "minecraft:" + block;
|
||||
// Get the Bedrock identifier of the item and replace it.
|
||||
// This will unfortunately be limited - for example, beds and banners will be translated weirdly
|
||||
canModifyBedrock[i] = BlockTranslator.getBedrockBlockIdentifier(block).replace("minecraft:", "");
|
||||
}
|
||||
}
|
||||
return canModifyBedrock;
|
||||
}
|
||||
|
||||
private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() {
|
||||
@Override
|
||||
public List<ItemEntry> getAppliedItems() {
|
||||
|
@ -375,6 +380,38 @@ public abstract class ItemTranslator {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the display name of the item
|
||||
* @param session the Bedrock client's session
|
||||
* @param tag the tag to translate
|
||||
*/
|
||||
public static void translateDisplayProperties(GeyserSession session, CompoundTag tag) {
|
||||
if (tag != null) {
|
||||
CompoundTag display = tag.get("display");
|
||||
if (display != null && !display.isEmpty() && display.contains("Name")) {
|
||||
String name = ((StringTag) display.get("Name")).getValue();
|
||||
|
||||
// If its not a message convert it
|
||||
if (!MessageUtils.isMessage(name)) {
|
||||
Component component = LegacyComponentSerializer.legacySection().deserialize(name);
|
||||
name = GsonComponentSerializer.gson().serialize(component);
|
||||
}
|
||||
|
||||
// Check if its a message to translate
|
||||
if (MessageUtils.isMessage(name)) {
|
||||
// Get the translated name
|
||||
name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(name), session.getClientData().getLanguageCode());
|
||||
|
||||
// Add the new name tag
|
||||
display.put(new StringTag("Name", name));
|
||||
|
||||
// Add to the new root tag
|
||||
tag.put(display);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an {@link ItemStack} is equal to another item stack
|
||||
*
|
||||
|
|
|
@ -26,17 +26,33 @@
|
|||
package org.geysermc.connector.network.translators.item;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class NbtItemStackTranslator {
|
||||
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
/**
|
||||
* Translate the item NBT to Bedrock
|
||||
* @param session the client's current session
|
||||
* @param itemTag the item's CompoundTag
|
||||
* @param itemEntry Geyser's item entry
|
||||
*/
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the item NBT to Java.
|
||||
* @param itemTag the item's CompoundTag
|
||||
* @param itemEntry Geyser's item entry
|
||||
*/
|
||||
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param itemEntry Geyser's item entry
|
||||
* @return if the item should be processed under this class
|
||||
*/
|
||||
public boolean acceptItem(ItemEntry itemEntry) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public enum Potion {
|
|||
STRONG_SWIFTNESS(16),
|
||||
LONG_SWIFTNESS(15),
|
||||
SLOWNESS(17),
|
||||
STRONG_SLOWNESS(18), //does not exist
|
||||
STRONG_SLOWNESS(42),
|
||||
LONG_SLOWNESS(18),
|
||||
WATER_BREATHING(19),
|
||||
LONG_WATER_BREATHING(20),
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.item;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Manages any recipe-related storing
|
||||
*/
|
||||
public class RecipeRegistry {
|
||||
|
||||
/**
|
||||
* A list of all possible leather armor dyeing recipes.
|
||||
* Created manually.
|
||||
*/
|
||||
public static List<CraftingData> LEATHER_DYEING_RECIPES = new ObjectArrayList<>();
|
||||
/**
|
||||
* A list of all possible firework rocket recipes, including the base rocket.
|
||||
* Obtained from a ProxyPass dump of protocol v407
|
||||
*/
|
||||
public static List<CraftingData> FIREWORK_ROCKET_RECIPES = new ObjectArrayList<>(21);
|
||||
/**
|
||||
* A list of all possible firework star recipes.
|
||||
* Obtained from a ProxyPass dump of protocol v407
|
||||
*/
|
||||
public static List<CraftingData> FIREWORK_STAR_RECIPES = new ObjectArrayList<>(40);
|
||||
/**
|
||||
* A list of all possible shulker box dyeing options.
|
||||
* Obtained from a ProxyPass dump of protocol v407
|
||||
*/
|
||||
public static List<CraftingData> SHULKER_BOX_DYEING_RECIPES = new ObjectArrayList<>();
|
||||
|
||||
static {
|
||||
// Get all recipes that are not directly sent from a Java server
|
||||
InputStream stream = FileUtils.getResource("mappings/recipes.json");
|
||||
|
||||
JsonNode items;
|
||||
try {
|
||||
items = GeyserConnector.JSON_MAPPER.readTree(stream);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e);
|
||||
}
|
||||
|
||||
for (JsonNode entry: items.get("leather_armor")) {
|
||||
// This won't be perfect, as we can't possibly send every leather input for every kind of color
|
||||
// But it does display the correct output from a base leather armor, and besides visuals everything works fine
|
||||
LEATHER_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry));
|
||||
}
|
||||
for (JsonNode entry : items.get("firework_rockets")) {
|
||||
FIREWORK_ROCKET_RECIPES.add(getCraftingDataFromJsonNode(entry));
|
||||
}
|
||||
for (JsonNode entry : items.get("firework_stars")) {
|
||||
FIREWORK_STAR_RECIPES.add(getCraftingDataFromJsonNode(entry));
|
||||
}
|
||||
for (JsonNode entry : items.get("shulker_boxes")) {
|
||||
SHULKER_BOX_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a Bedrock crafting recipe from the given JSON data.
|
||||
* @param node the JSON data to compute
|
||||
* @return the {@link CraftingData} to send to the Bedrock client.
|
||||
*/
|
||||
private static CraftingData getCraftingDataFromJsonNode(JsonNode node) {
|
||||
ItemData output = ItemRegistry.getBedrockItemFromJson(node.get("output").get(0));
|
||||
List<ItemData> inputs = new ObjectArrayList<>();
|
||||
for (JsonNode entry : node.get("input")) {
|
||||
inputs.add(ItemRegistry.getBedrockItemFromJson(entry));
|
||||
}
|
||||
UUID uuid = UUID.randomUUID();
|
||||
if (node.get("type").asInt() == 5) {
|
||||
// Shulker box
|
||||
return CraftingData.fromShulkerBox(uuid.toString(),
|
||||
inputs.toArray(new ItemData[0]), new ItemData[]{output}, uuid, "crafting_table", 0);
|
||||
}
|
||||
return CraftingData.fromShapeless(uuid.toString(),
|
||||
inputs.toArray(new ItemData[0]), new ItemData[]{output}, uuid, "crafting_table", 0);
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
// no-op
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.item;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Potion identifiers and their respective Bedrock IDs used with arrows.
|
||||
* https://minecraft.gamepedia.com/Arrow#Item_Data
|
||||
*/
|
||||
@Getter
|
||||
public enum TippedArrowPotion {
|
||||
MUNDANE(2, ArrowParticleColors.NONE), // 3 is extended?
|
||||
THICK(4, ArrowParticleColors.NONE),
|
||||
AWKWARD(5, ArrowParticleColors.NONE),
|
||||
NIGHT_VISION(6, ArrowParticleColors.NIGHT_VISION),
|
||||
LONG_NIGHT_VISION(7, ArrowParticleColors.NIGHT_VISION),
|
||||
INVISIBILITY(8, ArrowParticleColors.INVISIBILITY),
|
||||
LONG_INVISIBILITY(9, ArrowParticleColors.INVISIBILITY),
|
||||
LEAPING(10, ArrowParticleColors.LEAPING),
|
||||
LONG_LEAPING(11, ArrowParticleColors.LEAPING),
|
||||
STRONG_LEAPING(12, ArrowParticleColors.LEAPING),
|
||||
FIRE_RESISTANCE(13, ArrowParticleColors.FIRE_RESISTANCE),
|
||||
LONG_FIRE_RESISTANCE(14, ArrowParticleColors.FIRE_RESISTANCE),
|
||||
SWIFTNESS(15, ArrowParticleColors.SWIFTNESS),
|
||||
LONG_SWIFTNESS(16, ArrowParticleColors.SWIFTNESS),
|
||||
STRONG_SWIFTNESS(17, ArrowParticleColors.SWIFTNESS),
|
||||
SLOWNESS(18, ArrowParticleColors.SLOWNESS),
|
||||
LONG_SLOWNESS(19, ArrowParticleColors.SLOWNESS),
|
||||
STRONG_SLOWNESS(43, ArrowParticleColors.SLOWNESS),
|
||||
WATER_BREATHING(20, ArrowParticleColors.WATER_BREATHING),
|
||||
LONG_WATER_BREATHING(21, ArrowParticleColors.WATER_BREATHING),
|
||||
HEALING(22, ArrowParticleColors.HEALING),
|
||||
STRONG_HEALING(23, ArrowParticleColors.HEALING),
|
||||
HARMING(24, ArrowParticleColors.HARMING),
|
||||
STRONG_HARMING(25, ArrowParticleColors.HARMING),
|
||||
POISON(26, ArrowParticleColors.POISON),
|
||||
LONG_POISON(27, ArrowParticleColors.POISON),
|
||||
STRONG_POISON(28, ArrowParticleColors.POISON),
|
||||
REGENERATION(29, ArrowParticleColors.REGENERATION),
|
||||
LONG_REGENERATION(30, ArrowParticleColors.REGENERATION),
|
||||
STRONG_REGENERATION(31, ArrowParticleColors.REGENERATION),
|
||||
STRENGTH(32, ArrowParticleColors.STRENGTH),
|
||||
LONG_STRENGTH(33, ArrowParticleColors.STRENGTH),
|
||||
STRONG_STRENGTH(34, ArrowParticleColors.STRENGTH),
|
||||
WEAKNESS(35, ArrowParticleColors.WEAKNESS),
|
||||
LONG_WEAKNESS(36, ArrowParticleColors.WEAKNESS),
|
||||
LUCK(2, ArrowParticleColors.NONE), // does not exist in Bedrock
|
||||
TURTLE_MASTER(38, ArrowParticleColors.TURTLE_MASTER),
|
||||
LONG_TURTLE_MASTER(39, ArrowParticleColors.TURTLE_MASTER),
|
||||
STRONG_TURTLE_MASTER(40, ArrowParticleColors.TURTLE_MASTER),
|
||||
SLOW_FALLING(41, ArrowParticleColors.SLOW_FALLING),
|
||||
LONG_SLOW_FALLING(42, ArrowParticleColors.SLOW_FALLING);
|
||||
|
||||
private final String javaIdentifier;
|
||||
private final short bedrockId;
|
||||
/**
|
||||
* The Java color associated with this ID.
|
||||
* Used for looking up Java arrow color entity metadata as Bedrock potion IDs, which is what is used for entities in Bedrock
|
||||
*/
|
||||
private final int javaColor;
|
||||
|
||||
TippedArrowPotion(int bedrockId, ArrowParticleColors arrowParticleColor) {
|
||||
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH);
|
||||
this.bedrockId = (short) bedrockId;
|
||||
this.javaColor = arrowParticleColor.getColor();
|
||||
}
|
||||
|
||||
public static TippedArrowPotion getByJavaIdentifier(String javaIdentifier) {
|
||||
for (TippedArrowPotion potion : TippedArrowPotion.values()) {
|
||||
if (potion.javaIdentifier.equals(javaIdentifier)) {
|
||||
return potion;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static TippedArrowPotion getByBedrockId(short bedrockId) {
|
||||
for (TippedArrowPotion potion : TippedArrowPotion.values()) {
|
||||
if (potion.bedrockId == bedrockId) {
|
||||
return potion;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param color the potion color to look up
|
||||
* @return the tipped arrow potion that most closely resembles that color.
|
||||
*/
|
||||
public static TippedArrowPotion getByJavaColor(int color) {
|
||||
for (TippedArrowPotion potion : TippedArrowPotion.values()) {
|
||||
if (potion.javaColor == color) {
|
||||
return potion;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum ArrowParticleColors {
|
||||
NONE(-1),
|
||||
NIGHT_VISION(2039713),
|
||||
INVISIBILITY(8356754),
|
||||
LEAPING(2293580),
|
||||
FIRE_RESISTANCE(14981690),
|
||||
SWIFTNESS(8171462),
|
||||
SLOWNESS(5926017),
|
||||
TURTLE_MASTER(7691106),
|
||||
WATER_BREATHING(3035801),
|
||||
HEALING(16262179),
|
||||
HARMING(4393481),
|
||||
POISON(5149489),
|
||||
REGENERATION(13458603),
|
||||
STRENGTH(9643043),
|
||||
WEAKNESS(4738376),
|
||||
LUCK(3381504),
|
||||
SLOW_FALLING(16773073);
|
||||
|
||||
@Getter
|
||||
private final int color;
|
||||
|
||||
ArrowParticleColors(int color) {
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ import java.util.stream.Collectors;
|
|||
@ItemRemapper
|
||||
public class BannerTranslator extends ItemTranslator {
|
||||
|
||||
private List<ItemEntry> appliedItems;
|
||||
private final List<ItemEntry> appliedItems;
|
||||
|
||||
public BannerTranslator() {
|
||||
appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("banner")).collect(Collectors.toList());
|
||||
|
|
|
@ -40,7 +40,7 @@ import java.util.stream.Collectors;
|
|||
@ItemRemapper
|
||||
public class CompassTranslator extends ItemTranslator {
|
||||
|
||||
private List<ItemEntry> appliedItems;
|
||||
private final List<ItemEntry> appliedItems;
|
||||
|
||||
public CompassTranslator() {
|
||||
appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("compass")).collect(Collectors.toList());
|
||||
|
|
|
@ -42,7 +42,7 @@ import java.util.stream.Collectors;
|
|||
@ItemRemapper
|
||||
public class PotionTranslator extends ItemTranslator {
|
||||
|
||||
private List<ItemEntry> appliedItems;
|
||||
private final List<ItemEntry> appliedItems;
|
||||
|
||||
public PotionTranslator() {
|
||||
appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("potion")).collect(Collectors.toList());
|
||||
|
@ -57,7 +57,7 @@ public class PotionTranslator extends ItemTranslator {
|
|||
if (potion != null) {
|
||||
return ItemData.of(itemEntry.getBedrockId(), potion.getBedrockId(), itemStack.getAmount(), translateNbtToBedrock(itemStack.getNbt()));
|
||||
}
|
||||
GeyserConnector.getInstance().getLogger().debug("Unknown java potion: " + potionTag.getValue());
|
||||
GeyserConnector.getInstance().getLogger().debug("Unknown Java potion: " + potionTag.getValue());
|
||||
}
|
||||
return super.translateToBedrock(itemStack, itemEntry);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.item.translators;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.network.translators.item.TippedArrowPotion;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ItemRemapper
|
||||
public class TippedArrowTranslator extends ItemTranslator {
|
||||
|
||||
private final List<ItemEntry> appliedItems;
|
||||
|
||||
private static final int TIPPED_ARROW_JAVA_ID = ItemRegistry.getItemEntry("minecraft:tipped_arrow").getJavaId();
|
||||
|
||||
public TippedArrowTranslator() {
|
||||
appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry ->
|
||||
entry.getJavaIdentifier().contains("arrow") && !entry.getJavaIdentifier().contains("spectral")).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) {
|
||||
if (!itemEntry.getJavaIdentifier().equals("minecraft:tipped_arrow") || itemStack.getNbt() == null) {
|
||||
// We're only concerned about minecraft:arrow when translating Bedrock -> Java
|
||||
return super.translateToBedrock(itemStack, itemEntry);
|
||||
}
|
||||
Tag potionTag = itemStack.getNbt().get("Potion");
|
||||
if (potionTag instanceof StringTag) {
|
||||
TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByJavaIdentifier(((StringTag) potionTag).getValue());
|
||||
if (tippedArrowPotion != null) {
|
||||
return ItemData.of(itemEntry.getBedrockId(), tippedArrowPotion.getBedrockId(), itemStack.getAmount(), translateNbtToBedrock(itemStack.getNbt()));
|
||||
}
|
||||
GeyserConnector.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionTag.getValue());
|
||||
}
|
||||
return super.translateToBedrock(itemStack, itemEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) {
|
||||
TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByBedrockId(itemData.getDamage());
|
||||
ItemStack itemStack = super.translateToJava(itemData, itemEntry);
|
||||
if (tippedArrowPotion != null) {
|
||||
itemStack = new ItemStack(TIPPED_ARROW_JAVA_ID, itemStack.getAmount(), itemStack.getNbt());
|
||||
StringTag potionTag = new StringTag("Potion", tippedArrowPotion.getJavaIdentifier());
|
||||
itemStack.getNbt().put(potionTag);
|
||||
}
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemEntry> getAppliedItems() {
|
||||
return appliedItems;
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import net.kyori.adventure.text.Component;
|
|||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
|
@ -45,7 +46,7 @@ import java.util.List;
|
|||
public class BasicItemTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (!itemTag.contains("display")) {
|
||||
return;
|
||||
}
|
||||
|
@ -100,7 +101,7 @@ public class BasicItemTranslator extends NbtItemStackTranslator {
|
|||
if (message.startsWith("§r")) {
|
||||
message = message.replaceFirst("§r", "");
|
||||
}
|
||||
Component component = TextComponent.of(message);
|
||||
Component component = Component.text(message);
|
||||
return GsonComponentSerializer.gson().serialize(component);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
|
@ -41,7 +42,7 @@ import java.util.List;
|
|||
public class BookPagesTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (!itemTag.contains("pages")) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -25,30 +25,37 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.item.translators.nbt;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
|
||||
@ItemRemapper
|
||||
public class CrossbowTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (itemTag.get("ChargedProjectiles") != null) {
|
||||
ListTag chargedProjectiles = itemTag.get("ChargedProjectiles");
|
||||
if (!chargedProjectiles.getValue().isEmpty()) {
|
||||
CompoundTag projectile = (CompoundTag) chargedProjectiles.getValue().get(0);
|
||||
|
||||
CompoundTag newProjectile = new CompoundTag("chargedItem");
|
||||
newProjectile.put(new ByteTag("Count", (byte) projectile.get("Count").getValue()));
|
||||
newProjectile.put(new StringTag("Name", (String) projectile.get("id").getValue()));
|
||||
ItemEntry entry = ItemRegistry.getItemEntry((String) projectile.get("id").getValue());
|
||||
if (entry == null) return;
|
||||
CompoundTag tag = projectile.get("tag");
|
||||
ItemStack itemStack = new ItemStack(itemEntry.getJavaId(), (byte) projectile.get("Count").getValue(), tag);
|
||||
ItemData itemData = ItemTranslator.translateToBedrock(session, itemStack);
|
||||
|
||||
// Not sure what this is for
|
||||
newProjectile.put(new ByteTag("Damage", (byte) 0));
|
||||
CompoundTag newProjectile = new CompoundTag("chargedItem");
|
||||
newProjectile.put(new ByteTag("Count", (byte) itemData.getCount()));
|
||||
newProjectile.put(new StringTag("Name", ItemRegistry.getBedrockIdentifer(entry)));
|
||||
|
||||
newProjectile.put(new ShortTag("Damage", itemData.getDamage()));
|
||||
|
||||
itemTag.put(newProjectile);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt;
|
|||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
|
@ -36,7 +37,7 @@ import org.geysermc.connector.network.translators.item.ItemEntry;
|
|||
public class EnchantedBookTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (!itemTag.contains("StoredEnchantments")) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt;
|
|||
|
||||
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
import org.geysermc.connector.network.translators.item.Enchantment;
|
||||
|
@ -40,7 +41,7 @@ import java.util.Map;
|
|||
public class EnchantmentTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
List<Tag> newTags = new ArrayList<>();
|
||||
if (itemTag.contains("Enchantments")) {
|
||||
ListTag enchantmentTag = itemTag.get("Enchantments");
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.connector.network.translators.item.translators.nbt;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
|
@ -36,7 +37,7 @@ import org.geysermc.connector.utils.MathUtils;
|
|||
public class FireworkTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (!itemTag.contains("Fireworks")) {
|
||||
return;
|
||||
}
|
||||
|
@ -106,6 +107,9 @@ public class FireworkTranslator extends NbtItemStackTranslator {
|
|||
fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue())));
|
||||
}
|
||||
|
||||
if (!itemTag.contains("Explosions")) {
|
||||
return;
|
||||
}
|
||||
ListTag explosions = fireworks.get("Explosions");
|
||||
for (Tag effect : explosions.getValue()) {
|
||||
CompoundTag effectData = (CompoundTag) effect;
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt;
|
|||
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntTag;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
|
@ -37,7 +38,7 @@ public class LeatherArmorTranslator extends NbtItemStackTranslator {
|
|||
private static final String[] ITEMS = new String[]{"minecraft:leather_helmet", "minecraft:leather_chestplate", "minecraft:leather_leggings", "minecraft:leather_boots"};
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (!itemTag.contains("display")) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.ByteTag;
|
|||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.LongTag;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
|
@ -37,7 +38,7 @@ import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
|||
public class MapItemTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
IntTag mapId = itemTag.get("map");
|
||||
|
||||
if (mapId != null) {
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.item.translators.nbt;
|
||||
|
||||
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.ItemRemapper;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.network.translators.item.NbtItemStackTranslator;
|
||||
|
||||
@ItemRemapper
|
||||
public class ShulkerBoxItemTranslator extends NbtItemStackTranslator {
|
||||
|
||||
@Override
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (!itemTag.contains("BlockEntityTag")) return; // Empty shulker box
|
||||
|
||||
CompoundTag blockEntityTag = itemTag.get("BlockEntityTag");
|
||||
if (blockEntityTag.get("Items") == null) return;
|
||||
ListTag itemsList = new ListTag("Items");
|
||||
for (Tag item : (ListTag) blockEntityTag.get("Items")) {
|
||||
CompoundTag itemData = (CompoundTag) item; // Information about the item
|
||||
CompoundTag boxItemTag = new CompoundTag(""); // Final item tag to add to the list
|
||||
boxItemTag.put(new ByteTag("Slot", ((ByteTag) itemData.get("Slot")).getValue()));
|
||||
boxItemTag.put(new ByteTag("WasPickedUp", (byte) 0)); // ???
|
||||
|
||||
ItemEntry boxItemEntry = ItemRegistry.getItemEntry(((StringTag) itemData.get("id")).getValue());
|
||||
String blockName = ItemRegistry.getBedrockIdentifer(boxItemEntry);
|
||||
|
||||
boxItemTag.put(new StringTag("Name", blockName));
|
||||
boxItemTag.put(new ShortTag("Damage", (short) boxItemEntry.getBedrockData()));
|
||||
boxItemTag.put(new ByteTag("Count", ((ByteTag) itemData.get("Count")).getValue()));
|
||||
if (itemData.contains("tag")) {
|
||||
// Only the display name is what we have interest in, so just translate that if relevant
|
||||
CompoundTag displayTag = itemData.get("tag");
|
||||
ItemTranslator.translateDisplayProperties(session, displayTag);
|
||||
boxItemTag.put(displayTag);
|
||||
}
|
||||
|
||||
itemsList.add(boxItemTag);
|
||||
}
|
||||
itemTag.put(itemsList);
|
||||
// Don't actually bother with removing the block entity tag. Too risky to translate
|
||||
// if the user is on creative and messing with a shulker box
|
||||
//itemTag.remove("BlockEntityTag");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) {
|
||||
if (itemTag.contains("Items")) { // Remove any extraneous Bedrock tag and don't touch the Java one
|
||||
itemTag.remove("Items");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(ItemEntry itemEntry) {
|
||||
return itemEntry.getJavaIdentifier().contains("shulker_box");
|
||||
}
|
||||
}
|
|
@ -41,10 +41,7 @@ import lombok.EqualsAndHashCode;
|
|||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.item.ItemEntry;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.network.translators.item.PotionMixRegistry;
|
||||
import org.geysermc.connector.network.translators.item.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -83,6 +80,24 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator<ServerDeclare
|
|||
}
|
||||
break;
|
||||
}
|
||||
case CRAFTING_SPECIAL_FIREWORK_ROCKET: {
|
||||
// Java doesn't actually tell us the recipes so we need to calculate this ahead of time.
|
||||
craftingDataPacket.getCraftingData().addAll(RecipeRegistry.FIREWORK_ROCKET_RECIPES);
|
||||
break;
|
||||
}
|
||||
case CRAFTING_SPECIAL_FIREWORK_STAR: {
|
||||
craftingDataPacket.getCraftingData().addAll(RecipeRegistry.FIREWORK_STAR_RECIPES);
|
||||
break;
|
||||
}
|
||||
case CRAFTING_SPECIAL_SHULKERBOXCOLORING: {
|
||||
craftingDataPacket.getCraftingData().addAll(RecipeRegistry.SHULKER_BOX_DYEING_RECIPES);
|
||||
break;
|
||||
}
|
||||
case CRAFTING_SPECIAL_ARMORDYE: {
|
||||
// This one's even worse since it's not actually on Bedrock, but it still works!
|
||||
craftingDataPacket.getCraftingData().addAll(RecipeRegistry.LEATHER_DYEING_RECIPES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
craftingDataPacket.getPotionMixData().addAll(PotionMixRegistry.POTION_MIXES);
|
||||
|
|
|
@ -40,5 +40,7 @@ public class JavaDifficultyTranslator extends PacketTranslator<ServerDifficultyP
|
|||
SetDifficultyPacket setDifficultyPacket = new SetDifficultyPacket();
|
||||
setDifficultyPacket.setDifficulty(packet.getDifficulty().ordinal());
|
||||
session.sendUpstreamPacket(setDifficultyPacket);
|
||||
|
||||
session.getWorldCache().setDifficulty(packet.getDifficulty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,15 +23,19 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.world;
|
||||
package org.geysermc.connector.network.translators.java;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDisconnectPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
public class CachedChunkManager extends WorldManager {
|
||||
@Translator(packet = ServerDisconnectPacket.class)
|
||||
public class JavaDisconnectPacket extends PacketTranslator<ServerDisconnectPacket> {
|
||||
|
||||
@Override
|
||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||
return session.getChunkCache().getBlockAt(new Position(x, y, z));
|
||||
public void translate(ServerDisconnectPacket packet, GeyserSession session) {
|
||||
session.disconnect(MessageUtils.getTranslatedBedrockMessage(packet.getReason(), session.getClientData().getLanguageCode(), true));
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.java;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference;
|
||||
import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility;
|
||||
import com.github.steveice10.mc.protocol.data.game.setting.SkinPart;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientSettingsPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePacket;
|
||||
import com.nukkitx.protocol.bedrock.data.GameRuleData;
|
||||
|
@ -38,6 +39,7 @@ import org.geysermc.connector.network.session.GeyserSession;
|
|||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
import org.geysermc.connector.utils.PluginMessageUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -51,12 +53,13 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
|
|||
entity.setEntityId(packet.getEntityId());
|
||||
// If the player is already initialized and a join game packet is sent, they
|
||||
// are swapping servers
|
||||
String newDimension = DimensionUtils.getNewDimension(packet.getDimension());
|
||||
if (session.isSpawned()) {
|
||||
String fakeDim = entity.getDimension().equals(DimensionUtils.OVERWORLD) ? DimensionUtils.NETHER : DimensionUtils.OVERWORLD;
|
||||
DimensionUtils.switchDimension(session, fakeDim);
|
||||
DimensionUtils.switchDimension(session, packet.getDimension());
|
||||
DimensionUtils.switchDimension(session, newDimension);
|
||||
|
||||
session.getScoreboardCache().removeScoreboard();
|
||||
session.getWorldCache().removeScoreboard();
|
||||
}
|
||||
|
||||
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();
|
||||
|
@ -91,8 +94,10 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
|
|||
ClientSettingsPacket clientSettingsPacket = new ClientSettingsPacket(locale, (byte) session.getRenderDistance(), ChatVisibility.FULL, true, skinParts, HandPreference.RIGHT_HAND);
|
||||
session.sendDownstreamPacket(clientSettingsPacket);
|
||||
|
||||
if (!packet.getDimension().equals(entity.getDimension())) {
|
||||
DimensionUtils.switchDimension(session, packet.getDimension());
|
||||
session.sendDownstreamPacket(new ClientPluginMessagePacket("minecraft:brand", PluginMessageUtils.getGeyserBrandData()));
|
||||
|
||||
if (!newDimension.equals(entity.getDimension())) {
|
||||
DimensionUtils.switchDimension(session, newDimension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 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.connector.network.translators.java;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerKeepAlivePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.NetworkStackLatencyPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
/**
|
||||
* Used to forward the keep alive packet to the client in order to get back a reliable ping.
|
||||
*/
|
||||
@Translator(packet = ServerKeepAlivePacket.class)
|
||||
public class JavaKeepAliveTranslator extends PacketTranslator<ServerKeepAlivePacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerKeepAlivePacket packet, GeyserSession session) {
|
||||
session.setLastKeepAliveTimestamp(packet.getPingId());
|
||||
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
|
||||
latencyPacket.setFromServer(true);
|
||||
latencyPacket.setTimestamp(packet.getPingId());
|
||||
session.sendUpstreamPacket(latencyPacket);
|
||||
}
|
||||
}
|
|
@ -25,17 +25,15 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPluginMessagePacket;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.addon.AddonListener;
|
||||
import org.geysermc.connector.network.addon.AddonListenerRegistry;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPluginMessagePacket;
|
||||
import org.geysermc.connector.utils.NetworkUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -44,24 +42,8 @@ import java.util.Map;
|
|||
@Translator(packet = ServerPluginMessagePacket.class)
|
||||
public class JavaPluginMessageTranslator extends PacketTranslator<ServerPluginMessagePacket> {
|
||||
|
||||
private static byte[] brandData;
|
||||
|
||||
static {
|
||||
byte[] data = GeyserConnector.NAME.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] varInt = writeVarInt(data.length);
|
||||
brandData = new byte[varInt.length + data.length];
|
||||
System.arraycopy(varInt, 0, brandData, 0, varInt.length);
|
||||
System.arraycopy(data, 0, brandData, varInt.length, data.length);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void translate(ServerPluginMessagePacket packet, GeyserSession session) {
|
||||
if (packet.getChannel().equals("minecraft:brand")) {
|
||||
session.sendDownstreamPacket(
|
||||
new ClientPluginMessagePacket(packet.getChannel(), brandData)
|
||||
);
|
||||
}
|
||||
if (packet.getChannel().equals("minecraft:register")) {
|
||||
session.sendDownstreamPacket(new ClientPluginMessagePacket(packet.getChannel(), AddonListener.PLUGIN_MESSAGE_CHANNEL.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
@ -80,32 +62,4 @@ public class JavaPluginMessageTranslator extends PacketTranslator<ServerPluginMe
|
|||
entry.getValue().onMessageReceive(session, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] writeVarInt(int value) {
|
||||
byte[] data = new byte[getVarIntLength(value)];
|
||||
int index = 0;
|
||||
do {
|
||||
byte temp = (byte)(value & 0b01111111);
|
||||
value >>>= 7;
|
||||
if (value != 0) {
|
||||
temp |= 0b10000000;
|
||||
}
|
||||
data[index] = temp;
|
||||
index++;
|
||||
} while (value != 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static int getVarIntLength(int number) {
|
||||
if ((number & 0xFFFFFF80) == 0) {
|
||||
return 1;
|
||||
} else if ((number & 0xFFFFC000) == 0) {
|
||||
return 2;
|
||||
} else if ((number & 0xFFE00000) == 0) {
|
||||
return 3;
|
||||
} else if ((number & 0xF0000000) == 0) {
|
||||
return 4;
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,6 @@ import org.geysermc.connector.network.translators.PacketTranslator;
|
|||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@Translator(packet = ServerRespawnPacket.class)
|
||||
public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket> {
|
||||
|
||||
|
@ -59,19 +57,23 @@ public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket>
|
|||
session.sendUpstreamPacket(playerGameTypePacket);
|
||||
session.setGameMode(packet.getGamemode());
|
||||
|
||||
LevelEventPacket stopRainPacket = new LevelEventPacket();
|
||||
stopRainPacket.setType(LevelEventType.STOP_RAINING);
|
||||
stopRainPacket.setData(ThreadLocalRandom.current().nextInt(50000) + 10000);
|
||||
stopRainPacket.setPosition(Vector3f.ZERO);
|
||||
session.sendUpstreamPacket(stopRainPacket);
|
||||
if (session.isRaining()) {
|
||||
LevelEventPacket stopRainPacket = new LevelEventPacket();
|
||||
stopRainPacket.setType(LevelEventType.STOP_RAINING);
|
||||
stopRainPacket.setData(0);
|
||||
stopRainPacket.setPosition(Vector3f.ZERO);
|
||||
session.sendUpstreamPacket(stopRainPacket);
|
||||
session.setRaining(false);
|
||||
}
|
||||
|
||||
if (!entity.getDimension().equals(packet.getDimension())) {
|
||||
DimensionUtils.switchDimension(session, packet.getDimension());
|
||||
String newDimension = DimensionUtils.getNewDimension(packet.getDimension());
|
||||
if (!entity.getDimension().equals(newDimension)) {
|
||||
DimensionUtils.switchDimension(session, newDimension);
|
||||
} else {
|
||||
if (session.isManyDimPackets()) { //reloading world
|
||||
String fakeDim = entity.getDimension().equals(DimensionUtils.OVERWORLD) ? DimensionUtils.NETHER : DimensionUtils.OVERWORLD;
|
||||
DimensionUtils.switchDimension(session, fakeDim);
|
||||
DimensionUtils.switchDimension(session, packet.getDimension());
|
||||
DimensionUtils.switchDimension(session, newDimension);
|
||||
} else {
|
||||
// Handled in JavaPlayerPositionRotationTranslator
|
||||
session.setSpawned(false);
|
||||
|
|
|
@ -39,15 +39,16 @@ public class JavaTitleTranslator extends PacketTranslator<ServerTitlePacket> {
|
|||
@Override
|
||||
public void translate(ServerTitlePacket packet, GeyserSession session) {
|
||||
SetTitlePacket titlePacket = new SetTitlePacket();
|
||||
String locale = session.getClientData().getLanguageCode();
|
||||
|
||||
switch (packet.getAction()) {
|
||||
case TITLE:
|
||||
titlePacket.setType(SetTitlePacket.Type.TITLE);
|
||||
titlePacket.setText(MessageUtils.getBedrockMessage(packet.getTitle()));
|
||||
titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale));
|
||||
break;
|
||||
case SUBTITLE:
|
||||
titlePacket.setType(SetTitlePacket.Type.SUBTITLE);
|
||||
titlePacket.setText(MessageUtils.getBedrockMessage(packet.getTitle()));
|
||||
titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale));
|
||||
break;
|
||||
case CLEAR:
|
||||
case RESET:
|
||||
|
@ -56,7 +57,7 @@ public class JavaTitleTranslator extends PacketTranslator<ServerTitlePacket> {
|
|||
break;
|
||||
case ACTION_BAR:
|
||||
titlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
|
||||
titlePacket.setText(MessageUtils.getBedrockMessage(packet.getTitle()));
|
||||
titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale));
|
||||
break;
|
||||
case TIMES:
|
||||
titlePacket.setFadeInTime(packet.getFadeIn());
|
||||
|
|
|
@ -23,33 +23,52 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.java.world;
|
||||
package org.geysermc.connector.network.translators.java.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityCollectItemPacket;
|
||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.TakeItemEntityPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.ExpOrbEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
/**
|
||||
* This packet is called whenever a player picks up an item.
|
||||
* In Java, this is called for item entities, experience orbs and arrows
|
||||
* Bedrock uses it for arrows and item entities, but not experience orbs.
|
||||
*/
|
||||
@Translator(packet = ServerEntityCollectItemPacket.class)
|
||||
public class JavaCollectItemTranslator extends PacketTranslator<ServerEntityCollectItemPacket> {
|
||||
public class JavaEntityCollectItemTranslator extends PacketTranslator<ServerEntityCollectItemPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerEntityCollectItemPacket packet, GeyserSession session) {
|
||||
// This is the definition of translating - both packets take the same values
|
||||
TakeItemEntityPacket takeItemEntityPacket = new TakeItemEntityPacket();
|
||||
// Collected entity is the item
|
||||
// Collected entity is the other entity
|
||||
Entity collectedEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectedEntityId());
|
||||
// Collector is the entity picking up the item
|
||||
if (collectedEntity == null) return;
|
||||
// Collector is the entity 'picking up' the item
|
||||
Entity collectorEntity;
|
||||
if (packet.getCollectorEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||
collectorEntity = session.getPlayerEntity();
|
||||
} else {
|
||||
collectorEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectorEntityId());
|
||||
}
|
||||
takeItemEntityPacket.setRuntimeEntityId(collectorEntity.getGeyserId());
|
||||
takeItemEntityPacket.setItemRuntimeEntityId(collectedEntity.getGeyserId());
|
||||
session.sendUpstreamPacket(takeItemEntityPacket);
|
||||
if (collectorEntity == null) return;
|
||||
if (collectedEntity instanceof ExpOrbEntity) {
|
||||
// Player just picked up an experience orb
|
||||
LevelEventPacket xpPacket = new LevelEventPacket();
|
||||
xpPacket.setType(LevelEventType.SOUND_EXPERIENCE_ORB_PICKUP);
|
||||
xpPacket.setPosition(collectedEntity.getPosition());
|
||||
xpPacket.setData(0);
|
||||
session.sendUpstreamPacket(xpPacket);
|
||||
} else {
|
||||
// Item is being picked up (visual only)
|
||||
TakeItemEntityPacket takeItemEntityPacket = new TakeItemEntityPacket();
|
||||
takeItemEntityPacket.setRuntimeEntityId(collectorEntity.getGeyserId());
|
||||
takeItemEntityPacket.setItemRuntimeEntityId(collectedEntity.getGeyserId());
|
||||
session.sendUpstreamPacket(takeItemEntityPacket);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -82,7 +82,6 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
|
|||
}
|
||||
|
||||
passenger.updateBedrockMetadata(session);
|
||||
this.updateOffset(passenger, entity.getEntityType(), session, rider, true, (passengers.size() > 1));
|
||||
rider = false;
|
||||
}
|
||||
|
||||
|
@ -90,6 +89,9 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
|
|||
|
||||
for (long passengerId : entity.getPassengers()) {
|
||||
Entity passenger = session.getEntityCache().getEntityByJavaId(passengerId);
|
||||
if (passengerId == session.getPlayerEntity().getEntityId()) {
|
||||
passenger = session.getPlayerEntity();
|
||||
}
|
||||
if (passenger == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -99,66 +101,160 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
|
|||
session.sendUpstreamPacket(linkPacket);
|
||||
passengers.remove(passenger.getEntityId());
|
||||
|
||||
this.updateOffset(passenger, entity.getEntityType(), session, false, false, (passengers.size() > 1));
|
||||
this.updateOffset(passenger, entity, session, false, false, (packet.getPassengerIds().length > 1));
|
||||
} else {
|
||||
this.updateOffset(passenger, entity, session, (packet.getPassengerIds()[0] == passengerId), true, (packet.getPassengerIds().length > 1));
|
||||
}
|
||||
|
||||
// Force an update to the passenger metadata
|
||||
passenger.updateBedrockMetadata(session);
|
||||
}
|
||||
|
||||
if (entity.getEntityType() == EntityType.HORSE) {
|
||||
entity.getMetadata().put(EntityData.RIDER_SEAT_POSITION, Vector3f.from(0.0f, 2.3200102f, -0.2f));
|
||||
entity.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 181.0f);
|
||||
|
||||
entity.updateBedrockMetadata(session);
|
||||
switch (entity.getEntityType()) {
|
||||
case HORSE:
|
||||
case SKELETON_HORSE:
|
||||
case DONKEY:
|
||||
case MULE:
|
||||
case RAVAGER:
|
||||
entity.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 181.0f);
|
||||
entity.updateBedrockMetadata(session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOffset(Entity passenger, EntityType mountType, GeyserSession session, boolean rider, boolean riding, boolean moreThanOneEntity) {
|
||||
// Without the Y offset, Bedrock players will find themselves in the floor when mounting
|
||||
float yOffset = 0;
|
||||
private float getMountedHeightOffset(Entity mount) {
|
||||
final EntityType mountType = mount.getEntityType();
|
||||
float mountedHeightOffset = mountType.getHeight() * 0.75f;
|
||||
switch (mountType) {
|
||||
case BOAT:
|
||||
yOffset = passenger.getEntityType() == EntityType.PLAYER ? 1.02001f : -0.2f;
|
||||
break;
|
||||
case MINECART:
|
||||
yOffset = passenger.getEntityType() == EntityType.PLAYER ? 1.02001f : 0f;
|
||||
case CHICKEN:
|
||||
case SPIDER:
|
||||
mountedHeightOffset = mountType.getHeight() * 0.5f;
|
||||
break;
|
||||
case DONKEY:
|
||||
yOffset = 2.1f;
|
||||
break;
|
||||
case HORSE:
|
||||
case SKELETON_HORSE:
|
||||
case ZOMBIE_HORSE:
|
||||
case MULE:
|
||||
yOffset = 2.3f;
|
||||
mountedHeightOffset -= 0.25f;
|
||||
break;
|
||||
case LLAMA:
|
||||
case TRADER_LLAMA:
|
||||
yOffset = 2.5f;
|
||||
mountedHeightOffset = mountType.getHeight() * 0.67f;
|
||||
break;
|
||||
case PIG:
|
||||
yOffset = 1.85001f;
|
||||
case MINECART:
|
||||
case MINECART_HOPPER:
|
||||
case MINECART_TNT:
|
||||
case MINECART_CHEST:
|
||||
case MINECART_FURNACE:
|
||||
case MINECART_SPAWNER:
|
||||
case MINECART_COMMAND_BLOCK:
|
||||
mountedHeightOffset = 0;
|
||||
break;
|
||||
case ARMOR_STAND:
|
||||
yOffset = 1.3f;
|
||||
case BOAT:
|
||||
mountedHeightOffset = -0.1f;
|
||||
break;
|
||||
case HOGLIN:
|
||||
case ZOGLIN:
|
||||
boolean isBaby = mount.getMetadata().getFlags().getFlag(EntityFlag.BABY);
|
||||
mountedHeightOffset = mountType.getHeight() - (isBaby ? 0.2f : 0.15f);
|
||||
break;
|
||||
case PIGLIN:
|
||||
mountedHeightOffset = mountType.getHeight() * 0.92f;
|
||||
break;
|
||||
case RAVAGER:
|
||||
mountedHeightOffset = 2.1f;
|
||||
break;
|
||||
case SKELETON_HORSE:
|
||||
mountedHeightOffset -= 0.1875f;
|
||||
break;
|
||||
case STRIDER:
|
||||
yOffset = passenger.getEntityType() == EntityType.PLAYER ? 2.8200102f : 1.6f;
|
||||
mountedHeightOffset = mountType.getHeight() - 0.19f;
|
||||
break;
|
||||
}
|
||||
Vector3f offset = Vector3f.from(0f, yOffset, 0f);
|
||||
if (mountType == EntityType.STRIDER) {
|
||||
offset = offset.add(0f, 0f, -0.2f);
|
||||
}
|
||||
// Without the X offset, more than one entity on a boat is stacked on top of each other
|
||||
if (rider && moreThanOneEntity) {
|
||||
offset = offset.add(Vector3f.from(0.2, 0, 0));
|
||||
} else if (moreThanOneEntity) {
|
||||
offset = offset.add(Vector3f.from(-0.6, 0, 0));
|
||||
return mountedHeightOffset;
|
||||
}
|
||||
|
||||
private float getHeightOffset(Entity passenger) {
|
||||
boolean isBaby;
|
||||
switch (passenger.getEntityType()) {
|
||||
case SKELETON:
|
||||
case STRAY:
|
||||
case WITHER_SKELETON:
|
||||
return -0.6f;
|
||||
case ARMOR_STAND:
|
||||
// Armor stand isn't a marker
|
||||
if (passenger.getMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT) != 0.0f) {
|
||||
return 0.1f;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
case ENDERMITE:
|
||||
case SILVERFISH:
|
||||
return 0.1f;
|
||||
case PIGLIN:
|
||||
case PIGLIN_BRUTE:
|
||||
case ZOMBIFIED_PIGLIN:
|
||||
isBaby = passenger.getMetadata().getFlags().getFlag(EntityFlag.BABY);
|
||||
return isBaby ? -0.05f : -0.45f;
|
||||
case ZOMBIE:
|
||||
isBaby = passenger.getMetadata().getFlags().getFlag(EntityFlag.BABY);
|
||||
return isBaby ? 0.0f : -0.45f;
|
||||
case EVOKER:
|
||||
case ILLUSIONER:
|
||||
case PILLAGER:
|
||||
case RAVAGER:
|
||||
case VINDICATOR:
|
||||
case WITCH:
|
||||
return -0.45f;
|
||||
case PLAYER:
|
||||
return -0.35f;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
private void updateOffset(Entity passenger, Entity mount, GeyserSession session, boolean rider, boolean riding, boolean moreThanOneEntity) {
|
||||
passenger.getMetadata().getFlags().setFlag(EntityFlag.RIDING, riding);
|
||||
if (riding) {
|
||||
// Without the Y offset, Bedrock players will find themselves in the floor when mounting
|
||||
float mountedHeightOffset = getMountedHeightOffset(mount);
|
||||
float heightOffset = getHeightOffset(passenger);
|
||||
|
||||
float xOffset = 0;
|
||||
float yOffset = mountedHeightOffset + heightOffset;
|
||||
float zOffset = 0;
|
||||
switch (mount.getEntityType()) {
|
||||
case BOAT:
|
||||
// Without the X offset, more than one entity on a boat is stacked on top of each other
|
||||
if (rider && moreThanOneEntity) {
|
||||
xOffset = 0.2f;
|
||||
} else if (moreThanOneEntity) {
|
||||
xOffset = -0.6f;
|
||||
}
|
||||
break;
|
||||
case CHICKEN:
|
||||
zOffset = -0.1f;
|
||||
break;
|
||||
case LLAMA:
|
||||
zOffset = -0.3f;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Bedrock Differences
|
||||
* Zoglin & Hoglin seem to be taller in Bedrock edition
|
||||
* Horses are tinier
|
||||
* Players, Minecarts, and Boats have different origins
|
||||
*/
|
||||
if (passenger.getEntityType() == EntityType.PLAYER) {
|
||||
yOffset += EntityType.PLAYER.getOffset();
|
||||
}
|
||||
switch (mount.getEntityType()) {
|
||||
case MINECART:
|
||||
case MINECART_HOPPER:
|
||||
case MINECART_TNT:
|
||||
case MINECART_CHEST:
|
||||
case MINECART_FURNACE:
|
||||
case MINECART_SPAWNER:
|
||||
case MINECART_COMMAND_BLOCK:
|
||||
case BOAT:
|
||||
yOffset -= mount.getEntityType().getHeight() * 0.5f;
|
||||
}
|
||||
Vector3f offset = Vector3f.from(xOffset, yOffset, zOffset);
|
||||
passenger.getMetadata().put(EntityData.RIDER_SEAT_POSITION, offset);
|
||||
}
|
||||
passenger.updateBedrockMetadata(session);
|
||||
|
|
|
@ -30,6 +30,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
|||
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
@ -51,6 +52,33 @@ public class JavaEntityStatusTranslator extends PacketTranslator<ServerEntitySta
|
|||
EntityEventPacket entityEventPacket = new EntityEventPacket();
|
||||
entityEventPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
switch (packet.getStatus()) {
|
||||
case PLAYER_ENABLE_REDUCED_DEBUG:
|
||||
session.setReducedDebugInfo(true);
|
||||
return;
|
||||
case PLAYER_DISABLE_REDUCED_DEBUG:
|
||||
session.setReducedDebugInfo(false);
|
||||
return;
|
||||
case PLAYER_OP_PERMISSION_LEVEL_0:
|
||||
session.setOpPermissionLevel(0);
|
||||
session.sendAdventureSettings();
|
||||
return;
|
||||
case PLAYER_OP_PERMISSION_LEVEL_1:
|
||||
session.setOpPermissionLevel(1);
|
||||
session.sendAdventureSettings();
|
||||
return;
|
||||
case PLAYER_OP_PERMISSION_LEVEL_2:
|
||||
session.setOpPermissionLevel(2);
|
||||
session.sendAdventureSettings();
|
||||
return;
|
||||
case PLAYER_OP_PERMISSION_LEVEL_3:
|
||||
session.setOpPermissionLevel(3);
|
||||
session.sendAdventureSettings();
|
||||
return;
|
||||
case PLAYER_OP_PERMISSION_LEVEL_4:
|
||||
session.setOpPermissionLevel(4);
|
||||
session.sendAdventureSettings();
|
||||
return;
|
||||
|
||||
// EntityEventType.HURT sends extra data depending on the type of damage. However this appears to have no visual changes
|
||||
case LIVING_BURN:
|
||||
case LIVING_DROWN:
|
||||
|
@ -68,8 +96,20 @@ public class JavaEntityStatusTranslator extends PacketTranslator<ServerEntitySta
|
|||
entityEventPacket.setType(EntityEventType.USE_ITEM);
|
||||
break;
|
||||
case FISHING_HOOK_PULL_PLAYER:
|
||||
entityEventPacket.setType(EntityEventType.FISH_HOOK_TEASE); //TODO: CHECK
|
||||
break;
|
||||
// Player is pulled from a fishing rod
|
||||
// The physics of this are clientside on Java
|
||||
long pulledById = entity.getMetadata().getLong(EntityData.TARGET_EID);
|
||||
if (session.getPlayerEntity().getGeyserId() == pulledById) {
|
||||
Entity hookOwner = session.getEntityCache().getEntityByGeyserId(entity.getMetadata().getLong(EntityData.OWNER_EID));
|
||||
if (hookOwner != null) {
|
||||
// https://minecraft.gamepedia.com/Fishing_Rod#Hooking_mobs_and_other_entities
|
||||
SetEntityMotionPacket motionPacket = new SetEntityMotionPacket();
|
||||
motionPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
motionPacket.setMotion(hookOwner.getPosition().sub(session.getPlayerEntity().getPosition()).mul(0.1f));
|
||||
session.sendUpstreamPacket(motionPacket);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case TAMEABLE_TAMING_FAILED:
|
||||
entityEventPacket.setType(EntityEventType.TAME_FAILED);
|
||||
break;
|
||||
|
|
|
@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
|||
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
@ -43,24 +44,12 @@ public class JavaPlayerAbilitiesTranslator extends PacketTranslator<ServerPlayer
|
|||
|
||||
@Override
|
||||
public void translate(ServerPlayerAbilitiesPacket packet, GeyserSession session) {
|
||||
Entity entity = session.getPlayerEntity();
|
||||
PlayerEntity entity = session.getPlayerEntity();
|
||||
if (entity == null)
|
||||
return;
|
||||
|
||||
Set<AdventureSetting> playerFlags = new ObjectOpenHashSet<>();
|
||||
playerFlags.add(AdventureSetting.AUTO_JUMP);
|
||||
if (packet.isCanFly())
|
||||
playerFlags.add(AdventureSetting.MAY_FLY);
|
||||
|
||||
if (packet.isFlying())
|
||||
playerFlags.add(AdventureSetting.FLYING);
|
||||
|
||||
AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket();
|
||||
adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER);
|
||||
// Required or the packet simply is not sent
|
||||
adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL);
|
||||
adventureSettingsPacket.setUniqueEntityId(entity.getGeyserId());
|
||||
adventureSettingsPacket.getSettings().addAll(playerFlags);
|
||||
session.sendUpstreamPacket(adventureSettingsPacket);
|
||||
session.setCanFly(packet.isCanFly());
|
||||
session.setFlying(packet.isFlying());
|
||||
session.sendAdventureSettings();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,9 +57,8 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator<ServerPlayer
|
|||
if (self) {
|
||||
// Entity is ourself
|
||||
playerEntity = session.getPlayerEntity();
|
||||
SkinUtils.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape -> {
|
||||
GeyserConnector.getInstance().getLogger().debug("Loading Local Bedrock Java Skin Data");
|
||||
});
|
||||
SkinUtils.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape ->
|
||||
GeyserConnector.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data"));
|
||||
} else {
|
||||
playerEntity = session.getEntityCache().getPlayerEntity(entry.getProfile().getId());
|
||||
}
|
||||
|
@ -88,18 +87,13 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator<ServerPlayer
|
|||
break;
|
||||
case REMOVE_PLAYER:
|
||||
PlayerEntity entity = session.getEntityCache().getPlayerEntity(entry.getProfile().getId());
|
||||
if (entity != null && entity.isValid()) {
|
||||
// remove from tablist but player entity is still there
|
||||
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);
|
||||
} else {
|
||||
if (entity == null) {
|
||||
// just remove it from caching
|
||||
session.getEntityCache().removePlayerEntity(entry.getProfile().getId());
|
||||
} else {
|
||||
entity.setPlayerList(false);
|
||||
session.getEntityCache().removeEntity(entity, false);
|
||||
}
|
||||
}
|
||||
// As the player entity is no longer present, we can remove the entry
|
||||
session.getEntityCache().removePlayerEntity(entry.getProfile().getId());
|
||||
if (entity == session.getPlayerEntity()) {
|
||||
// If removing ourself we use our AuthData UUID
|
||||
translate.getEntries().add(new PlayerListPacket.Entry(session.getAuthData().getUUID()));
|
||||
|
|
|
@ -36,7 +36,7 @@ public class JavaDisplayScoreboardTranslator extends PacketTranslator<ServerDisp
|
|||
|
||||
@Override
|
||||
public void translate(ServerDisplayScoreboardPacket packet, GeyserSession session) {
|
||||
session.getScoreboardCache().getScoreboard().registerNewObjective(
|
||||
session.getWorldCache().getScoreboard().registerNewObjective(
|
||||
packet.getName(), packet.getPosition()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
package org.geysermc.connector.network.translators.java.scoreboard;
|
||||
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.ScoreboardCache;
|
||||
import org.geysermc.connector.network.session.cache.WorldCache;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.scoreboard.Objective;
|
||||
|
@ -41,13 +41,11 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
|
|||
|
||||
@Override
|
||||
public void translate(ServerScoreboardObjectivePacket packet, GeyserSession session) {
|
||||
ScoreboardCache cache = session.getScoreboardCache();
|
||||
Scoreboard scoreboard = cache.getScoreboard();
|
||||
|
||||
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
|
||||
Objective objective = scoreboard.getObjective(packet.getName());
|
||||
|
||||
if (objective == null && packet.getAction() != ObjectiveAction.REMOVE) {
|
||||
objective = scoreboard.registerNewObjective(packet.getName(), true);
|
||||
objective = scoreboard.registerNewObjective(packet.getName(), false);
|
||||
}
|
||||
|
||||
switch (packet.getAction()) {
|
||||
|
@ -61,6 +59,8 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
|
|||
break;
|
||||
}
|
||||
|
||||
if (objective != null && !objective.isTemp()) scoreboard.onUpdate();
|
||||
if (objective != null && objective.isActive()) {
|
||||
scoreboard.onUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,12 @@ package org.geysermc.connector.network.translators.java.scoreboard;
|
|||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerTeamPacket;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.connector.scoreboard.Team;
|
||||
import org.geysermc.connector.scoreboard.UpdateType;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
@ -42,54 +44,76 @@ import java.util.Set;
|
|||
|
||||
@Translator(packet = ServerTeamPacket.class)
|
||||
public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||
private static final GeyserLogger LOGGER = GeyserConnector.getInstance().getLogger();
|
||||
|
||||
@Override
|
||||
public void translate(ServerTeamPacket packet, GeyserSession session) {
|
||||
GeyserConnector.getInstance().getLogger().debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers()));
|
||||
if (LOGGER.isDebug()) {
|
||||
LOGGER.debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers()));
|
||||
}
|
||||
|
||||
Scoreboard scoreboard = session.getScoreboardCache().getScoreboard();
|
||||
int pps = session.getWorldCache().increaseAndGetScoreboardPacketsPerSecond();
|
||||
|
||||
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
|
||||
Team team = scoreboard.getTeam(packet.getTeamName());
|
||||
switch (packet.getAction()) {
|
||||
case CREATE:
|
||||
scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
|
||||
.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setNameTagVisibility(packet.getNameTagVisibility())
|
||||
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode()))
|
||||
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode()));
|
||||
break;
|
||||
case UPDATE:
|
||||
if (team != null) {
|
||||
team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode()))
|
||||
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode()))
|
||||
.setUpdateType(UpdateType.UPDATE);
|
||||
} else {
|
||||
GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
||||
if (team == null) {
|
||||
LOGGER.debug(LanguageUtils.getLocaleStringLog(
|
||||
"geyser.network.translator.team.failed_not_registered",
|
||||
packet.getAction(), packet.getTeamName()
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setNameTagVisibility(packet.getNameTagVisibility())
|
||||
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode()))
|
||||
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode()))
|
||||
.setUpdateType(UpdateType.UPDATE);
|
||||
break;
|
||||
case ADD_PLAYER:
|
||||
if (team != null) {
|
||||
team.addEntities(packet.getPlayers());
|
||||
} else {
|
||||
GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
||||
if (team == null) {
|
||||
LOGGER.debug(LanguageUtils.getLocaleStringLog(
|
||||
"geyser.network.translator.team.failed_not_registered",
|
||||
packet.getAction(), packet.getTeamName()
|
||||
));
|
||||
return;
|
||||
}
|
||||
team.addEntities(packet.getPlayers());
|
||||
break;
|
||||
case REMOVE_PLAYER:
|
||||
if (team != null) {
|
||||
team.removeEntities(packet.getPlayers());
|
||||
} else {
|
||||
GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
||||
if (team == null) {
|
||||
LOGGER.debug(LanguageUtils.getLocaleStringLog(
|
||||
"geyser.network.translator.team.failed_not_registered",
|
||||
packet.getAction(), packet.getTeamName()
|
||||
));
|
||||
return;
|
||||
}
|
||||
team.removeEntities(packet.getPlayers());
|
||||
break;
|
||||
case REMOVE:
|
||||
scoreboard.removeTeam(packet.getTeamName());
|
||||
break;
|
||||
}
|
||||
scoreboard.onUpdate();
|
||||
|
||||
// ScoreboardUpdater will handle it for us if the packets per second
|
||||
// (for score and team packets) is higher then the first threshold
|
||||
if (pps < ScoreboardUpdater.FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) {
|
||||
scoreboard.onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> toPlayerSet(String[] players) {
|
||||
return new ObjectOpenHashSet<>(Arrays.asList(players));
|
||||
return new ObjectOpenHashSet<>(players);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,48 +25,58 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.scoreboard;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardAction;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.WorldCache;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.scoreboard.Objective;
|
||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardAction;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
@Translator(packet = ServerUpdateScorePacket.class)
|
||||
public class JavaUpdateScoreTranslator extends PacketTranslator<ServerUpdateScorePacket> {
|
||||
private final GeyserLogger logger;
|
||||
|
||||
public JavaUpdateScoreTranslator() {
|
||||
logger = GeyserConnector.getInstance().getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translate(ServerUpdateScorePacket packet, GeyserSession session) {
|
||||
try {
|
||||
Scoreboard scoreboard = session.getScoreboardCache().getScoreboard();
|
||||
WorldCache worldCache = session.getWorldCache();
|
||||
Scoreboard scoreboard = worldCache.getScoreboard();
|
||||
int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond();
|
||||
|
||||
Objective objective = scoreboard.getObjective(packet.getObjective());
|
||||
if (objective == null && packet.getAction() != ScoreboardAction.REMOVE) {
|
||||
GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.translator.score.failed_objective", packet.getObjective()));
|
||||
return;
|
||||
}
|
||||
Objective objective = scoreboard.getObjective(packet.getObjective());
|
||||
if (objective == null && packet.getAction() != ScoreboardAction.REMOVE) {
|
||||
logger.info(LanguageUtils.getLocaleStringLog("geyser.network.translator.score.failed_objective", packet.getObjective()));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (packet.getAction()) {
|
||||
case ADD_OR_UPDATE:
|
||||
objective.setScore(packet.getEntry(), packet.getValue());
|
||||
break;
|
||||
case REMOVE:
|
||||
if (objective != null) {
|
||||
objective.resetScore(packet.getEntry());
|
||||
} else {
|
||||
for (Objective objective1 : scoreboard.getObjectives().values()) {
|
||||
objective1.resetScore(packet.getEntry());
|
||||
}
|
||||
switch (packet.getAction()) {
|
||||
case ADD_OR_UPDATE:
|
||||
objective.setScore(packet.getEntry(), packet.getValue());
|
||||
break;
|
||||
case REMOVE:
|
||||
if (objective != null) {
|
||||
objective.removeScore(packet.getEntry());
|
||||
} else {
|
||||
for (Objective objective1 : scoreboard.getObjectives().values()) {
|
||||
objective1.removeScore(packet.getEntry());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ScoreboardUpdater will handle it for us if the packets per second
|
||||
// (for score and team packets) is higher then the first threshold
|
||||
if (pps < ScoreboardUpdater.FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) {
|
||||
scoreboard.onUpdate();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,18 +25,17 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.window;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
@Translator(packet = ServerOpenWindowPacket.class)
|
||||
public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> {
|
||||
|
@ -58,18 +57,10 @@ public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowP
|
|||
return;
|
||||
}
|
||||
|
||||
String name = packet.getName();
|
||||
try {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject jsonObject = parser.parse(packet.getName()).getAsJsonObject();
|
||||
if (jsonObject.has("text")) {
|
||||
name = jsonObject.get("text").getAsString();
|
||||
} else if (jsonObject.has("translate")) {
|
||||
name = jsonObject.get("translate").getAsString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
GeyserConnector.getInstance().getLogger().debug("JavaOpenWindowTranslator: " + e.toString());
|
||||
}
|
||||
String name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(packet.getName()),
|
||||
session.getClientData().getLanguageCode());
|
||||
|
||||
name = LocaleUtils.getLocaleString(name, session.getClientData().getLanguageCode());
|
||||
|
||||
Inventory newInventory = new Inventory(name, packet.getWindowId(), packet.getType(), newTranslator.size + 36);
|
||||
session.getInventoryCache().cacheInventory(newInventory);
|
||||
|
|
|
@ -25,89 +25,111 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.world;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
|
||||
import com.nukkitx.nbt.NBTOutputStream;
|
||||
import com.nukkitx.nbt.NbtMap;
|
||||
import com.nukkitx.nbt.NbtUtils;
|
||||
import com.nukkitx.network.VarInts;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.BiomeTranslator;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
|
||||
@Translator(packet = ServerChunkDataPacket.class)
|
||||
public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPacket> {
|
||||
|
||||
/**
|
||||
* Determines if we should process non-full chunks
|
||||
*/
|
||||
private final boolean isCacheChunks;
|
||||
|
||||
public JavaChunkDataTranslator() {
|
||||
isCacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translate(ServerChunkDataPacket packet, GeyserSession session) {
|
||||
if (session.isSpawned()) {
|
||||
ChunkUtils.updateChunkPosition(session, session.getPlayerEntity().getPosition().toInt());
|
||||
}
|
||||
|
||||
if (packet.getColumn().getBiomeData() == null) //Non-full chunk
|
||||
if (packet.getColumn().getBiomeData() == null && !isCacheChunks) {
|
||||
// Non-full chunk without chunk caching
|
||||
session.getConnector().getLogger().debug("Not sending non-full chunk because chunk caching is off.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Merge received column with cache on network thread
|
||||
Column mergedColumn = session.getChunkCache().addToCache(packet.getColumn());
|
||||
if (mergedColumn == null) { // There were no changes?!?
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isNonFullChunk = packet.getColumn().getBiomeData() == null;
|
||||
|
||||
GeyserConnector.getInstance().getGeneralThreadPool().execute(() -> {
|
||||
try {
|
||||
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn());
|
||||
ByteBuf byteBuf = Unpooled.buffer(32);
|
||||
ChunkSection[] sections = chunkData.sections;
|
||||
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, mergedColumn, isNonFullChunk);
|
||||
ChunkSection[] sections = chunkData.getSections();
|
||||
|
||||
// Find highest section
|
||||
int sectionCount = sections.length - 1;
|
||||
while (sectionCount >= 0 && sections[sectionCount].isEmpty()) {
|
||||
while (sectionCount >= 0 && sections[sectionCount] == null) {
|
||||
sectionCount--;
|
||||
}
|
||||
sectionCount++;
|
||||
|
||||
// Estimate chunk size
|
||||
int size = 0;
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
ChunkSection section = chunkData.sections[i];
|
||||
section.writeToNetwork(byteBuf);
|
||||
ChunkSection section = sections[i];
|
||||
size += (section != null ? section : ChunkUtils.EMPTY_SECTION).estimateNetworkSize();
|
||||
}
|
||||
size += 256; // Biomes
|
||||
size += 1; // Border blocks
|
||||
size += 1; // Extra data length (always 0)
|
||||
size += chunkData.getBlockEntities().length * 64; // Conservative estimate of 64 bytes per tile entity
|
||||
|
||||
byte[] bedrockBiome = BiomeTranslator.toBedrockBiome(packet.getColumn().getBiomeData());
|
||||
// Allocate output buffer
|
||||
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(size);
|
||||
byte[] payload;
|
||||
try {
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
ChunkSection section = sections[i];
|
||||
(section != null ? section : ChunkUtils.EMPTY_SECTION).writeToNetwork(byteBuf);
|
||||
}
|
||||
|
||||
byteBuf.writeBytes(bedrockBiome); // Biomes - 256 bytes
|
||||
byteBuf.writeByte(0); // Border blocks - Edu edition only
|
||||
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
|
||||
byteBuf.writeBytes(BiomeTranslator.toBedrockBiome(mergedColumn.getBiomeData())); // Biomes - 256 bytes
|
||||
byteBuf.writeByte(0); // Border blocks - Edu edition only
|
||||
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
|
||||
|
||||
ByteBufOutputStream stream = new ByteBufOutputStream(Unpooled.buffer());
|
||||
NBTOutputStream nbtStream = NbtUtils.createNetworkWriter(stream);
|
||||
for (NbtMap blockEntity : chunkData.getBlockEntities()) {
|
||||
nbtStream.writeTag(blockEntity);
|
||||
// Encode tile entities into buffer
|
||||
NBTOutputStream nbtStream = NbtUtils.createNetworkWriter(new ByteBufOutputStream(byteBuf));
|
||||
for (NbtMap blockEntity : chunkData.getBlockEntities()) {
|
||||
nbtStream.writeTag(blockEntity);
|
||||
}
|
||||
|
||||
// Copy data into byte[], because the protocol lib really likes things that are s l o w
|
||||
byteBuf.readBytes(payload = new byte[byteBuf.readableBytes()]);
|
||||
} finally {
|
||||
byteBuf.release(); // Release buffer to allow buffer pooling to be useful
|
||||
}
|
||||
|
||||
byteBuf.writeBytes(stream.buffer());
|
||||
|
||||
byte[] payload = new byte[byteBuf.writerIndex()];
|
||||
byteBuf.readBytes(payload);
|
||||
|
||||
LevelChunkPacket levelChunkPacket = new LevelChunkPacket();
|
||||
levelChunkPacket.setSubChunksLength(sectionCount);
|
||||
levelChunkPacket.setCachingEnabled(false);
|
||||
levelChunkPacket.setChunkX(packet.getColumn().getX());
|
||||
levelChunkPacket.setChunkZ(packet.getColumn().getZ());
|
||||
levelChunkPacket.setChunkX(mergedColumn.getX());
|
||||
levelChunkPacket.setChunkZ(mergedColumn.getZ());
|
||||
levelChunkPacket.setData(payload);
|
||||
session.sendUpstreamPacket(levelChunkPacket);
|
||||
|
||||
// Some block entities need to be loaded in later or else text doesn't show (signs) or they crash the game (end gateway blocks)
|
||||
for (Object2IntMap.Entry<NbtMap> blockEntityEntry : chunkData.getLoadBlockEntitiesLater().object2IntEntrySet()) {
|
||||
int x = blockEntityEntry.getKey().getInt("x");
|
||||
int y = blockEntityEntry.getKey().getInt("y");
|
||||
int z = blockEntityEntry.getKey().getInt("z");
|
||||
ChunkUtils.updateBlock(session, blockEntityEntry.getIntValue(), new Position(x, y, z));
|
||||
}
|
||||
chunkData.getLoadBlockEntitiesLater().clear();
|
||||
session.getChunkCache().addToCache(packet.getColumn());
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
|||
import com.nukkitx.protocol.bedrock.data.SoundEvent;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
@ -64,5 +65,12 @@ public class JavaExplosionTranslator extends PacketTranslator<ServerExplosionPac
|
|||
levelSoundEventPacket.setIdentifier(":");
|
||||
levelSoundEventPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
session.sendUpstreamPacket(levelSoundEventPacket);
|
||||
|
||||
if (packet.getPushX() > 0f || packet.getPushY() > 0f || packet.getPushZ() > 0f) {
|
||||
SetEntityMotionPacket motionPacket = new SetEntityMotionPacket();
|
||||
motionPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
motionPacket.setMotion(Vector3f.from(packet.getPushX(), packet.getPushY(), packet.getPushZ()));
|
||||
session.sendUpstreamPacket(motionPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,34 +28,29 @@ package org.geysermc.connector.network.translators.java.world;
|
|||
import com.github.steveice10.mc.protocol.data.game.ClientRequest;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.notify.EnterCreditsValue;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.notify.RainStrengthValue;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.notify.RespawnScreenValue;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.notify.ThunderStrengthValue;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientRequestPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerNotifyClientPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.AdventureSetting;
|
||||
import com.nukkitx.protocol.bedrock.data.GameRuleData;
|
||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
||||
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.inventory.PlayerInventoryTranslator;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
|
||||
@Translator(packet = ServerNotifyClientPacket.class)
|
||||
public class JavaNotifyClientTranslator extends PacketTranslator<ServerNotifyClientPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerNotifyClientPacket packet, GeyserSession session) {
|
||||
Entity entity = session.getPlayerEntity();
|
||||
PlayerEntity entity = session.getPlayerEntity();
|
||||
if (entity == null)
|
||||
return;
|
||||
|
||||
|
@ -63,51 +58,58 @@ public class JavaNotifyClientTranslator extends PacketTranslator<ServerNotifyCli
|
|||
case START_RAIN:
|
||||
LevelEventPacket startRainPacket = new LevelEventPacket();
|
||||
startRainPacket.setType(LevelEventType.START_RAINING);
|
||||
startRainPacket.setData(ThreadLocalRandom.current().nextInt(50000) + 10000);
|
||||
startRainPacket.setData(Integer.MAX_VALUE);
|
||||
startRainPacket.setPosition(Vector3f.ZERO);
|
||||
session.sendUpstreamPacket(startRainPacket);
|
||||
session.setRaining(true);
|
||||
break;
|
||||
case STOP_RAIN:
|
||||
LevelEventPacket stopRainPacket = new LevelEventPacket();
|
||||
stopRainPacket.setType(LevelEventType.STOP_RAINING);
|
||||
stopRainPacket.setData(ThreadLocalRandom.current().nextInt(50000) + 10000);
|
||||
stopRainPacket.setData(0);
|
||||
stopRainPacket.setPosition(Vector3f.ZERO);
|
||||
session.sendUpstreamPacket(stopRainPacket);
|
||||
session.setRaining(false);
|
||||
break;
|
||||
case RAIN_STRENGTH:
|
||||
// While the above values are used, they CANNOT BE TRUSTED on a vanilla server as they are swapped around
|
||||
// Spigot and forks implement it correctly
|
||||
// Rain strength is your best way for determining if there is any rain
|
||||
RainStrengthValue value = (RainStrengthValue) packet.getValue();
|
||||
boolean isCurrentlyRaining = value.getStrength() > 0f;
|
||||
// Java sends the rain level. Bedrock doesn't care, so we don't care if it's already raining.
|
||||
if (isCurrentlyRaining != session.isRaining()) {
|
||||
LevelEventPacket changeRainPacket = new LevelEventPacket();
|
||||
changeRainPacket.setType(isCurrentlyRaining ? LevelEventType.START_RAINING : LevelEventType.STOP_RAINING);
|
||||
changeRainPacket.setData(Integer.MAX_VALUE); // Dunno what this does; used to be implemented with ThreadLocalRandom
|
||||
changeRainPacket.setPosition(Vector3f.ZERO);
|
||||
session.sendUpstreamPacket(changeRainPacket);
|
||||
session.setRaining(isCurrentlyRaining);
|
||||
}
|
||||
break;
|
||||
case THUNDER_STRENGTH:
|
||||
// See above, same process
|
||||
ThunderStrengthValue thunderValue = (ThunderStrengthValue) packet.getValue();
|
||||
boolean isCurrentlyThundering = thunderValue.getStrength() > 0f;
|
||||
if (isCurrentlyThundering != session.isThunder()) {
|
||||
LevelEventPacket changeThunderPacket = new LevelEventPacket();
|
||||
changeThunderPacket.setType(isCurrentlyThundering ? LevelEventType.START_THUNDERSTORM : LevelEventType.STOP_THUNDERSTORM);
|
||||
changeThunderPacket.setData(Integer.MAX_VALUE);
|
||||
changeThunderPacket.setPosition(Vector3f.ZERO);
|
||||
session.sendUpstreamPacket(changeThunderPacket);
|
||||
session.setThunder(isCurrentlyThundering);
|
||||
}
|
||||
break;
|
||||
case CHANGE_GAMEMODE:
|
||||
Set<AdventureSetting> playerFlags = new ObjectOpenHashSet<>();
|
||||
GameMode gameMode = (GameMode) packet.getValue();
|
||||
if (gameMode == GameMode.ADVENTURE)
|
||||
playerFlags.add(AdventureSetting.WORLD_IMMUTABLE);
|
||||
|
||||
if (gameMode == GameMode.CREATIVE)
|
||||
playerFlags.add(AdventureSetting.MAY_FLY);
|
||||
|
||||
if (gameMode == GameMode.SPECTATOR) {
|
||||
playerFlags.add(AdventureSetting.MAY_FLY);
|
||||
playerFlags.add(AdventureSetting.NO_CLIP);
|
||||
playerFlags.add(AdventureSetting.FLYING);
|
||||
playerFlags.add(AdventureSetting.WORLD_IMMUTABLE);
|
||||
gameMode = GameMode.CREATIVE; // spectator doesnt exist on bedrock
|
||||
}
|
||||
|
||||
playerFlags.add(AdventureSetting.AUTO_JUMP);
|
||||
session.sendAdventureSettings();
|
||||
|
||||
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
||||
playerGameTypePacket.setGamemode(gameMode.ordinal());
|
||||
session.sendUpstreamPacket(playerGameTypePacket);
|
||||
session.setGameMode(gameMode);
|
||||
|
||||
// We need to delay this because otherwise it's overridden by the adventure settings from the abilities packet
|
||||
session.getConnector().getGeneralThreadPool().schedule(() -> {
|
||||
AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket();
|
||||
adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER);
|
||||
adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL);
|
||||
adventureSettingsPacket.setUniqueEntityId(entity.getGeyserId());
|
||||
adventureSettingsPacket.getSettings().addAll(playerFlags);
|
||||
session.sendUpstreamPacket(adventureSettingsPacket);
|
||||
}, 50, TimeUnit.MILLISECONDS);
|
||||
|
||||
// Update the crafting grid to add/remove barriers for creative inventory
|
||||
PlayerInventoryTranslator.updateCraftingGrid(session, session.getInventory());
|
||||
break;
|
||||
|
@ -138,6 +140,19 @@ public class JavaNotifyClientTranslator extends PacketTranslator<ServerNotifyCli
|
|||
packet.getValue() == RespawnScreenValue.IMMEDIATE_RESPAWN));
|
||||
session.sendUpstreamPacket(gamerulePacket);
|
||||
break;
|
||||
case INVALID_BED:
|
||||
// Not sent as a proper message? Odd.
|
||||
session.sendMessage(LocaleUtils.getLocaleString("block.minecraft.spawn.not_valid",
|
||||
session.getClientData().getLanguageCode()));
|
||||
break;
|
||||
case ARROW_HIT_PLAYER:
|
||||
PlaySoundPacket arrowSoundPacket = new PlaySoundPacket();
|
||||
arrowSoundPacket.setSound("random.orb");
|
||||
arrowSoundPacket.setPitch(0.5f);
|
||||
arrowSoundPacket.setVolume(0.5f);
|
||||
arrowSoundPacket.setPosition(entity.getPosition());
|
||||
session.sendUpstreamPacket(arrowSoundPacket);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnPar
|
|||
LevelEventPacket particle = new LevelEventPacket();
|
||||
switch (packet.getParticle().getType()) {
|
||||
case BLOCK:
|
||||
particle.setType(LevelEventType.PARTICLE_DESTROY_BLOCK);
|
||||
particle.setType(LevelEventType.PARTICLE_DESTROY_BLOCK_NO_SOUND);
|
||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
particle.setData(BlockTranslator.getBedrockBlockId(((BlockParticleData) packet.getParticle().getData()).getBlockState()));
|
||||
session.sendUpstreamPacket(particle);
|
||||
|
@ -78,7 +78,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnPar
|
|||
int r = (int) (data.getRed()*255);
|
||||
int g = (int) (data.getGreen()*255);
|
||||
int b = (int) (data.getBlue()*255);
|
||||
particle.setType(LevelEventType.PARTICLE_FALLING_DUST);
|
||||
particle.setType(LevelEventType.PARTICLE_REDSTONE);
|
||||
particle.setData(((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff));
|
||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
session.sendUpstreamPacket(particle);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue