Merge pull request #2247 from GeyserMC/feature/1.17

1.17 Support
This commit is contained in:
Camotoy 2021-06-08 11:06:42 -04:00 committed by GitHub
commit 2b7c75968b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
240 changed files with 6516 additions and 4876 deletions

View file

@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
### Currently supporting Minecraft Bedrock v1.16.100 - v1.16.220 and Minecraft Java v1.16.4 - v1.16.5.
### Currently supporting Minecraft Bedrock 1.17 and Minecraft Java 1.17.
## Setting Up
Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser.
@ -39,6 +39,8 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
- Some Entity Flags
- Structure block UI
Extended height features can be "supported", but require additional work.
## What can't be fixed
There are a few things Geyser is unable to support due to various differences between Minecraft Bedrock and Java. For a list of these limitations, see the [Current Limitations](https://github.com/GeyserMC/Geyser/wiki/Current-Limitations) page.
@ -53,6 +55,7 @@ Any contributions are appreciated. Please feel free to reach out to us on [Disco
you're interested in helping out with Geyser.
## Libraries Used:
- [Adventure Text Library](https://github.com/KyoriPowered/adventure)
- [NukkitX Bedrock Protocol Library](https://github.com/NukkitX/Protocol)
- [Steveice10's Java Protocol Library](https://github.com/Steveice10/MCProtocolLib)
- [TerminalConsoleAppender](https://github.com/Minecrell/TerminalConsoleAppender)

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-bungeecord</artifactId>
@ -14,7 +14,7 @@
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>

View file

@ -41,7 +41,7 @@ public final class GeyserBungeeConfiguration extends GeyserJacksonConfiguration
private Path floodgateKeyPath;
public void loadFloodgate(GeyserBungeePlugin plugin) {
Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate-bungee");
Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate");
Path geyserDataFolder = plugin.getDataFolder().toPath();
Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null;

View file

@ -94,10 +94,16 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate-bungee") == null) {
// Remove this in like a year
if (getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/"));
return;
}
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate") == null) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
return;
} else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) {
} else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate") != null) {
// Floodgate installed means that the user wants Floodgate authentication
geyserLogger.debug("Auto-setting to Floodgate authentication.");
geyserConfig.getRemote().setAuthType("floodgate");

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>geyser-parent</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-parent</artifactId>
<packaging>pom</packaging>

View file

@ -6,15 +6,22 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-spigot</artifactId>
<repositories>
<repository>
<id>viaversion-repo</id>
<url>https://repo.viaversion.com</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -24,9 +31,9 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>us.myles</groupId>
<groupId>com.viaversion</groupId>
<artifactId>viaversion</artifactId>
<version>3.2.0</version>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -42,15 +42,10 @@ public final class GeyserSpigotConfiguration extends GeyserJacksonConfiguration
private Path floodgateKeyPath;
public void loadFloodgate(GeyserSpigotPlugin plugin) {
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate-bukkit");
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate");
Path geyserDataFolder = plugin.getDataFolder().toPath();
Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null;
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgate, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger());
}
@Override
public boolean isCacheChunks() {
return true; // We override this as with Bukkit, we have direct access to the server implementation
}
}

View file

@ -26,6 +26,10 @@
package org.geysermc.platform.spigot;
import com.github.steveice10.mc.protocol.MinecraftConstants;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.common.PlatformType;
@ -46,16 +50,9 @@ import org.geysermc.platform.spigot.command.SpigotCommandSender;
import org.geysermc.platform.spigot.world.GeyserSpigot1_11CraftingListener;
import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener;
import org.geysermc.platform.spigot.world.manager.*;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.MappingData;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.UUID;
@ -81,12 +78,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
try {
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
File bukkitConfig = new File("plugins/Geyser-Bukkit/config.yml");
if (bukkitConfig.exists()) { // Copy over old configs
getLogger().log(Level.INFO, LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.copy_bukkit_config"));
Files.copy(bukkitConfig.toPath(), new File(getDataFolder().toString() + "/config.yml").toPath());
getLogger().log(Level.INFO, LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.copied_bukkit_config"));
}
}
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class);
@ -129,11 +120,18 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && Bukkit.getPluginManager().getPlugin("floodgate-bukkit") == null) {
// Remove this in like a year
if (Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/"));
this.getPluginLoader().disablePlugin(this);
return;
}
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && Bukkit.getPluginManager().getPlugin("floodgate") == null) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
this.getPluginLoader().disablePlugin(this);
return;
} else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) {
} else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate") != null) {
// Floodgate installed means that the user wants Floodgate authentication
geyserLogger.debug("Auto-setting to Floodgate authentication.");
geyserConfig.getRemote().setAuthType("floodgate");
@ -154,12 +152,18 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserCommandManager = new GeyserSpigotCommandManager(this, connector);
boolean isViaVersion = (Bukkit.getPluginManager().getPlugin("ViaVersion") != null);
boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null;
if (isViaVersion) {
if (!isCompatible(Via.getAPI().getVersion().replace("-SNAPSHOT", ""), "3.2.0")) {
try {
// Ensure that we have the latest 4.0.0 changes and not an older ViaVersion version
Class.forName("com.viaversion.viaversion.api.ViaManager");
} catch (ClassNotFoundException e) {
geyserLogger.warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.viaversion.too_old",
"https://ci.viaversion.com/job/ViaVersion/"));
isViaVersion = false;
if (this.geyserConfig.isDebugMode()) {
e.printStackTrace();
}
}
}
// Used to determine if Block.getBlockData() is present.
@ -167,11 +171,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
if (isLegacy)
geyserLogger.debug("Legacy version of Minecraft (1.12.2 or older) detected; falling back to ViaVersion for block state retrieval.");
boolean use3dBiomes = isCompatible(Bukkit.getServer().getVersion(), "1.16.0");
if (!use3dBiomes) {
geyserLogger.debug("Legacy version of Minecraft (1.15.2 or older) detected; not using 3D biomes.");
}
boolean isPre1_12 = !isCompatible(Bukkit.getServer().getVersion(), "1.12.0");
// Set if we need to use a different method for getting a player's locale
SpigotCommandSender.setUseLegacyLocaleMethod(isPre1_12);
@ -187,11 +186,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserWorldManager = new GeyserSpigot1_12NativeWorldManager(this);
} else {
// Post-1.13
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this, use3dBiomes);
this.geyserWorldManager = new GeyserSpigotLegacyNativeWorldManager(this);
}
} else {
// No ViaVersion
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this, use3dBiomes);
this.geyserWorldManager = new GeyserSpigotNativeWorldManager(this);
}
geyserLogger.debug("Using NMS adapter: " + this.geyserWorldManager.getClass() + ", " + nmsVersion);
} catch (Exception e) {
@ -213,7 +212,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
this.geyserWorldManager = new GeyserSpigotFallbackWorldManager(this);
} else {
// Post-1.13
this.geyserWorldManager = new GeyserSpigotWorldManager(this, use3dBiomes);
this.geyserWorldManager = new GeyserSpigotWorldManager(this);
}
geyserLogger.debug("Using default world manager: " + this.geyserWorldManager.getClass());
}
@ -323,14 +322,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
*/
private boolean isViaVersionNeeded() {
ProtocolVersion serverVersion = getServerProtocolVersion();
List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
List<ProtocolPathEntry> protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
serverVersion.getVersion());
if (protocolList == null) {
// No translation needed!
return false;
}
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
if (mappingData != null) {
return true;
}

View file

@ -34,6 +34,12 @@ import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeDa
import com.nukkitx.protocol.bedrock.data.inventory.CraftingData;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viaversion.util.Pair;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@ -45,12 +51,6 @@ import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.network.translators.item.RecipeRegistry;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.data.MappingData;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import java.util.*;
@ -68,12 +68,12 @@ public class GeyserSpigot1_11CraftingListener implements Listener {
/**
* The list of all protocols from the client's version to 1.13.
*/
private final List<Pair<Integer, Protocol>> protocolList;
private final List<ProtocolPathEntry> protocolList;
public GeyserSpigot1_11CraftingListener(GeyserConnector connector) {
this.connector = connector;
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
this.protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
this.mappingData1_12to1_13 = Via.getManager().getProtocolManager().getProtocol(Protocol1_13To1_12_2.class).getMappingData();
this.protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
ProtocolVersion.v1_13.getVersion());
}
@ -187,7 +187,7 @@ public class GeyserSpigot1_11CraftingListener implements Listener {
}
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
if (mappingData != null) {
itemId = mappingData.getNewItemId(itemId);
}

View file

@ -25,6 +25,8 @@
package org.geysermc.platform.spigot.world.manager;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@ -32,8 +34,6 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
/**
* Used with ViaVersion and pre-1.13.
@ -54,7 +54,7 @@ public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldMan
return BlockTranslator.JAVA_AIR_ID;
}
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
BlockStorage storage = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId()).get(BlockStorage.class);
int blockId = adapter.getBlockAt(player.getWorld(), x, y, z);
return getLegacyBlock(storage, blockId, x, y, z);
}

View file

@ -25,23 +25,19 @@
package org.geysermc.platform.spigot.world.manager;
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.MappingData;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage;
import java.util.List;
@ -60,12 +56,12 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
/**
* The list of all protocols from the client's version to 1.13.
*/
private final List<Pair<Integer, Protocol>> protocolList;
private final List<ProtocolPathEntry> protocolList;
public GeyserSpigot1_12WorldManager(Plugin plugin) {
super(plugin, false);
this.mappingData1_12to1_13 = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData();
this.protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION,
super(plugin);
this.mappingData1_12to1_13 = Via.getManager().getProtocolManager().getProtocol(Protocol1_13To1_12_2.class).getMappingData();
this.protocolList = Via.getManager().getProtocolManager().getProtocolPath(CLIENT_PROTOCOL_VERSION,
ProtocolVersion.v1_13.getVersion());
}
@ -81,7 +77,7 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
return BlockTranslator.JAVA_AIR_ID;
}
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
BlockStorage storage = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId()).get(BlockStorage.class);
Block block = player.getWorld().getBlockAt(x, y, z);
// Black magic that gets the old block state ID
int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
@ -109,7 +105,7 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
}
}
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
if (mappingData != null) {
blockId = mappingData.getNewBlockStateId(blockId);
}
@ -117,28 +113,6 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager {
return blockId;
}
@SuppressWarnings("deprecation")
@Override
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername());
if (player == null) {
return;
}
World world = player.getWorld();
// Get block entity storage
BlockStorage storage = Via.getManager().getConnection(player.getUniqueId()).get(BlockStorage.class);
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
for (int blockZ = 0; blockZ < 16; blockZ++) {
for (int blockX = 0; blockX < 16; blockX++) {
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
// Black magic that gets the old block state ID
int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF);
chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, blockId, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ));
}
}
}
}
@Override
public boolean isLegacy() {
return true;

View file

@ -25,7 +25,6 @@
package org.geysermc.platform.spigot.world.manager;
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import org.bukkit.plugin.Plugin;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
@ -37,8 +36,7 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
*/
public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
public GeyserSpigotFallbackWorldManager(Plugin plugin) {
// Since this is pre-1.13 (and thus pre-1.15), there will never be 3D biomes.
super(plugin, false);
super(plugin);
}
@Override
@ -46,11 +44,6 @@ public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager {
return BlockTranslator.JAVA_AIR_ID;
}
@Override
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
// Do nothing, since we can't do anything with the chunk
}
@Override
public boolean hasOwnChunkCache() {
return false;

View file

@ -26,16 +26,15 @@
package org.geysermc.platform.spigot.world.manager;
import com.github.steveice10.mc.protocol.MinecraftConstants;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntList;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.platform.spigot.GeyserSpigotPlugin;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.data.MappingData;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import java.util.List;
@ -46,18 +45,18 @@ public class GeyserSpigotLegacyNativeWorldManager extends GeyserSpigotNativeWorl
private final Int2IntMap oldToNewBlockId;
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin, boolean use3dBiomes) {
super(plugin, use3dBiomes);
public GeyserSpigotLegacyNativeWorldManager(GeyserSpigotPlugin plugin) {
super(plugin);
IntList allBlockStates = adapter.getAllBlockStates();
oldToNewBlockId = new Int2IntOpenHashMap(allBlockStates.size());
ProtocolVersion serverVersion = plugin.getServerProtocolVersion();
List<Pair<Integer, Protocol>> protocolList = ProtocolRegistry.getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
List<ProtocolPathEntry> protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftConstants.PROTOCOL_VERSION,
serverVersion.getVersion());
for (int oldBlockId : allBlockStates) {
int newBlockId = oldBlockId;
// protocolList should *not* be null; we checked for that before initializing this class
for (int i = protocolList.size() - 1; i >= 0; i--) {
MappingData mappingData = protocolList.get(i).getValue().getMappingData();
MappingData mappingData = protocolList.get(i).getProtocol().getMappingData();
if (mappingData != null) {
newBlockId = mappingData.getNewBlockStateId(newBlockId);
}

View file

@ -36,8 +36,8 @@ import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter;
public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager {
protected final SpigotWorldAdapter adapter;
public GeyserSpigotNativeWorldManager(Plugin plugin, boolean use3dBiomes) {
super(plugin, use3dBiomes);
public GeyserSpigotNativeWorldManager(Plugin plugin) {
super(plugin);
adapter = SpigotAdapters.getWorldAdapter();
}

View file

@ -25,18 +25,13 @@
package org.geysermc.platform.spigot.world.manager;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.mc.protocol.MinecraftConstants;
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.Lectern;
import org.bukkit.block.data.BlockData;
@ -44,17 +39,13 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.plugin.Plugin;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator;
import org.geysermc.connector.network.translators.world.GeyserWorldManager;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.BlockEntityUtils;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.GameRule;
import org.geysermc.connector.utils.LanguageUtils;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@ -67,48 +58,10 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
*/
protected static final int CLIENT_PROTOCOL_VERSION = MinecraftConstants.PROTOCOL_VERSION;
/**
* Whether the server is pre-1.16 and therefore does not support 3D biomes on an API level guaranteed.
*/
private final boolean use3dBiomes;
/**
* Stores a list of {@link Biome} ordinal numbers to Minecraft biome numeric IDs.
*
* Working with the Biome enum in Spigot poses two problems:
* 1: The Biome enum values change in both order and names over the years.
* 2: There is no way to get the Minecraft biome ID from the name itself with Spigot.
* To solve both of these problems, we store a JSON file of every Biome enum that has existed,
* along with its 1.16 biome number.
*
* The key is the Spigot Biome ordinal; the value is the Minecraft Java biome numerical ID
*/
private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length);
private final Plugin plugin;
public GeyserSpigotWorldManager(Plugin plugin, boolean use3dBiomes) {
this.use3dBiomes = use3dBiomes;
public GeyserSpigotWorldManager(Plugin plugin) {
this.plugin = plugin;
// Load the values into the biome-to-ID map
InputStream biomeStream = FileUtils.getResource("biomes.json");
JsonNode biomes;
try {
biomes = GeyserConnector.JSON_MAPPER.readTree(biomeStream);
} catch (Exception e) {
throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e);
}
// Only load in the biomes that are present in this version of Minecraft
for (Biome enumBiome : Biome.values()) {
JsonNode biome = biomes.get(enumBiome.toString());
if (biome != null) {
biomeToIdMap.put(enumBiome.ordinal(), biome.intValue());
} else {
GeyserConnector.getInstance().getLogger().debug("No biome mapping found for " + enumBiome.toString() +
", defaulting to 0");
biomeToIdMap.put(enumBiome.ordinal(), 0);
}
}
}
@Override
@ -121,64 +74,11 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager {
return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
}
@Override
public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) {
Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
return;
}
World world = bukkitPlayer.getWorld();
for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order
for (int blockZ = 0; blockZ < 16; blockZ++) {
for (int blockX = 0; blockX < 16; blockX++) {
Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ);
int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID);
chunk.set(blockX, blockY, blockZ, id);
}
}
}
}
@Override
public boolean hasOwnChunkCache() {
return true;
}
@Override
@SuppressWarnings("deprecation")
public int[] getBiomeDataAt(GeyserSession session, int x, int z) {
int[] biomeData = new int[1024];
World world = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld();
int chunkX = x << 4;
int chunkZ = z << 4;
int chunkXmax = chunkX + 16;
int chunkZmax = chunkZ + 16;
// 3D biomes didn't exist until 1.15
if (use3dBiomes) {
for (int localX = chunkX; localX < chunkXmax; localX += 4) {
for (int localY = 0; localY < 255; localY += + 4) {
for (int localZ = chunkZ; localZ < chunkZmax; localZ += 4) {
// Index is based on wiki.vg's index requirements
final int i = ((localY >> 2) & 63) << 4 | ((localZ >> 2) & 3) << 2 | ((localX >> 2) & 3);
biomeData[i] = biomeToIdMap.getOrDefault(world.getBiome(localX, localY, localZ).ordinal(), 0);
}
}
}
} else {
// Looks like the same code, but we're not checking the Y coordinate here
for (int localX = chunkX; localX < chunkXmax; localX += 4) {
for (int localY = 0; localY < 255; localY += + 4) {
for (int localZ = chunkZ; localZ < chunkZmax; localZ += 4) {
// Index is based on wiki.vg's index requirements
final int i = ((localY >> 2) & 63) << 4 | ((localZ >> 2) & 3) << 2 | ((localX >> 2) & 3);
biomeData[i] = biomeToIdMap.getOrDefault(world.getBiome(localX, localZ).ordinal(), 0);
}
}
}
}
return biomeData;
}
@Override
public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
// Run as a task to prevent async issues

View file

@ -1,155 +0,0 @@
{
"MUTATED_ICE_FLATS" : 140,
"MUTATED_TAIGA" : 133,
"SAVANNA_PLATEAU_MOUNTAINS" : 164,
"DEEP_WARM_OCEAN" : 47,
"REDWOOD_TAIGA_HILLS" : 33,
"THE_VOID" : 127,
"COLD_TAIGA_MOUNTAINS" : 158,
"BAMBOO_JUNGLE_HILLS" : 169,
"MOUNTAINS" : 3,
"MESA_PLATEAU" : 39,
"SNOWY_TAIGA_HILLS" : 31,
"DEEP_FROZEN_OCEAN" : 50,
"EXTREME_HILLS" : 3,
"BIRCH_FOREST_MOUNTAINS" : 155,
"FOREST" : 4,
"BIRCH_FOREST" : 27,
"SNOWY_TUNDRA" : 12,
"ICE_SPIKES" : 140,
"FROZEN_OCEAN" : 10,
"WARPED_FOREST" : 172,
"WOODED_BADLANDS_PLATEAU" : 38,
"BADLANDS_PLATEAU" : 39,
"ICE_PLAINS_SPIKES" : 140,
"MEGA_TAIGA" : 32,
"MUTATED_SAVANNA_ROCK" : 164,
"SAVANNA_PLATEAU" : 36,
"DARK_FOREST_HILLS" : 157,
"END_MIDLANDS" : 41,
"SHATTERED_SAVANNA_PLATEAU" : 164,
"SAVANNA" : 35,
"MUSHROOM_ISLAND_SHORE" : 15,
"SWAMP" : 6,
"ICE_MOUNTAINS" : 13,
"BEACH" : 16,
"MUTATED_MESA_CLEAR_ROCK" : 167,
"END_HIGHLANDS" : 42,
"COLD_BEACH" : 26,
"JUNGLE" : 21,
"MUTATED_TAIGA_COLD" : 158,
"TALL_BIRCH_HILLS" : 156,
"DARK_FOREST" : 29,
"WOODED_HILLS" : 18,
"HELL" : 8,
"MUTATED_REDWOOD_TAIGA" : 160,
"MESA_PLATEAU_FOREST" : 38,
"MUSHROOM_ISLAND" : 14,
"BADLANDS" : 37,
"END_BARRENS" : 43,
"MUTATED_EXTREME_HILLS_WITH_TREES" : 162,
"MUTATED_JUNGLE_EDGE" : 151,
"MODIFIED_BADLANDS_PLATEAU" : 167,
"ROOFED_FOREST_MOUNTAINS" : 157,
"SOUL_SAND_VALLEY" : 170,
"DESERT" : 2,
"MUTATED_PLAINS" : 129,
"MUTATED_BIRCH_FOREST" : 155,
"WOODED_MOUNTAINS" : 34,
"TAIGA_HILLS" : 19,
"BAMBOO_JUNGLE" : 168,
"SWAMPLAND_MOUNTAINS" : 134,
"DESERT_MOUNTAINS" : 130,
"REDWOOD_TAIGA" : 32,
"MUSHROOM_FIELDS" : 14,
"GIANT_TREE_TAIGA_HILLS" : 33,
"PLAINS" : 1,
"JUNGLE_EDGE" : 23,
"SAVANNA_MOUNTAINS" : 163,
"DEEP_COLD_OCEAN" : 49,
"DESERT_LAKES" : 130,
"MOUNTAIN_EDGE" : 20,
"SNOWY_MOUNTAINS" : 13,
"MESA_PLATEAU_MOUNTAINS" : 167,
"JUNGLE_MOUNTAINS" : 149,
"SMALLER_EXTREME_HILLS" : 20,
"MESA_PLATEAU_FOREST_MOUNTAINS" : 166,
"NETHER_WASTES" : 8,
"BIRCH_FOREST_HILLS_MOUNTAINS" : 156,
"MUTATED_JUNGLE" : 149,
"WARM_OCEAN" : 44,
"DEEP_OCEAN" : 24,
"STONE_BEACH" : 25,
"MODIFIED_JUNGLE" : 149,
"MUTATED_SAVANNA" : 163,
"TAIGA_COLD_HILLS" : 31,
"OCEAN" : 0,
"SMALL_END_ISLANDS" : 40,
"MUSHROOM_FIELD_SHORE" : 15,
"GRAVELLY_MOUNTAINS" : 131,
"FROZEN_RIVER" : 11,
"TAIGA_COLD" : 30,
"BASALT_DELTAS" : 173,
"EXTREME_HILLS_WITH_TREES" : 34,
"MEGA_TAIGA_HILLS" : 33,
"MUTATED_FOREST" : 132,
"MUTATED_BIRCH_FOREST_HILLS" : 156,
"SKY" : 9,
"LUKEWARM_OCEAN" : 45,
"EXTREME_HILLS_MOUNTAINS" : 131,
"COLD_TAIGA_HILLS" : 31,
"THE_END" : 9,
"SUNFLOWER_PLAINS" : 129,
"SAVANNA_ROCK" : 36,
"ERODED_BADLANDS" : 165,
"STONE_SHORE" : 25,
"EXTREME_HILLS_PLUS_MOUNTAINS" : 162,
"CRIMSON_FOREST" : 171,
"VOID" : 127,
"SNOWY_TAIGA" : 30,
"SNOWY_TAIGA_MOUNTAINS" : 158,
"FLOWER_FOREST" : 132,
"COLD_OCEAN" : 46,
"BEACHES" : 16,
"MESA" : 37,
"MUSHROOM_SHORE" : 15,
"MESA_CLEAR_ROCK" : 39,
"NETHER" : 8,
"ICE_PLAINS" : 12,
"SHATTERED_SAVANNA" : 163,
"ROOFED_FOREST" : 29,
"GIANT_SPRUCE_TAIGA_HILLS" : 161,
"SNOWY_BEACH" : 26,
"MESA_BRYCE" : 165,
"JUNGLE_EDGE_MOUNTAINS" : 151,
"MUTATED_DESERT" : 130,
"MODIFIED_GRAVELLY_MOUNTAINS" : 158,
"MEGA_SPRUCE_TAIGA" : 160,
"TAIGA_MOUNTAINS" : 133,
"SMALL_MOUNTAINS" : 20,
"EXTREME_HILLS_PLUS" : 34,
"GIANT_SPRUCE_TAIGA" : 160,
"FOREST_HILLS" : 18,
"DESERT_HILLS" : 17,
"MUTATED_REDWOOD_TAIGA_HILLS" : 161,
"MEGA_SPRUCE_TAIGA_HILLS" : 161,
"RIVER" : 7,
"GIANT_TREE_TAIGA" : 32,
"SWAMPLAND" : 6,
"JUNGLE_HILLS" : 22,
"TALL_BIRCH_FOREST" : 155,
"DEEP_LUKEWARM_OCEAN" : 48,
"MESA_ROCK" : 38,
"SWAMP_HILLS" : 134,
"MODIFIED_WOODED_BADLANDS_PLATEAU" : 166,
"MODIFIED_JUNGLE_EDGE" : 151,
"BIRCH_FOREST_HILLS" : 28,
"COLD_TAIGA" : 30,
"TAIGA" : 5,
"MUTATED_MESA_ROCK" : 166,
"MUTATED_SWAMPLAND" : 134,
"ICE_FLATS" : 12,
"MUTATED_ROOFED_FOREST" : 157,
"MUTATED_MESA" : 165,
"MUTATED_EXTREME_HILLS" : 131
}

View file

@ -3,7 +3,7 @@ name: ${outputName}-Spigot
author: ${project.organization.name}
website: ${project.organization.url}
version: ${project.version}
softdepend: ["ViaVersion"]
softdepend: ["ViaVersion", "floodgate"]
api-version: 1.13
commands:
geyser:

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-sponge</artifactId>
@ -14,7 +14,7 @@
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-standalone</artifactId>
@ -14,7 +14,7 @@
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>bootstrap-parent</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<artifactId>bootstrap-velocity</artifactId>
@ -14,7 +14,7 @@
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>connector</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>

View file

@ -107,6 +107,15 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
// Remove this in like a year
try {
// Should only exist on 1.0
Class.forName("org.geysermc.floodgate.FloodgateAPI");
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.outdated", "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/"));
return;
} catch (ClassNotFoundException ignored) {
}
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && !proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
return;

View file

@ -6,22 +6,20 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>geyser-parent</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<artifactId>common</artifactId>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
<scope>compile</scope>
<groupId>org.geysermc.cumulus</groupId>
<artifactId>cumulus</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.8</version>
<scope>compile</scope>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
</dependencies>
</project>

View file

@ -31,7 +31,6 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public enum PlatformType {
ANDROID("Android"),
BUNGEECORD("BungeeCord"),
FABRIC("Fabric"),

View file

@ -1,71 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window;
import lombok.Getter;
import org.geysermc.common.window.CustomFormWindow;
import org.geysermc.common.window.button.FormImage;
import org.geysermc.common.window.component.FormComponent;
import org.geysermc.common.window.response.CustomFormResponse;
public class CustomFormBuilder {
@Getter
private CustomFormWindow form;
public CustomFormBuilder(String title) {
form = new CustomFormWindow(title);
}
public CustomFormBuilder setTitle(String title) {
form.setTitle(title);
return this;
}
public CustomFormBuilder setIcon(FormImage icon) {
form.setIcon(icon);
return this;
}
public CustomFormBuilder setResponse(String data) {
form.setResponse(data);
return this;
}
public CustomFormBuilder setResponse(CustomFormResponse response) {
form.setResponse(response);
return this;
}
public CustomFormBuilder addComponent(FormComponent component) {
form.addComponent(component);
return this;
}
public CustomFormWindow build() {
return form;
}
}

View file

@ -1,165 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.common.window.button.FormImage;
import org.geysermc.common.window.component.*;
import org.geysermc.common.window.response.CustomFormResponse;
import org.geysermc.common.window.response.FormResponseData;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CustomFormWindow extends FormWindow {
@Getter
@Setter
private String title;
@Getter
@Setter
private FormImage icon;
@Getter
private List<FormComponent> content;
public CustomFormWindow(String title) {
this(title, new ArrayList<>());
}
public CustomFormWindow(String title, List<FormComponent> content) {
this(title, content, (FormImage) null);
}
public CustomFormWindow(String title, List<FormComponent> content, String icon) {
this(title, content, new FormImage(FormImage.FormImageType.URL, icon));
}
public CustomFormWindow(String title, List<FormComponent> content, FormImage icon) {
super("custom_form");
this.title = title;
this.content = content;
this.icon = icon;
}
public void addComponent(FormComponent component) {
content.add(component);
}
public String getJSONData() {
String toModify = "";
try {
toModify = new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) { }
//We need to replace this due to Java not supporting declaring class field 'default'
return toModify.replace("defaultOptionIndex", "default")
.replace("defaultText", "default")
.replace("defaultValue", "default")
.replace("defaultStepIndex", "default");
}
public void setResponse(String data) {
if (data == null || data.trim().equalsIgnoreCase("null") || data.isEmpty()) {
closed = true;
return;
}
int i = 0;
Map<Integer, FormResponseData> dropdownResponses = new HashMap<Integer, FormResponseData>();
Map<Integer, String> inputResponses = new HashMap<Integer, String>();
Map<Integer, Float> sliderResponses = new HashMap<Integer, Float>();
Map<Integer, FormResponseData> stepSliderResponses = new HashMap<Integer, FormResponseData>();
Map<Integer, Boolean> toggleResponses = new HashMap<Integer, Boolean>();
Map<Integer, Object> responses = new HashMap<Integer, Object>();
Map<Integer, String> labelResponses = new HashMap<Integer, String>();
List<String> componentResponses = new ArrayList<>();
try {
componentResponses = new ObjectMapper().readValue(data.trim(), new TypeReference<List<String>>(){});
} catch (IOException e) { }
for (String response : componentResponses) {
if (i >= content.size()) {
break;
}
FormComponent component = content.get(i);
if (component == null)
return;
if (component instanceof LabelComponent) {
LabelComponent labelComponent = (LabelComponent) component;
labelResponses.put(i, labelComponent.getText());
}
if (component instanceof DropdownComponent) {
DropdownComponent dropdownComponent = (DropdownComponent) component;
String option = dropdownComponent.getOptions().get(Integer.parseInt(response));
dropdownResponses.put(i, new FormResponseData(Integer.parseInt(response), option));
responses.put(i, option);
}
if (component instanceof InputComponent) {
inputResponses.put(i, response);
responses.put(i, response);
}
if (component instanceof SliderComponent) {
float value = Float.parseFloat(response);
sliderResponses.put(i, value);
responses.put(i, value);
}
if (component instanceof StepSliderComponent) {
StepSliderComponent stepSliderComponent = (StepSliderComponent) component;
String step = stepSliderComponent.getSteps().get(Integer.parseInt(response));
stepSliderResponses.put(i, new FormResponseData(Integer.parseInt(response), step));
responses.put(i, step);
}
if (component instanceof ToggleComponent) {
boolean answer = Boolean.parseBoolean(response);
toggleResponses.put(i, answer);
responses.put(i, answer);
}
i++;
}
this.response = new CustomFormResponse(responses, dropdownResponses, inputResponses,
sliderResponses, stepSliderResponses, toggleResponses, labelResponses);
}
}

View file

@ -1,82 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.common.window.response.ModalFormResponse;
public class ModalFormWindow extends FormWindow {
@Getter
@Setter
private String title;
@Getter
@Setter
private String content;
@Getter
@Setter
private String button1;
@Getter
@Setter
private String button2;
public ModalFormWindow(String title, String content, String button1, String button2) {
super("modal");
this.title = title;
this.content = content;
this.button1 = button1;
this.button2 = button2;
}
@Override
public String getJSONData() {
try {
return new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
return "";
}
}
public void setResponse(String data) {
if (data == null || data.equalsIgnoreCase("null")) {
closed = true;
return;
}
if (Boolean.parseBoolean(data)) {
response = new ModalFormResponse(0, button1);
} else {
response = new ModalFormResponse(1, button2);
}
}
}

View file

@ -1,94 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.common.window.button.FormButton;
import org.geysermc.common.window.response.SimpleFormResponse;
import java.util.ArrayList;
import java.util.List;
public class SimpleFormWindow extends FormWindow {
@Getter
@Setter
private String title;
@Getter
@Setter
private String content;
@Getter
@Setter
private List<FormButton> buttons;
public SimpleFormWindow(String title, String content) {
this(title, content, new ArrayList<FormButton>());
}
public SimpleFormWindow(String title, String content, List<FormButton> buttons) {
super("form");
this.title = title;
this.content = content;
this.buttons = buttons;
}
@Override
public String getJSONData() {
try {
return new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
return "";
}
}
public void setResponse(String data) {
if (data == null || data.trim().equalsIgnoreCase("null")) {
closed = true;
return;
}
int buttonID;
try {
buttonID = Integer.parseInt(data.trim());
} catch (Exception ex) {
return;
}
if (buttonID >= buttons.size()) {
response = new SimpleFormResponse(buttonID, null);
return;
}
response = new SimpleFormResponse(buttonID, buttons.get(buttonID));
}
}

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window.component;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
public class DropdownComponent extends FormComponent {
@Getter
@Setter
private String text;
@Getter
@Setter
private List<String> options;
@Getter
@Setter
private int defaultOptionIndex;
public DropdownComponent() {
super("dropdown");
}
public void addOption(String option, boolean isDefault) {
options.add(option);
if (isDefault)
defaultOptionIndex = options.size() - 1;
}
}

View file

@ -1,52 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window.component;
import lombok.Getter;
import lombok.Setter;
public class InputComponent extends FormComponent {
@Getter
@Setter
private String text;
@Getter
@Setter
private String placeholder;
@Getter
@Setter
private String defaultText;
public InputComponent(String text, String placeholder, String defaultText) {
super("input");
this.text = text;
this.placeholder = placeholder;
this.defaultText = defaultText;
}
}

View file

@ -1,65 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window.component;
import lombok.Getter;
import lombok.Setter;
public class SliderComponent extends FormComponent {
@Getter
@Setter
private String text;
@Getter
@Setter
private float min;
@Getter
@Setter
private float max;
@Getter
@Setter
private int step;
@Getter
@Setter
private float defaultValue;
public SliderComponent(String text, float min, float max, int step, float defaultValue) {
super("slider");
this.text = text;
this.min = Math.max(min, 0f);
this.max = max > this.min ? max : this.min;
if (step != -1f && step > 0)
this.step = step;
if (defaultValue != -1f)
this.defaultValue = defaultValue;
}
}

View file

@ -1,51 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window.component;
import lombok.Getter;
import lombok.Setter;
public class ToggleComponent extends FormComponent {
@Getter
@Setter
private String text;
@Getter
@Setter
private boolean defaultValue;
public ToggleComponent(String text) {
this(text, false);
}
public ToggleComponent(String text, boolean defaultValue) {
super("toggle");
this.text = text;
this.defaultValue = defaultValue;
}
}

View file

@ -1,46 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window.response;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.geysermc.common.window.response.FormResponse;
import org.geysermc.common.window.response.FormResponseData;
import java.util.Map;
@Getter
@AllArgsConstructor
public class CustomFormResponse implements FormResponse {
private Map<Integer, Object> responses;
private Map<Integer, FormResponseData> dropdownResponses;
private Map<Integer, String> inputResponses;
private Map<Integer, Float> sliderResponses;
private Map<Integer, FormResponseData> stepSliderResponses;
private Map<Integer, Boolean> toggleResponses;
private Map<Integer, String> labelResponses;
}

View file

@ -1,39 +0,0 @@
/*
* Copyright (c) 2019-2021 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.common.window.response;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.geysermc.common.window.button.FormButton;
import org.geysermc.common.window.response.FormResponse;
@Getter
@AllArgsConstructor
public class SimpleFormResponse implements FormResponse {
private int clickedButtonId;
private FormButton clickedButton;
}

View file

@ -0,0 +1,125 @@
/*
* 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/Floodgate
*
*/
package org.geysermc.floodgate.crypto;
import lombok.RequiredArgsConstructor;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.Key;
import java.security.SecureRandom;
@RequiredArgsConstructor
public final class AesCipher implements FloodgateCipher {
public static final int IV_LENGTH = 12;
private static final int TAG_BIT_LENGTH = 128;
private static final String CIPHER_NAME = "AES/GCM/NoPadding";
private final SecureRandom secureRandom = new SecureRandom();
private final Topping topping;
private SecretKey secretKey;
public void init(Key key) {
if (!"AES".equals(key.getAlgorithm())) {
throw new RuntimeException(
"Algorithm was expected to be AES, but got " + key.getAlgorithm()
);
}
secretKey = (SecretKey) key;
}
public byte[] encrypt(byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
byte[] iv = new byte[IV_LENGTH];
secureRandom.nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(TAG_BIT_LENGTH, iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, spec);
byte[] cipherText = cipher.doFinal(data);
if (topping != null) {
iv = topping.encode(iv);
cipherText = topping.encode(cipherText);
}
return ByteBuffer.allocate(iv.length + cipherText.length + HEADER_LENGTH + 1)
.put(IDENTIFIER) // header
.put(iv)
.put((byte) 0x21)
.put(cipherText)
.array();
}
public byte[] decrypt(byte[] cipherTextWithIv) throws Exception {
checkHeader(cipherTextWithIv);
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
int bufferLength = cipherTextWithIv.length - HEADER_LENGTH;
ByteBuffer buffer = ByteBuffer.wrap(cipherTextWithIv, HEADER_LENGTH, bufferLength);
int ivLength = IV_LENGTH;
if (topping != null) {
int mark = buffer.position();
// we need the first index, the second is for the optional RawSkin
boolean found = false;
while (buffer.hasRemaining() && !found) {
if (buffer.get() == 0x21) {
found = true;
}
}
ivLength = buffer.position() - mark - 1; // don't include the splitter itself
// don't remove this cast, it'll cause problems if you remove it
((Buffer) buffer).position(mark); // reset to the pre-while index
}
byte[] iv = new byte[ivLength];
buffer.get(iv);
// don't remove this cast, it'll cause problems if you remove it
((Buffer) buffer).position(buffer.position() + 1); // skip splitter
byte[] cipherText = new byte[buffer.remaining()];
buffer.get(cipherText);
if (topping != null) {
iv = topping.decode(iv);
cipherText = topping.decode(cipherText);
}
GCMParameterSpec spec = new GCMParameterSpec(TAG_BIT_LENGTH, iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
return cipher.doFinal(cipherText);
}
}

View file

@ -0,0 +1,74 @@
/*
* 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/Floodgate
*
*/
package org.geysermc.floodgate.crypto;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public final class AesKeyProducer implements KeyProducer {
public static int KEY_SIZE = 128;
@Override
public SecretKey produce() {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(KEY_SIZE, getSecureRandom());
return keyGenerator.generateKey();
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
@Override
public SecretKey produceFrom(byte[] keyFileData) {
try {
return new SecretKeySpec(keyFileData, "AES");
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
private SecureRandom getSecureRandom() throws NoSuchAlgorithmException {
// use Windows-PRNG for windows (default impl is SHA1PRNG)
if (System.getProperty("os.name").startsWith("Windows")) {
return SecureRandom.getInstance("Windows-PRNG");
} else {
try {
// NativePRNG (which should be the default on unix-systems) can still block your
// system. Even though it isn't as bad as NativePRNGBlocking, we still try to
// prevent that if possible
return SecureRandom.getInstance("NativePRNGNonBlocking");
} catch (NoSuchAlgorithmException ignored) {
// at this point we just have to go with the default impl even if it blocks
return new SecureRandom();
}
}
}
}

View file

@ -23,16 +23,18 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.common.window.response;
package org.geysermc.floodgate.crypto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.geysermc.common.window.response.FormResponse;
import java.util.Base64;
@Getter
@AllArgsConstructor
public class ModalFormResponse implements FormResponse {
public final class Base64Topping implements Topping {
@Override
public byte[] encode(byte[] data) {
return Base64.getEncoder().encode(data);
}
private int clickedButtonId;
private String clickedButtonText;
@Override
public byte[] decode(byte[] data) {
return Base64.getDecoder().decode(data);
}
}

View file

@ -0,0 +1,163 @@
/*
* 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/Floodgate
*
*/
package org.geysermc.floodgate.crypto;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.geysermc.floodgate.util.InvalidFormatException;
import java.nio.charset.StandardCharsets;
import java.security.Key;
/**
* Responsible for both encrypting and decrypting data
*/
public interface FloodgateCipher {
// use invalid username characters at the beginning and the end of the identifier,
// to make sure that it doesn't get messed up with usernames
byte[] IDENTIFIER = "^Floodgate^".getBytes(StandardCharsets.UTF_8);
int HEADER_LENGTH = IDENTIFIER.length;
static boolean hasHeader(String data) {
if (data.length() < IDENTIFIER.length) {
return false;
}
for (int i = 0; i < IDENTIFIER.length; i++) {
if (IDENTIFIER[i] != data.charAt(i)) {
return false;
}
}
return true;
}
/**
* Initializes the instance by giving it the key it needs to encrypt or decrypt data
*
* @param key the key used to encrypt and decrypt data
*/
void init(Key key);
/**
* Encrypts the given data using the Key provided in {@link #init(Key)}
*
* @param data the data to encrypt
* @return the encrypted data
* @throws Exception when the encryption failed
*/
byte[] encrypt(byte[] data) throws Exception;
/**
* Encrypts data from a String.<br> This method internally calls {@link #encrypt(byte[])}
*
* @param data the data to encrypt
* @return the encrypted data
* @throws Exception when the encryption failed
*/
default byte[] encryptFromString(String data) throws Exception {
return encrypt(data.getBytes(StandardCharsets.UTF_8));
}
/**
* Decrypts the given data using the Key provided in {@link #init(Key)}
*
* @param data the data to decrypt
* @return the decrypted data
* @throws Exception when the decrypting failed
*/
byte[] decrypt(byte[] data) throws Exception;
/**
* Decrypts a byte[] and turn it into a String.<br> This method internally calls {@link
* #decrypt(byte[])} and converts the returned byte[] into a String.
*
* @param data the data to encrypt
* @return the decrypted data in a UTF-8 String
* @throws Exception when the decrypting failed
*/
default String decryptToString(byte[] data) throws Exception {
byte[] decrypted = decrypt(data);
if (decrypted == null) {
return null;
}
return new String(decrypted, StandardCharsets.UTF_8);
}
/**
* Decrypts a String.<br> This method internally calls {@link #decrypt(byte[])} by converting
* the UTF-8 String into a byte[]
*
* @param data the data to decrypt
* @return the decrypted data in a byte[]
* @throws Exception when the decrypting failed
*/
default byte[] decryptFromString(String data) throws Exception {
return decrypt(data.getBytes(StandardCharsets.UTF_8));
}
/**
* Checks if the header is valid. This method will throw an InvalidFormatException when the
* header is invalid.
*
* @param data the data to check
* @throws InvalidFormatException when the header is invalid
*/
default void checkHeader(byte[] data) throws InvalidFormatException {
final int identifierLength = IDENTIFIER.length;
if (data.length <= HEADER_LENGTH) {
throw new InvalidFormatException("Data length is smaller then header." +
"Needed " + HEADER_LENGTH + ", got " + data.length,
true
);
}
for (int i = 0; i < identifierLength; i++) {
if (IDENTIFIER[i] != data[i]) {
StringBuilder receivedIdentifier = new StringBuilder();
for (byte b : IDENTIFIER) {
receivedIdentifier.append(b);
}
throw new InvalidFormatException(
String.format("Expected identifier %s, got %s",
new String(IDENTIFIER, StandardCharsets.UTF_8),
receivedIdentifier.toString()
),
true
);
}
}
}
@Data
@AllArgsConstructor
class HeaderResult {
private int version;
private int startIndex;
}
}

View file

@ -0,0 +1,41 @@
/*
* 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/Floodgate
*
*/
package org.geysermc.floodgate.crypto;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Key;
public interface KeyProducer {
Key produce();
Key produceFrom(byte[] keyFileData);
default Key produceFrom(Path keyFileLocation) throws IOException {
return produceFrom(Files.readAllBytes(keyFileLocation));
}
}

View file

@ -23,15 +23,9 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.common.window.response;
package org.geysermc.floodgate.crypto;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public class FormResponseData {
private int elementID;
private String elementContent;
public interface Topping {
byte[] encode(byte[] data);
byte[] decode(byte[] data);
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2019-2021 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.floodgate.news;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.geysermc.floodgate.news.data.ItemData;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public final class NewsItem {
private final int id;
private final String project;
private final boolean active;
private final NewsType type;
private final ItemData data;
private final boolean priority;
private final String message;
private final Set<NewsItemAction> actions;
private final String url;
private NewsItem(int id, String project, boolean active, NewsType type, ItemData data,
boolean priority, String message, Set<NewsItemAction> actions, String url) {
this.id = id;
this.project = project;
this.active = active;
this.type = type;
this.data = data;
this.priority = priority;
this.message = message;
this.actions = Collections.unmodifiableSet(actions);
this.url = url;
}
public static NewsItem readItem(JsonObject newsItem) {
NewsType newsType = NewsType.getByName(newsItem.get("type").getAsString());
if (newsType == null) {
return null;
}
JsonObject messageObject = newsItem.getAsJsonObject("message");
NewsItemMessage itemMessage = NewsItemMessage.getById(messageObject.get("id").getAsInt());
String message = "Received an unknown news message type. Please update";
if (itemMessage != null) {
message = itemMessage.getFormattedMessage(messageObject.getAsJsonArray("args"));
}
Set<NewsItemAction> actions = new HashSet<>();
for (JsonElement actionElement : newsItem.getAsJsonArray("actions")) {
NewsItemAction action = NewsItemAction.getByName(actionElement.getAsString());
if (action != null) {
actions.add(action);
}
}
return new NewsItem(
newsItem.get("id").getAsInt(),
newsItem.get("project").getAsString(),
newsItem.get("active").getAsBoolean(),
newsType,
newsType.read(newsItem.getAsJsonObject("data")),
newsItem.get("priority").getAsBoolean(),
message,
actions,
newsItem.get("url").getAsString()
);
}
public int getId() {
return id;
}
public String getProject() {
return project;
}
public boolean isGlobal() {
return "all".equals(getProject());
}
public boolean isActive() {
return active;
}
public NewsType getType() {
return type;
}
public ItemData getData() {
return data;
}
@SuppressWarnings("unchecked")
public <T extends ItemData> T getDataAs(Class<T> type) {
return (T) data;
}
public boolean isPriority() {
return priority;
}
public String getRawMessage() {
return message;
}
public String getMessage() {
return message + " See " + getUrl() + " for more information.";
}
public Set<NewsItemAction> getActions() {
return actions;
}
public String getUrl() {
return url;
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2019-2021 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.floodgate.news;
public enum NewsItemAction {
ON_SERVER_STARTED,
ON_OPERATOR_JOIN,
BROADCAST_TO_CONSOLE,
BROADCAST_TO_OPERATORS;
private static final NewsItemAction[] VALUES = values();
public static NewsItemAction getByName(String actionName) {
for (NewsItemAction type : VALUES) {
if (type.name().equalsIgnoreCase(actionName)) {
return type;
}
}
return null;
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2019-2021 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.floodgate.news;
import com.google.gson.JsonArray;
// {} is used for things that have to be filled in by the server,
// {@} is for things that have to be filled in by us
public enum NewsItemMessage {
UPDATE_AVAILABLE("There is an update available for {}. The newest version is: {}"),
UPDATE_RECOMMENDED(UPDATE_AVAILABLE + ". Your version is quite old, updating is recommend."),
UPDATE_HIGHLY_RECOMMENDED(UPDATE_AVAILABLE + ". We highly recommend updating because some important changes have been made."),
UPDATE_ANCIENT_VERSION(UPDATE_AVAILABLE + ". You are running an ancient version, updating is recommended."),
DOWNTIME_GENERIC("The {} is temporarily going down for maintenance soon."),
DOWNTIME_WITH_START("The {} is temporarily going down for maintenance on {}."),
DOWNTIME_TIMEFRAME(DOWNTIME_WITH_START + " The maintenance is expected to last till {}.");
private static final NewsItemMessage[] VALUES = values();
private final String messageFormat;
private final String[] messageSplitted;
NewsItemMessage(String messageFormat) {
this.messageFormat = messageFormat;
this.messageSplitted = messageFormat.split(" ");
}
public static NewsItemMessage getById(int id) {
return VALUES.length > id ? VALUES[id] : null;
}
public String getMessageFormat() {
return messageFormat;
}
public String getFormattedMessage(JsonArray serverArguments) {
int serverArgumentsIndex = 0;
StringBuilder message = new StringBuilder();
for (String split : messageSplitted) {
if (message.length() > 0) {
message.append(' ');
}
String result = split;
if (serverArgumentsIndex < serverArguments.size()) {
String argument = serverArguments.get(serverArgumentsIndex).getAsString();
result = result.replace("{}", argument);
if (!result.equals(split)) {
serverArgumentsIndex++;
}
}
message.append(result);
}
return message.toString();
}
@Override
public String toString() {
return getMessageFormat();
}
}

View file

@ -23,47 +23,37 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.common.window.component;
package org.geysermc.floodgate.news;
import lombok.Getter;
import lombok.Setter;
import com.google.gson.JsonObject;
import org.geysermc.floodgate.news.data.BuildSpecificData;
import org.geysermc.floodgate.news.data.CheckAfterData;
import org.geysermc.floodgate.news.data.ItemData;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class StepSliderComponent extends FormComponent {
public enum NewsType {
BUILD_SPECIFIC(BuildSpecificData::read),
CHECK_AFTER(CheckAfterData::read);
@Getter
@Setter
private String text;
private static final NewsType[] VALUES = values();
@Getter
private List<String> steps;
private final Function<JsonObject, ? extends ItemData> readFunction;
@Getter
@Setter
private int defaultStepIndex;
public StepSliderComponent(String text) {
this(text, new ArrayList<String>());
NewsType(Function<JsonObject, ? extends ItemData> readFunction) {
this.readFunction = readFunction;
}
public StepSliderComponent(String text, List<String> steps) {
this(text, steps, 0);
public static NewsType getByName(String newsType) {
for (NewsType type : VALUES) {
if (type.name().equalsIgnoreCase(newsType)) {
return type;
}
}
return null;
}
public StepSliderComponent(String text, List<String> steps, int defaultStepIndex) {
super("step_slider");
this.text = text;
this.steps = steps;
this.defaultStepIndex = defaultStepIndex;
}
public void addStep(String step, boolean isDefault) {
steps.add(step);
if (isDefault)
defaultStepIndex = steps.size() - 1;
public ItemData read(JsonObject data) {
return readFunction.apply(data);
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2019-2021 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.floodgate.news.data;
import com.google.gson.JsonObject;
public final class BuildSpecificData implements ItemData {
private String branch;
private boolean allAffected;
private int affectedGreaterThan;
private int affectedLessThan;
public static BuildSpecificData read(JsonObject data) {
BuildSpecificData updateData = new BuildSpecificData();
updateData.branch = data.get("branch").getAsString();
JsonObject affectedBuilds = data.getAsJsonObject("affected_builds");
if (affectedBuilds.has("all")) {
updateData.allAffected = affectedBuilds.get("all").getAsBoolean();
}
if (!updateData.allAffected) {
updateData.affectedGreaterThan = affectedBuilds.get("gt").getAsInt();
updateData.affectedLessThan = affectedBuilds.get("lt").getAsInt();
}
return updateData;
}
public boolean isAffected(String branch, int buildId) {
return this.branch.equals(branch) &&
(allAffected || buildId > affectedGreaterThan && buildId < affectedLessThan);
}
public String getBranch() {
return branch;
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019-2021 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.floodgate.news.data;
import com.google.gson.JsonObject;
public class CheckAfterData implements ItemData {
private long checkAfter;
public static CheckAfterData read(JsonObject data) {
CheckAfterData checkAfterData = new CheckAfterData();
checkAfterData.checkAfter = data.get("check_after").getAsLong();
return checkAfterData;
}
public long getCheckAfter() {
return checkAfter;
}
}

View file

@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.common.window.response;
package org.geysermc.floodgate.news.data;
public interface FormResponse {
public interface ItemData {
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2019-2021 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.floodgate.time;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
/*
* Thanks:
* https://datatracker.ietf.org/doc/html/rfc1769
* https://github.com/jonsagara/SimpleNtpClient
* https://stackoverflow.com/a/29138806
*/
public final class SntpClientUtils {
private static final int NTP_PORT = 123;
private static final int NTP_PACKET_SIZE = 48;
private static final int NTP_MODE = 3; // client
private static final int NTP_VERSION = 3;
private static final int RECEIVE_TIME_POSITION = 32;
private static final long NTP_TIME_OFFSET = ((365L * 70L) + 17L) * 24L * 60L * 60L;
public static long requestTimeOffset(String host, int timeout) {
try (DatagramSocket socket = new DatagramSocket()) {
socket.setSoTimeout(timeout);
InetAddress address = InetAddress.getByName(host);
ByteBuffer buff = ByteBuffer.allocate(NTP_PACKET_SIZE);
DatagramPacket request = new DatagramPacket(
buff.array(), NTP_PACKET_SIZE, address, NTP_PORT
);
// mode is in the least signification 3 bits
// version is in bits 3-5
buff.put((byte) (NTP_MODE | (NTP_VERSION << 3)));
long originateTime = System.currentTimeMillis();
socket.send(request);
DatagramPacket response = new DatagramPacket(buff.array(), NTP_PACKET_SIZE);
socket.receive(response);
long responseTime = System.currentTimeMillis();
// everything before isn't important for us
buff.position(RECEIVE_TIME_POSITION);
long receiveTime = readTimestamp(buff);
long transmitTime = readTimestamp(buff);
return ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2;
} catch (Exception ignored) {
}
return Long.MIN_VALUE;
}
private static long readTimestamp(ByteBuffer buffer) {
//todo look into the ntp 2036 problem
long seconds = buffer.getInt() & 0xffffffffL;
long fraction = buffer.getInt() & 0xffffffffL;
return ((seconds - NTP_TIME_OFFSET) * 1000) + ((fraction * 1000) / 0x100000000L);
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2019-2021 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.floodgate.time;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public final class TimeSyncer {
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private long timeOffset = Long.MIN_VALUE; // value when it failed to get the offset
public TimeSyncer(String timeServer) {
executorService.scheduleWithFixedDelay(() -> {
// 5 tries to get the time offset, since UDP doesn't guaranty a response
for (int i = 0; i < 5; i++) {
long offset = SntpClientUtils.requestTimeOffset(timeServer, 3000);
if (offset != Long.MIN_VALUE) {
timeOffset = offset;
return;
}
}
}, 0, 30, TimeUnit.MINUTES);
}
public void shutdown() {
executorService.shutdown();
}
public long getTimeOffset() {
return timeOffset;
}
public long getRealMillis() {
if (hasUsefulOffset()) {
return System.currentTimeMillis() + getTimeOffset();
}
return System.currentTimeMillis();
}
public boolean hasUsefulOffset() {
return timeOffset != Long.MIN_VALUE;
}
}

View file

@ -23,16 +23,13 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.common.window.component;
package org.geysermc.floodgate.util;
import lombok.Getter;
public abstract class FormComponent {
@Getter
private final String type;
public FormComponent(String type) {
this.type = type;
public final class Base64Utils {
public static int getEncodedLength(int length) {
if (length <= 0) {
return -1;
}
return 4 * ((length + 2) / 3);
}
}

View file

@ -25,47 +25,91 @@
package org.geysermc.floodgate.util;
import lombok.AllArgsConstructor;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.time.TimeSyncer;
import java.util.UUID;
@AllArgsConstructor
/**
* This class contains the raw data send by Geyser to Floodgate or from Floodgate to Floodgate. This
* class is only used internally, and you should look at FloodgatePlayer instead (FloodgatePlayer is
* present in the API module of the Floodgate repo)
*/
@Getter
public class BedrockData {
public static final int EXPECTED_LENGTH = 7;
public static final String FLOODGATE_IDENTIFIER = "Geyser-Floodgate";
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public final class BedrockData implements Cloneable {
public static final int EXPECTED_LENGTH = 13;
private String version;
private String username;
private String xuid;
private int deviceId;
private String languageCode;
private int inputMode;
private String ip;
private int dataLength;
private final String version;
private final String username;
private final String xuid;
private final int deviceOs;
private final String languageCode;
private final int uiProfile;
private final int inputMode;
private final String ip;
private final LinkedPlayer linkedPlayer;
private final boolean fromProxy;
public BedrockData(String version, String username, String xuid, int deviceId, String languageCode, int inputMode, String ip) {
this(version, username, xuid, deviceId, languageCode, inputMode, ip, EXPECTED_LENGTH);
private final int subscribeId;
private final String verifyCode;
private final long timestamp;
private final int dataLength;
public static BedrockData of(
String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip,
LinkedPlayer linkedPlayer, boolean fromProxy, int subscribeId,
String verifyCode, TimeSyncer timeSyncer) {
return new BedrockData(version, username, xuid, deviceOs, languageCode, inputMode,
uiProfile, ip, linkedPlayer, fromProxy, subscribeId, verifyCode,
timeSyncer.getRealMillis(), EXPECTED_LENGTH);
}
public static BedrockData of(
String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip,
int subscribeId, String verifyCode, TimeSyncer timeSyncer) {
return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null,
false, subscribeId, verifyCode, timeSyncer);
}
public static BedrockData fromString(String data) {
String[] split = data.split("\0");
if (split.length != EXPECTED_LENGTH) return null;
if (split.length != EXPECTED_LENGTH) {
return emptyData(split.length);
}
LinkedPlayer linkedPlayer = LinkedPlayer.fromString(split[8]);
// The format is the same as the order of the fields in this class
return new BedrockData(
split[0], split[1], split[2], Integer.parseInt(split[3]),
split[4], Integer.parseInt(split[5]), split[6], split.length
split[0], split[1], split[2], Integer.parseInt(split[3]), split[4],
Integer.parseInt(split[5]), Integer.parseInt(split[6]), split[7], linkedPlayer,
"1".equals(split[9]), Integer.parseInt(split[10]), split[11], Long.parseLong(split[12]), split.length
);
}
public static BedrockData fromRawData(byte[] data) {
return fromString(new String(data));
private static BedrockData emptyData(int dataLength) {
return new BedrockData(null, null, null, -1, null, -1, -1, null, null, false, -1, null, -1,
dataLength);
}
public boolean hasPlayerLink() {
return linkedPlayer != null;
}
@Override
public String toString() {
return version +'\0'+ username +'\0'+ xuid +'\0'+ deviceId +'\0'+ languageCode +'\0'+
inputMode +'\0'+ ip;
// The format is the same as the order of the fields in this class
return version + '\0' + username + '\0' + xuid + '\0' + deviceOs + '\0' +
languageCode + '\0' + uiProfile + '\0' + inputMode + '\0' + ip + '\0' +
(linkedPlayer != null ? linkedPlayer.toString() : "null") + '\0' +
(fromProxy ? 1 : 0) + '\0' + subscribeId + '\0' + verifyCode + '\0' + timestamp;
}
@Override
public BedrockData clone() throws CloneNotSupportedException {
return (BedrockData) super.clone();
}
}

View file

@ -25,36 +25,41 @@
package org.geysermc.floodgate.util;
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
public enum DeviceOS {
@JsonEnumDefaultValue
/**
* The Operation Systems where Bedrock players can connect with
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public enum DeviceOs {
UNKNOWN("Unknown"),
ANDROID("Android"),
GOOGLE("Android"),
IOS("iOS"),
OSX("macOS"),
FIREOS("FireOS"),
AMAZON("Amazon"),
GEARVR("Gear VR"),
HOLOLENS("Hololens"),
WIN10("Windows 10"),
WIN32("Windows"),
UWP("Windows 10"),
WIN32("Windows x86"),
DEDICATED("Dedicated"),
ORBIS("PS4"),
TVOS("Apple TV"),
PS4("PS4"),
NX("Switch"),
SWITCH("Switch"),
XBOX_ONE("Xbox One"),
WIN_PHONE("Windows Phone");
XBOX("Xbox One"),
WINDOWS_PHONE("Windows Phone");
private static final DeviceOS[] VALUES = values();
private static final DeviceOs[] VALUES = values();
private final String displayName;
DeviceOS(final String displayName) {
this.displayName = displayName;
}
public static DeviceOS getById(int id) {
/**
* Get the DeviceOs instance from the identifier.
*
* @param id the DeviceOs identifier
* @return The DeviceOs or {@link #UNKNOWN} if the DeviceOs wasn't found
*/
public static DeviceOs getById(int id) {
return id < VALUES.length ? VALUES[id] : VALUES[0];
}

View file

@ -1,101 +0,0 @@
/*
* Copyright (c) 2019-2021 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.floodgate.util;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.*;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class EncryptionUtil {
public static String encrypt(Key key, String data) throws IllegalBlockSizeException,
InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(128);
SecretKey secretKey = generator.generateKey();
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedText = cipher.doFinal(data.getBytes());
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(key instanceof PublicKey ? Cipher.PUBLIC_KEY : Cipher.PRIVATE_KEY, key);
return Base64.getEncoder().encodeToString(cipher.doFinal(secretKey.getEncoded())) + '\0' +
Base64.getEncoder().encodeToString(encryptedText);
}
public static String encryptBedrockData(Key key, BedrockData data) throws IllegalBlockSizeException,
InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
return encrypt(key, data.toString());
}
public static byte[] decrypt(Key key, String encryptedData) throws IllegalBlockSizeException,
InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
String[] split = encryptedData.split("\0");
if (split.length != 2) {
throw new IllegalArgumentException("Expected two arguments, got " + split.length);
}
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(key instanceof PublicKey ? Cipher.PUBLIC_KEY : Cipher.PRIVATE_KEY, key);
byte[] decryptedKey = cipher.doFinal(Base64.getDecoder().decode(split[0]));
SecretKey secretKey = new SecretKeySpec(decryptedKey, 0, decryptedKey.length, "AES");
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(Base64.getDecoder().decode(split[1]));
}
public static BedrockData decryptBedrockData(Key key, String encryptedData) throws IllegalBlockSizeException,
InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
return BedrockData.fromRawData(decrypt(key, encryptedData));
}
@SuppressWarnings("unchecked")
public static <T extends Key> T getKeyFromFile(Path fileLocation, Class<T> keyType) throws
IOException, InvalidKeySpecException, NoSuchAlgorithmException {
boolean isPublicKey = keyType == PublicKey.class;
if (!isPublicKey && keyType != PrivateKey.class) {
throw new RuntimeException("I can only read public and private keys!");
}
byte[] key = Files.readAllBytes(fileLocation);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec keySpec = isPublicKey ? new X509EncodedKeySpec(key) : new PKCS8EncodedKeySpec(key);
return (T) (isPublicKey ?
keyFactory.generatePublic(keySpec) :
keyFactory.generatePrivate(keySpec)
);
}
}

View file

@ -23,20 +23,18 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.common.window.component;
package org.geysermc.floodgate.util;
import lombok.Getter;
import lombok.Setter;
public class LabelComponent extends FormComponent {
import java.util.Properties;
public final class FloodgateInfoHolder {
@Getter
@Setter
private String text;
public LabelComponent(String text) {
super("label");
this.text = text;
}
private static Object config;
@Getter
@Setter
private static Properties gitProperties;
}

View file

@ -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.floodgate.util;
public enum InputMode {
UNKNOWN,
KEYBOARD_MOUSE,
TOUCH,
CONTROLLER,
VR;
private static final InputMode[] VALUES = values();
/**
* Get the InputMode instance from the identifier.
*
* @param id the InputMode identifier
* @return The InputMode or {@link #UNKNOWN} if the DeviceOs wasn't found
*/
public static InputMode getById(int id) {
return VALUES.length > id ? VALUES[id] : VALUES[0];
}
}

View file

@ -0,0 +1,51 @@
/*
* 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.floodgate.util;
import lombok.Getter;
@Getter
public class InvalidFormatException extends Exception {
private boolean header = false;
public InvalidFormatException() {
super();
}
public InvalidFormatException(String message) {
super(message);
}
public InvalidFormatException(String message, boolean header) {
super(message);
this.header = header;
}
public InvalidFormatException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -0,0 +1,82 @@
/*
* 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.floodgate.util;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.UUID;
@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public final class LinkedPlayer implements Cloneable {
/**
* The Java username of the linked player
*/
private final String javaUsername;
/**
* The Java UUID of the linked player
*/
private final UUID javaUniqueId;
/**
* The UUID of the Bedrock player
*/
private final UUID bedrockId;
/**
* If the LinkedPlayer is sent from a different platform. For example the LinkedPlayer is from
* Bungee but the data has been sent to the Bukkit server.
*/
private boolean fromDifferentPlatform = false;
public static LinkedPlayer of(String javaUsername, UUID javaUniqueId, UUID bedrockId) {
return new LinkedPlayer(javaUsername, javaUniqueId, bedrockId);
}
public static LinkedPlayer fromString(String data) {
String[] split = data.split(";");
if (split.length != 3) {
return null;
}
LinkedPlayer player = new LinkedPlayer(
split[0], UUID.fromString(split[1]), UUID.fromString(split[2])
);
player.fromDifferentPlatform = true;
return player;
}
@Override
public String toString() {
return javaUsername + ';' + javaUniqueId.toString() + ';' + bedrockId.toString();
}
@Override
public LinkedPlayer clone() throws CloneNotSupportedException {
return (LinkedPlayer) super.clone();
}
}

View file

@ -0,0 +1,44 @@
/*
* 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.floodgate.util;
public enum UiProfile {
CLASSIC,
POCKET;
private static final UiProfile[] VALUES = values();
/**
* Get the UiProfile instance from the identifier.
*
* @param id the UiProfile identifier
* @return The UiProfile or {@link #CLASSIC} if the UiProfile wasn't found
*/
public static UiProfile getById(int id) {
return VALUES.length > id ? VALUES[id] : VALUES[0];
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2019-2021 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.floodgate.util;
public enum WebsocketEventType {
/**
* Sent once we successfully connected to the server
*/
SUBSCRIBER_CREATED(0),
/**
* Sent every time a subscriber got added or disconnected
*/
SUBSCRIBER_COUNT(1),
/**
* Sent once the creator disconnected. After this packet the server will automatically close the
* connection once the queue size (sent in {@link #ADDED_TO_QUEUE} and {@link #SKIN_UPLOADED}
* reaches 0.
*/
CREATOR_DISCONNECTED(4),
/**
* Sent every time a skin got added to the upload queue
*/
ADDED_TO_QUEUE(2),
/**
* Sent every time a skin got successfully uploaded
*/
SKIN_UPLOADED(3),
/**
* Sent every time a news item was added
*/
NEWS_ADDED(6),
/**
* Sent when the server wants you to know something. Currently used for violations that aren't
* bad enough to close the connection
*/
LOG_MESSAGE(5);
private static final WebsocketEventType[] VALUES;
static {
WebsocketEventType[] values = values();
VALUES = new WebsocketEventType[values.length];
for (WebsocketEventType value : values) {
VALUES[value.id] = value;
}
}
/**
* The ID is based of the time it got added. However, to keep the enum organized as time goes on,
* it looks nicer to sort the events based of categories.
*/
private final int id;
WebsocketEventType(int id) {
this.id = id;
}
public static WebsocketEventType getById(int id) {
return VALUES.length > id ? VALUES[id] : null;
}
public int getId() {
return id;
}
}

View file

@ -6,33 +6,59 @@
<parent>
<groupId>org.geysermc</groupId>
<artifactId>geyser-parent</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<artifactId>connector</artifactId>
<properties>
<netty.version>4.1.59.Final</netty.version>
<adventure.version>4.8.0</adventure.version>
<fastutil.version>8.5.2</fastutil.version>
<adventure.version>4.7.0</adventure.version>
<jackson.version>2.10.2</jackson.version>
<netty.version>4.1.59.Final</netty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>common</artifactId>
<version>1.2.1-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!-- Jackson JSON and YAML serialization -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.10.2</version>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.CloudburstMC.Protocol</groupId>
<artifactId>bedrock-v431</artifactId>
<version>530a0e3</version>
<artifactId>bedrock-v440</artifactId>
<version>1656151</version>
<scope>compile</scope>
<exclusions>
<exclusion>
@ -120,9 +146,15 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>mcprotocollib</artifactId>
<version>8c204eb</version>
<groupId>com.github.GeyserMC</groupId>
<artifactId>MCAuthLib</artifactId>
<version>0e48a094f2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.GeyserMC</groupId>
<artifactId>MCProtocolLib</artifactId>
<version>e316986</version>
<scope>compile</scope>
<exclusions>
<exclusion>
@ -203,12 +235,15 @@
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version> <!-- This isn't the latest version to get round https://github.com/ronmamo/reflections/issues/273 -->
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
<scope>compile</scope>
</dependency>
<!-- Adventure text serialization -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
@ -239,11 +274,6 @@
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.GeyserMC</groupId>
<artifactId>MCAuthLib</artifactId>
<version>0e48a094f2</version>
</dependency>
</dependencies>
<build>
@ -305,6 +335,7 @@
</replacement>
<replacement>
<token>String GIT_VERSION = ".*"</token>
<!--suppress UnresolvedMavenProperty -->
<value>String GIT_VERSION = "git-${git.branch}-${git.commit.id.abbrev}"</value>
</replacement>
</replacements>

View file

@ -33,11 +33,20 @@ import java.nio.file.Path;
public class FloodgateKeyLoader {
public static Path getKeyPath(GeyserJacksonConfiguration config, Object floodgate, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) {
if (!config.getRemote().getAuthType().equals("floodgate")) {
return geyserDataFolder.resolve(config.getFloodgateKeyFile());
}
Path floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile());
if (!Files.exists(floodgateKey) && config.getRemote().getAuthType().equals("floodgate")) {
if (config.getFloodgateKeyFile().equals("public-key.pem")) {
logger.info("Floodgate 2.0 doesn't use a public/private key system anymore. We'll search for key.pem instead");
floodgateKey = geyserDataFolder.resolve("key.pem");
}
if (!Files.exists(floodgateKey)) {
if (floodgate != null) {
Path autoKey = floodgateDataFolder.resolve("public-key.pem");
Path autoKey = floodgateDataFolder.resolve("key.pem");
if (Files.exists(autoKey)) {
logger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.auto_loaded"));
floodgateKey = autoKey;

View file

@ -28,6 +28,7 @@ package org.geysermc.connector;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.mc.protocol.MinecraftConstants;
import com.nukkitx.network.raknet.RakNetConstants;
import com.nukkitx.network.util.EventLoops;
import com.nukkitx.protocol.bedrock.BedrockServer;
@ -58,7 +59,14 @@ 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.network.translators.world.block.entity.SkullBlockEntityTranslator;
import org.geysermc.connector.skin.FloodgateSkinUploader;
import org.geysermc.connector.utils.*;
import org.geysermc.floodgate.crypto.AesCipher;
import org.geysermc.floodgate.crypto.AesKeyProducer;
import org.geysermc.floodgate.crypto.Base64Topping;
import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.news.NewsItemAction;
import org.geysermc.floodgate.time.TimeSyncer;
import org.jetbrains.annotations.Contract;
import javax.naming.directory.Attribute;
@ -66,16 +74,18 @@ import javax.naming.directory.InitialDirContext;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.security.Key;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Getter
public class GeyserConnector {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper()
.enable(JsonParser.Feature.IGNORE_UNDEFINED)
.enable(JsonParser.Feature.ALLOW_COMMENTS)
@ -86,7 +96,7 @@ public class GeyserConnector {
public static final String NAME = "Geyser";
public static final String GIT_VERSION = "DEV"; // A fallback for running in IDEs
public static final String VERSION = "DEV"; // A fallback for running in IDEs
public static final String MINECRAFT_VERSION = "1.16.4 - 1.16.5";
public static final String MINECRAFT_VERSION = MinecraftConstants.GAME_VERSION; // Change if multiple version strings are supported
/**
* Oauth client ID for Microsoft authentication
@ -108,11 +118,16 @@ public class GeyserConnector {
@Setter
private AuthType defaultAuthType;
private final TimeSyncer timeSyncer;
private FloodgateCipher cipher;
private FloodgateSkinUploader skinUploader;
private final NewsHandler newsHandler;
private boolean shuttingDown = false;
private final ScheduledExecutorService generalThreadPool;
private BedrockServer bedrockServer;
private final BedrockServer bedrockServer;
private final PlatformType platformType;
private final GeyserBootstrap bootstrap;
@ -196,6 +211,36 @@ public class GeyserConnector {
defaultAuthType = AuthType.getByName(config.getRemote().getAuthType());
TimeSyncer timeSyncer = null;
if (defaultAuthType == AuthType.FLOODGATE) {
timeSyncer = new TimeSyncer(Constants.NTP_SERVER);
try {
Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath());
cipher = new AesCipher(new Base64Topping());
cipher.init(key);
logger.info(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.loaded_key"));
skinUploader = new FloodgateSkinUploader(this).start();
} catch (Exception exception) {
logger.severe(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.bad_key"), exception);
}
}
this.timeSyncer = timeSyncer;
String branch = "unknown";
int buildNumber = -1;
try {
Properties gitProperties = new Properties();
gitProperties.load(FileUtils.getResource("git.properties"));
branch = gitProperties.getProperty("git.branch");
String build = gitProperties.getProperty("git.build.number");
if (build != null) {
buildNumber = Integer.parseInt(build);
}
} catch (Throwable e) {
logger.error("Failed to read git.properties", e);
}
newsHandler = new NewsHandler(branch, buildNumber);
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS = config.isAllowCustomSkulls();
@ -250,7 +295,7 @@ public class GeyserConnector {
for (GeyserSession session : players) {
if (session == null) continue;
if (session.getClientData() == null) continue;
String os = session.getClientData().getDeviceOS().toString();
String os = session.getClientData().getDeviceOs().toString();
if (!valueMap.containsKey(os)) {
valueMap.put(os, 1);
} else {
@ -287,6 +332,40 @@ public class GeyserConnector {
return versionMap;
}));
}
// The following code can be attributed to the PaperMC project
// https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614
metrics.addCustomChart(new Metrics.DrilldownPie("javaVersion", () -> {
Map<String, Map<String, Integer>> map = new HashMap<>();
String javaVersion = System.getProperty("java.version");
Map<String, Integer> entry = new HashMap<>();
entry.put(javaVersion, 1);
// http://openjdk.java.net/jeps/223
// Java decided to change their versioning scheme and in doing so modified the
// java.version system property to return $major[.$minor][.$security][-ea], as opposed to
// 1.$major.0_$identifier we can handle pre-9 by checking if the "major" is equal to "1",
// otherwise, 9+
String majorVersion = javaVersion.split("\\.")[0];
String release;
int indexOf = javaVersion.lastIndexOf('.');
if (majorVersion.equals("1")) {
release = "Java " + javaVersion.substring(0, indexOf);
} else {
// of course, it really wouldn't be all that simple if they didn't add a quirk, now
// would it valid strings for the major may potentially include values such as -ea to
// denote a pre release
Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion);
if (versionMatcher.find()) {
majorVersion = versionMatcher.group(0);
}
release = "Java " + majorVersion;
}
map.put(release, entry);
return map;
}));
}
boolean isGui = false;
@ -312,6 +391,8 @@ public class GeyserConnector {
if (platformType == PlatformType.STANDALONE) {
logger.warning(LanguageUtils.getLocaleStringLog("geyser.core.movement_warn"));
}
newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED);
}
public void shutdown() {
@ -356,6 +437,10 @@ public class GeyserConnector {
generalThreadPool.shutdown();
bedrockServer.close();
if (timeSyncer != null) {
timeSyncer.shutdown();
}
newsHandler.shutdown();
players.clear();
defaultAuthType = null;
this.getCommandManager().getCommands().clear();
@ -434,6 +519,10 @@ public class GeyserConnector {
return bootstrap.getWorldManager();
}
public TimeSyncer getTimeSyncer() {
return timeSyncer;
}
/**
* 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.

View file

@ -53,7 +53,7 @@ public abstract class CommandManager {
registerCommand(new VersionCommand(connector, "version", "geyser.commands.version.desc", "geyser.command.version"));
registerCommand(new SettingsCommand(connector, "settings", "geyser.commands.settings.desc", "geyser.command.settings"));
registerCommand(new StatisticsCommand(connector, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics"));
registerCommand(new AdvancementsCommand(connector, "advancements", "geyser.commands.advancements.desc", "geyser.command.advancements"));
registerCommand(new AdvancementsCommand("advancements", "geyser.commands.advancements.desc", "geyser.command.advancements"));
}
public void registerCommand(GeyserCommand command) {

View file

@ -25,25 +25,20 @@
package org.geysermc.connector.command.defaults;
import org.geysermc.common.window.SimpleFormWindow;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.session.cache.AdvancementsCache;
public class AdvancementsCommand extends GeyserCommand {
public AdvancementsCommand(GeyserConnector connector, String name, String description, String permission) {
public AdvancementsCommand(String name, String description, String permission) {
super(name, description, permission);
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
if (session == null) return;
SimpleFormWindow window = session.getAdvancementsCache().buildMenuForm();
session.sendForm(window, AdvancementsCache.ADVANCEMENTS_MENU_FORM_ID);
if (session != null) {
session.getAdvancementsCache().buildAndShowMenuForm();
}
}
@Override

View file

@ -32,6 +32,8 @@ import org.geysermc.connector.command.GeyserCommand;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.LanguageUtils;
import java.util.ArrayList;
public class ReloadCommand extends GeyserCommand {
private final GeyserConnector connector;
@ -51,8 +53,8 @@ public class ReloadCommand extends GeyserCommand {
sender.sendMessage(message);
for (GeyserSession otherSession : connector.getPlayers()) {
otherSession.disconnect(LanguageUtils.getPlayerLocaleString("geyser.commands.reload.kick", session.getLocale()));
for (GeyserSession otherSession : new ArrayList<>(connector.getPlayers())) {
otherSession.disconnect(LanguageUtils.getPlayerLocaleString("geyser.commands.reload.kick", otherSession.getLocale()));
}
connector.reload();
}

View file

@ -32,17 +32,15 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.SettingsUtils;
public class SettingsCommand extends GeyserCommand {
public SettingsCommand(GeyserConnector connector, String name, String description, String permission) {
super(name, description, permission);
}
@Override
public void execute(GeyserSession session, CommandSender sender, String[] args) {
if (session == null) return;
SettingsUtils.buildForm(session);
session.sendForm(session.getSettingsForm(), SettingsUtils.SETTINGS_FORM_ID);
if (session != null) {
session.sendForm(SettingsUtils.buildForm(session));
}
}
@Override

View file

@ -87,8 +87,6 @@ public interface GeyserConfiguration {
boolean isAboveBedrockNetherBuilding();
boolean isCacheChunks();
boolean isForceResourcePacks();
boolean isXboxAchievementsEnabled();

View file

@ -111,9 +111,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
@JsonProperty("default-locale")
private String defaultLocale = null; // is null by default so system language takes priority
@JsonProperty("cache-chunks")
private boolean cacheChunks = false;
@JsonProperty("cache-images")
private int cacheImages = 0;

View file

@ -34,8 +34,7 @@ import java.util.List;
@Getter
public class BootstrapDumpInfo {
private PlatformType platform;
private final PlatformType platform;
public BootstrapDumpInfo() {
this.platform = GeyserConnector.getInstance().getPlatformType();
@ -44,7 +43,6 @@ public class BootstrapDumpInfo {
@Getter
@AllArgsConstructor
public static class PluginInfo {
public boolean enabled;
public String name;
public String version;
@ -55,7 +53,6 @@ public class BootstrapDumpInfo {
@Getter
@AllArgsConstructor
public static class ListenerInfo {
public String ip;
public int port;
}

View file

@ -41,7 +41,8 @@ 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;
import org.geysermc.floodgate.util.DeviceOS;
import org.geysermc.floodgate.util.DeviceOs;
import org.geysermc.floodgate.util.FloodgateInfoHolder;
import java.io.File;
import java.io.IOException;
@ -53,27 +54,29 @@ import java.util.Properties;
@Getter
public class DumpInfo {
@JsonIgnore
private static final long MEGABYTE = 1024L * 1024L;
private final DumpInfo.VersionInfo versionInfo;
private Properties gitInfo;
private final GeyserConfiguration config;
private final Floodgate floodgate;
private final Object2IntMap<DeviceOs> userPlatforms;
private final HashInfo hashInfo;
private final Object2IntMap<DeviceOS> userPlatforms;
private final RamInfo ramInfo;
private final BootstrapDumpInfo bootstrapInfo;
public DumpInfo() {
this.versionInfo = new DumpInfo.VersionInfo();
this.versionInfo = new VersionInfo();
try {
this.gitInfo = new Properties();
this.gitInfo.load(FileUtils.getResource("git.properties"));
} catch (IOException ignored) { }
} catch (IOException ignored) {
}
this.config = GeyserConnector.getInstance().getConfig();
this.floodgate = new Floodgate();
String md5Hash = "unknown";
String sha256Hash = "unknown";
@ -92,14 +95,13 @@ public class DumpInfo {
e.printStackTrace();
}
}
this.hashInfo = new HashInfo(md5Hash, sha256Hash);
this.ramInfo = new DumpInfo.RamInfo();
this.userPlatforms = new Object2IntOpenHashMap<>();
for (GeyserSession session : GeyserConnector.getInstance().getPlayers()) {
DeviceOS device = session.getClientData().getDeviceOS();
DeviceOs device = session.getClientData().getDeviceOs();
userPlatforms.put(device, userPlatforms.getOrDefault(device, 0) + 1);
}
@ -108,7 +110,6 @@ public class DumpInfo {
@Getter
public static class VersionInfo {
private final String name;
private final String version;
private final String javaVersion;
@ -123,7 +124,8 @@ public class DumpInfo {
this.name = GeyserConnector.NAME;
this.version = GeyserConnector.VERSION;
this.javaVersion = System.getProperty("java.version");
this.architecture = System.getProperty("os.arch"); // Usually gives Java architecture but still may be helpful.
// Usually gives Java architecture but still may be helpful.
this.architecture = System.getProperty("os.arch");
this.operatingSystem = System.getProperty("os.name");
this.operatingSystemVersion = System.getProperty("os.version");
@ -132,18 +134,10 @@ public class DumpInfo {
}
}
@AllArgsConstructor
@Getter
public static class HashInfo {
private final String md5Hash;
private final String sha256Hash;
}
@Getter
public static class NetworkInfo {
private String internalIP;
private final boolean dockerCheck;
private String internalIP;
NetworkInfo() {
if (AsteriskSerializer.showSensitive) {
@ -156,7 +150,8 @@ public class DumpInfo {
try {
// Fallback to the normal way of getting the local IP
this.internalIP = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException ignored) { }
} catch (UnknownHostException ignored) {
}
}
} else {
// Sometimes the internal IP is the external IP...
@ -169,7 +164,6 @@ public class DumpInfo {
@Getter
public static class MCInfo {
private final String bedrockVersion;
private final int bedrockProtocol;
private final String javaVersion;
@ -184,8 +178,25 @@ public class DumpInfo {
}
@Getter
public static class RamInfo {
public static class Floodgate {
private final Properties gitInfo;
private final Object config;
Floodgate() {
this.gitInfo = FloodgateInfoHolder.getGitProperties();
this.config = FloodgateInfoHolder.getConfig();
}
}
@AllArgsConstructor
@Getter
public static class HashInfo {
private final String md5Hash;
private final String sha256Hash;
}
@Getter
public static class RamInfo {
private final long free;
private final long total;
private final long max;

View file

@ -44,7 +44,7 @@ public class AbstractArrowEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
if (entityMetadata.getId() == 8) {
byte data = (byte) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.CRITICAL, (data & 0x01) == 0x01);

View file

@ -52,12 +52,12 @@ public class AreaEffectCloudEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
if (entityMetadata.getId() == 8) {
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) {
} else if (entityMetadata.getId() == 9) {
metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue());
} else if (entityMetadata.getId() == 10) {
} else if (entityMetadata.getId() == 11) {
Particle particle = (Particle) entityMetadata.getValue();
int particleId = EffectRegistry.getParticleId(session, particle.getType());
if (particleId != -1) {

View file

@ -99,24 +99,24 @@ public class BoatEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Time since last hit
if (entityMetadata.getId() == 7) {
if (entityMetadata.getId() == 8) {
metadata.put(EntityData.HURT_TIME, entityMetadata.getValue());
}
// Rocking direction
if (entityMetadata.getId() == 8) {
if (entityMetadata.getId() == 9) {
metadata.put(EntityData.HURT_DIRECTION, entityMetadata.getValue());
}
// 'Health' in Bedrock, damage taken in Java
if (entityMetadata.getId() == 9) {
if (entityMetadata.getId() == 10) {
// Not exactly health but it makes motion in Bedrock
metadata.put(EntityData.HEALTH, 40 - ((int) (float) entityMetadata.getValue()));
}
if (entityMetadata.getId() == 10) {
if (entityMetadata.getId() == 11) {
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
} else if (entityMetadata.getId() == 11) {
} else if (entityMetadata.getId() == 12) {
isPaddlingLeft = (boolean) entityMetadata.getValue();
if (isPaddlingLeft) {
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
@ -136,7 +136,7 @@ public class BoatEntity extends Entity {
metadata.put(EntityData.ROW_TIME_LEFT, 0.0f);
}
}
else if (entityMetadata.getId() == 12) {
else if (entityMetadata.getId() == 13) {
isPaddlingRight = (boolean) entityMetadata.getValue();
if (isPaddlingRight) {
paddleTimeRight = 0f;
@ -151,7 +151,7 @@ public class BoatEntity extends Entity {
} else {
metadata.put(EntityData.ROW_TIME_RIGHT, 0.0f);
}
} else if (entityMetadata.getId() == 13) {
} else if (entityMetadata.getId() == 14) {
// Possibly - I don't think this does anything?
metadata.put(EntityData.BOAT_BUBBLE_TIME, entityMetadata.getValue());
}

View file

@ -46,10 +46,10 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 13) {
if (entityMetadata.getId() == 14) {
metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue());
}
if (entityMetadata.getId() == 14) {
if (entityMetadata.getId() == 15) {
metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage((Component) entityMetadata.getValue()));
}
super.updateBedrockMetadata(entityMetadata, session);

View file

@ -56,7 +56,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity {
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Custom block
if (entityMetadata.getId() == 10) {
if (entityMetadata.getId() == 11) {
customBlock = (int) entityMetadata.getValue();
if (showCustomBlock) {
@ -65,7 +65,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity {
}
// Custom block offset
if (entityMetadata.getId() == 11) {
if (entityMetadata.getId() == 12) {
customBlockOffset = (int) entityMetadata.getValue();
if (showCustomBlock) {
@ -74,7 +74,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity {
}
// If the custom block should be enabled
if (entityMetadata.getId() == 12) {
if (entityMetadata.getId() == 13) {
if ((boolean) entityMetadata.getValue()) {
showCustomBlock = true;
metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock));

View file

@ -47,7 +47,7 @@ public class EnderCrystalEntity extends Entity {
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Show beam
// Usually performed client-side on Bedrock except for Ender Dragon respawn event
if (entityMetadata.getId() == 7) {
if (entityMetadata.getId() == 8) {
if (entityMetadata.getValue() instanceof Position) {
Position pos = (Position) entityMetadata.getValue();
metadata.put(EntityData.BLOCK_TARGET, Vector3i.from(pos.getX(), pos.getY(), pos.getZ()));
@ -56,7 +56,7 @@ public class EnderCrystalEntity extends Entity {
}
}
// There is a base located on the ender crystal
if (entityMetadata.getId() == 8) {
if (entityMetadata.getId() == 9) {
metadata.getFlags().setFlag(EntityFlag.SHOW_BOTTOM, (boolean) entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);

View file

@ -303,31 +303,13 @@ public class Entity {
metadata.getFlags().setFlag(EntityFlag.SLEEPING, pose.equals(Pose.SLEEPING));
// Triggered when crawling
metadata.getFlags().setFlag(EntityFlag.SWIMMING, pose.equals(Pose.SWIMMING));
float width = entityType.getWidth();
float height = entityType.getHeight();
switch (pose) {
case SLEEPING:
if (this instanceof LivingEntity) {
width = 0.2f;
height = 0.2f;
}
break;
case SNEAKING:
if (entityType == EntityType.PLAYER) {
height = 1.5f;
}
break;
case FALL_FLYING:
case SPIN_ATTACK:
case SWIMMING:
if (entityType == EntityType.PLAYER) {
// Seems like this is only cared about for players; nothing else
height = 0.6f;
}
break;
}
metadata.put(EntityData.BOUNDING_BOX_WIDTH, width);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, height);
setDimensions(pose);
break;
case 7: // Freezing ticks
// The value that Java edition gives us is in ticks, but Bedrock uses a float percentage of the strength 0.0 -> 1.0
// The Java client caps its freezing tick percentage at 140
int freezingTicks = Math.min((int) entityMetadata.getValue(), 140);
metadata.put(EntityData.FREEZING_EFFECT_STRENGTH, (freezingTicks / 140f));
break;
}
}
@ -345,6 +327,15 @@ public class Entity {
session.sendUpstreamPacket(entityDataPacket);
}
/**
* Set the height and width of the entity's bounding box
*/
protected void setDimensions(Pose pose) {
// No flexibility options for basic entities
metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth());
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight());
}
/**
* x = Pitch, y = HeadYaw, z = Yaw
*

View file

@ -32,7 +32,7 @@ import org.geysermc.connector.network.session.GeyserSession;
public class ExpOrbEntity extends Entity {
private int amount;
private final int amount;
public ExpOrbEntity(int amount, long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);

View file

@ -41,7 +41,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 org.geysermc.floodgate.util.DeviceOs;
import java.util.ArrayList;
import java.util.List;
@ -55,7 +55,7 @@ public class FireworkEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
if (entityMetadata.getId() == 8) {
ItemStack item = (ItemStack) entityMetadata.getValue();
if (item == null) {
return;
@ -68,7 +68,8 @@ public class FireworkEntity extends Entity {
// 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) {
if (session.getClientData().getDeviceOs() == DeviceOs.XBOX
|| session.getClientData().getDeviceOs() == DeviceOs.PS4) {
return;
}
@ -134,7 +135,7 @@ public class FireworkEntity extends Entity {
NbtMapBuilder builder = NbtMap.builder();
builder.put("Fireworks", fireworksBuilder.build());
metadata.put(EntityData.DISPLAY_ITEM, builder.build());
} else if (entityMetadata.getId() == 8 && !entityMetadata.getValue().equals(OptionalInt.empty()) && ((OptionalInt) entityMetadata.getValue()).getAsInt() == session.getPlayerEntity().getEntityId()) {
} else if (entityMetadata.getId() == 9 && !entityMetadata.getValue().equals(OptionalInt.empty()) && ((OptionalInt) entityMetadata.getValue()).getAsInt() == session.getPlayerEntity().getEntityId()) {
//Checks if the firework has an entity ID (used when a player is gliding) and checks to make sure the player that is gliding is the one getting sent the packet or else every player near the gliding player will boost too.
PlayerEntity entity = session.getPlayerEntity();
float yaw = entity.getRotation().getX();

View file

@ -67,7 +67,7 @@ public class FishingHookEntity extends ThrowableEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) { // Hooked entity
if (entityMetadata.getId() == 8) { // Hooked entity
int hookedEntityId = (int) entityMetadata.getValue() - 1;
Entity entity = session.getEntityCache().getEntityByJavaId(hookedEntityId);
if (entity == null && session.getPlayerEntity().getEntityId() == hookedEntityId) {
@ -174,11 +174,8 @@ public class FishingHookEntity extends ThrowableEntity {
* @return true if this entity is currently in air.
*/
protected boolean isInAir(GeyserSession session) {
if (session.getConnector().getConfig().isCacheChunks()) {
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return block == BlockTranslator.JAVA_AIR_ID;
}
return false;
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return block == BlockTranslator.JAVA_AIR_ID;
}
@Override

View file

@ -42,7 +42,7 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 13 && !showCustomBlock) {
if (entityMetadata.getId() == 14 && !showCustomBlock) {
hasFuel = (boolean) entityMetadata.getValue();
updateDefaultBlockMetadata(session);
}

View file

@ -54,7 +54,7 @@ public class ItemEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
if (entityMetadata.getId() == 8) {
AddItemEntityPacket itemPacket = new AddItemEntityPacket();
itemPacket.setRuntimeEntityId(geyserId);
itemPacket.setPosition(position.add(0d, this.entityType.getOffset(), 0d));

View file

@ -84,7 +84,7 @@ public class ItemFrameEntity extends Entity {
@Override
public void spawnEntity(GeyserSession session) {
NbtMapBuilder blockBuilder = NbtMap.builder()
.putString("name", "minecraft:frame")
.putString("name", this.entityType == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame")
.putInt("version", session.getBlockTranslator().getBlockStateVersion());
blockBuilder.put("states", NbtMap.builder()
.putInt("facing_direction", direction.ordinal())
@ -105,7 +105,7 @@ public class ItemFrameEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7 && entityMetadata.getValue() != null) {
if (entityMetadata.getId() == 8 && entityMetadata.getValue() != null) {
this.heldItem = (ItemStack) entityMetadata.getValue();
ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem);
ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue());
@ -124,11 +124,11 @@ public class ItemFrameEntity extends Entity {
cachedTag = tag.build();
updateBlock(session);
}
else if (entityMetadata.getId() == 7 && entityMetadata.getValue() == null && cachedTag != null) {
else if (entityMetadata.getId() == 8 && entityMetadata.getValue() == null && cachedTag != null) {
cachedTag = getDefaultTag();
updateBlock(session);
}
else if (entityMetadata.getId() == 8) {
else if (entityMetadata.getId() == 9) {
rotation = ((int) entityMetadata.getValue()) * 45;
if (cachedTag == null) {
updateBlock(session);
@ -167,7 +167,7 @@ public class ItemFrameEntity extends Entity {
builder.putInt("y", bedrockPosition.getY());
builder.putInt("z", bedrockPosition.getZ());
builder.putByte("isMovable", (byte) 1);
builder.putString("id", "ItemFrame");
builder.putString("id", this.entityType == EntityType.GLOW_ITEM_FRAME ? "GlowItemFrame" : "ItemFrame");
return builder.build();
}

View file

@ -26,6 +26,7 @@
package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
@ -68,7 +69,7 @@ public class LivingEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
switch (entityMetadata.getId()) {
case 7: // blocking
case 8: // blocking
byte xd = (byte) entityMetadata.getValue();
//blocking gets triggered when using a bow, but if we set USING_ITEM for all items, it may look like
@ -81,24 +82,22 @@ public class LivingEntity extends Entity {
// Riptide spin attack
metadata.getFlags().setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04);
break;
case 8:
case 9:
metadata.put(EntityData.HEALTH, entityMetadata.getValue());
break;
case 9:
case 10:
metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue());
break;
case 10:
case 11:
metadata.put(EntityData.EFFECT_AMBIENT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0));
break;
case 13: // Bed Position
case 14: // Bed Position
Position bedPosition = (Position) entityMetadata.getValue();
if (bedPosition != null) {
metadata.put(EntityData.BED_POSITION, Vector3i.from(bedPosition.getX(), bedPosition.getY(), bedPosition.getZ()));
if (session.getConnector().getConfig().isCacheChunks()) {
int bed = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
// Bed has to be updated, or else player is floating in the air
ChunkUtils.updateBlock(session, bed, bedPosition);
}
int bed = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
// Bed has to be updated, or else player is floating in the air
ChunkUtils.updateBlock(session, bed, bedPosition);
// Indicate that the player should enter the sleep cycle
// Has to be a byte or it does not work
// (Bed position is what actually triggers sleep - "pose" is only optional)
@ -113,6 +112,16 @@ public class LivingEntity extends Entity {
super.updateBedrockMetadata(entityMetadata, session);
}
@Override
protected void setDimensions(Pose pose) {
if (pose == Pose.SLEEPING) {
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.2f);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.2f);
} else {
super.setDimensions(pose);
}
}
public void updateAllEquipment(GeyserSession session) {
if (!valid) return;

View file

@ -40,33 +40,33 @@ public class MinecartEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
if (entityMetadata.getId() == 8) {
metadata.put(EntityData.HEALTH, entityMetadata.getValue());
}
// Direction in which the minecart is shaking
if (entityMetadata.getId() == 8) {
if (entityMetadata.getId() == 9) {
metadata.put(EntityData.HURT_DIRECTION, entityMetadata.getValue());
}
// Power in Java, time in Bedrock
if (entityMetadata.getId() == 9) {
if (entityMetadata.getId() == 10) {
metadata.put(EntityData.HURT_TIME, Math.min((int) (float) entityMetadata.getValue(), 15));
}
if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class
// Custom block
if (entityMetadata.getId() == 10) {
if (entityMetadata.getId() == 11) {
metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue()));
}
// Custom block offset
if (entityMetadata.getId() == 11) {
if (entityMetadata.getId() == 12) {
metadata.put(EntityData.DISPLAY_OFFSET, entityMetadata.getValue());
}
// If the custom block should be enabled
if (entityMetadata.getId() == 12) {
if (entityMetadata.getId() == 13) {
// Needs a byte based off of Java's boolean
metadata.put(EntityData.CUSTOM_DISPLAY, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0));
}

View file

@ -45,7 +45,7 @@ public class TNTEntity extends Entity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
if (entityMetadata.getId() == 8) {
currentTick = (int) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.IGNITED, true);
metadata.put(EntityData.FUSE_LENGTH, currentTick);

View file

@ -166,13 +166,8 @@ public class ThrowableEntity extends Entity implements Tickable {
* @return true if this entity is currently in water.
*/
protected boolean isInWater(GeyserSession session) {
if (session.getConnector().getConfig().isCacheChunks()) {
if (0 <= position.getFloorY() && position.getFloorY() <= 255) {
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return BlockStateValues.getWaterLevel(block) != -1;
}
}
return false;
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return BlockStateValues.getWaterLevel(block) != -1;
}
@Override

View file

@ -51,7 +51,7 @@ public class ThrownPotionEntity extends ThrowableEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7 && entityMetadata.getType() == MetadataType.ITEM) {
if (entityMetadata.getId() == 8 && entityMetadata.getType() == MetadataType.ITEM) {
ItemStack itemStack = (ItemStack) entityMetadata.getValue();
ItemEntry itemEntry = ItemRegistry.getItem(itemStack);
if (itemEntry.getJavaIdentifier().endsWith("potion") && itemStack.getNbt() != null) {

View file

@ -44,17 +44,17 @@ public class TippedArrowEntity extends AbstractArrowEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
// Arrow potion effect color
if (entityMetadata.getId() == 9) {
if (entityMetadata.getId() == 10) {
int potionColor = (int) entityMetadata.getValue();
// -1 means no color
if (potionColor == -1) {
metadata.remove(EntityData.CUSTOM_DISPLAY);
metadata.put(EntityData.CUSTOM_DISPLAY, 0);
} 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);
metadata.put(EntityData.CUSTOM_DISPLAY, 0);
}
}
}

View file

@ -39,7 +39,7 @@ public class TridentEntity extends AbstractArrowEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 10) {
if (entityMetadata.getId() == 11) {
metadata.getFlags().setFlag(EntityFlag.ENCHANTED, (boolean) entityMetadata.getValue());
}

View file

@ -46,7 +46,7 @@ public class WitherSkullEntity extends ItemedFireballEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 7) {
if (entityMetadata.getId() == 8) {
boolean newIsCharged = (boolean) entityMetadata.getValue();
if (newIsCharged != isCharged) {
isCharged = newIsCharged;

View file

@ -40,7 +40,7 @@ public class AgeableEntity extends CreatureEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
if (entityMetadata.getId() == 16) {
boolean isBaby = (boolean) entityMetadata.getValue();
metadata.put(EntityData.SCALE, isBaby ? .55f : 1f);
metadata.getFlags().setFlag(EntityFlag.BABY, isBaby);

View file

@ -126,7 +126,7 @@ public class ArmorStandEntity extends LivingEntity {
}
} else if (entityMetadata.getId() == 2) {
updateSecondEntityStatus(false);
} else if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) {
} else if (entityMetadata.getId() == 15 && entityMetadata.getType() == MetadataType.BYTE) {
byte xd = (byte) entityMetadata.getValue();
// isSmall
@ -169,37 +169,37 @@ public class ArmorStandEntity extends LivingEntity {
EntityFlag negativeYToggle = null;
EntityFlag negativeZToggle = null;
switch (entityMetadata.getId()) {
case 15: // Head
case 16: // Head
dataLeech = EntityData.MARK_VARIANT;
negativeXToggle = EntityFlag.INTERESTED;
negativeYToggle = EntityFlag.CHARGED;
negativeZToggle = EntityFlag.POWERED;
break;
case 16: // Body
case 17: // Body
dataLeech = EntityData.VARIANT;
negativeXToggle = EntityFlag.IN_LOVE;
negativeYToggle = EntityFlag.CELEBRATING;
negativeZToggle = EntityFlag.CELEBRATING_SPECIAL;
break;
case 17: // Left arm
case 18: // Left arm
dataLeech = EntityData.TRADE_TIER;
negativeXToggle = EntityFlag.CHARGING;
negativeYToggle = EntityFlag.CRITICAL;
negativeZToggle = EntityFlag.DANCING;
break;
case 18: // Right arm
case 19: // Right arm
dataLeech = EntityData.MAX_TRADE_TIER;
negativeXToggle = EntityFlag.ELDER;
negativeYToggle = EntityFlag.EMOTING;
negativeZToggle = EntityFlag.IDLING;
break;
case 19: // Left leg
case 20: // Left leg
dataLeech = EntityData.SKIN_ID;
negativeXToggle = EntityFlag.IS_ILLAGER_CAPTAIN;
negativeYToggle = EntityFlag.IS_IN_UI;
negativeZToggle = EntityFlag.LINGERING;
break;
case 20: // Right leg
case 21: // Right leg
dataLeech = EntityData.HURT_DIRECTION;
negativeXToggle = EntityFlag.IS_PREGNANT;
negativeYToggle = EntityFlag.SHEARED;

View file

@ -39,7 +39,7 @@ public class BatEntity extends AmbientEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
if (entityMetadata.getId() == 16) {
byte xd = (byte) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.RESTING, (xd & 0x01) == 0x01);
}

View file

@ -23,37 +23,21 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.common.window;
package org.geysermc.connector.entity.living;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.common.window.response.FormResponse;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
public abstract class FormWindow {
@Getter
private final String type;
@Getter
protected FormResponse response;
@Getter
@Setter
protected boolean closed;
public FormWindow(String type) {
this.type = type;
public class GlowSquidEntity extends SquidEntity {
public GlowSquidEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
// Lombok won't work here, so we need to make our own method
public void setResponse(FormResponse response) {
this.response = response;
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
super.updateBedrockMetadata(entityMetadata, session);
// TODO "dark ticks remaining" ??? does this have a Bedrock equivalent?
}
@JsonIgnore
public abstract String getJSONData();
public abstract void setResponse(String response);
}

View file

@ -41,7 +41,7 @@ public class InsentientEntity extends LivingEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 14 && entityMetadata.getType() == MetadataType.BYTE) {
if (entityMetadata.getId() == 15 && entityMetadata.getType() == MetadataType.BYTE) {
byte xd = (byte) entityMetadata.getValue();
metadata.getFlags().setFlag(EntityFlag.NO_AI, (xd & 0x01) == 0x01);
}

View file

@ -53,7 +53,7 @@ public class IronGolemEntity extends GolemEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
super.updateBedrockMetadata(entityMetadata, session);
if (entityMetadata.getId() == 8) {
if (entityMetadata.getId() == 9) {
// Required so the resource pack sees the entity health
attributes.put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(metadata.getFloat(EntityData.HEALTH), 100f));
updateBedrockAttributes(session);

View file

@ -39,7 +39,7 @@ public class SlimeEntity extends InsentientEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
if (entityMetadata.getId() == 16) {
this.metadata.put(EntityData.SCALE, 0.10f + (int) entityMetadata.getValue());
}
super.updateBedrockMetadata(entityMetadata, session);

View file

@ -39,7 +39,7 @@ public class SnowGolemEntity extends GolemEntity {
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 15) {
if (entityMetadata.getId() == 16) {
byte xd = (byte) entityMetadata.getValue();
// Handle the visibility of the pumpkin
metadata.getFlags().setFlag(EntityFlag.SHEARED, (xd & 0x10) != 0x10);

View file

@ -29,7 +29,6 @@ import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.type.EntityType;
public class SquidEntity extends WaterEntity {
public SquidEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2019-2021 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.living.animal;
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 com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemEntry;
public class AxolotlEntity extends AnimalEntity {
public AxolotlEntity(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) {
super.updateBedrockMetadata(entityMetadata, session);
if (entityMetadata.getId() == 17) {
int variant = (int) entityMetadata.getValue();
switch (variant) {
case 1: // Java - "Wild" (brown)
variant = 3;
break;
case 3: // Java - cyan
variant = 1;
break;
}
metadata.put(EntityData.VARIANT, variant);
}
else if (entityMetadata.getId() == 18) {
metadata.getFlags().setFlag(EntityFlag.PLAYING_DEAD, (boolean) entityMetadata.getValue());
}
}
@Override
public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) {
return javaIdentifierStripped.equals("tropical_fish_bucket");
}
}

Some files were not shown because too many files have changed in this diff Show more