diff --git a/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraShake.java b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraShake.java
new file mode 100644
index 000000000..18bafb428
--- /dev/null
+++ b/api/src/main/java/org/geysermc/geyser/api/bedrock/camera/CameraShake.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019-2023 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.api.bedrock.camera;
+
+public enum CameraShake {
+ POSITIONAL,
+ ROTATIONAL;
+}
diff --git a/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java
index 8d695bf02..7094812a0 100644
--- a/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java
+++ b/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java
@@ -29,10 +29,12 @@ import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.api.connection.Connection;
+import org.geysermc.geyser.api.bedrock.camera.CameraShake;
import org.geysermc.geyser.api.command.CommandSource;
import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
+import java.util.Set;
import java.util.concurrent.CompletableFuture;
/**
@@ -47,9 +49,50 @@ public interface GeyserConnection extends Connection, CommandSource {
CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
/**
+ * Displays a player entity as emoting to this client.
*
* @param emoter the player entity emoting.
- * @param emoteId the emote ID to send to the client.
+ * @param emoteId the emote ID to send to this client.
*/
void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId);
+
+ /**
+ * Shakes the client's camera.
+ * If the camera is already shaking with the same {@link CameraShake} type, then the additional intensity
+ * will be layered on top of the existing intensity, with their own distinct durations.
+ * If the existing shake type is different and the new intensity/duration are not positive, the existing shake only
+ * switches to the new type. Otherwise, the existing shake is completely overridden.
+ *
+ * @param intensity the intensity of the shake. The client has a maximum total intensity of 4.
+ * @param duration the time in seconds that the shake will occur for
+ * @param type the type of shake
+ */
+ void shakeCamera(float intensity, float duration, @NonNull CameraShake type);
+
+ /**
+ * Stops all camera shake of any type.
+ */
+ void stopCameraShake();
+
+ /**
+ * Adds the given fog IDs to the fog cache, then sends all fog IDs in the cache to the client.
+ *
+ * Fog IDs can be found here
+ *
+ * @param fogNameSpaces the fog IDs to add. If empty, the existing cached IDs will still be sent.
+ */
+ void sendFog(String... fogNameSpaces);
+
+ /**
+ * Removes the given fog IDs from the fog cache, then sends all fog IDs in the cache to the client.
+ *
+ * @param fogNameSpaces the fog IDs to remove. If empty, all fog IDs will be removed.
+ */
+ void removeFog(String... fogNameSpaces);
+
+ /**
+ * Returns an immutable copy of all fog affects currently applied to this client.
+ */
+ @NonNull
+ Set fogEffects();
}
diff --git a/build.gradle.kts b/build.gradle.kts
index c64667ad2..d8fe1c769 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -6,7 +6,7 @@ plugins {
allprojects {
group = "org.geysermc.geyser"
- version = "2.1.1-SNAPSHOT"
+ version = "2.1.2-SNAPSHOT"
description = "Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers."
tasks.withType {
diff --git a/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java b/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java
index 78c6b2c6a..250c0f7a4 100644
--- a/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java
+++ b/core/src/main/java/org/geysermc/geyser/level/BedrockDimension.java
@@ -35,7 +35,7 @@ package org.geysermc.geyser.level;
* @param doUpperHeightWarn whether to warn in the console if the Java dimension height exceeds Bedrock's.
*/
public record BedrockDimension(int minY, int height, boolean doUpperHeightWarn) {
- public static BedrockDimension OVERWORLD = new BedrockDimension(-64, 384, true);
- public static BedrockDimension THE_NETHER = new BedrockDimension(0, 128, false);
- public static BedrockDimension THE_END = new BedrockDimension(0, 256, true);
+ public static final BedrockDimension OVERWORLD = new BedrockDimension(-64, 384, true);
+ public static final BedrockDimension THE_NETHER = new BedrockDimension(0, 128, false);
+ public static final BedrockDimension THE_END = new BedrockDimension(0, 256, true);
}
diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
index 73dd253f3..dba4bd112 100644
--- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
+++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
@@ -98,6 +98,7 @@ import org.cloudburstmc.protocol.common.util.OptionalBoolean;
import org.geysermc.api.util.BedrockPlatform;
import org.geysermc.api.util.InputMode;
import org.geysermc.api.util.UiProfile;
+import org.geysermc.geyser.api.bedrock.camera.CameraShake;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.cumulus.form.Form;
import org.geysermc.cumulus.form.util.FormBuilder;
@@ -533,7 +534,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter
private boolean waitingForStatistics = false;
- private final Set fogNameSpaces = new HashSet<>();
+ /**
+ * All fog effects that are currently applied to the client.
+ */
+ private final Set appliedFog = new HashSet<>();
private final Set emotes;
@@ -1828,38 +1832,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
}
}
- /**
- * Send the following fog IDs, as well as the cached ones, to the client.
- *
- * Fog IDs can be found here:
- * https://wiki.bedrock.dev/documentation/fog-ids.html
- *
- * @param fogNameSpaces the fog ids to add
- */
- public void sendFog(String... fogNameSpaces) {
- this.fogNameSpaces.addAll(Arrays.asList(fogNameSpaces));
-
- PlayerFogPacket packet = new PlayerFogPacket();
- packet.getFogStack().addAll(this.fogNameSpaces);
- sendUpstreamPacket(packet);
- }
-
- /**
- * Removes the following fog IDs from the client and the cache.
- *
- * @param fogNameSpaces the fog ids to remove
- */
- public void removeFog(String... fogNameSpaces) {
- if (fogNameSpaces.length == 0) {
- this.fogNameSpaces.clear();
- } else {
- this.fogNameSpaces.removeAll(Arrays.asList(fogNameSpaces));
- }
- PlayerFogPacket packet = new PlayerFogPacket();
- packet.getFogStack().addAll(this.fogNameSpaces);
- sendUpstreamPacket(packet);
- }
-
public boolean canUseCommandBlocks() {
return instabuild && opPermissionLevel >= 2;
}
@@ -1971,6 +1943,54 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
sendUpstreamPacket(packet);
}
+ @Override
+ public void shakeCamera(float intensity, float duration, @NonNull CameraShake type) {
+ CameraShakePacket packet = new CameraShakePacket();
+ packet.setIntensity(intensity);
+ packet.setDuration(duration);
+ packet.setShakeType(type == CameraShake.POSITIONAL ? CameraShakeType.POSITIONAL : CameraShakeType.ROTATIONAL);
+ packet.setShakeAction(CameraShakeAction.ADD);
+ sendUpstreamPacket(packet);
+ }
+
+ @Override
+ public void stopCameraShake() {
+ CameraShakePacket packet = new CameraShakePacket();
+ // CameraShakeAction.STOP removes all types regardless of the given type, but regardless it can't be null
+ packet.setShakeType(CameraShakeType.POSITIONAL);
+ packet.setShakeAction(CameraShakeAction.STOP);
+ sendUpstreamPacket(packet);
+ }
+
+ @Override
+ public void sendFog(String... fogNameSpaces) {
+ Collections.addAll(this.appliedFog, fogNameSpaces);
+
+ PlayerFogPacket packet = new PlayerFogPacket();
+ packet.getFogStack().addAll(this.appliedFog);
+ sendUpstreamPacket(packet);
+ }
+
+ @Override
+ public void removeFog(String... fogNameSpaces) {
+ if (fogNameSpaces.length == 0) {
+ this.appliedFog.clear();
+ } else {
+ for (String id : fogNameSpaces) {
+ this.appliedFog.remove(id);
+ }
+ }
+ PlayerFogPacket packet = new PlayerFogPacket();
+ packet.getFogStack().addAll(this.appliedFog);
+ sendUpstreamPacket(packet);
+ }
+
+ @Override
+ public @NonNull Set fogEffects() {
+ // Use a copy so that sendFog/removeFog can be called while iterating the returned set (avoid CME)
+ return Set.copyOf(this.appliedFog);
+ }
+
public void addCommandEnum(String name, String enums) {
softEnumPacket(name, SoftEnumUpdateType.ADD, enums);
}
diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java
index 3d9f08ec7..9f7fd4f40 100644
--- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java
+++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java
@@ -148,7 +148,7 @@ public class JavaLoginTranslator extends PacketTranslator