Geyser/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java

2095 lines
83 KiB
Java
Raw Normal View History

/*
2022-01-01 19:03:05 +00:00
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.session;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.auth.exception.request.RequestException;
import com.github.steveice10.mc.auth.service.MsaAuthenticationService;
import com.github.steveice10.mc.protocol.MinecraftConstants;
import com.github.steveice10.mc.protocol.MinecraftProtocol;
import com.github.steveice10.mc.protocol.data.ProtocolState;
import com.github.steveice10.mc.protocol.data.UnexpectedEncryptionException;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility;
import com.github.steveice10.mc.protocol.data.game.setting.SkinPart;
import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic;
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundClientInformationPacket;
import com.github.steveice10.mc.protocol.packet.handshake.serverbound.ClientIntentionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatCommandPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
2021-11-23 03:14:41 +00:00
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket;
import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryAnswerPacket;
import com.github.steveice10.packetlib.BuiltinFlags;
2021-11-25 04:38:21 +00:00
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.event.session.ConnectedEvent;
import com.github.steveice10.packetlib.event.session.DisconnectedEvent;
import com.github.steveice10.packetlib.event.session.PacketErrorEvent;
import com.github.steveice10.packetlib.event.session.PacketSendingEvent;
import com.github.steveice10.packetlib.event.session.SessionAdapter;
import com.github.steveice10.packetlib.packet.Packet;
import com.github.steveice10.packetlib.tcp.TcpClientSession;
import com.github.steveice10.packetlib.tcp.TcpSession;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
2022-03-20 02:55:29 +00:00
import lombok.experimental.Accessors;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.value.qual.IntRange;
import org.cloudburstmc.math.vector.*;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons;
2022-10-30 00:23:21 +00:00
import org.cloudburstmc.protocol.bedrock.BedrockServerSession;
import org.cloudburstmc.protocol.bedrock.data.*;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
2022-10-30 00:23:21 +00:00
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
2022-10-30 00:23:21 +00:00
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
2022-10-30 00:23:21 +00:00
import org.cloudburstmc.protocol.bedrock.packet.*;
import org.cloudburstmc.protocol.common.DefinitionRegistry;
import org.cloudburstmc.protocol.common.util.OptionalBoolean;
2022-08-11 23:01:26 +00:00
import org.geysermc.api.util.BedrockPlatform;
import org.geysermc.api.util.InputMode;
import org.geysermc.api.util.UiProfile;
import org.geysermc.cumulus.form.Form;
2022-05-29 21:39:40 +00:00
import org.geysermc.cumulus.form.util.FormBuilder;
2021-11-23 03:14:41 +00:00
import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.bedrock.camera.CameraData;
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
2021-11-23 03:14:41 +00:00
import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.entity.EntityData;
import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent;
import org.geysermc.geyser.api.event.bedrock.SessionLoginEvent;
2022-06-08 12:09:14 +00:00
import org.geysermc.geyser.api.network.AuthType;
2022-03-20 02:55:29 +00:00
import org.geysermc.geyser.api.network.RemoteServer;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.GeyserEntityData;
2021-11-23 03:14:41 +00:00
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.entity.type.Tickable;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.erosion.AbstractGeyserboundPacketHandler;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.impl.camera.CameraDefinitions;
import org.geysermc.geyser.impl.camera.GeyserCameraData;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
import org.geysermc.geyser.item.Items;
2022-05-24 23:16:40 +00:00
import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.level.WorldManager;
2021-11-23 03:14:41 +00:00
import org.geysermc.geyser.level.physics.CollisionManager;
import org.geysermc.geyser.network.netty.LocalSession;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.BlockMappings;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.auth.AuthData;
import org.geysermc.geyser.session.auth.BedrockClientData;
import org.geysermc.geyser.session.cache.*;
2021-11-23 03:14:41 +00:00
import org.geysermc.geyser.skin.FloodgateSkinUploader;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.text.MinecraftLocale;
2022-07-24 23:32:22 +00:00
import org.geysermc.geyser.text.TextDecoration;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
2021-11-23 03:14:41 +00:00
import org.geysermc.geyser.translator.text.MessageTranslator;
2022-06-08 12:09:14 +00:00
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.geyser.util.EntityUtils;
2022-06-08 12:09:14 +00:00
import org.geysermc.geyser.util.LoginEncryptionUtils;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Getter
public class GeyserSession implements GeyserConnection, GeyserCommandSource {
2022-10-30 00:23:21 +00:00
private final GeyserImpl geyser;
private final UpstreamSession upstream;
private DownstreamSession downstream;
/**
* The loop where all packets and ticking is processed to prevent concurrency issues.
* If this is manually called, ensure that any exceptions are properly handled.
*/
2022-10-30 00:23:21 +00:00
private final EventLoop eventLoop;
@Setter
private AuthData authData;
@Setter
private BedrockClientData clientData;
/**
* Used for Floodgate skin uploading
*/
@Setter
private List<String> certChainData;
@NonNull
@Setter
private AbstractGeyserboundPacketHandler erosionHandler;
2022-03-20 02:55:29 +00:00
@Accessors(fluent = true)
@Setter
2022-03-20 02:55:29 +00:00
private RemoteServer remoteServer;
private final SessionPlayerEntity playerEntity;
private final AdvancementsCache advancementsCache;
private final BookEditCache bookEditCache;
private final ChunkCache chunkCache;
private final EntityCache entityCache;
private final EntityEffectCache effectCache;
private final FormCache formCache;
private final LodestoneCache lodestoneCache;
private final PistonCache pistonCache;
private final PreferencesCache preferencesCache;
private final SkullCache skullCache;
Feature: Structure block translation (#4521) * ported camotoy's attempt of implementing structure blocks, removal of a few TODO's * no more parsing of java structure templates * Don't attempt to re-request structure size * ensure we can load structures in even if we know the size * init: send correct structure size/offset/rotation to java, not fully working yet * restore offsets so we are sending correct values to the java server regarding where we want the structure to be placed * something something mirror * attempt at proper offsets for mirroring AND rotations. this was not fun at all * rotation, mirror, offsetting all seem to work * undo import changes * fix NPE * Proper handling of empty structures, ensure that we can clear the structure block outline when a zero vector was sent for structure size * oops * Update core/src/main/java/org/geysermc/geyser/session/cache/StructureBlockCache.java Co-authored-by: rtm516 <rtm516@users.noreply.github.com> * Update core/src/main/java/org/geysermc/geyser/translator/level/block/entity/StructureBlockBlockEntityTranslator.java Co-authored-by: rtm516 <rtm516@users.noreply.github.com> * Update core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockStructureBlockUpdateTranslator.java Co-authored-by: rtm516 <rtm516@users.noreply.github.com> * Update core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockStructureTemplateDataRequestTranslator.java Co-authored-by: rtm516 <rtm516@users.noreply.github.com> * consolidate java structure sending into one method * fix merge conflict --------- Co-authored-by: rtm516 <rtm516@users.noreply.github.com>
2024-04-17 21:21:25 +00:00
private final StructureBlockCache structureBlockCache;
private final TagCache tagCache;
private final WorldCache worldCache;
@Setter
private TeleportCache unconfirmedTeleport;
private final WorldBorder worldBorder;
/**
* Whether simulated fog has been sent to the client or not.
*/
private boolean isInWorldBorderWarningArea = false;
private final PlayerInventory playerInventory;
@Setter
private Inventory openInventory;
@Setter
private boolean closingInventory;
@Setter
private InventoryTranslator inventoryTranslator = InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR;
/**
* Use {@link #getNextItemNetId()} instead for consistency
*/
@Getter(AccessLevel.NONE)
private final AtomicInteger itemNetId = new AtomicInteger(2);
@Setter
private ScheduledFuture<?> craftingGridFuture;
/**
* Stores session collision
*/
private final CollisionManager collisionManager;
/**
* Stores the block mappings for this specific version.
*/
@Setter
private BlockMappings blockMappings;
/**
* Stores the item translations for this specific version.
*/
@Setter
private ItemMappings itemMappings;
/**
* Required to decode biomes correctly.
*/
@Setter
private int biomeGlobalPalette;
/**
* Stores the map between Java and Bedrock biome network IDs.
*/
2023-04-09 19:11:46 +00:00
@Setter
private int[] biomeTranslations = null;
/**
* A map of Vector3i positions to Java entities.
* Used for translating Bedrock block actions to Java entity actions.
*/
private final Map<Vector3i, ItemFrameEntity> itemFrameCache = new Object2ObjectOpenHashMap<>();
/**
* Stores a list of all lectern locations and their block entity tags.
* See {@link WorldManager#sendLecternData(GeyserSession, int, int, int)}
* for more information.
*/
private final @Nullable Set<Vector3i> lecternCache;
/**
* A list of all players that have a player head on with a custom texture.
* Our workaround for these players is to give them a custom skin and geometry to emulate wearing a custom skull.
*/
private final Set<UUID> playerWithCustomHeads = new ObjectOpenHashSet<>();
@Setter
private boolean droppingLecternBook;
@Setter
private Vector2i lastChunkPosition = null;
@Setter
private int clientRenderDistance = -1;
private int serverRenderDistance = -1;
2021-11-30 03:04:02 +00:00
// Exposed for GeyserConnect usage
protected boolean sentSpawnPacket;
private boolean loggedIn;
private boolean loggingIn;
@Setter
private boolean spawned;
/**
* Accessed on the initial Java and Bedrock packet processing threads
*/
private volatile boolean closed;
@Setter
private GameMode gameMode = GameMode.SURVIVAL;
/**
* Keeps track of the world name for respawning.
*/
@Setter
private String worldName = null;
/**
* As of Java 1.19.3, the client only uses these for commands.
*/
@Setter
private String[] levels;
private boolean sneaking;
/**
* Stores the Java pose that the server and/or Geyser believes the player currently has.
*/
@Setter
private Pose pose = Pose.STANDING;
@Setter
private boolean sprinting;
/**
* Whether the player is swimming in water.
* Used to update speed when crawling.
*/
@Setter
private boolean swimmingInWater;
/**
* Tracks the original speed attribute.
* <p>
* We need to do this in order to emulate speeds when sneaking under 1.5-blocks-tall areas if the player isn't sneaking,
* and when crawling.
*/
@Setter
private float originalSpeedAttribute;
/**
* The dimension of the player.
* As all entities are in the same world, this can be safely applied to all other entities.
*/
@Setter
private String dimension = DimensionUtils.OVERWORLD;
@MonotonicNonNull
@Setter
private JavaDimension dimensionType = null;
/**
2022-05-24 23:16:40 +00:00
* All dimensions that the client could possibly connect to.
*/
2022-05-24 23:16:40 +00:00
private final Map<String, JavaDimension> dimensions = new Object2ObjectOpenHashMap<>(3);
2022-07-24 23:32:22 +00:00
private final Int2ObjectMap<TextDecoration> chatTypes = new Int2ObjectOpenHashMap<>(7);
@Setter
private int breakingBlock;
@Setter
private Vector3i lastBlockPlacePosition;
@Setter
private String lastBlockPlacedId;
@Setter
private boolean interacting;
/**
* Stores the last position of the block the player interacted with. This can either be a block that the client
* placed or an existing block the player interacted with (for example, a chest). <br>
* Initialized as (0, 0, 0) so it is always not-null.
*/
@Setter
private Vector3i lastInteractionBlockPosition = Vector3i.ZERO;
/**
* Stores the position of the player the last time they interacted.
* Used to verify that the player did not move since their last interaction. <br>
* Initialized as (0, 0, 0) so it is always not-null.
*/
@Setter
private Vector3f lastInteractionPlayerPosition = Vector3f.ZERO;
/**
* The entity that the client is currently looking at.
*/
@Setter
private Entity mouseoverEntity;
/**
* Stores all Java recipes by recipe identifier, and matches them to all possible Bedrock recipe identifiers.
* They are not 1:1, since Bedrock can have multiple recipes for the same Java recipe.
*/
@Setter
private Map<String, List<String>> javaToBedrockRecipeIds;
@Setter
private Int2ObjectMap<GeyserRecipe> craftingRecipes;
private final AtomicInteger lastRecipeNetId;
/**
* Saves a list of all stonecutter recipes, for use in a stonecutter inventory.
* The key is the Java ID of the item; the values are all the possible outputs' Java IDs sorted by their string identifier
*/
@Setter
private Int2ObjectMap<GeyserStonecutterData> stonecutterRecipes;
/**
* Whether to work around 1.13's different behavior in villager trading menus.
*/
@Setter
private boolean emulatePost1_13Logic = true;
/**
* Starting in 1.17, Java servers expect the <code>carriedItem</code> parameter of the serverbound click container
* packet to be the current contents of the mouse after the transaction has been done. 1.16 expects the clicked slot
* contents before any transaction is done. With the current ViaVersion structure, if we do not send what 1.16 expects
* and send multiple click container packets, then successive transactions will be rejected.
*/
@Setter
private boolean emulatePost1_16Logic = true;
@Setter
private boolean emulatePost1_18Logic = true;
/**
* Whether to emulate pre-1.20 smithing table behavior.
* Adapts ViaVersion's furnace UI to one Bedrock can use.
* See {@link org.geysermc.geyser.translator.inventory.OldSmithingTableTranslator}.
*/
@Setter
private boolean oldSmithingTable = false;
/**
* The current attack speed of the player. Used for sending proper cooldown timings.
* Setting a default fixes cooldowns not showing up on a fresh world.
*/
@Setter
private double attackSpeed = 4.0d;
/**
* The time of the last hit. Used to gauge how long the cooldown is taking.
* This is a session variable in order to prevent more scheduled threads than necessary.
*/
@Setter
private long lastHitTime;
/**
* Saves if the client is steering left on a boat.
*/
@Setter
private boolean steeringLeft;
/**
* Saves if the client is steering right on a boat.
*/
@Setter
private boolean steeringRight;
/**
* Store the last time the player interacted. Used to fix a right-click spam bug.
* See <a href="https://github.com/GeyserMC/Geyser/issues/503">this</a> for context.
*/
@Setter
private long lastInteractionTime;
Add support for custom blocks and skulls (#3505) * Super cursed custom skulls custom block * Rename some stuff * Attempt to clean up some code * Remove skull translation events and define custom blocks for custom skulls Clean up skull block translation a bit * Auto generate skull resource pack Change `davchoo` to `geyser` in geometry * Add config options for custom blocks and custom skull blocks * Fix formatting and names for player skulls * Use block states more efficiently for custom skulls 21 block states vs 48 block states * Clean up custom block api a bit * Apply some suggestions from Camotoy * Move custom skull config stuff to its own file Custom skulls can now be added by username, uuid, and textures Move skull nbt stuff from requestTexturesFromUsername to SkullBlockEntityTranslator Add requestTexturesFromUUID * Update custom block nbt for v534 * Disable collision box & selection box when box is empty Fix incorrect collision names used in CustomBlockComponentsBuilder * Add custom block stuff to provider registry loader * More API changes Convert CustomBlockPermutation into a record Change materialInstances in CustomBlockComponents Builder to materialInstance Reuse box components in CustomSkull * Convert skull floor geometries into a template Should be easier to modify in needed in the future. * Crop and reorder skull textures to eliminate unused space Should reduce memory & storage usage for Bedrock clients * Revert "Crop and reorder skull textures to eliminate unused space" This reverts commit 15fd5353e1c2b8bcffd6d5986095a3646a6e7bf9. * Use identifier from CustomBlockData in SkullResourcePackManager * Fix isIncorrectHeldItem check for custom skull blocks Add defaultBlockState to CustomBlockData * Fix adding duplicate block states for custom blocks with 0 properties Remove defaultBlockState CustomBlockState field from GeyserCustomBlockData since it creates a circular reference * Add basis for overriding Bedrock block states Fix missing providers when used in GeyserDefineCustomBlocksEvent * Fix custom blocks in 1.19.50 * Decouple mappings from items * Decouple mappings from items * Null check * Move to CustomBlockRegistryPopulator * Remove name_hash from blocksTag/vanillaBlockStates Fixes creative inventory contents with custom blocks registered * Limit Bedrock versions to 1.19.40+ Custom blocks were released in 1.19.40 * Un-revert Crop and reorder skull textures to eliminate unused space Should reduce memory & storage usage for Bedrock clients Bug with top face flipping + per-face uv's was fixed in 1.19.40+ https://bugs.mojang.com/browse/MCPE-160073 Geometry is still offset by 0.5 to prevent lighting bugs * Add validation custom block components and s/lightFilter/lightDampening/ Also validate custom block names * Add display name component and add toggle for client block placing The display name component allows blocks to use other locale keys. placeAir will prevent the client from placing the default block state. * Begin parsing block mappings (still much to do!) * CustomBlockMapping stores block w/ all states * Mappings almost :/ * Ok now they work at least * Read most mapping components * Block mappings mostly done * Translate block item * Add docs for custom blocks * Add tags * More docs * Accidentally added name comp. * Fix collide box and warn for >16 props * add registerBlockItemOverride event + refactor * Inventory overrides for multistate bedrock blocks * Implement all remaining block components * Minor cleanup and javadocs * Update custom skull config example * Address @Camotoy's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix light_emission and light_dampening components * Remove redundant populate method and remove BLOCKS_JSON after last use * Fix inventories with block state overrides not opening * API event for skull blocks & let register via URL Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Use skin hash instead of URL Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @davchoo's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Rework MappingsReader_v1 to avoid passing maps around * Treat all properties as string properties There isn't a real need to check for boolean and int properties * Fix block registry scan in MappingsReader * Skin hashes can have less than 64 characters? * Include entry when logging exceptions from block mappings * Submodule Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix block break speeds thanks to @Camotoy Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Temporarily fix build on eclipse so I may work... Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Custom tool breakspeed by server; Closes #3348 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Account for if custom skulls are added on 1st run Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Initial framework for extended collision boxes Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Add some notes for the extended collision box impl Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * We have our extended collision registry Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Notes for me Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Extended collision boxes almost work Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Extended collision boxes actually work Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Consider all hitboxes in calculation Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * X is mirrored... Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Extended collision boxes are much improved Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Upstream fallout Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @Redned235's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Oops my bad that makes no sense :) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Ext collision box chunk translation optimization Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Trunc skinhash to 32 chars due to 80 char limit Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Use new transformation cmpnt vs legacy rotation Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * keep arr null on get extcolstor Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Properly handle if extended collision box is below Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Less ugly (realized it can go here) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Prevent 2x placement due to extended collision box Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Properly build on eclipse via indra Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Ensure enough bits in bedrockData for paletteIDs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix not needed whitespace Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update license headers to 2023 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Use release indra over snapshot Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Revert "Update license headers to 2023" This reverts commit f750059e8ee01dfd4e9f9d13d3bdb85eb90e1f74. * Account for collisions in chunk section y0 layer Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix extended collision @ air section bottom Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @davchoo's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @rtm516's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * More @rtm516's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @Camotoy's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update javadocs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @davchoo's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Lock extended collision to section Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Clear ext col even when air Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Let override vanilla items in creative inventory Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Avoid creating 12 HashSets for every overrided block state * Super minor nitpicks + Custom Skull NBT fix * Check custom skull is within Bedrock bounds Fixes NPE with custom skulls above y=320 or below y=-64 * Add static builder methods to match CustomItemData API * Upstream Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Initial API setup for modded blocks (no impl yet) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * More work on nonvanilla blocks (nonfunctional) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix compile Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update submodules Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Modded reg so far (not done) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Add non-vanilla registration and fix a few bugs * Fixes for non-vanilla blocks * Remove import * CustomRegPop. go1st for now; must split for modded Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address silent change to geo component for blocks Co-Authored-By: Unoqwy <pm@unoqwy.dev> Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Seperate bedrock, vanilla, & nonvanilla block reg Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Single event Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Impl MaterialInstance as builder per @Redned235 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Added creative category enum & added some missing overrides (#7) * Add material instance to provider registry Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * oops Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix case of correctBedrockIdentifier not found Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix docs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @Camotoy's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address review from @davchoo Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Set namespace of custom blocks vs ident direct Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address review from @rtm516 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * One more Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Remove rogue space * Geo component as builder Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * use super name Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Bump version Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --------- Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> Signed-off-by: GitHub <noreply@github.com> Co-authored-by: davchoo <davchoo@users.noreply.github.com> Co-authored-by: davchoo <4722249+davchoo@users.noreply.github.com> Co-authored-by: Unoqwy <pm@unoqwy.dev> Co-authored-by: RednedEpic <redned235@gmail.com> Co-authored-by: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Co-authored-by: rtm516 <rtm516@users.noreply.github.com>
2023-08-21 23:04:08 +00:00
/**
* Stores when the player started to break a block. Used to allow correct break time for custom blocks.
*/
@Setter
private long blockBreakStartTime;
/**
Fix some item interactions (#3083) * Remove Bedrock only banner patterns from the creative inventory * Add sound for tadpole bucket * Fix lily pad and frogspawn placing on mobile/single stacks * Workaround? Fix? for bucket usage on mobile * Simplify math and update position+rotation whenever ServerboundUseItemPacket is sent * Rotate the player back after using an item and fix glass bottles * ITEM_USE actionType 1 does not need the rotation fix Increase delay for look back * Add some checks * Prevent buckets and spawn eggs from being unintentionally placed when interacting with special blocks As of 1.19 Bedrock no longer sends a PlayerActionPacket with action=BLOCK_INTERACT. Bedrock now sends action=ITEM_USE_ON_START before and action=ITEM_USE_ON_STOP after using an item on a block. However, this is not useful as it is sent for all block interactions. * Fix inventory transactions being rejected after restoreCorrectBlock The held item's netId is always 0 in the InventoryTransactionPacket. * Touch ups * Fix lookAt for different poses and sneaking + cauldron + bucket interactions Fix boat items being desynced when placing them very close to collision Fix bottles being desynced when tapping above water Resend the held item if we do encounter a desync * Avoid getting blockstate twice and fix comment * Use generated interaction data * Fix glass bottles being double filled and phantom water bottles/water buckets * Don't update the entire inventory on useItem * Use Geyser's inventory copy for check * Use ItemTranslator#getBedrockItemMapping to avoid NBT translation * mappings Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com>
2022-06-24 20:48:28 +00:00
* Stores whether the player intended to place a bucket.
*/
@Setter
Fix some item interactions (#3083) * Remove Bedrock only banner patterns from the creative inventory * Add sound for tadpole bucket * Fix lily pad and frogspawn placing on mobile/single stacks * Workaround? Fix? for bucket usage on mobile * Simplify math and update position+rotation whenever ServerboundUseItemPacket is sent * Rotate the player back after using an item and fix glass bottles * ITEM_USE actionType 1 does not need the rotation fix Increase delay for look back * Add some checks * Prevent buckets and spawn eggs from being unintentionally placed when interacting with special blocks As of 1.19 Bedrock no longer sends a PlayerActionPacket with action=BLOCK_INTERACT. Bedrock now sends action=ITEM_USE_ON_START before and action=ITEM_USE_ON_STOP after using an item on a block. However, this is not useful as it is sent for all block interactions. * Fix inventory transactions being rejected after restoreCorrectBlock The held item's netId is always 0 in the InventoryTransactionPacket. * Touch ups * Fix lookAt for different poses and sneaking + cauldron + bucket interactions Fix boat items being desynced when placing them very close to collision Fix bottles being desynced when tapping above water Resend the held item if we do encounter a desync * Avoid getting blockstate twice and fix comment * Use generated interaction data * Fix glass bottles being double filled and phantom water bottles/water buckets * Don't update the entire inventory on useItem * Use Geyser's inventory copy for check * Use ItemTranslator#getBedrockItemMapping to avoid NBT translation * mappings Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com>
2022-06-24 20:48:28 +00:00
private boolean placedBucket;
/**
* Used to send a movement packet every three seconds if the player hasn't moved. Prevents timeouts when AFK in certain instances.
*/
@Setter
private long lastMovementTimestamp = System.currentTimeMillis();
/**
* Used to send a ServerboundMoveVehiclePacket for every PlayerInputPacket after idling on a boat/horse for more than 100ms
*/
@Setter
private long lastVehicleMoveTimestamp = System.currentTimeMillis();
/**
* Counts how many ticks have occurred since an arm animation started.
* -1 means there is no active arm swing; -2 means an arm swing will start in a tick.
*/
private int armAnimationTicks = -1;
/**
* Controls whether the daylight cycle gamerule has been sent to the client, so the sun/moon remain motionless.
*/
private boolean daylightCycle = true;
private boolean reducedDebugInfo = false;
/**
* The op permission level set by the server
*/
@Setter
private int opPermissionLevel = 0;
/**
* If the current player can fly
*/
@Setter
private boolean canFly = false;
/**
* If the current player is flying
*/
private boolean flying = false;
@Setter
private boolean instabuild = false;
2022-07-10 17:33:39 +00:00
@Setter
private float flySpeed;
@Setter
private float walkSpeed;
/**
* Caches current rain status.
*/
@Setter
private boolean raining = false;
/**
* Caches current thunder status.
*/
@Setter
private boolean thunder = false;
/**
* Stores a map of all statistics sent from the server.
* The server only sends new statistics back to us, so in order to show all statistics we need to cache existing ones.
*/
private final Object2IntMap<Statistic> statistics = new Object2IntOpenHashMap<>(0);
/**
* Whether we're expecting statistics to be sent back to us.
*/
@Setter
private boolean waitingForStatistics = false;
private final Set<UUID> emotes;
/**
* Whether advanced tooltips will be added to the player's items.
*/
@Setter
private boolean advancedTooltips = false;
/**
* The thread that will run every 50 milliseconds - one Minecraft tick.
*/
private ScheduledFuture<?> tickThread = null;
Fix some item interactions (#3083) * Remove Bedrock only banner patterns from the creative inventory * Add sound for tadpole bucket * Fix lily pad and frogspawn placing on mobile/single stacks * Workaround? Fix? for bucket usage on mobile * Simplify math and update position+rotation whenever ServerboundUseItemPacket is sent * Rotate the player back after using an item and fix glass bottles * ITEM_USE actionType 1 does not need the rotation fix Increase delay for look back * Add some checks * Prevent buckets and spawn eggs from being unintentionally placed when interacting with special blocks As of 1.19 Bedrock no longer sends a PlayerActionPacket with action=BLOCK_INTERACT. Bedrock now sends action=ITEM_USE_ON_START before and action=ITEM_USE_ON_STOP after using an item on a block. However, this is not useful as it is sent for all block interactions. * Fix inventory transactions being rejected after restoreCorrectBlock The held item's netId is always 0 in the InventoryTransactionPacket. * Touch ups * Fix lookAt for different poses and sneaking + cauldron + bucket interactions Fix boat items being desynced when placing them very close to collision Fix bottles being desynced when tapping above water Resend the held item if we do encounter a desync * Avoid getting blockstate twice and fix comment * Use generated interaction data * Fix glass bottles being double filled and phantom water bottles/water buckets * Don't update the entire inventory on useItem * Use Geyser's inventory copy for check * Use ItemTranslator#getBedrockItemMapping to avoid NBT translation * mappings Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com>
2022-06-24 20:48:28 +00:00
/**
* Used to return the player to their original rotation after using an item in BedrockInventoryTransactionTranslator
*/
@Setter
private ScheduledFuture<?> lookBackScheduledFuture = null;
/**
* Used to return players back to their vehicles if the server doesn't want them unmounting.
*/
@Setter
private ScheduledFuture<?> mountVehicleScheduledFuture = null;
/**
* A cache of IDs from ClientboundKeepAlivePackets that have been sent to the Bedrock client, but haven't been returned to the server.
* Only used if {@link GeyserConfiguration#isForwardPlayerPing()} is enabled.
*/
private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>();
/**
* Stores the book that is currently being read. Used in {@link org.geysermc.geyser.translator.protocol.java.inventory.JavaOpenBookTranslator}
*/
@Setter
private @Nullable ItemData currentBook = null;
private final GeyserCameraData cameraData;
private final GeyserEntityData entityData;
private MinecraftProtocol protocol;
2021-11-22 19:52:26 +00:00
public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop eventLoop) {
this.geyser = geyser;
this.upstream = new UpstreamSession(bedrockServerSession);
this.eventLoop = eventLoop;
this.erosionHandler = new GeyserboundHandshakePacketHandler(this);
this.advancementsCache = new AdvancementsCache(this);
this.bookEditCache = new BookEditCache(this);
this.chunkCache = new ChunkCache(this);
this.entityCache = new EntityCache(this);
this.effectCache = new EntityEffectCache();
this.formCache = new FormCache(this);
this.lodestoneCache = new LodestoneCache();
this.pistonCache = new PistonCache(this);
this.preferencesCache = new PreferencesCache(this);
this.skullCache = new SkullCache(this);
Feature: Structure block translation (#4521) * ported camotoy's attempt of implementing structure blocks, removal of a few TODO's * no more parsing of java structure templates * Don't attempt to re-request structure size * ensure we can load structures in even if we know the size * init: send correct structure size/offset/rotation to java, not fully working yet * restore offsets so we are sending correct values to the java server regarding where we want the structure to be placed * something something mirror * attempt at proper offsets for mirroring AND rotations. this was not fun at all * rotation, mirror, offsetting all seem to work * undo import changes * fix NPE * Proper handling of empty structures, ensure that we can clear the structure block outline when a zero vector was sent for structure size * oops * Update core/src/main/java/org/geysermc/geyser/session/cache/StructureBlockCache.java Co-authored-by: rtm516 <rtm516@users.noreply.github.com> * Update core/src/main/java/org/geysermc/geyser/translator/level/block/entity/StructureBlockBlockEntityTranslator.java Co-authored-by: rtm516 <rtm516@users.noreply.github.com> * Update core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockStructureBlockUpdateTranslator.java Co-authored-by: rtm516 <rtm516@users.noreply.github.com> * Update core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockStructureTemplateDataRequestTranslator.java Co-authored-by: rtm516 <rtm516@users.noreply.github.com> * consolidate java structure sending into one method * fix merge conflict --------- Co-authored-by: rtm516 <rtm516@users.noreply.github.com>
2024-04-17 21:21:25 +00:00
this.structureBlockCache = new StructureBlockCache();
this.tagCache = new TagCache();
this.worldCache = new WorldCache(this);
this.cameraData = new GeyserCameraData(this);
this.entityData = new GeyserEntityData(this);
this.worldBorder = new WorldBorder(this);
this.collisionManager = new CollisionManager(this);
this.playerEntity = new SessionPlayerEntity(this);
collisionManager.updatePlayerBoundingBox(this.playerEntity.getPosition());
this.playerInventory = new PlayerInventory();
this.openInventory = null;
this.craftingRecipes = new Int2ObjectOpenHashMap<>();
this.javaToBedrockRecipeIds = new Object2ObjectOpenHashMap<>();
this.lastRecipeNetId = new AtomicInteger(1);
this.spawned = false;
this.loggedIn = false;
if (geyser.getWorldManager().shouldExpectLecternHandled(this)) {
// Unneeded on these platforms
this.lecternCache = null;
} else {
this.lecternCache = new ObjectOpenHashSet<>();
}
if (geyser.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) {
this.emotes = new HashSet<>();
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
} else {
this.emotes = null;
}
2022-07-09 22:39:02 +00:00
this.remoteServer = geyser.defaultRemoteServer();
}
/**
* Send all necessary packets to load Bedrock into the server
*/
public void connect() {
startGame();
sentSpawnPacket = true;
// Set the hardcoded shield ID to the ID we just defined in StartGamePacket
2022-10-30 00:23:21 +00:00
// upstream.getSession().getHardcodedBlockingId().set(this.itemMappings.getStoredItems().shield().getBedrockId());
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
ItemComponentPacket componentPacket = new ItemComponentPacket();
componentPacket.getItems().addAll(itemMappings.getComponentItemData());
upstream.sendPacket(componentPacket);
}
ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false);
BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
biomeDefinitionListPacket.setDefinitions(Registries.BIOMES_NBT.get());
upstream.sendPacket(biomeDefinitionListPacket);
AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket();
entityPacket.setIdentifiers(Registries.BEDROCK_ENTITY_IDENTIFIERS.get());
upstream.sendPacket(entityPacket);
CameraPresetsPacket cameraPresetsPacket = new CameraPresetsPacket();
cameraPresetsPacket.getPresets().addAll(CameraDefinitions.CAMERA_PRESETS);
upstream.sendPacket(cameraPresetsPacket);
CreativeContentPacket creativePacket = new CreativeContentPacket();
creativePacket.setContents(this.itemMappings.getCreativeItems());
upstream.sendPacket(creativePacket);
// Potion mixes are registered by default, as they are needed to be able to put ingredients into the brewing stand.
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
craftingDataPacket.setCleanRecipes(true);
craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(this.upstream.getProtocolVersion()));
upstream.sendPacket(craftingDataPacket);
PlayStatusPacket playStatusPacket = new PlayStatusPacket();
playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
upstream.sendPacket(playStatusPacket);
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(getPlayerEntity().getGeyserId());
// Default move speed
// Bedrock clients move very fast by default until they get an attribute packet correcting the speed
attributesPacket.setAttributes(Collections.singletonList(
GeyserAttributeType.MOVEMENT_SPEED.getAttribute()));
upstream.sendPacket(attributesPacket);
GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket();
// Only allow the server to send health information
// Setting this to false allows natural regeneration to work false but doesn't break it being true
gamerulePacket.getGameRules().add(new GameRuleData<>("naturalregeneration", false));
// Don't let the client modify the inventory on death
// Setting this to true allows keep inventory to work if enabled but doesn't break functionality being false
gamerulePacket.getGameRules().add(new GameRuleData<>("keepinventory", true));
// Ensure client doesn't try and do anything funky; the server handles this for us
gamerulePacket.getGameRules().add(new GameRuleData<>("spawnradius", 0));
2023-11-27 12:37:29 +00:00
// Recipe unlocking
gamerulePacket.getGameRules().add(new GameRuleData<>("recipesunlock", true));
upstream.sendPacket(gamerulePacket);
}
public void authenticate(String username) {
if (loggedIn) {
geyser.getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.auth.already_loggedin", username));
return;
}
loggingIn = true;
// Always replace spaces with underscores to avoid illegal nicknames, e.g. with GeyserConnect
protocol = new MinecraftProtocol(username.replace(' ', '_'));
try {
connectDownstream();
} catch (Throwable t) {
t.printStackTrace();
}
}
public void authenticateWithRefreshToken(String refreshToken) {
if (loggedIn) {
2021-11-21 18:36:42 +00:00
geyser.getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.auth.already_loggedin", getAuthData().name()));
return;
}
loggingIn = true;
CompletableFuture.supplyAsync(() -> {
MsaAuthenticationService service = new MsaAuthenticationService(GeyserImpl.OAUTH_CLIENT_ID);
service.setRefreshToken(refreshToken);
try {
service.login();
} catch (RequestException e) {
2022-08-09 18:06:53 +00:00
geyser.getLogger().error("Error while attempting to use refresh token for " + bedrockUsername() + "!", e);
return Boolean.FALSE;
}
GameProfile profile = service.getSelectedProfile();
if (profile == null) {
// Java account is offline
disconnect(GeyserLocale.getPlayerLocaleString("geyser.network.remote.invalid_account", clientData.getLanguageCode()));
return null;
}
protocol = new MinecraftProtocol(profile, service.getAccessToken());
2022-08-09 18:06:53 +00:00
geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken());
return Boolean.TRUE;
}).whenComplete((successful, ex) -> {
if (this.closed) {
return;
}
if (successful == Boolean.FALSE) {
// The player is waiting for a spawn packet, so let's spawn them in now to show them forms
connect();
// Will be cached for after login
LoginEncryptionUtils.buildAndShowTokenExpiredWindow(this);
return;
}
2023-03-22 15:10:04 +00:00
try {
connectDownstream();
} catch (Throwable t) {
t.printStackTrace();
}
});
}
public void authenticateWithMicrosoftCode() {
authenticateWithMicrosoftCode(false);
}
/**
* Present a form window to the user asking to log in with another web browser
*/
public void authenticateWithMicrosoftCode(boolean offlineAccess) {
if (loggedIn) {
2021-11-21 18:36:42 +00:00
geyser.getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.auth.already_loggedin", getAuthData().name()));
return;
}
loggingIn = true;
// This just looks cool
SetTimePacket packet = new SetTimePacket();
packet.setTime(16000);
sendUpstreamPacket(packet);
final PendingMicrosoftAuthentication.AuthenticationTask task = geyser.getPendingMicrosoftAuthentication().getOrCreateTask(
getAuthData().xuid()
);
task.setOnline(true);
task.resetTimer();
if (task.getAuthentication().isDone()) {
onMicrosoftLoginComplete(task);
} else {
task.getCode(offlineAccess).whenComplete((response, ex) -> {
boolean connected = !closed;
if (ex != null) {
if (connected) {
geyser.getLogger().error("Failed to get Microsoft auth code", ex);
disconnect(ex.toString());
}
task.cleanup(); // error getting auth code -> clean up immediately
} else if (connected) {
LoginEncryptionUtils.buildAndShowMicrosoftCodeWindow(this, response);
task.getAuthentication().whenComplete((r, $) -> onMicrosoftLoginComplete(task));
}
});
}
}
/**
* If successful, also begins connecting to the Java server.
*/
public boolean onMicrosoftLoginComplete(PendingMicrosoftAuthentication.AuthenticationTask task) {
if (closed) {
return false;
}
task.cleanup(); // player is online -> remove pending authentication immediately
Throwable ex = task.getLoginException();
if (ex != null) {
geyser.getLogger().error("Failed to log in with Microsoft code!", ex);
disconnect(ex.toString());
} else {
MsaAuthenticationService service = task.getMsaAuthenticationService();
GameProfile selectedProfile = service.getSelectedProfile();
if (selectedProfile == null) {
disconnect(GeyserLocale.getPlayerLocaleString(
"geyser.network.remote.invalid_account",
clientData.getLanguageCode()
));
} else {
this.protocol = new MinecraftProtocol(
selectedProfile,
service.getAccessToken()
);
2023-03-22 15:10:04 +00:00
try {
connectDownstream();
} catch (Throwable t) {
t.printStackTrace();
return false;
}
// Save our refresh token for later use
2022-08-09 18:06:53 +00:00
geyser.saveRefreshToken(bedrockUsername(), service.getRefreshToken());
return true;
}
}
return false;
}
/**
* After getting whatever credentials needed, we attempt to join the Java server.
*/
private void connectDownstream() {
SessionLoginEvent loginEvent = new SessionLoginEvent(this, remoteServer);
GeyserImpl.getInstance().eventBus().fire(loginEvent);
if (loginEvent.isCancelled()) {
String disconnectReason = loginEvent.disconnectReason() == null ?
BedrockDisconnectReasons.DISCONNECTED : loginEvent.disconnectReason();
disconnect(disconnectReason);
return;
}
this.remoteServer = loginEvent.remoteServer();
boolean floodgate = this.remoteServer.authType() == AuthType.FLOODGATE;
// Start ticking
tickThread = eventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS);
2022-10-30 00:23:21 +00:00
TcpSession downstream;
if (geyser.getBootstrap().getSocketAddress() != null) {
// We're going to connect through the JVM and not through TCP
2022-03-20 02:55:29 +00:00
downstream = new LocalSession(this.remoteServer.address(), this.remoteServer.port(),
2022-06-05 18:12:36 +00:00
geyser.getBootstrap().getSocketAddress(), upstream.getAddress().getAddress().getHostAddress(),
this.protocol, this.protocol.createHelper());
this.downstream = new DownstreamSession(downstream);
} else {
2022-03-20 02:55:29 +00:00
downstream = new TcpClientSession(this.remoteServer.address(), this.remoteServer.port(), this.protocol);
this.downstream = new DownstreamSession(downstream);
boolean resolveSrv = false;
try {
resolveSrv = this.remoteServer.resolveSrv();
} catch (AbstractMethodError | NoSuchMethodError ignored) {
// Ignore if the method doesn't exist
// This will happen with extensions using old APIs
}
this.downstream.getSession().setFlag(BuiltinFlags.ATTEMPT_SRV_RESOLVE, resolveSrv);
}
if (geyser.getConfig().getRemote().isUseProxyProtocol()) {
downstream.setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true);
downstream.setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress());
}
if (geyser.getConfig().isForwardPlayerPing()) {
// Let Geyser handle sending the keep alive
downstream.setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false);
}
downstream.addListener(new SessionAdapter() {
@Override
public void packetSending(PacketSendingEvent event) {
//todo move this somewhere else
if (event.getPacket() instanceof ClientIntentionPacket) {
String addressSuffix;
if (floodgate) {
byte[] encryptedData;
try {
FloodgateSkinUploader skinUploader = geyser.getSkinUploader();
FloodgateCipher cipher = geyser.getCipher();
2022-01-28 12:08:10 +00:00
String bedrockAddress = upstream.getAddress().getAddress().getHostAddress();
// both BungeeCord and Velocity remove the IPv6 scope (if there is one) for Spigot
int ipv6ScopeIndex = bedrockAddress.indexOf('%');
if (ipv6ScopeIndex != -1) {
bedrockAddress = bedrockAddress.substring(0, ipv6ScopeIndex);
}
encryptedData = cipher.encryptFromString(BedrockData.of(
clientData.getGameVersion(),
2021-11-21 18:36:42 +00:00
authData.name(),
authData.xuid(),
clientData.getDeviceOs().ordinal(),
clientData.getLanguageCode(),
clientData.getUiProfile().ordinal(),
clientData.getCurrentInputMode().ordinal(),
2022-01-28 12:08:10 +00:00
bedrockAddress,
skinUploader.getId(),
skinUploader.getVerifyCode()
).toString());
} catch (Exception e) {
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e);
disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.floodgate.encrypt_fail", getClientData().getLanguageCode()));
return;
}
addressSuffix = '\0' + new String(encryptedData, StandardCharsets.UTF_8);
} else {
addressSuffix = "";
}
ClientIntentionPacket intentionPacket = event.getPacket();
String address;
if (geyser.getConfig().getRemote().isForwardHost()) {
address = clientData.getServerAddress().split(":")[0];
} else {
address = intentionPacket.getHostname();
}
event.setPacket(intentionPacket.withHostname(address + addressSuffix));
}
}
@Override
public void connected(ConnectedEvent event) {
loggingIn = false;
loggedIn = true;
if (downstream instanceof LocalSession) {
// Connected directly to the server
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.connect_internal",
2021-11-21 18:36:42 +00:00
authData.name(), protocol.getProfile().getName()));
} else {
// Connected to an IP address
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.connect",
2022-03-20 02:55:29 +00:00
authData.name(), protocol.getProfile().getName(), remoteServer.address()));
}
UUID uuid = protocol.getProfile().getId();
if (uuid == null) {
// Set what our UUID *probably* is going to be
if (remoteServer.authType() == AuthType.FLOODGATE) {
2021-11-21 18:36:42 +00:00
uuid = new UUID(0, Long.parseLong(authData.xuid()));
} else {
uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + protocol.getProfile().getName()).getBytes(StandardCharsets.UTF_8));
}
}
playerEntity.setUuid(uuid);
playerEntity.setUsername(protocol.getProfile().getName());
String locale = clientData.getLanguageCode();
// Let the user know there locale may take some time to download
// as it has to be extracted from a JAR
if (locale.equalsIgnoreCase("en_us") && !MinecraftLocale.LOCALE_MAPPINGS.containsKey("en_us")) {
// This should probably be left hardcoded as it will only show for en_us clients
sendMessage("Loading your locale (en_us); if this isn't already downloaded, this may take some time");
}
// Download and load the language for the player
MinecraftLocale.downloadAndLoadLocale(locale);
}
@Override
public void disconnected(DisconnectedEvent event) {
loggingIn = false;
loggedIn = false;
String disconnectMessage;
Throwable cause = event.getCause();
if (cause instanceof UnexpectedEncryptionException) {
if (remoteServer.authType() != AuthType.FLOODGATE) {
// Server expects online mode
disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", locale());
// Explain that they may be looking for Floodgate.
geyser.getLogger().warning(GeyserLocale.getLocaleStringLog(
geyser.getPlatformType() == PlatformType.STANDALONE ?
"geyser.network.remote.floodgate_explanation_standalone"
: "geyser.network.remote.floodgate_explanation_plugin",
Constants.FLOODGATE_DOWNLOAD_LOCATION
));
} else {
// Likely that Floodgate is not configured correctly.
disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.floodgate_login_error", locale());
if (geyser.getPlatformType() == PlatformType.STANDALONE) {
geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.remote.floodgate_login_error_standalone"));
}
}
} else if (cause instanceof ConnectException) {
// Server is offline, probably
disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.server_offline", locale());
} else {
2023-03-13 03:51:51 +00:00
disconnectMessage = MessageTranslator.convertMessage(event.getReason());
}
if (downstream instanceof LocalSession) {
2021-11-21 18:36:42 +00:00
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.disconnect_internal", authData.name(), disconnectMessage));
} else {
2022-03-20 02:55:29 +00:00
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.remote.disconnect", authData.name(), remoteServer.address(), disconnectMessage));
}
if (cause != null) {
if (cause.getMessage() != null) {
GeyserImpl.getInstance().getLogger().error(cause.getMessage());
} else {
GeyserImpl.getInstance().getLogger().error("An exception occurred: ", cause);
}
// GeyserSession is disconnected via session.disconnect() called indirectly be the server
// This only needs to be "initiated" here when there is an exception, hence the cause clause
GeyserSession.this.disconnect(disconnectMessage);
if (geyser.getConfig().isDebugMode()) {
cause.printStackTrace();
}
}
}
@Override
2021-11-25 04:38:21 +00:00
public void packetReceived(Session session, Packet packet) {
2021-11-22 19:52:26 +00:00
Registries.JAVA_PACKET_TRANSLATORS.translate(packet.getClass(), packet, GeyserSession.this);
}
@Override
public void packetError(PacketErrorEvent event) {
geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.downstream_error", event.getCause().getMessage()));
if (geyser.getConfig().isDebugMode())
event.getCause().printStackTrace();
event.setSuppress(true);
}
});
if (!daylightCycle) {
setDaylightCycle(true);
}
downstream.connect(false);
}
public void disconnect(String reason) {
if (!closed) {
loggedIn = false;
// Fire SessionDisconnectEvent
SessionDisconnectEvent disconnectEvent = new SessionDisconnectEvent(this, reason);
geyser.getEventBus().fire(disconnectEvent);
// Disconnect downstream if necessary
if (downstream != null) {
// No need to disconnect if already closed
if (!downstream.isClosed()) {
downstream.disconnect(reason);
}
} else {
// Downstream's disconnect will fire an event that prints a log message
// Otherwise, we print a message here
String address = geyser.getConfig().isLogPlayerIpAddresses() ? upstream.getAddress().getAddress().toString() : "<IP address withheld>";
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.disconnect", address, reason));
}
// Disconnect upstream if necessary
if (!upstream.isClosed()) {
upstream.disconnect(disconnectEvent.disconnectReason());
}
// Remove from session manager
geyser.getSessionManager().removeSession(this);
if (authData != null) {
PendingMicrosoftAuthentication.AuthenticationTask task = geyser.getPendingMicrosoftAuthentication().getTask(authData.xuid());
if (task != null) {
task.setOnline(false);
}
}
}
if (tickThread != null) {
tickThread.cancel(false);
}
erosionHandler.close();
closed = true;
}
/**
* Moves task to the session event loop if already not in it. Otherwise, the task is automatically ran.
*/
public void ensureInEventLoop(Runnable runnable) {
if (eventLoop.inEventLoop()) {
runnable.run();
return;
}
executeInEventLoop(runnable);
}
/**
* Executes a task and prints a stack trace if an error occurs.
*/
public void executeInEventLoop(Runnable runnable) {
eventLoop.execute(() -> {
try {
runnable.run();
} catch (Throwable e) {
2022-08-09 18:06:53 +00:00
geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e);
}
});
}
/**
* Schedules a task and prints a stack trace if an error occurs.
*/
public ScheduledFuture<?> scheduleInEventLoop(Runnable runnable, long duration, TimeUnit timeUnit) {
return eventLoop.schedule(() -> {
try {
if (!closed) {
runnable.run();
}
} catch (Throwable e) {
2022-08-09 18:06:53 +00:00
geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e);
}
}, duration, timeUnit);
}
/**
* Called every 50 milliseconds - one Minecraft tick.
*/
protected void tick() {
try {
pistonCache.tick();
// Check to see if the player's position needs updating - a position update should be sent once every 3 seconds
if (spawned && (System.currentTimeMillis() - lastMovementTimestamp) > 3000) {
// Recalculate in case something else changed position
Vector3d position = collisionManager.adjustBedrockPosition(playerEntity.getPosition(), playerEntity.isOnGround(), false);
// A null return value cancels the packet
if (position != null) {
ServerboundMovePlayerPosPacket packet = new ServerboundMovePlayerPosPacket(playerEntity.isOnGround(),
position.getX(), position.getY(), position.getZ());
sendDownstreamGamePacket(packet);
}
lastMovementTimestamp = System.currentTimeMillis();
}
if (worldBorder.isResizing()) {
worldBorder.resize();
}
boolean shouldShowFog = !worldBorder.isWithinWarningBoundaries();
if (shouldShowFog || worldBorder.isCloseToBorderBoundaries()) {
// Show particles representing where the world border is
worldBorder.drawWall();
// Set the mood
if (shouldShowFog && !isInWorldBorderWarningArea) {
isInWorldBorderWarningArea = true;
camera().sendFog("minecraft:fog_crimson_forest");
}
}
if (!shouldShowFog && isInWorldBorderWarningArea) {
// Clear fog as we are outside the world border now
camera().removeFog("minecraft:fog_crimson_forest");
isInWorldBorderWarningArea = false;
}
for (Tickable entity : entityCache.getTickableEntities()) {
entity.tick();
}
if (armAnimationTicks >= 0) {
// As of 1.18.2 Java Edition, it appears that the swing time is dynamically updated depending on the
// player's effect status, but the animation can cut short if the duration suddenly decreases
// (from suddenly no longer having mining fatigue, for example)
// This math is referenced from Java Edition 1.18.2
int swingTotalDuration;
int hasteLevel = Math.max(effectCache.getHaste(), effectCache.getConduitPower());
if (hasteLevel > 0) {
swingTotalDuration = 6 - hasteLevel;
} else {
int miningFatigueLevel = effectCache.getMiningFatigue();
if (miningFatigueLevel > 0) {
swingTotalDuration = 6 + miningFatigueLevel * 2;
} else {
swingTotalDuration = 6;
}
}
if (++armAnimationTicks >= swingTotalDuration) {
if (sneaking) {
// Attempt to re-activate blocking as our swing animation is up
if (attemptToBlock()) {
playerEntity.updateBedrockMetadata();
}
}
armAnimationTicks = -1;
}
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
public void setAuthenticationData(AuthData authData) {
this.authData = authData;
}
public void startSneaking() {
// Toggle the shield, if there is no ongoing arm animation
// This matches Bedrock Edition behavior as of 1.18.12
if (armAnimationTicks < 0) {
attemptToBlock();
}
setSneaking(true);
}
public void stopSneaking() {
disableBlocking();
setSneaking(false);
}
private void setSneaking(boolean sneaking) {
this.sneaking = sneaking;
// Update pose and bounding box on our end
AttributeData speedAttribute;
if (!sneaking && (speedAttribute = adjustSpeed()) != null) {
// Update attributes since we're still "sneaking" under a 1.5-block-tall area
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(playerEntity.getGeyserId());
attributesPacket.setAttributes(Collections.singletonList(speedAttribute));
sendUpstreamPacket(attributesPacket);
// the server *should* update our pose once it has returned to normal
} else {
if (!flying) {
// The pose and bounding box should not be updated if the player is flying
setSneakingPose(sneaking);
}
collisionManager.updateScaffoldingFlags(false);
}
playerEntity.updateBedrockMetadata();
if (mouseoverEntity != null) {
// Horses, etc can change their property depending on if you're sneaking
mouseoverEntity.updateInteractiveTag();
}
}
private void setSneakingPose(boolean sneaking) {
if (this.pose == Pose.SNEAKING && !sneaking) {
this.pose = Pose.STANDING;
playerEntity.setBoundingBoxHeight(playerEntity.getDefinition().height());
} else if (sneaking) {
this.pose = Pose.SNEAKING;
playerEntity.setBoundingBoxHeight(1.5f);
}
playerEntity.setFlag(EntityFlag.SNEAKING, sneaking);
}
2021-12-26 03:46:16 +00:00
public void setSwimming(boolean swimming) {
if (swimming) {
this.pose = Pose.SWIMMING;
playerEntity.setBoundingBoxHeight(0.6f);
} else {
this.pose = Pose.STANDING;
playerEntity.setBoundingBoxHeight(playerEntity.getDefinition().height());
}
playerEntity.setFlag(EntityFlag.SWIMMING, swimming);
playerEntity.updateBedrockMetadata();
}
public void setFlying(boolean flying) {
this.flying = flying;
if (sneaking) {
// update bounding box as it is not reduced when flying
setSneakingPose(!flying);
playerEntity.updateBedrockMetadata();
}
}
/**
* Adjusts speed if the player is crawling.
*
* @return not null if attributes should be updated.
*/
public @Nullable AttributeData adjustSpeed() {
AttributeData currentPlayerSpeed = playerEntity.getAttributes().get(GeyserAttributeType.MOVEMENT_SPEED);
if (currentPlayerSpeed != null) {
2021-12-26 03:46:16 +00:00
if ((pose.equals(Pose.SNEAKING) && !sneaking && collisionManager.mustPlayerSneakHere()) ||
(!swimmingInWater && playerEntity.getFlag(EntityFlag.SWIMMING) && !collisionManager.isPlayerInWater())) {
// Either of those conditions means that Bedrock goes zoom when they shouldn't be
AttributeData speedAttribute = GeyserAttributeType.MOVEMENT_SPEED.getAttribute(originalSpeedAttribute / 3.32f);
playerEntity.getAttributes().put(GeyserAttributeType.MOVEMENT_SPEED, speedAttribute);
return speedAttribute;
} else if (originalSpeedAttribute != currentPlayerSpeed.getValue()) {
// Speed has reset to normal
AttributeData speedAttribute = GeyserAttributeType.MOVEMENT_SPEED.getAttribute(originalSpeedAttribute);
playerEntity.getAttributes().put(GeyserAttributeType.MOVEMENT_SPEED, speedAttribute);
return speedAttribute;
}
}
return null;
}
/**
* Checks to see if a shield is in either hand to activate blocking. If so, it sets the Bedrock client to display
* blocking and sends a packet to the Java server.
*/
private boolean attemptToBlock() {
ServerboundUseItemPacket useItemPacket;
if (playerInventory.getItemInHand().asItem() == Items.SHIELD) {
useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND, worldCache.nextPredictionSequence());
} else if (playerInventory.getOffhand().asItem() == Items.SHIELD) {
useItemPacket = new ServerboundUseItemPacket(Hand.OFF_HAND, worldCache.nextPredictionSequence());
} else {
// No blocking
return false;
}
sendDownstreamGamePacket(useItemPacket);
playerEntity.setFlag(EntityFlag.BLOCKING, true);
// Metadata should be updated later
return true;
}
/**
* Starts ticking the amount of time that the Bedrock client has been swinging their arm, and disables blocking if
* blocking.
*/
public void activateArmAnimationTicking() {
armAnimationTicks = 0;
if (disableBlocking()) {
playerEntity.updateBedrockMetadata();
}
}
/**
* For <a href="https://github.com/GeyserMC/Geyser/issues/2113">issue 2113</a> and combating arm ticking activating being delayed in
* BedrockAnimateTranslator.
*/
public void armSwingPending() {
if (armAnimationTicks == -1) {
armAnimationTicks = -2;
}
}
/**
* Indicates to the client to stop blocking and tells the Java server the same.
*/
private boolean disableBlocking() {
if (playerEntity.getFlag(EntityFlag.BLOCKING)) {
ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM,
Vector3i.ZERO, Direction.DOWN, 0);
sendDownstreamGamePacket(releaseItemPacket);
playerEntity.setFlag(EntityFlag.BLOCKING, false);
return true;
}
return false;
}
public void requestOffhandSwap() {
ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO,
Direction.DOWN, 0);
sendDownstreamGamePacket(swapHandsPacket);
}
@Override
2022-08-11 23:01:26 +00:00
public String name() {
return null;
}
@Override
public void sendMessage(@NonNull String message) {
TextPacket textPacket = new TextPacket();
textPacket.setPlatformChatId("");
textPacket.setSourceName("");
textPacket.setXuid("");
textPacket.setType(TextPacket.Type.CHAT);
textPacket.setNeedsTranslation(false);
textPacket.setMessage(message);
upstream.sendPacket(textPacket);
}
@Override
public boolean isConsole() {
return false;
}
@Override
public String locale() {
return clientData.getLanguageCode();
}
/**
* Sends a chat message to the Java server.
*/
public void sendChat(String message) {
sendDownstreamGamePacket(new ServerboundChatPacket(message, Instant.now().toEpochMilli(), 0L, null, 0, new BitSet()));
}
/**
* Sends a command to the Java server.
*/
public void sendCommand(String command) {
sendDownstreamGamePacket(new ServerboundChatCommandPacket(command, Instant.now().toEpochMilli(), 0L, Collections.emptyList(), 0, new BitSet()));
}
public void setServerRenderDistance(int renderDistance) {
// Ensure render distance is not above 96 as sending a larger value at any point crashes mobile clients and 96 is the max of any bedrock platform
renderDistance = Math.min(renderDistance, 96);
this.serverRenderDistance = renderDistance;
ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket();
chunkRadiusUpdatedPacket.setRadius(renderDistance);
upstream.sendPacket(chunkRadiusUpdatedPacket);
}
public InetSocketAddress getSocketAddress() {
return this.upstream.getAddress();
}
@Override
2022-08-11 23:01:26 +00:00
public boolean sendForm(@NonNull Form form) {
formCache.showForm(form);
2022-08-11 23:01:26 +00:00
return true;
}
@Override
2022-08-11 23:01:26 +00:00
public boolean sendForm(@NonNull FormBuilder<?, ?, ?> formBuilder) {
formCache.showForm(formBuilder.build());
2022-08-11 23:01:26 +00:00
return true;
}
2022-06-05 22:25:45 +00:00
/**
* @deprecated since Cumulus version 1.1, and will be removed when Cumulus 2.0 releases. Please use the new forms instead.
*/
@Deprecated
public void sendForm(org.geysermc.cumulus.Form<?> form) {
sendForm(form.newForm());
}
/**
* @deprecated since Cumulus version 1.1, and will be removed when Cumulus 2.0 releases. Please use the new forms instead.
*/
@Deprecated
2022-06-06 08:03:39 +00:00
public void sendForm(org.geysermc.cumulus.util.FormBuilder<?, ?> formBuilder) {
2022-06-05 22:25:45 +00:00
sendForm(formBuilder.build());
}
private void startGame() {
this.upstream.getCodecHelper().setItemDefinitions(this.itemMappings);
this.upstream.getCodecHelper().setBlockDefinitions((DefinitionRegistry) this.blockMappings); //FIXME
this.upstream.getCodecHelper().setCameraPresetDefinitions(CameraDefinitions.CAMERA_DEFINITIONS);
2022-10-30 03:02:11 +00:00
StartGamePacket startGamePacket = new StartGamePacket();
startGamePacket.setUniqueEntityId(playerEntity.getGeyserId());
startGamePacket.setRuntimeEntityId(playerEntity.getGeyserId());
startGamePacket.setPlayerGameType(EntityUtils.toBedrockGamemode(gameMode));
startGamePacket.setPlayerPosition(Vector3f.from(0, 69, 0));
startGamePacket.setRotation(Vector2f.from(1, 1));
startGamePacket.setSeed(-1L);
startGamePacket.setDimensionId(DimensionUtils.javaToBedrock(chunkCache.getBedrockDimension()));
startGamePacket.setGeneratorId(1);
startGamePacket.setLevelGameType(GameType.SURVIVAL);
startGamePacket.setDifficulty(1);
startGamePacket.setDefaultSpawn(Vector3i.ZERO);
startGamePacket.setAchievementsDisabled(!geyser.getConfig().isXboxAchievementsEnabled());
startGamePacket.setCurrentTick(-1);
startGamePacket.setEduEditionOffers(0);
startGamePacket.setEduFeaturesEnabled(false);
startGamePacket.setRainLevel(0);
startGamePacket.setLightningLevel(0);
startGamePacket.setMultiplayerGame(true);
startGamePacket.setBroadcastingToLan(true);
startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC);
startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC);
startGamePacket.setCommandsEnabled(!geyser.getConfig().isXboxAchievementsEnabled());
startGamePacket.setTexturePacksRequired(false);
startGamePacket.setBonusChestEnabled(false);
startGamePacket.setStartingWithMap(false);
startGamePacket.setTrustingPlayers(true);
startGamePacket.setDefaultPlayerPermission(PlayerPermission.MEMBER);
startGamePacket.setServerChunkTickRange(4);
startGamePacket.setBehaviorPackLocked(false);
startGamePacket.setResourcePackLocked(false);
startGamePacket.setFromLockedWorldTemplate(false);
startGamePacket.setUsingMsaGamertagsOnly(false);
startGamePacket.setFromWorldTemplate(false);
startGamePacket.setWorldTemplateOptionLocked(false);
startGamePacket.setSpawnBiomeType(SpawnBiomeType.DEFAULT);
startGamePacket.setCustomBiomeName("");
startGamePacket.setEducationProductionId("");
startGamePacket.setForceExperimentalGameplay(OptionalBoolean.empty());
2022-07-09 22:39:02 +00:00
String serverName = geyser.getConfig().getBedrock().serverName();
startGamePacket.setLevelId(serverName);
startGamePacket.setLevelName(serverName);
startGamePacket.setPremiumWorldTemplateId("00000000-0000-0000-0000-000000000000");
// startGamePacket.setCurrentTick(0);
startGamePacket.setEnchantmentSeed(0);
startGamePacket.setMultiplayerCorrelationId("");
startGamePacket.setItemDefinitions(this.itemMappings.getItemDefinitions().values().stream().toList()); // TODO
2022-10-30 03:02:11 +00:00
// startGamePacket.setBlockPalette(this.blockMappings.getBedrockBlockPalette());
Add support for custom blocks and skulls (#3505) * Super cursed custom skulls custom block * Rename some stuff * Attempt to clean up some code * Remove skull translation events and define custom blocks for custom skulls Clean up skull block translation a bit * Auto generate skull resource pack Change `davchoo` to `geyser` in geometry * Add config options for custom blocks and custom skull blocks * Fix formatting and names for player skulls * Use block states more efficiently for custom skulls 21 block states vs 48 block states * Clean up custom block api a bit * Apply some suggestions from Camotoy * Move custom skull config stuff to its own file Custom skulls can now be added by username, uuid, and textures Move skull nbt stuff from requestTexturesFromUsername to SkullBlockEntityTranslator Add requestTexturesFromUUID * Update custom block nbt for v534 * Disable collision box & selection box when box is empty Fix incorrect collision names used in CustomBlockComponentsBuilder * Add custom block stuff to provider registry loader * More API changes Convert CustomBlockPermutation into a record Change materialInstances in CustomBlockComponents Builder to materialInstance Reuse box components in CustomSkull * Convert skull floor geometries into a template Should be easier to modify in needed in the future. * Crop and reorder skull textures to eliminate unused space Should reduce memory & storage usage for Bedrock clients * Revert "Crop and reorder skull textures to eliminate unused space" This reverts commit 15fd5353e1c2b8bcffd6d5986095a3646a6e7bf9. * Use identifier from CustomBlockData in SkullResourcePackManager * Fix isIncorrectHeldItem check for custom skull blocks Add defaultBlockState to CustomBlockData * Fix adding duplicate block states for custom blocks with 0 properties Remove defaultBlockState CustomBlockState field from GeyserCustomBlockData since it creates a circular reference * Add basis for overriding Bedrock block states Fix missing providers when used in GeyserDefineCustomBlocksEvent * Fix custom blocks in 1.19.50 * Decouple mappings from items * Decouple mappings from items * Null check * Move to CustomBlockRegistryPopulator * Remove name_hash from blocksTag/vanillaBlockStates Fixes creative inventory contents with custom blocks registered * Limit Bedrock versions to 1.19.40+ Custom blocks were released in 1.19.40 * Un-revert Crop and reorder skull textures to eliminate unused space Should reduce memory & storage usage for Bedrock clients Bug with top face flipping + per-face uv's was fixed in 1.19.40+ https://bugs.mojang.com/browse/MCPE-160073 Geometry is still offset by 0.5 to prevent lighting bugs * Add validation custom block components and s/lightFilter/lightDampening/ Also validate custom block names * Add display name component and add toggle for client block placing The display name component allows blocks to use other locale keys. placeAir will prevent the client from placing the default block state. * Begin parsing block mappings (still much to do!) * CustomBlockMapping stores block w/ all states * Mappings almost :/ * Ok now they work at least * Read most mapping components * Block mappings mostly done * Translate block item * Add docs for custom blocks * Add tags * More docs * Accidentally added name comp. * Fix collide box and warn for >16 props * add registerBlockItemOverride event + refactor * Inventory overrides for multistate bedrock blocks * Implement all remaining block components * Minor cleanup and javadocs * Update custom skull config example * Address @Camotoy's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix light_emission and light_dampening components * Remove redundant populate method and remove BLOCKS_JSON after last use * Fix inventories with block state overrides not opening * API event for skull blocks & let register via URL Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Use skin hash instead of URL Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @davchoo's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Rework MappingsReader_v1 to avoid passing maps around * Treat all properties as string properties There isn't a real need to check for boolean and int properties * Fix block registry scan in MappingsReader * Skin hashes can have less than 64 characters? * Include entry when logging exceptions from block mappings * Submodule Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix block break speeds thanks to @Camotoy Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Temporarily fix build on eclipse so I may work... Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Custom tool breakspeed by server; Closes #3348 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Account for if custom skulls are added on 1st run Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Initial framework for extended collision boxes Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Add some notes for the extended collision box impl Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * We have our extended collision registry Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Notes for me Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Extended collision boxes almost work Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Extended collision boxes actually work Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Consider all hitboxes in calculation Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * X is mirrored... Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Extended collision boxes are much improved Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Upstream fallout Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @Redned235's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Oops my bad that makes no sense :) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Ext collision box chunk translation optimization Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Trunc skinhash to 32 chars due to 80 char limit Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Use new transformation cmpnt vs legacy rotation Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * keep arr null on get extcolstor Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Properly handle if extended collision box is below Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Less ugly (realized it can go here) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Prevent 2x placement due to extended collision box Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Properly build on eclipse via indra Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Ensure enough bits in bedrockData for paletteIDs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix not needed whitespace Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update license headers to 2023 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Use release indra over snapshot Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Revert "Update license headers to 2023" This reverts commit f750059e8ee01dfd4e9f9d13d3bdb85eb90e1f74. * Account for collisions in chunk section y0 layer Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix extended collision @ air section bottom Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @davchoo's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @rtm516's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * More @rtm516's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @Camotoy's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update javadocs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @davchoo's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Lock extended collision to section Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Clear ext col even when air Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Let override vanilla items in creative inventory Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Avoid creating 12 HashSets for every overrided block state * Super minor nitpicks + Custom Skull NBT fix * Check custom skull is within Bedrock bounds Fixes NPE with custom skulls above y=320 or below y=-64 * Add static builder methods to match CustomItemData API * Upstream Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Initial API setup for modded blocks (no impl yet) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * More work on nonvanilla blocks (nonfunctional) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix compile Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update submodules Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Modded reg so far (not done) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Add non-vanilla registration and fix a few bugs * Fixes for non-vanilla blocks * Remove import * CustomRegPop. go1st for now; must split for modded Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address silent change to geo component for blocks Co-Authored-By: Unoqwy <pm@unoqwy.dev> Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Seperate bedrock, vanilla, & nonvanilla block reg Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Single event Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Impl MaterialInstance as builder per @Redned235 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Added creative category enum & added some missing overrides (#7) * Add material instance to provider registry Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * oops Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix case of correctBedrockIdentifier not found Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix docs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @Camotoy's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address review from @davchoo Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Set namespace of custom blocks vs ident direct Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address review from @rtm516 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * One more Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Remove rogue space * Geo component as builder Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * use super name Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Bump version Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --------- Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> Signed-off-by: GitHub <noreply@github.com> Co-authored-by: davchoo <davchoo@users.noreply.github.com> Co-authored-by: davchoo <4722249+davchoo@users.noreply.github.com> Co-authored-by: Unoqwy <pm@unoqwy.dev> Co-authored-by: RednedEpic <redned235@gmail.com> Co-authored-by: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Co-authored-by: rtm516 <rtm516@users.noreply.github.com>
2023-08-21 23:04:08 +00:00
// Needed for custom block mappings and custom skulls system
startGamePacket.getBlockProperties().addAll(this.blockMappings.getBlockProperties());
// See https://learn.microsoft.com/en-us/minecraft/creator/documents/experimentalfeaturestoggle for info on each experiment
// data_driven_items (Holiday Creator Features) is needed for blocks and items
startGamePacket.getExperiments().add(new ExperimentData("data_driven_items", true));
// Needed for block properties for states
startGamePacket.getExperiments().add(new ExperimentData("upcoming_creator_features", true));
// Needed for certain molang queries used in blocks and items
startGamePacket.getExperiments().add(new ExperimentData("experimental_molang_features", true));
2023-11-30 07:51:48 +00:00
// Required for experimental 1.21 features
startGamePacket.getExperiments().add(new ExperimentData("updateAnnouncedLive2023", true));
Add support for custom blocks and skulls (#3505) * Super cursed custom skulls custom block * Rename some stuff * Attempt to clean up some code * Remove skull translation events and define custom blocks for custom skulls Clean up skull block translation a bit * Auto generate skull resource pack Change `davchoo` to `geyser` in geometry * Add config options for custom blocks and custom skull blocks * Fix formatting and names for player skulls * Use block states more efficiently for custom skulls 21 block states vs 48 block states * Clean up custom block api a bit * Apply some suggestions from Camotoy * Move custom skull config stuff to its own file Custom skulls can now be added by username, uuid, and textures Move skull nbt stuff from requestTexturesFromUsername to SkullBlockEntityTranslator Add requestTexturesFromUUID * Update custom block nbt for v534 * Disable collision box & selection box when box is empty Fix incorrect collision names used in CustomBlockComponentsBuilder * Add custom block stuff to provider registry loader * More API changes Convert CustomBlockPermutation into a record Change materialInstances in CustomBlockComponents Builder to materialInstance Reuse box components in CustomSkull * Convert skull floor geometries into a template Should be easier to modify in needed in the future. * Crop and reorder skull textures to eliminate unused space Should reduce memory & storage usage for Bedrock clients * Revert "Crop and reorder skull textures to eliminate unused space" This reverts commit 15fd5353e1c2b8bcffd6d5986095a3646a6e7bf9. * Use identifier from CustomBlockData in SkullResourcePackManager * Fix isIncorrectHeldItem check for custom skull blocks Add defaultBlockState to CustomBlockData * Fix adding duplicate block states for custom blocks with 0 properties Remove defaultBlockState CustomBlockState field from GeyserCustomBlockData since it creates a circular reference * Add basis for overriding Bedrock block states Fix missing providers when used in GeyserDefineCustomBlocksEvent * Fix custom blocks in 1.19.50 * Decouple mappings from items * Decouple mappings from items * Null check * Move to CustomBlockRegistryPopulator * Remove name_hash from blocksTag/vanillaBlockStates Fixes creative inventory contents with custom blocks registered * Limit Bedrock versions to 1.19.40+ Custom blocks were released in 1.19.40 * Un-revert Crop and reorder skull textures to eliminate unused space Should reduce memory & storage usage for Bedrock clients Bug with top face flipping + per-face uv's was fixed in 1.19.40+ https://bugs.mojang.com/browse/MCPE-160073 Geometry is still offset by 0.5 to prevent lighting bugs * Add validation custom block components and s/lightFilter/lightDampening/ Also validate custom block names * Add display name component and add toggle for client block placing The display name component allows blocks to use other locale keys. placeAir will prevent the client from placing the default block state. * Begin parsing block mappings (still much to do!) * CustomBlockMapping stores block w/ all states * Mappings almost :/ * Ok now they work at least * Read most mapping components * Block mappings mostly done * Translate block item * Add docs for custom blocks * Add tags * More docs * Accidentally added name comp. * Fix collide box and warn for >16 props * add registerBlockItemOverride event + refactor * Inventory overrides for multistate bedrock blocks * Implement all remaining block components * Minor cleanup and javadocs * Update custom skull config example * Address @Camotoy's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix light_emission and light_dampening components * Remove redundant populate method and remove BLOCKS_JSON after last use * Fix inventories with block state overrides not opening * API event for skull blocks & let register via URL Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Use skin hash instead of URL Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @davchoo's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Rework MappingsReader_v1 to avoid passing maps around * Treat all properties as string properties There isn't a real need to check for boolean and int properties * Fix block registry scan in MappingsReader * Skin hashes can have less than 64 characters? * Include entry when logging exceptions from block mappings * Submodule Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix block break speeds thanks to @Camotoy Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Temporarily fix build on eclipse so I may work... Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Custom tool breakspeed by server; Closes #3348 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Account for if custom skulls are added on 1st run Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Initial framework for extended collision boxes Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Add some notes for the extended collision box impl Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * We have our extended collision registry Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Notes for me Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Extended collision boxes almost work Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Extended collision boxes actually work Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Consider all hitboxes in calculation Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * X is mirrored... Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Extended collision boxes are much improved Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Upstream fallout Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @Redned235's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Oops my bad that makes no sense :) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Ext collision box chunk translation optimization Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Trunc skinhash to 32 chars due to 80 char limit Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Use new transformation cmpnt vs legacy rotation Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * keep arr null on get extcolstor Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Properly handle if extended collision box is below Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Less ugly (realized it can go here) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Prevent 2x placement due to extended collision box Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Properly build on eclipse via indra Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Ensure enough bits in bedrockData for paletteIDs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix not needed whitespace Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update license headers to 2023 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Use release indra over snapshot Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Revert "Update license headers to 2023" This reverts commit f750059e8ee01dfd4e9f9d13d3bdb85eb90e1f74. * Account for collisions in chunk section y0 layer Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix extended collision @ air section bottom Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @davchoo's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @rtm516's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * More @rtm516's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @Camotoy's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update javadocs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @davchoo's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Lock extended collision to section Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Clear ext col even when air Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Let override vanilla items in creative inventory Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Avoid creating 12 HashSets for every overrided block state * Super minor nitpicks + Custom Skull NBT fix * Check custom skull is within Bedrock bounds Fixes NPE with custom skulls above y=320 or below y=-64 * Add static builder methods to match CustomItemData API * Upstream Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Initial API setup for modded blocks (no impl yet) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * More work on nonvanilla blocks (nonfunctional) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix compile Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update submodules Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Modded reg so far (not done) Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Add non-vanilla registration and fix a few bugs * Fixes for non-vanilla blocks * Remove import * CustomRegPop. go1st for now; must split for modded Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address silent change to geo component for blocks Co-Authored-By: Unoqwy <pm@unoqwy.dev> Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Seperate bedrock, vanilla, & nonvanilla block reg Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Single event Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Impl MaterialInstance as builder per @Redned235 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Added creative category enum & added some missing overrides (#7) * Add material instance to provider registry Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * oops Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix case of correctBedrockIdentifier not found Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Fix docs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address @Camotoy's review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address review from @davchoo Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Set namespace of custom blocks vs ident direct Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address review from @rtm516 Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * One more Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Remove rogue space * Geo component as builder Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * use super name Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Bump version Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --------- Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> Signed-off-by: GitHub <noreply@github.com> Co-authored-by: davchoo <davchoo@users.noreply.github.com> Co-authored-by: davchoo <4722249+davchoo@users.noreply.github.com> Co-authored-by: Unoqwy <pm@unoqwy.dev> Co-authored-by: RednedEpic <redned235@gmail.com> Co-authored-by: ImDaBigBoss <67973871+ImDaBigBoss@users.noreply.github.com> Co-authored-by: rtm516 <rtm516@users.noreply.github.com>
2023-08-21 23:04:08 +00:00
startGamePacket.setVanillaVersion("*");
startGamePacket.setInventoriesServerAuthoritative(true);
startGamePacket.setServerEngine(""); // Do we want to fill this in?
startGamePacket.setPlayerPropertyData(NbtMap.EMPTY);
startGamePacket.setWorldTemplateId(UUID.randomUUID());
2022-08-07 16:09:54 +00:00
startGamePacket.setChatRestrictionLevel(ChatRestrictionLevel.NONE);
2022-10-30 00:23:21 +00:00
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT);
startGamePacket.setRewindHistorySize(0);
startGamePacket.setServerAuthoritativeBlockBreaking(false);
upstream.sendPacket(startGamePacket);
}
/**
* @return the next Bedrock item network ID to use for a new item
*/
public int getNextItemNetId() {
return itemNetId.getAndIncrement();
}
public void confirmTeleport(Vector3d position) {
if (unconfirmedTeleport == null) {
return;
}
if (unconfirmedTeleport.canConfirm(position)) {
unconfirmedTeleport = null;
return;
}
// Resend the teleport every few packets until Bedrock responds
unconfirmedTeleport.incrementUnconfirmedFor();
if (unconfirmedTeleport.shouldResend()) {
unconfirmedTeleport.resetUnconfirmedFor();
geyser.getLogger().debug("Resending teleport " + unconfirmedTeleport.getTeleportConfirmId());
getPlayerEntity().moveAbsolute(Vector3f.from(unconfirmedTeleport.getX(), unconfirmedTeleport.getY(), unconfirmedTeleport.getZ()),
unconfirmedTeleport.getYaw(), unconfirmedTeleport.getPitch(), playerEntity.isOnGround(), true);
}
}
/**
* Queue a packet to be sent to player.
*
* @param packet the bedrock packet from the NukkitX protocol lib
*/
public void sendUpstreamPacket(BedrockPacket packet) {
upstream.sendPacket(packet);
}
/**
* Send a packet immediately to the player.
*
* @param packet the bedrock packet from the NukkitX protocol lib
*/
public void sendUpstreamPacketImmediately(BedrockPacket packet) {
upstream.sendPacketImmediately(packet);
}
/**
* Send a packet to the remote server if in the game state.
*
* @param packet the java edition packet from MCProtocolLib
*/
public void sendDownstreamGamePacket(Packet packet) {
sendDownstreamPacket(packet, ProtocolState.GAME);
}
/**
* Send a packet to the remote server if in the login state.
*
* @param packet the java edition packet from MCProtocolLib
*/
public void sendDownstreamLoginPacket(Packet packet) {
sendDownstreamPacket(packet, ProtocolState.LOGIN);
}
/**
* Send a packet to the remote server if in the specified state.
*
* @param packet the java edition packet from MCProtocolLib
* @param intendedState the state the client should be in
*/
public void sendDownstreamPacket(Packet packet, ProtocolState intendedState) {
// protocol can be null when we're not yet logged in (online auth)
if (protocol == null) {
if (geyser.getConfig().isDebugMode()) {
geyser.getLogger().debug("Tried to send downstream packet with no downstream session!");
Thread.dumpStack();
}
return;
}
if (protocol.getState() != intendedState) {
geyser.getLogger().debug("Tried to send " + packet.getClass().getSimpleName() + " packet while not in " + intendedState.name() + " state");
return;
}
sendDownstreamPacket(packet);
}
/**
* Send a packet to the remote server.
*
* @param packet the java edition packet from MCProtocolLib
*/
public void sendDownstreamPacket(Packet packet) {
if (!closed && this.downstream != null) {
2022-10-30 00:23:21 +00:00
Channel channel = this.downstream.getSession().getChannel();
if (channel == null) {
// Channel is only null before the connection has initialized
geyser.getLogger().warning("Tried to send a packet to the Java server too early!");
if (geyser.getConfig().isDebugMode()) {
Thread.dumpStack();
}
return;
}
EventLoop eventLoop = channel.eventLoop();
if (eventLoop.inEventLoop()) {
sendDownstreamPacket0(packet);
} else {
eventLoop.execute(() -> sendDownstreamPacket0(packet));
}
}
}
private void sendDownstreamPacket0(Packet packet) {
ProtocolState state = protocol.getState();
if (state == ProtocolState.GAME || state == ProtocolState.CONFIGURATION || packet.getClass() == ServerboundCustomQueryAnswerPacket.class) {
2022-10-30 00:23:21 +00:00
downstream.sendPacket(packet);
} else {
geyser.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
}
}
/**
* Update the cached value for the reduced debug info gamerule.
* If enabled, also hides the player's coordinates.
*
* @param value The new value for reducedDebugInfo
*/
public void setReducedDebugInfo(boolean value) {
reducedDebugInfo = value;
// Set the showCoordinates data. This is done because updateShowCoordinates() uses this gamerule as a variable.
preferencesCache.updateShowCoordinates();
}
/**
* Changes the daylight cycle gamerule on the client
* This is used in the login screen along-side normal usage
*
* @param doCycle If the cycle should continue
*/
public void setDaylightCycle(boolean doCycle) {
sendGameRule("dodaylightcycle", doCycle);
// Save the value so we don't have to constantly send a daylight cycle gamerule update
this.daylightCycle = doCycle;
}
/**
* Send a gamerule value to the client
*
* @param gameRule The gamerule to send
* @param value The value of the gamerule
*/
public void sendGameRule(String gameRule, Object value) {
GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket();
gameRulesChangedPacket.getGameRules().add(new GameRuleData<>(gameRule, value));
upstream.sendPacket(gameRulesChangedPacket);
}
/**
* Checks if the given session's player has a permission
*
* @param permission The permission node to check
* @return true if the player has the requested permission, false if not
*/
@Override
public boolean hasPermission(String permission) {
return geyser.getWorldManager().hasPermission(this, permission);
}
2022-07-10 17:33:39 +00:00
private static final Ability[] USED_ABILITIES = Ability.values();
/**
* Send an AdventureSettingsPacket to the client with the latest flags
*/
public void sendAdventureSettings() {
2022-07-10 17:33:39 +00:00
long bedrockId = playerEntity.getGeyserId();
// Set command permission if OP permission level is high enough
// This allows mobile players access to a GUI for doing commands. The commands there do not change above OPERATOR
// and all commands there are accessible with OP permission level 2
2022-10-30 00:23:21 +00:00
CommandPermission commandPermission = opPermissionLevel >= 2 ? CommandPermission.GAME_DIRECTORS : CommandPermission.ANY;
// Required to make command blocks destroyable
2022-07-10 17:33:39 +00:00
PlayerPermission playerPermission = opPermissionLevel >= 2 ? PlayerPermission.OPERATOR : PlayerPermission.MEMBER;
// Update the noClip and worldImmutable values based on the current gamemode
boolean spectator = gameMode == GameMode.SPECTATOR;
boolean worldImmutable = gameMode == GameMode.ADVENTURE || spectator;
2022-11-29 02:53:17 +00:00
UpdateAdventureSettingsPacket adventureSettingsPacket = new UpdateAdventureSettingsPacket();
adventureSettingsPacket.setNoMvP(false);
adventureSettingsPacket.setNoPvM(false);
adventureSettingsPacket.setImmutableWorld(worldImmutable);
adventureSettingsPacket.setShowNameTags(false);
adventureSettingsPacket.setAutoJump(true);
sendUpstreamPacket(adventureSettingsPacket);
2022-07-10 17:33:39 +00:00
2022-11-29 02:53:17 +00:00
UpdateAbilitiesPacket updateAbilitiesPacket = new UpdateAbilitiesPacket();
updateAbilitiesPacket.setUniqueEntityId(bedrockId);
updateAbilitiesPacket.setCommandPermission(commandPermission);
updateAbilitiesPacket.setPlayerPermission(playerPermission);
2022-07-10 17:33:39 +00:00
2022-11-29 02:53:17 +00:00
AbilityLayer abilityLayer = new AbilityLayer();
Set<Ability> abilities = abilityLayer.getAbilityValues();
if (canFly) {
2022-11-29 02:53:17 +00:00
abilities.add(Ability.MAY_FLY);
2022-07-10 17:33:39 +00:00
}
2022-11-29 02:53:17 +00:00
// Default stuff we have to fill in
abilities.add(Ability.BUILD);
abilities.add(Ability.MINE);
// Needed so you can drop items
abilities.add(Ability.DOORS_AND_SWITCHES);
// Required for lecterns to work (likely started around 1.19.10; confirmed on 1.19.70)
abilities.add(Ability.OPEN_CONTAINERS);
2022-11-29 02:53:17 +00:00
if (gameMode == GameMode.CREATIVE) {
// Needed so the client doesn't attempt to take away items
abilities.add(Ability.INSTABUILD);
}
2022-07-10 17:33:39 +00:00
if (commandPermission == CommandPermission.GAME_DIRECTORS) {
2022-11-29 02:53:17 +00:00
// Fixes a bug? since 1.19.11 where the player can change their gamemode in Bedrock settings and
// a packet is not sent to the server.
// https://github.com/GeyserMC/Geyser/issues/3191
abilities.add(Ability.OPERATOR_COMMANDS);
}
if (flying || spectator) {
if (spectator && !flying) {
// We're "flying locked" in this gamemode
flying = true;
ServerboundPlayerAbilitiesPacket abilitiesPacket = new ServerboundPlayerAbilitiesPacket(true);
sendDownstreamGamePacket(abilitiesPacket);
}
2022-11-29 02:53:17 +00:00
abilities.add(Ability.FLYING);
}
if (spectator) {
AbilityLayer spectatorLayer = new AbilityLayer();
spectatorLayer.setLayerType(AbilityLayer.Type.SPECTATOR);
// Setting all abilitySet causes the zoom issue... BDS only sends these, so ig we will too
Set<Ability> abilitySet = spectatorLayer.getAbilitiesSet();
abilitySet.add(Ability.BUILD);
abilitySet.add(Ability.MINE);
abilitySet.add(Ability.DOORS_AND_SWITCHES);
abilitySet.add(Ability.OPEN_CONTAINERS);
abilitySet.add(Ability.ATTACK_PLAYERS);
abilitySet.add(Ability.ATTACK_MOBS);
abilitySet.add(Ability.INVULNERABLE);
abilitySet.add(Ability.FLYING);
abilitySet.add(Ability.MAY_FLY);
abilitySet.add(Ability.INSTABUILD);
abilitySet.add(Ability.NO_CLIP);
Set<Ability> abilityValues = spectatorLayer.getAbilityValues();
abilityValues.add(Ability.INVULNERABLE);
abilityValues.add(Ability.FLYING);
abilityValues.add(Ability.NO_CLIP);
updateAbilitiesPacket.getAbilityLayers().add(spectatorLayer);
}
abilityLayer.setLayerType(AbilityLayer.Type.BASE);
2022-11-29 02:53:17 +00:00
abilityLayer.setFlySpeed(flySpeed);
// https://github.com/GeyserMC/Geyser/issues/3139 as of 1.19.10
abilityLayer.setWalkSpeed(walkSpeed == 0f ? 0.01f : walkSpeed);
Collections.addAll(abilityLayer.getAbilitiesSet(), USED_ABILITIES);
2022-11-29 02:53:17 +00:00
updateAbilitiesPacket.getAbilityLayers().add(abilityLayer);
sendUpstreamPacket(updateAbilitiesPacket);
}
private int getRenderDistance() {
if (clientRenderDistance != -1) {
// The client has sent a render distance
return clientRenderDistance;
} else if (serverRenderDistance != -1) {
// only known once ClientboundLoginPacket is received
return serverRenderDistance;
}
return 2; // unfortunate default until we got more info
}
// We need to send our skin parts to the server otherwise java sees us with no hat, jacket etc
private static final List<SkinPart> SKIN_PARTS = Arrays.asList(SkinPart.values());
/**
* Send a packet to the server to indicate client render distance, locale, skin parts, and hand preference.
*/
public void sendJavaClientSettings() {
ServerboundClientInformationPacket clientSettingsPacket = new ServerboundClientInformationPacket(locale(),
getRenderDistance(), ChatVisibility.FULL, true, SKIN_PARTS,
HandPreference.RIGHT_HAND, false, true);
sendDownstreamPacket(clientSettingsPacket);
}
/**
* Used for updating statistic values since we only get changes from the server
*
* @param statistics Updated statistics values
*/
2022-08-11 23:01:26 +00:00
public void updateStatistics(@NonNull Object2IntMap<Statistic> statistics) {
if (this.statistics.isEmpty()) {
// Initialize custom statistics to 0, so that they appear in the form
for (CustomStatistic customStatistic : CustomStatistic.values()) {
this.statistics.put(customStatistic, 0);
}
}
this.statistics.putAll(statistics);
}
public void refreshEmotes(List<UUID> emotes) {
this.emotes.addAll(emotes);
2021-11-22 19:52:26 +00:00
for (GeyserSession player : geyser.getSessionManager().getSessions().values()) {
List<UUID> pieces = new ArrayList<>();
for (UUID piece : emotes) {
if (!player.getEmotes().contains(piece)) {
pieces.add(piece);
}
player.getEmotes().add(piece);
}
EmoteListPacket emoteList = new EmoteListPacket();
emoteList.setRuntimeEntityId(player.getPlayerEntity().getGeyserId());
emoteList.getPieceIds().addAll(pieces);
player.sendUpstreamPacket(emoteList);
}
}
public boolean canUseCommandBlocks() {
return instabuild && opPermissionLevel >= 2;
}
public void playSoundEvent(SoundEvent sound, Vector3f position) {
LevelSoundEvent2Packet packet = new LevelSoundEvent2Packet();
packet.setPosition(position);
packet.setSound(sound);
packet.setIdentifier(":");
packet.setExtraData(-1);
sendUpstreamPacket(packet);
}
2022-06-05 18:12:36 +00:00
public float getEyeHeight() {
return switch (pose) {
case SNEAKING -> 1.27f;
case SWIMMING,
FALL_FLYING, // Elytra
SPIN_ATTACK -> 0.4f; // Trident spin attack
case SLEEPING -> 0.2f;
default -> EntityDefinitions.PLAYER.offset();
};
}
2022-08-11 23:01:26 +00:00
@Override
public @NonNull String bedrockUsername() {
2022-08-11 23:01:26 +00:00
return authData.name();
}
@Override
public @MonotonicNonNull String javaUsername() {
return playerEntity.getUsername();
}
@Override
public UUID javaUuid() {
return playerEntity.getUuid();
}
@Override
public @NonNull String xuid() {
2022-08-11 23:01:26 +00:00
return authData.xuid();
}
@Override
public @NonNull String version() {
return clientData.getGameVersion();
}
@Override
public @NonNull BedrockPlatform platform() {
return BedrockPlatform.values()[clientData.getDeviceOs().ordinal()]; //todo
}
@Override
public @NonNull String languageCode() {
return locale();
}
@Override
public @NonNull UiProfile uiProfile() {
return UiProfile.values()[clientData.getUiProfile().ordinal()]; //todo
}
@Override
public @NonNull InputMode inputMode() {
return InputMode.values()[clientData.getCurrentInputMode().ordinal()]; //todo
}
@Override
public boolean isLinked() {
return false; //todo
}
@SuppressWarnings("ConstantConditions") // Need to enforce the parameter annotations
@Override
public boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port) {
if (address == null || address.isBlank()) {
throw new IllegalArgumentException("Server address cannot be null or blank");
} else if (port < 0 || port > 65535) {
throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port);
}
TransferPacket transferPacket = new TransferPacket();
transferPacket.setAddress(address);
transferPacket.setPort(port);
sendUpstreamPacket(transferPacket);
return true;
}
2023-01-25 16:05:04 +00:00
@Override
public @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId) {
return entities().entityByJavaId(javaId);
}
@Override
public void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId) {
entities().showEmote(emoter, emoteId);
}
public void lockInputs(boolean camera, boolean movement) {
UpdateClientInputLocksPacket packet = new UpdateClientInputLocksPacket();
final int cameraOffset = 1 << 1;
final int movementOffset = 1 << 2;
int result = 0;
if (camera) {
result |= cameraOffset;
}
if (movement) {
result |= movementOffset;
}
packet.setLockComponentData(result);
packet.setServerPosition(this.playerEntity.getPosition());
sendUpstreamPacket(packet);
}
@Override
public @NonNull CameraData camera() {
return this.cameraData;
}
@Override
public @NonNull EntityData entities() {
return this.entityData;
}
@Override
public void shakeCamera(float intensity, float duration, @NonNull CameraShake type) {
this.cameraData.shakeCamera(intensity, duration, type);
}
@Override
public void stopCameraShake() {
this.cameraData.stopCameraShake();
}
@Override
public void sendFog(String... fogNameSpaces) {
this.cameraData.sendFog(fogNameSpaces);
}
@Override
public void removeFog(String... fogNameSpaces) {
this.cameraData.removeFog(fogNameSpaces);
}
@Override
public @NonNull Set<String> fogEffects() {
return this.cameraData.fogEffects();
}
public void addCommandEnum(String name, String enums) {
2023-01-25 16:05:04 +00:00
softEnumPacket(name, SoftEnumUpdateType.ADD, enums);
}
public void removeCommandEnum(String name, String enums) {
2023-01-25 16:05:04 +00:00
softEnumPacket(name, SoftEnumUpdateType.REMOVE, enums);
}
private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) {
// There is no need to send command enums if command suggestions are disabled
if (!this.geyser.getConfig().isCommandSuggestions()) {
return;
}
2023-01-25 16:05:04 +00:00
UpdateSoftEnumPacket packet = new UpdateSoftEnumPacket();
packet.setType(type);
2023-04-08 20:19:42 +00:00
packet.setSoftEnum(new CommandEnumData(name, Collections.singletonMap(enums, Collections.emptySet()), true));
2023-01-25 16:05:04 +00:00
sendUpstreamPacket(packet);
}
}