3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-19 14:30:17 +01:00

Feature: Camera/Input locking API (#4332)

Adds API methods to control player cameras - including fancy transitions,  color fades, or simple input locks.
Dieser Commit ist enthalten in:
chris 2024-01-31 11:21:06 +01:00 committet von GitHub
Ursprung 07150db592
Commit f555dc0a92
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
36 geänderte Dateien mit 1523 neuen und 149 gelöschten Zeilen

Datei anzeigen

@ -4,4 +4,5 @@ plugins {
dependencies { dependencies {
api(libs.base.api) api(libs.base.api)
api(libs.math)
} }

Datei anzeigen

@ -0,0 +1,148 @@
/*
* 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;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.connection.GeyserConnection;
import java.util.Set;
import java.util.UUID;
/**
* This interface holds all the methods that relate to a client's camera.
* Can be accessed through {@link GeyserConnection#camera()}.
*/
public interface CameraData {
/**
* Sends a camera fade instruction to the client.
* If an existing camera fade is already in progress, the current fade will be prolonged.
* Can be built using {@link CameraFade.Builder}.
* To stop a fade early, use {@link #clearCameraInstructions()}.
*
* @param fade the camera fade instruction to send
*/
void sendCameraFade(@NonNull CameraFade fade);
/**
* Sends a camera position instruction to the client.
* If an existing camera movement is already in progress,
* the final camera position will be the one of the latest instruction, and
* the (optional) camera fade will be added on top of the existing fade.
* Can be built using {@link CameraPosition.Builder}.
* To stop reset the camera position/stop ongoing instructions, use {@link #clearCameraInstructions()}.
*
* @param position the camera position instruction to send
*/
void sendCameraPosition(@NonNull CameraPosition position);
/**
* Stops all sent camera instructions (fades, movements, and perspective locks).
* This will not stop any camera shakes/input locks/fog effects, use the respective methods for those.
*/
void clearCameraInstructions();
/**
* Forces a {@link CameraPerspective} on the client. This will prevent the client
* from changing their camera perspective until it is unlocked via {@link #clearCameraInstructions()}.
* <p>
* Note: You cannot force a client into a free camera perspective with this method.
* To do that, send a {@link CameraPosition} via {@link #sendCameraPosition(CameraPosition)} - it requires a set position
* instead of being relative to the player.
*
* @param perspective the {@link CameraPerspective} to force
*/
void forceCameraPerspective(@NonNull CameraPerspective perspective);
/**
* Gets the client's current {@link CameraPerspective}, if one is currently forced.
* This will return {@code null} if the client is not currently forced into a perspective.
* If a perspective is forced, the client will not be able to change their camera perspective until it is unlocked.
*
* @return the forced perspective, or {@code null} if none is forced
*/
@Nullable CameraPerspective forcedCameraPerspective();
/**
* Shakes the client's camera.
* <p>
* 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.<br>
* 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 shakes 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.
* <p>
* Fog IDs can be found <a href="https://wiki.bedrock.dev/documentation/fog-ids.html">here</a>
*
* @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<String> fogEffects();
/**
* (Un)locks the client's camera, so that they cannot look around.
* To ensure the camera is only unlocked when all locks are released, you must supply
* a UUID when using method, and use the same UUID to unlock the camera.
*
* @param lock whether to lock the camera
* @param owner the owner of the lock, represented with a UUID
* @return if the camera is locked after this method call
*/
boolean lockCamera(boolean lock, @NonNull UUID owner);
/**
* Returns whether the client's camera is locked.
*
* @return whether the camera is currently locked
*/
boolean isCameraLocked();
}

Datei anzeigen

@ -0,0 +1,77 @@
/*
* 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;
/**
* These are all the easing types that can be used when sending a {@link CameraPosition} instruction.
* When using these, the client won't teleport to the new camera position, but instead transition to it.
* <p>
* See <a href="https://easings.net/">https://easings.net/</a> for more information.
*/
public enum CameraEaseType {
LINEAR("linear"),
SPRING("spring"),
EASE_IN_SINE("in_sine"),
EASE_OUT_SINE("out_sine"),
EASE_IN_OUT_SINE("in_out_sine"),
EASE_IN_QUAD("in_quad"),
EASE_OUT_QUAD("out_quad"),
EASE_IN_OUT_QUAD("in_out_quad"),
EASE_IN_CUBIC("in_cubic"),
EASE_OUT_CUBIC("out_cubic"),
EASE_IN_OUT_CUBIC("in_out_cubic"),
EASE_IN_QUART("in_quart"),
EASE_OUT_QUART("out_quart"),
EASE_IN_OUT_QUART("in_out_quart"),
EASE_IN_QUINT("in_quint"),
EASE_OUT_QUINT("out_quint"),
EASE_IN_OUT_QUINT("in_out_quint"),
EASE_IN_EXPO("in_expo"),
EASE_OUT_EXPO("out_expo"),
EASE_IN_OUT_EXPO("in_out_expo"),
EASE_IN_CIRC("in_circ"),
EASE_OUT_CIRC("out_circ"),
EASE_IN_OUT_CIRC("in_out_circ"),
EASE_IN_BACK("in_back"),
EASE_OUT_BACK("out_back"),
EASE_IN_OUT_BACK("in_out_back"),
EASE_IN_ELASTIC("in_elastic"),
EASE_OUT_ELASTIC("out_elastic"),
EASE_IN_OUT_ELASTIC("in_out_elastic"),
EASE_IN_BOUNCE("in_bounce"),
EASE_OUT_BOUNCE("out_bounce"),
EASE_IN_OUT_BOUNCE("in_out_bounce");
private final String id;
CameraEaseType(String id) {
this.id = id;
}
public String id() {
return this.id;
}
}

Datei anzeigen

@ -0,0 +1,94 @@
/*
* 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;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.common.value.qual.IntRange;
import org.geysermc.geyser.api.GeyserApi;
import java.awt.Color;
/**
* Represents a coloured fade overlay on the camera.
* <p>
* Can be sent with {@link CameraData#sendCameraFade(CameraFade)}, or with a {@link CameraPosition} instruction.
*/
public interface CameraFade {
/**
* Gets the color overlay of the camera.
* Bedrock uses an RGB color system.
*
* @return the color of the fade
*/
@NonNull Color color();
/**
* Gets the seconds it takes to fade in.
* All fade times combined must take at least 0.5 seconds, and at most 30 seconds.
*
* @return the seconds it takes to fade in
*/
float fadeInSeconds();
/**
* Gets the seconds the overlay is held.
* All fade times combined must take at least 0.5 seconds, and at most 30 seconds.
*
* @return the seconds the overlay is held
*/
float fadeHoldSeconds();
/**
* Gets the seconds it takes to fade out.
* All fade times combined must take at least 0.5 seconds, and at most 30 seconds.
*
* @return the seconds it takes to fade out
*/
float fadeOutSeconds();
/**
* Creates a Builder for CameraFade
*
* @return a CameraFade Builder
*/
static CameraFade.Builder builder() {
return GeyserApi.api().provider(CameraFade.Builder.class);
}
interface Builder {
Builder color(@NonNull Color color);
Builder fadeInSeconds(@IntRange(from = 0, to = 10) float fadeInSeconds);
Builder fadeHoldSeconds(@IntRange(from = 0, to = 10) float fadeHoldSeconds);
Builder fadeOutSeconds(@IntRange(from = 0, to = 10) float fadeOutSeconds);
CameraFade build();
}
}

Datei anzeigen

@ -0,0 +1,48 @@
/*
* 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;
/**
* Represents a camera perspective that a player's camera can take.
* All perspectives except for {@link #FREE} are locked to the player's head,
* and are therefore relative to the player's position and rotation.
*/
public enum CameraPerspective {
FIRST_PERSON("minecraft:first_person"),
FREE("minecraft:free"),
THIRD_PERSON("minecraft:third_person"),
THIRD_PERSON_FRONT("minecraft:third_person_front");
private final String id;
CameraPerspective(String id) {
this.id = id;
}
public String id() {
return this.id;
}
}

Datei anzeigen

@ -0,0 +1,150 @@
/*
* 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;
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.Vector3f;
import org.geysermc.geyser.api.GeyserApi;
/**
* This interface represents a camera position instruction. Can be built with the {@link #builder()}.
* <p>
* Any camera position instruction pins the client camera to a specific position and rotation.
* You can set {@link CameraEaseType} to ensure a smooth transition that will last {@link #easeSeconds()} seconds.
* A {@link CameraFade} can also be sent, which will transition the player to a coloured transition during the transition.
* <p>
* Use {@link CameraData#sendCameraPosition(CameraPosition)} to send such an instruction to any connection.
*/
public interface CameraPosition {
/**
* Gets the camera's position.
*
* @return camera position vector
*/
@NonNull Vector3f position();
/**
* Gets the {@link CameraEaseType} of the camera.
* If not set, there is no easing.
*
* @return camera ease type
*/
@Nullable CameraEaseType easeType();
/**
* Gets the {@link CameraFade} to be sent along the camera position instruction.
* If set, they will run at once.
*
* @return camera fade, or null if not present
*/
@Nullable CameraFade cameraFade();
/**
* Gets the easing duration of the camera, in seconds.
* Is only used if a {@link CameraEaseType} is set.
*
* @return camera easing duration in seconds
*/
float easeSeconds();
/**
* Gets the x-axis rotation of the camera.
* To prevent the camera from being upside down, Bedrock limits the range to -90 to 90.
* Will be overridden if {@link #facingPosition()} is set.
*
* @return camera x-axis rotation
*/
@IntRange(from = -90, to = 90) int rotationX();
/**
* Gets the y-axis rotation of the camera.
* Will be overridden if {@link #facingPosition()} is set.
*
* @return camera y-axis rotation
*/
int rotationY();
/**
* Gets the position that the camera is facing.
* Can be used instead of manually setting rotation values.
* <p>
* If set, the rotation values set via {@link #rotationX()} and {@link #rotationY()} will be ignored.
*
* @return Camera's facing position
*/
@Nullable Vector3f facingPosition();
/**
* Controls whether player effects, such as night vision or blindness, should be rendered on the camera.
* Defaults to false.
*
* @return whether player effects should be rendered
*/
boolean renderPlayerEffects();
/**
* Controls whether the player position should be used for directional audio.
* If false, the camera position will be used instead.
*
* @return whether the players position should be used for directional audio
*/
boolean playerPositionForAudio();
/**
* Creates a Builder for CameraPosition
*
* @return a CameraPosition Builder
*/
static CameraPosition.Builder builder() {
return GeyserApi.api().provider(CameraPosition.Builder.class);
}
interface Builder {
Builder cameraFade(@Nullable CameraFade cameraFade);
Builder renderPlayerEffects(boolean renderPlayerEffects);
Builder playerPositionForAudio(boolean playerPositionForAudio);
Builder easeType(@Nullable CameraEaseType easeType);
Builder easeSeconds(float easeSeconds);
Builder position(@NonNull Vector3f position);
Builder rotationX(@IntRange(from = -90, to = 90) int rotationX);
Builder rotationY(int rotationY);
Builder facingPosition(@Nullable Vector3f facingPosition);
CameraPosition build();
}
}

Datei anzeigen

@ -25,6 +25,9 @@
package org.geysermc.geyser.api.bedrock.camera; package org.geysermc.geyser.api.bedrock.camera;
/**
* Represents a camera shake instruction. Can be sent in {@link CameraData#shakeCamera(float, float, CameraShake)}
*/
public enum CameraShake { public enum CameraShake {
POSITIONAL, POSITIONAL,
ROTATIONAL ROTATIONAL

Datei anzeigen

@ -29,8 +29,10 @@ import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.api.connection.Connection; import org.geysermc.api.connection.Connection;
import org.geysermc.geyser.api.bedrock.camera.CameraData;
import org.geysermc.geyser.api.bedrock.camera.CameraShake; import org.geysermc.geyser.api.bedrock.camera.CameraShake;
import org.geysermc.geyser.api.command.CommandSource; import org.geysermc.geyser.api.command.CommandSource;
import org.geysermc.geyser.api.entity.EntityData;
import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
@ -41,10 +43,29 @@ import java.util.concurrent.CompletableFuture;
* Represents a player connection used in Geyser. * Represents a player connection used in Geyser.
*/ */
public interface GeyserConnection extends Connection, CommandSource { public interface GeyserConnection extends Connection, CommandSource {
/**
* Exposes the {@link CameraData} for this connection.
* It allows you to send fogs, camera shakes, force camera perspectives, and more.
*
* @return the CameraData for this connection.
*/
@NonNull CameraData camera();
/**
* Exposes the {@link EntityData} for this connection.
* It allows you to get entities by their Java entity ID, show emotes, and get the player entity.
*
* @return the EntityData for this connection.
*/
@NonNull EntityData entities();
/** /**
* @param javaId the Java entity ID to look up. * @param javaId the Java entity ID to look up.
* @return a {@link GeyserEntity} if present in this connection's entity tracker. * @return a {@link GeyserEntity} if present in this connection's entity tracker.
* @deprecated Use {@link EntityData#entityByJavaId(int)} instead
*/ */
@Deprecated
@NonNull @NonNull
CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId); CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
@ -53,11 +74,14 @@ public interface GeyserConnection extends Connection, CommandSource {
* *
* @param emoter the player entity emoting. * @param emoter the player entity emoting.
* @param emoteId the emote ID to send to this client. * @param emoteId the emote ID to send to this client.
* @deprecated use {@link EntityData#showEmote(GeyserPlayerEntity, String)} instead
*/ */
@Deprecated
void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId); void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId);
/** /**
* Shakes the client's camera.<br><br> * Shakes the client's camera.
* <p>
* If the camera is already shaking with the same {@link CameraShake} type, then the additional intensity * 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.<br> * will be layered on top of the existing intensity, with their own distinct durations.<br>
* If the existing shake type is different and the new intensity/duration are not positive, the existing shake only * If the existing shake type is different and the new intensity/duration are not positive, the existing shake only
@ -66,12 +90,18 @@ public interface GeyserConnection extends Connection, CommandSource {
* @param intensity the intensity of the shake. The client has a maximum total intensity of 4. * @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 duration the time in seconds that the shake will occur for
* @param type the type of shake * @param type the type of shake
*
* @deprecated Use {@link CameraData#shakeCamera(float, float, CameraShake)} instead.
*/ */
@Deprecated
void shakeCamera(float intensity, float duration, @NonNull CameraShake type); void shakeCamera(float intensity, float duration, @NonNull CameraShake type);
/** /**
* Stops all camera shake of any type. * Stops all camera shake of any type.
*
* @deprecated Use {@link CameraData#stopCameraShake()} instead.
*/ */
@Deprecated
void stopCameraShake(); void stopCameraShake();
/** /**
@ -80,19 +110,26 @@ public interface GeyserConnection extends Connection, CommandSource {
* Fog IDs can be found <a href="https://wiki.bedrock.dev/documentation/fog-ids.html">here</a> * Fog IDs can be found <a href="https://wiki.bedrock.dev/documentation/fog-ids.html">here</a>
* *
* @param fogNameSpaces the fog IDs to add. If empty, the existing cached IDs will still be sent. * @param fogNameSpaces the fog IDs to add. If empty, the existing cached IDs will still be sent.
* @deprecated Use {@link CameraData#sendFog(String...)} instead.
*/ */
@Deprecated
void sendFog(String... fogNameSpaces); void sendFog(String... fogNameSpaces);
/** /**
* Removes the given fog IDs from the fog cache, then sends all fog IDs in the cache to the client. * 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. * @param fogNameSpaces the fog IDs to remove. If empty, all fog IDs will be removed.
* @deprecated Use {@link CameraData#removeFog(String...)} instead.
*/ */
@Deprecated
void removeFog(String... fogNameSpaces); void removeFog(String... fogNameSpaces);
/** /**
* Returns an immutable copy of all fog affects currently applied to this client. * Returns an immutable copy of all fog affects currently applied to this client.
*
* @deprecated Use {@link CameraData#fogEffects()} instead.
*/ */
@Deprecated
@NonNull @NonNull
Set<String> fogEffects(); Set<String> fogEffects();
} }

Datei anzeigen

@ -0,0 +1,84 @@
/*
* 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.entity;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
* This class holds all the methods that relate to entities.
* Can be accessed through {@link GeyserConnection#entities()}.
*/
public interface EntityData {
/**
* Returns a {@link GeyserEntity} to e.g. make them play an emote.
*
* @param javaId the Java entity ID to look up
* @return a {@link GeyserEntity} if present in this connection's entity tracker
*/
@NonNull 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 this client
*/
void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId);
/**
* Gets the {@link GeyserPlayerEntity} of this connection.
*
* @return the {@link GeyserPlayerEntity} of this connection
*/
@NonNull GeyserPlayerEntity playerEntity();
/**
* (Un)locks the client's movement inputs, so that they cannot move.
* To ensure that movement is only unlocked when all locks are released, you must supply
* a UUID with this method, and use the same UUID to unlock the camera.
*
* @param lock whether to lock the movement
* @param owner the owner of the lock
* @return if the movement is locked after this method call
*/
boolean lockMovement(boolean lock, @NonNull UUID owner);
/**
* Returns whether the client's movement is currently locked.
*
* @return whether the movement is locked
*/
boolean isMovementLocked();
}

Datei anzeigen

@ -25,7 +25,15 @@
package org.geysermc.geyser.api.entity.type.player; package org.geysermc.geyser.api.entity.type.player;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.api.entity.type.GeyserEntity;
public interface GeyserPlayerEntity extends GeyserEntity { public interface GeyserPlayerEntity extends GeyserEntity {
/**
* Gets the position of the player.
*
* @return the position of the player.
*/
Vector3f position();
} }

Datei anzeigen

@ -0,0 +1,99 @@
/*
* 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.entity;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.packet.EmotePacket;
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.entity.type.Entity;
import org.geysermc.geyser.session.GeyserSession;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class GeyserEntityData implements EntityData {
private final GeyserSession session;
private final Set<UUID> movementLockOwners = new HashSet<>();
public GeyserEntityData(GeyserSession session) {
this.session = session;
}
@Override
public @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId) {
CompletableFuture<GeyserEntity> future = new CompletableFuture<>();
session.ensureInEventLoop(() -> future.complete(session.getEntityCache().getEntityByJavaId(javaId)));
return future;
}
@Override
public void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId) {
Objects.requireNonNull(emoter, "emoter must not be null!");
Entity entity = (Entity) emoter;
if (entity.getSession() != session) {
throw new IllegalStateException("Given entity must be from this session!");
}
EmotePacket packet = new EmotePacket();
packet.setRuntimeEntityId(entity.getGeyserId());
packet.setXuid("");
packet.setPlatformId(""); // BDS sends empty
packet.setEmoteId(emoteId);
session.sendUpstreamPacket(packet);
}
@Override
public @NonNull GeyserPlayerEntity playerEntity() {
return session.getPlayerEntity();
}
@Override
public boolean lockMovement(boolean lock, @NonNull UUID owner) {
Objects.requireNonNull(owner, "owner must not be null!");
if (lock) {
movementLockOwners.add(owner);
} else {
movementLockOwners.remove(owner);
}
session.lockInputs(session.camera().isCameraLocked(), isMovementLocked());
return isMovementLocked();
}
@Override
public boolean isMovementLocked() {
return !movementLockOwners.isEmpty();
}
}

Datei anzeigen

@ -39,12 +39,20 @@ import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.*; import org.cloudburstmc.protocol.bedrock.data.Ability;
import org.cloudburstmc.protocol.bedrock.data.AbilityLayer;
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
import org.cloudburstmc.protocol.bedrock.data.GameType;
import org.cloudburstmc.protocol.bedrock.data.PlayerPermission;
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
import org.cloudburstmc.protocol.bedrock.packet.*; import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Entity;
@ -433,4 +441,9 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
public UUID getTabListUuid() { public UUID getTabListUuid() {
return getUuid(); return getUuid();
} }
@Override
public Vector3f position() {
return this.position.clone();
}
} }

Datei anzeigen

@ -0,0 +1,96 @@
/*
* Copyright (c) 2019-2024 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.impl.camera;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraAudioListener;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraPreset;
import org.cloudburstmc.protocol.common.DefinitionRegistry;
import org.cloudburstmc.protocol.common.NamedDefinition;
import org.cloudburstmc.protocol.common.SimpleDefinitionRegistry;
import org.cloudburstmc.protocol.common.util.OptionalBoolean;
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
import java.util.List;
public class CameraDefinitions {
public static final DefinitionRegistry<NamedDefinition> CAMERA_DEFINITIONS;
public static final List<CameraPreset> CAMERA_PRESETS;
static {
CAMERA_PRESETS = List.of(
new CameraPreset(CameraPerspective.FIRST_PERSON.id(), "", null, null, null, null, OptionalBoolean.empty()),
new CameraPreset(CameraPerspective.FREE.id(), "", null, null, null, null, OptionalBoolean.empty()),
new CameraPreset(CameraPerspective.THIRD_PERSON.id(), "", null, null, null, null, OptionalBoolean.empty()),
new CameraPreset(CameraPerspective.THIRD_PERSON_FRONT.id(), "", null, null, null, null, OptionalBoolean.empty()),
new CameraPreset("geyser:free_audio", "minecraft:free", null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.of(false)),
new CameraPreset("geyser:free_effects", "minecraft:free", null, null, null, CameraAudioListener.CAMERA, OptionalBoolean.of(true)),
new CameraPreset("geyser:free_audio_effects", "minecraft:free", null, null, null, CameraAudioListener.PLAYER, OptionalBoolean.of(true)));
SimpleDefinitionRegistry.Builder<NamedDefinition> builder = SimpleDefinitionRegistry.builder();
for (int i = 0; i < CAMERA_PRESETS.size(); i++) {
builder.add(CameraDefinition.of(CAMERA_PRESETS.get(i).getIdentifier(), i));
}
CAMERA_DEFINITIONS = builder.build();
}
public static NamedDefinition getById(int id) {
return CAMERA_DEFINITIONS.getDefinition(id);
}
public static NamedDefinition getByFunctionality(boolean audio, boolean effects) {
if (!audio && !effects) {
return getById(1); // FREE
}
if (audio) {
if (effects) {
return getById(6); // FREE_AUDIO_EFFECTS
} else {
return getById(4); // FREE_AUDIO
}
} else {
return getById(5); // FREE_EFFECTS
}
}
public record CameraDefinition(String identifier, int runtimeId) implements NamedDefinition {
@Override
public String getIdentifier() {
return identifier;
}
@Override
public int getRuntimeId() {
return runtimeId;
}
public static CameraDefinition of(String identifier, int runtimeId) {
return new CameraDefinition(identifier, runtimeId);
}
}
}

Datei anzeigen

@ -0,0 +1,235 @@
/*
* Copyright (c) 2019-2024 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.impl.camera;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.CameraShakeAction;
import org.cloudburstmc.protocol.bedrock.data.CameraShakeType;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraEase;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraFadeInstruction;
import org.cloudburstmc.protocol.bedrock.data.camera.CameraSetInstruction;
import org.cloudburstmc.protocol.bedrock.packet.CameraInstructionPacket;
import org.cloudburstmc.protocol.bedrock.packet.CameraShakePacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerFogPacket;
import org.geysermc.geyser.api.bedrock.camera.CameraEaseType;
import org.geysermc.geyser.api.bedrock.camera.CameraData;
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
import org.geysermc.geyser.session.GeyserSession;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public class GeyserCameraData implements CameraData {
private final GeyserSession session;
@Getter
private CameraPerspective cameraPerspective;
/**
* All fog effects that are currently applied to the client.
*/
private final Set<String> appliedFog = new HashSet<>();
private final Set<UUID> cameraLockOwners = new HashSet<>();
public GeyserCameraData(GeyserSession session) {
this.session = session;
}
@Override
public void clearCameraInstructions() {
this.cameraPerspective = null;
CameraInstructionPacket packet = new CameraInstructionPacket();
packet.setClear(true);
session.sendUpstreamPacket(packet);
}
@Override
public void forceCameraPerspective(@NonNull CameraPerspective perspective) {
Objects.requireNonNull(perspective, "perspective cannot be null!");
if (perspective == cameraPerspective) {
return; // nothing to do
}
this.cameraPerspective = perspective;
CameraInstructionPacket packet = new CameraInstructionPacket();
CameraSetInstruction setInstruction = new CameraSetInstruction();
if (perspective == CameraPerspective.FREE) {
throw new IllegalArgumentException("Cannot force a stationary camera (CameraPerspective#FREE) on the player!" +
"Send a CameraPosition with an exact position instead");
}
setInstruction.setPreset(CameraDefinitions.getById(perspective.ordinal()));
packet.setSetInstruction(setInstruction);
session.sendUpstreamPacket(packet);
}
@Override
public @Nullable CameraPerspective forcedCameraPerspective() {
return this.cameraPerspective;
}
@Override
public void sendCameraFade(@NonNull CameraFade fade) {
Objects.requireNonNull(fade, "fade cannot be null!");
CameraFadeInstruction fadeInstruction = new CameraFadeInstruction();
fadeInstruction.setColor(fade.color());
fadeInstruction.setTimeData(
new CameraFadeInstruction.TimeData(
fade.fadeInSeconds(),
fade.fadeHoldSeconds(),
fade.fadeOutSeconds()
)
);
CameraInstructionPacket packet = new CameraInstructionPacket();
packet.setFadeInstruction(fadeInstruction);
session.sendUpstreamPacket(packet);
}
@Override
public void sendCameraPosition(@NonNull CameraPosition movement) {
Objects.requireNonNull(movement, "movement cannot be null!");
this.cameraPerspective = CameraPerspective.FREE; // Movements only work with the free preset
CameraSetInstruction setInstruction = new CameraSetInstruction();
CameraEaseType easeType = movement.easeType();
if (easeType != null) {
setInstruction.setEase(new CameraSetInstruction.EaseData(
CameraEase.fromName(easeType.id()),
movement.easeSeconds()
));
}
Vector3f facingPosition = movement.facingPosition();
if (facingPosition != null) {
setInstruction.setFacing(facingPosition);
}
setInstruction.setPos(movement.position());
setInstruction.setRot(Vector2f.from(movement.rotationX(), movement.rotationY()));
setInstruction.setPreset(CameraDefinitions.getByFunctionality(movement.playerPositionForAudio(), movement.renderPlayerEffects()));
CameraInstructionPacket packet = new CameraInstructionPacket();
packet.setSetInstruction(setInstruction);
// If present, also send the fade
CameraFade fade = movement.cameraFade();
if (fade != null) {
CameraFadeInstruction fadeInstruction = new CameraFadeInstruction();
fadeInstruction.setColor(fade.color());
fadeInstruction.setTimeData(
new CameraFadeInstruction.TimeData(
fade.fadeInSeconds(),
fade.fadeHoldSeconds(),
fade.fadeOutSeconds()
)
);
packet.setFadeInstruction(fadeInstruction);
}
session.sendUpstreamPacket(packet);
}
@Override
public void shakeCamera(float intensity, float duration, @NonNull CameraShake type) {
Objects.requireNonNull(type, "camera shake type must be non null!");
CameraShakePacket packet = new CameraShakePacket();
packet.setIntensity(intensity);
packet.setDuration(duration);
packet.setShakeType(type == CameraShake.POSITIONAL ? CameraShakeType.POSITIONAL : CameraShakeType.ROTATIONAL);
packet.setShakeAction(CameraShakeAction.ADD);
session.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);
session.sendUpstreamPacket(packet);
}
@Override
public void sendFog(String... fogNameSpaces) {
Collections.addAll(this.appliedFog, fogNameSpaces);
PlayerFogPacket packet = new PlayerFogPacket();
packet.getFogStack().addAll(this.appliedFog);
session.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);
session.sendUpstreamPacket(packet);
}
@Override
public @NonNull Set<String> fogEffects() {
// Use a copy so that sendFog/removeFog can be called while iterating the returned set (avoid CME)
return Set.copyOf(this.appliedFog);
}
@Override
public boolean lockCamera(boolean lock, @NonNull UUID owner) {
Objects.requireNonNull(owner, "owner cannot be null!");
if (lock) {
this.cameraLockOwners.add(owner);
} else {
this.cameraLockOwners.remove(owner);
}
session.lockInputs(isCameraLocked(), session.entities().isMovementLocked());
return isCameraLocked();
}
@Override
public boolean isCameraLocked() {
return !this.cameraLockOwners.isEmpty();
}
}

Datei anzeigen

@ -0,0 +1,104 @@
/*
* Copyright (c) 2019-2024 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.impl.camera;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.common.value.qual.IntRange;
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
import java.awt.Color;
import java.util.Objects;
public record GeyserCameraFade(
Color color,
float fadeInSeconds,
float fadeHoldSeconds,
float fadeOutSeconds
) implements CameraFade {
public static class Builder implements CameraFade.Builder {
private Color color;
private float fadeInSeconds;
private float fadeHoldSeconds;
private float fadeOutSeconds;
@Override
public CameraFade.Builder color(@NonNull Color color) {
Objects.requireNonNull(color, "color cannot be null!");
this.color = color;
return this;
}
@Override
public CameraFade.Builder fadeInSeconds(@IntRange(from = 0, to = 10) float fadeInSeconds) {
if (fadeInSeconds < 0f) {
throw new IllegalArgumentException("Fade in seconds must be at least 0 seconds");
}
if (fadeInSeconds > 10f) {
throw new IllegalArgumentException("Fade in seconds must be at most 10 seconds");
}
this.fadeInSeconds = fadeInSeconds;
return this;
}
@Override
public CameraFade.Builder fadeHoldSeconds(@IntRange(from = 0, to = 10) float fadeHoldSeconds) {
if (fadeHoldSeconds < 0f) {
throw new IllegalArgumentException("Fade hold seconds must be at least 0 seconds");
}
if (fadeHoldSeconds > 10f) {
throw new IllegalArgumentException("Fade hold seconds must be at most 10 seconds");
}
this.fadeHoldSeconds = fadeHoldSeconds;
return this;
}
@Override
public CameraFade.Builder fadeOutSeconds(@IntRange(from = 0, to = 10) float fadeOutSeconds) {
if (fadeOutSeconds < 0f) {
throw new IllegalArgumentException("Fade out seconds must be at least 0 seconds");
}
if (fadeOutSeconds > 10f) {
throw new IllegalArgumentException("Fade out seconds must be at most 10 seconds");
}
this.fadeOutSeconds = fadeOutSeconds;
return this;
}
@Override
public CameraFade build() {
Objects.requireNonNull(color, "color must be non null!");
if (fadeInSeconds + fadeHoldSeconds + fadeOutSeconds < 0.5f) {
throw new IllegalArgumentException("Total fade time (in, hold, out) must be at least 0.5 seconds");
}
return new GeyserCameraFade(color, fadeInSeconds, fadeHoldSeconds, fadeOutSeconds);
}
}
}

Datei anzeigen

@ -0,0 +1,131 @@
/*
* Copyright (c) 2019-2024 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.impl.camera;
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.Vector3f;
import org.geysermc.geyser.api.bedrock.camera.CameraEaseType;
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
import java.util.Objects;
public record GeyserCameraPosition(CameraFade cameraFade,
boolean renderPlayerEffects,
boolean playerPositionForAudio,
CameraEaseType easeType,
float easeSeconds,
Vector3f position,
@IntRange(from = -90, to = 90) int rotationX,
int rotationY,
Vector3f facingPosition
) implements CameraPosition {
public static class Builder implements CameraPosition.Builder {
private CameraFade cameraFade;
private boolean renderPlayerEffects;
private boolean playerPositionForAudio;
private CameraEaseType easeType;
private float easeSeconds;
private Vector3f position;
private @IntRange(from = -90, to = 90) int rotationX;
private int rotationY;
private Vector3f facingPosition;
@Override
public CameraPosition.Builder cameraFade(@Nullable CameraFade cameraFade) {
this.cameraFade = cameraFade;
return this;
}
@Override
public CameraPosition.Builder renderPlayerEffects(boolean renderPlayerEffects) {
this.renderPlayerEffects = renderPlayerEffects;
return this;
}
@Override
public CameraPosition.Builder playerPositionForAudio(boolean playerPositionForAudio) {
this.playerPositionForAudio = playerPositionForAudio;
return this;
}
@Override
public CameraPosition.Builder easeType(@Nullable CameraEaseType easeType) {
this.easeType = easeType;
return this;
}
@Override
public CameraPosition.Builder easeSeconds(float easeSeconds) {
if (easeSeconds < 0) {
throw new IllegalArgumentException("Camera ease duration cannot be negative!");
}
this.easeSeconds = easeSeconds;
return this;
}
@Override
public CameraPosition.Builder position(@NonNull Vector3f position) {
Objects.requireNonNull(position, "camera position cannot be null!");
this.position = position;
return this;
}
@Override
public CameraPosition.Builder rotationX(int rotationX) {
if (rotationX < -90 || rotationX > 90) {
throw new IllegalArgumentException("x-axis rotation needs to be between -90 and 90 degrees.");
}
this.rotationX = rotationX;
return this;
}
@Override
public CameraPosition.Builder rotationY(int rotationY) {
this.rotationY = rotationY;
return this;
}
@Override
public CameraPosition.Builder facingPosition(@Nullable Vector3f facingPosition) {
this.facingPosition = facingPosition;
return this;
}
@Override
public CameraPosition build() {
if (easeSeconds > 0 && easeType == null) {
throw new IllegalArgumentException("Camera ease type cannot be null if ease duration is greater than 0");
}
Objects.requireNonNull(position, "camera position must be non null!");
return new GeyserCameraPosition(cameraFade, renderPlayerEffects, playerPositionForAudio, easeType, easeSeconds, position, rotationX, rotationY, facingPosition);
}
}
}

Datei anzeigen

@ -115,7 +115,7 @@ public class GeyserCustomItemData implements CustomItemData {
return tags; return tags;
} }
public static class CustomItemDataBuilder implements Builder { public static class Builder implements CustomItemData.Builder {
protected String name = null; protected String name = null;
protected CustomItemOptions customItemOptions = null; protected CustomItemOptions customItemOptions = null;

Datei anzeigen

@ -37,7 +37,7 @@ public record GeyserCustomItemOptions(TriState unbreakable,
boolean defaultItem) implements CustomItemOptions { boolean defaultItem) implements CustomItemOptions {
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public static class CustomItemOptionsBuilder implements CustomItemOptions.Builder { public static class Builder implements CustomItemOptions.Builder {
private TriState unbreakable = TriState.NOT_SET; private TriState unbreakable = TriState.NOT_SET;
private OptionalInt customModelData = OptionalInt.empty(); private OptionalInt customModelData = OptionalInt.empty();
private OptionalInt damagePredicate = OptionalInt.empty(); private OptionalInt damagePredicate = OptionalInt.empty();

Datei anzeigen

@ -59,7 +59,7 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
private final boolean canAlwaysEat; private final boolean canAlwaysEat;
private final boolean isChargeable; private final boolean isChargeable;
public GeyserNonVanillaCustomItemData(NonVanillaCustomItemDataBuilder builder) { public GeyserNonVanillaCustomItemData(Builder builder) {
super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand, super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand,
builder.displayHandheld, builder.textureSize, builder.renderOffsets, builder.tags); builder.displayHandheld, builder.textureSize, builder.renderOffsets, builder.tags);
@ -168,7 +168,7 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
return isChargeable; return isChargeable;
} }
public static class NonVanillaCustomItemDataBuilder extends GeyserCustomItemData.CustomItemDataBuilder implements NonVanillaCustomItemData.Builder { public static class Builder extends GeyserCustomItemData.Builder implements NonVanillaCustomItemData.Builder {
private String identifier = null; private String identifier = null;
private int javaId = -1; private int javaId = -1;
@ -197,49 +197,49 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
private boolean chargeable = false; private boolean chargeable = false;
@Override @Override
public NonVanillaCustomItemData.Builder name(@NonNull String name) { public Builder name(@NonNull String name) {
return (NonVanillaCustomItemData.Builder) super.name(name); return (Builder) super.name(name);
} }
@Override @Override
public NonVanillaCustomItemData.Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) { public Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) {
//Do nothing, as that value won't be read //Do nothing, as that value won't be read
return this; return this;
} }
@Override @Override
public NonVanillaCustomItemData.Builder allowOffhand(boolean allowOffhand) { public Builder allowOffhand(boolean allowOffhand) {
return (NonVanillaCustomItemData.Builder) super.allowOffhand(allowOffhand); return (Builder) super.allowOffhand(allowOffhand);
} }
@Override @Override
public NonVanillaCustomItemData.Builder displayHandheld(boolean displayHandheld) { public Builder displayHandheld(boolean displayHandheld) {
return (NonVanillaCustomItemData.Builder) super.displayHandheld(displayHandheld); return (Builder) super.displayHandheld(displayHandheld);
} }
@Override @Override
public NonVanillaCustomItemData.Builder displayName(@NonNull String displayName) { public Builder displayName(@NonNull String displayName) {
return (NonVanillaCustomItemData.Builder) super.displayName(displayName); return (Builder) super.displayName(displayName);
} }
@Override @Override
public NonVanillaCustomItemData.Builder icon(@NonNull String icon) { public Builder icon(@NonNull String icon) {
return (NonVanillaCustomItemData.Builder) super.icon(icon); return (Builder) super.icon(icon);
} }
@Override @Override
public NonVanillaCustomItemData.Builder textureSize(int textureSize) { public Builder textureSize(int textureSize) {
return (NonVanillaCustomItemData.Builder) super.textureSize(textureSize); return (Builder) super.textureSize(textureSize);
} }
@Override @Override
public NonVanillaCustomItemData.Builder renderOffsets(CustomRenderOffsets renderOffsets) { public Builder renderOffsets(CustomRenderOffsets renderOffsets) {
return (NonVanillaCustomItemData.Builder) super.renderOffsets(renderOffsets); return (Builder) super.renderOffsets(renderOffsets);
} }
@Override @Override
public NonVanillaCustomItemData.Builder tags(@Nullable Set<String> tags) { public Builder tags(@Nullable Set<String> tags) {
return (NonVanillaCustomItemData.Builder) super.tags(tags); return (Builder) super.tags(tags);
} }
@Override @Override

Datei anzeigen

@ -62,7 +62,7 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
boolean placeAir; boolean placeAir;
Set<String> tags; Set<String> tags;
private GeyserCustomBlockComponents(CustomBlockComponentsBuilder builder) { private GeyserCustomBlockComponents(Builder builder) {
this.selectionBox = builder.selectionBox; this.selectionBox = builder.selectionBox;
this.collisionBox = builder.collisionBox; this.collisionBox = builder.collisionBox;
this.displayName = builder.displayName; this.displayName = builder.displayName;
@ -157,7 +157,7 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
return tags; return tags;
} }
public static class CustomBlockComponentsBuilder implements Builder { public static class Builder implements CustomBlockComponents.Builder {
protected BoxComponent selectionBox; protected BoxComponent selectionBox;
protected BoxComponent collisionBox; protected BoxComponent collisionBox;
protected String displayName; protected String displayName;

Datei anzeigen

@ -54,7 +54,7 @@ public class GeyserCustomBlockData implements CustomBlockData {
private final Map<String, Object> defaultProperties; private final Map<String, Object> defaultProperties;
GeyserCustomBlockData(CustomBlockDataBuilder builder) { GeyserCustomBlockData(Builder builder) {
this.name = builder.name; this.name = builder.name;
if (name == null) { if (name == null) {
throw new IllegalStateException("Name must be set"); throw new IllegalStateException("Name must be set");
@ -141,10 +141,10 @@ public class GeyserCustomBlockData implements CustomBlockData {
@Override @Override
public CustomBlockState.@NonNull Builder blockStateBuilder() { public CustomBlockState.@NonNull Builder blockStateBuilder() {
return new GeyserCustomBlockState.CustomBlockStateBuilder(this); return new GeyserCustomBlockState.Builder(this);
} }
public static class CustomBlockDataBuilder implements Builder { public static class Builder implements CustomBlockData.Builder {
private String name; private String name;
private boolean includedInCreativeInventory; private boolean includedInCreativeInventory;
private CreativeCategory creativeCategory; private CreativeCategory creativeCategory;

Datei anzeigen

@ -64,7 +64,7 @@ public class GeyserCustomBlockState implements CustomBlockState {
} }
@RequiredArgsConstructor @RequiredArgsConstructor
public static class CustomBlockStateBuilder implements CustomBlockState.Builder { public static class Builder implements CustomBlockState.Builder {
private final CustomBlockData blockData; private final CustomBlockData blockData;
private final Object2ObjectMap<String, Object> properties = new Object2ObjectOpenHashMap<>(); private final Object2ObjectMap<String, Object> properties = new Object2ObjectOpenHashMap<>();

Datei anzeigen

@ -37,7 +37,7 @@ public class GeyserGeometryComponent implements GeometryComponent {
private final String identifier; private final String identifier;
private final Map<String, String> boneVisibility; private final Map<String, String> boneVisibility;
GeyserGeometryComponent(GeometryComponentBuilder builder) { GeyserGeometryComponent(Builder builder) {
this.identifier = builder.identifier; this.identifier = builder.identifier;
this.boneVisibility = builder.boneVisibility; this.boneVisibility = builder.boneVisibility;
} }
@ -52,18 +52,18 @@ public class GeyserGeometryComponent implements GeometryComponent {
return boneVisibility; return boneVisibility;
} }
public static class GeometryComponentBuilder implements Builder { public static class Builder implements GeometryComponent.Builder {
private String identifier; private String identifier;
private Map<String, String> boneVisibility; private Map<String, String> boneVisibility;
@Override @Override
public GeometryComponent.Builder identifier(@NonNull String identifier) { public Builder identifier(@NonNull String identifier) {
this.identifier = identifier; this.identifier = identifier;
return this; return this;
} }
@Override @Override
public GeometryComponent.Builder boneVisibility(@Nullable Map<String, String> boneVisibility) { public Builder boneVisibility(@Nullable Map<String, String> boneVisibility) {
this.boneVisibility = boneVisibility; this.boneVisibility = boneVisibility;
return this; return this;
} }

Datei anzeigen

@ -18,7 +18,7 @@ public class GeyserJavaBlockState implements JavaBlockState {
String pistonBehavior; String pistonBehavior;
boolean hasBlockEntity; boolean hasBlockEntity;
private GeyserJavaBlockState(JavaBlockStateBuilder builder) { private GeyserJavaBlockState(Builder builder) {
this.identifier = builder.identifier; this.identifier = builder.identifier;
this.javaId = builder.javaId; this.javaId = builder.javaId;
this.stateGroupId = builder.stateGroupId; this.stateGroupId = builder.stateGroupId;
@ -81,7 +81,7 @@ public class GeyserJavaBlockState implements JavaBlockState {
return hasBlockEntity; return hasBlockEntity;
} }
public static class JavaBlockStateBuilder implements Builder { public static class Builder implements JavaBlockState.Builder {
private String identifier; private String identifier;
private int javaId; private int javaId;
private int stateGroupId; private int stateGroupId;

Datei anzeigen

@ -36,7 +36,7 @@ public class GeyserMaterialInstance implements MaterialInstance {
private final boolean faceDimming; private final boolean faceDimming;
private final boolean ambientOcclusion; private final boolean ambientOcclusion;
GeyserMaterialInstance(MaterialInstanceBuilder builder) { GeyserMaterialInstance(Builder builder) {
this.texture = builder.texture; this.texture = builder.texture;
this.renderMethod = builder.renderMethod; this.renderMethod = builder.renderMethod;
this.faceDimming = builder.faceDimming; this.faceDimming = builder.faceDimming;
@ -63,7 +63,7 @@ public class GeyserMaterialInstance implements MaterialInstance {
return ambientOcclusion; return ambientOcclusion;
} }
public static class MaterialInstanceBuilder implements Builder { public static class Builder implements MaterialInstance.Builder {
private String texture; private String texture;
private String renderMethod; private String renderMethod;
private boolean faceDimming; private boolean faceDimming;

Datei anzeigen

@ -37,7 +37,7 @@ import java.util.List;
public class GeyserNonVanillaCustomBlockData extends GeyserCustomBlockData implements NonVanillaCustomBlockData { public class GeyserNonVanillaCustomBlockData extends GeyserCustomBlockData implements NonVanillaCustomBlockData {
private final String namespace; private final String namespace;
GeyserNonVanillaCustomBlockData(NonVanillaCustomBlockDataBuilder builder) { GeyserNonVanillaCustomBlockData(Builder builder) {
super(builder); super(builder);
this.namespace = builder.namespace; this.namespace = builder.namespace;
@ -56,58 +56,58 @@ public class GeyserNonVanillaCustomBlockData extends GeyserCustomBlockData imple
return this.namespace; return this.namespace;
} }
public static class NonVanillaCustomBlockDataBuilder extends CustomBlockDataBuilder implements NonVanillaCustomBlockData.Builder { public static class Builder extends GeyserCustomBlockData.Builder implements NonVanillaCustomBlockData.Builder {
private String namespace; private String namespace;
@Override @Override
public NonVanillaCustomBlockDataBuilder namespace(@NonNull String namespace) { public Builder namespace(@NonNull String namespace) {
this.namespace = namespace; this.namespace = namespace;
return this; return this;
} }
@Override @Override
public NonVanillaCustomBlockDataBuilder name(@NonNull String name) { public Builder name(@NonNull String name) {
return (NonVanillaCustomBlockDataBuilder) super.name(name); return (Builder) super.name(name);
} }
@Override @Override
public NonVanillaCustomBlockDataBuilder includedInCreativeInventory(boolean includedInCreativeInventory) { public Builder includedInCreativeInventory(boolean includedInCreativeInventory) {
return (NonVanillaCustomBlockDataBuilder) super.includedInCreativeInventory(includedInCreativeInventory); return (Builder) super.includedInCreativeInventory(includedInCreativeInventory);
} }
@Override @Override
public NonVanillaCustomBlockDataBuilder creativeCategory(@Nullable CreativeCategory creativeCategories) { public Builder creativeCategory(@Nullable CreativeCategory creativeCategories) {
return (NonVanillaCustomBlockDataBuilder) super.creativeCategory(creativeCategories); return (Builder) super.creativeCategory(creativeCategories);
} }
@Override @Override
public NonVanillaCustomBlockDataBuilder creativeGroup(@Nullable String creativeGroup) { public Builder creativeGroup(@Nullable String creativeGroup) {
return (NonVanillaCustomBlockDataBuilder) super.creativeGroup(creativeGroup); return (Builder) super.creativeGroup(creativeGroup);
} }
@Override @Override
public NonVanillaCustomBlockDataBuilder components(@NonNull CustomBlockComponents components) { public Builder components(@NonNull CustomBlockComponents components) {
return (NonVanillaCustomBlockDataBuilder) super.components(components); return (Builder) super.components(components);
} }
@Override @Override
public NonVanillaCustomBlockDataBuilder booleanProperty(@NonNull String propertyName) { public Builder booleanProperty(@NonNull String propertyName) {
return (NonVanillaCustomBlockDataBuilder) super.booleanProperty(propertyName); return (Builder) super.booleanProperty(propertyName);
} }
@Override @Override
public NonVanillaCustomBlockDataBuilder intProperty(@NonNull String propertyName, List<Integer> values) { public Builder intProperty(@NonNull String propertyName, List<Integer> values) {
return (NonVanillaCustomBlockDataBuilder) super.intProperty(propertyName, values); return (Builder) super.intProperty(propertyName, values);
} }
@Override @Override
public NonVanillaCustomBlockDataBuilder stringProperty(@NonNull String propertyName, List<String> values) { public Builder stringProperty(@NonNull String propertyName, List<String> values) {
return (NonVanillaCustomBlockDataBuilder) super.stringProperty(propertyName, values); return (Builder) super.stringProperty(propertyName, values);
} }
@Override @Override
public NonVanillaCustomBlockDataBuilder permutations(@NonNull List<CustomBlockPermutation> permutations) { public Builder permutations(@NonNull List<CustomBlockPermutation> permutations) {
return (NonVanillaCustomBlockDataBuilder) super.permutations(permutations); return (Builder) super.permutations(permutations);
} }
@Override @Override

Datei anzeigen

@ -100,6 +100,16 @@ public class LoggingPacketHandler implements BedrockPacketHandler {
return defaultHandler(packet); return defaultHandler(packet);
} }
@Override
public PacketSignal handle(CameraPresetsPacket packet) {
return defaultHandler(packet);
}
@Override
public PacketSignal handle(CameraInstructionPacket packet) {
return defaultHandler(packet);
}
@Override @Override
public PacketSignal handle(CommandBlockUpdatePacket packet) { public PacketSignal handle(CommandBlockUpdatePacket packet) {
return defaultHandler(packet); return defaultHandler(packet);

Datei anzeigen

@ -25,6 +25,8 @@
package org.geysermc.geyser.registry.loader; package org.geysermc.geyser.registry.loader;
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
import org.geysermc.geyser.api.block.custom.CustomBlockData; import org.geysermc.geyser.api.block.custom.CustomBlockData;
import org.geysermc.geyser.api.block.custom.NonVanillaCustomBlockData; import org.geysermc.geyser.api.block.custom.NonVanillaCustomBlockData;
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents; import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
@ -38,6 +40,8 @@ import org.geysermc.geyser.api.item.custom.CustomItemData;
import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.item.custom.CustomItemOptions;
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData; import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
import org.geysermc.geyser.api.pack.PathPackCodec; import org.geysermc.geyser.api.pack.PathPackCodec;
import org.geysermc.geyser.impl.camera.GeyserCameraFade;
import org.geysermc.geyser.impl.camera.GeyserCameraPosition;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.event.GeyserEventRegistrar; import org.geysermc.geyser.event.GeyserEventRegistrar;
import org.geysermc.geyser.item.GeyserCustomItemData; import org.geysermc.geyser.item.GeyserCustomItemData;
@ -65,20 +69,24 @@ public class ProviderRegistryLoader implements RegistryLoader<Map<Class<?>, Prov
// misc // misc
providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Extension) args[0])); providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Extension) args[0]));
providers.put(CustomBlockComponents.Builder.class, args -> new GeyserCustomBlockComponents.CustomBlockComponentsBuilder()); providers.put(CustomBlockComponents.Builder.class, args -> new GeyserCustomBlockComponents.Builder());
providers.put(CustomBlockData.Builder.class, args -> new GeyserCustomBlockData.CustomBlockDataBuilder()); providers.put(CustomBlockData.Builder.class, args -> new GeyserCustomBlockData.Builder());
providers.put(JavaBlockState.Builder.class, args -> new GeyserJavaBlockState.JavaBlockStateBuilder()); providers.put(JavaBlockState.Builder.class, args -> new GeyserJavaBlockState.Builder());
providers.put(NonVanillaCustomBlockData.Builder.class, args -> new GeyserNonVanillaCustomBlockData.NonVanillaCustomBlockDataBuilder()); providers.put(NonVanillaCustomBlockData.Builder.class, args -> new GeyserNonVanillaCustomBlockData.Builder());
providers.put(MaterialInstance.Builder.class, args -> new GeyserMaterialInstance.MaterialInstanceBuilder()); providers.put(MaterialInstance.Builder.class, args -> new GeyserMaterialInstance.Builder());
providers.put(GeometryComponent.Builder.class, args -> new GeyserGeometryComponent.GeometryComponentBuilder()); providers.put(GeometryComponent.Builder.class, args -> new GeyserGeometryComponent.Builder());
providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0])); providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0]));
providers.put(PathPackCodec.class, args -> new GeyserPathPackCodec((Path) args[0])); providers.put(PathPackCodec.class, args -> new GeyserPathPackCodec((Path) args[0]));
// items // items
providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder()); providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.Builder());
providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder()); providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.Builder());
providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder()); providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.Builder());
// cameras
providers.put(CameraFade.Builder.class, args -> new GeyserCameraFade.Builder());
providers.put(CameraPosition.Builder.class, args -> new GeyserCameraPosition.Builder());
return providers; return providers;
} }

Datei anzeigen

@ -35,17 +35,22 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.block.custom.CustomBlockData; import org.geysermc.geyser.api.block.custom.CustomBlockData;
import org.geysermc.geyser.api.block.custom.CustomBlockPermutation; import org.geysermc.geyser.api.block.custom.CustomBlockPermutation;
import org.geysermc.geyser.api.block.custom.CustomBlockState; import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.api.block.custom.component.*; import org.geysermc.geyser.api.block.custom.component.BoxComponent;
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
import org.geysermc.geyser.api.block.custom.component.GeometryComponent;
import org.geysermc.geyser.api.block.custom.component.MaterialInstance;
import org.geysermc.geyser.api.block.custom.component.PlacementConditions;
import org.geysermc.geyser.api.block.custom.component.PlacementConditions.BlockFilterType; import org.geysermc.geyser.api.block.custom.component.PlacementConditions.BlockFilterType;
import org.geysermc.geyser.api.block.custom.component.PlacementConditions.Face; import org.geysermc.geyser.api.block.custom.component.PlacementConditions.Face;
import org.geysermc.geyser.api.block.custom.component.TransformationComponent;
import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomItemData;
import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.item.custom.CustomItemOptions;
import org.geysermc.geyser.api.util.CreativeCategory; import org.geysermc.geyser.api.util.CreativeCategory;
import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException; import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException;
import org.geysermc.geyser.level.block.GeyserCustomBlockComponents.CustomBlockComponentsBuilder; import org.geysermc.geyser.level.block.GeyserCustomBlockComponents;
import org.geysermc.geyser.level.block.GeyserCustomBlockData.CustomBlockDataBuilder; import org.geysermc.geyser.level.block.GeyserCustomBlockData;
import org.geysermc.geyser.level.block.GeyserGeometryComponent.GeometryComponentBuilder; import org.geysermc.geyser.level.block.GeyserGeometryComponent;
import org.geysermc.geyser.level.block.GeyserMaterialInstance.MaterialInstanceBuilder; import org.geysermc.geyser.level.block.GeyserMaterialInstance;
import org.geysermc.geyser.level.physics.BoundingBox; import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.mappings.util.CustomBlockComponentsMapping; import org.geysermc.geyser.registry.mappings.util.CustomBlockComponentsMapping;
@ -57,7 +62,14 @@ import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.geyser.util.MathUtils; import org.geysermc.geyser.util.MathUtils;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -248,7 +260,7 @@ public class MappingsReader_v1 extends MappingsReader {
boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").asBoolean(); boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").asBoolean();
// Create the data for the overall block // Create the data for the overall block
CustomBlockData.Builder customBlockDataBuilder = new CustomBlockDataBuilder() CustomBlockData.Builder customBlockDataBuilder = new GeyserCustomBlockData.Builder()
.name(name) .name(name)
.includedInCreativeInventory(includedInCreativeInventory) .includedInCreativeInventory(includedInCreativeInventory)
.creativeCategory(creativeCategory) .creativeCategory(creativeCategory)
@ -360,7 +372,7 @@ public class MappingsReader_v1 extends MappingsReader {
int id = BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(stateKey, -1); int id = BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(stateKey, -1);
BoxComponent boxComponent = createBoxComponent(id); BoxComponent boxComponent = createBoxComponent(id);
BoxComponent extendedBoxComponent = createExtendedBoxComponent(id); BoxComponent extendedBoxComponent = createExtendedBoxComponent(id);
CustomBlockComponents.Builder builder = new CustomBlockComponentsBuilder() CustomBlockComponents.Builder builder = new GeyserCustomBlockComponents.Builder()
.collisionBox(boxComponent) .collisionBox(boxComponent)
.selectionBox(boxComponent); .selectionBox(boxComponent);
@ -392,12 +404,12 @@ public class MappingsReader_v1 extends MappingsReader {
if (node.has("geometry")) { if (node.has("geometry")) {
if (node.get("geometry").isTextual()) { if (node.get("geometry").isTextual()) {
builder.geometry(new GeometryComponentBuilder() builder.geometry(new GeyserGeometryComponent.Builder()
.identifier(node.get("geometry").asText()) .identifier(node.get("geometry").asText())
.build()); .build());
} else { } else {
JsonNode geometry = node.get("geometry"); JsonNode geometry = node.get("geometry");
GeometryComponentBuilder geometryBuilder = new GeometryComponentBuilder(); GeometryComponent.Builder geometryBuilder = new GeyserGeometryComponent.Builder();
if (geometry.has("identifier")) { if (geometry.has("identifier")) {
geometryBuilder.identifier(geometry.get("identifier").asText()); geometryBuilder.identifier(geometry.get("identifier").asText());
} }
@ -654,7 +666,7 @@ public class MappingsReader_v1 extends MappingsReader {
ambientOcclusion = node.get("ambient_occlusion").asBoolean(); ambientOcclusion = node.get("ambient_occlusion").asBoolean();
} }
return new MaterialInstanceBuilder() return new GeyserMaterialInstance.Builder()
.texture(texture) .texture(texture)
.renderMethod(renderMethod) .renderMethod(renderMethod)
.faceDimming(faceDimming) .faceDimming(faceDimming)

Datei anzeigen

@ -23,11 +23,11 @@ import org.geysermc.geyser.api.block.custom.property.CustomBlockProperty;
import org.geysermc.geyser.api.block.custom.property.PropertyType; import org.geysermc.geyser.api.block.custom.property.PropertyType;
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomBlocksEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomBlocksEvent;
import org.geysermc.geyser.api.util.CreativeCategory; import org.geysermc.geyser.api.util.CreativeCategory;
import org.geysermc.geyser.level.block.GeyserCustomBlockComponents;
import org.geysermc.geyser.level.block.GeyserCustomBlockData;
import org.geysermc.geyser.level.block.GeyserCustomBlockState; import org.geysermc.geyser.level.block.GeyserCustomBlockState;
import org.geysermc.geyser.level.block.GeyserCustomBlockComponents.CustomBlockComponentsBuilder; import org.geysermc.geyser.level.block.GeyserGeometryComponent;
import org.geysermc.geyser.level.block.GeyserCustomBlockData.CustomBlockDataBuilder; import org.geysermc.geyser.level.block.GeyserMaterialInstance;
import org.geysermc.geyser.level.block.GeyserGeometryComponent.GeometryComponentBuilder;
import org.geysermc.geyser.level.block.GeyserMaterialInstance.MaterialInstanceBuilder;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.mappings.MappingsConfigReader; import org.geysermc.geyser.registry.mappings.MappingsConfigReader;
import org.geysermc.geyser.registry.type.CustomSkull; import org.geysermc.geyser.registry.type.CustomSkull;
@ -479,20 +479,20 @@ public class CustomBlockRegistryPopulator {
} }
private static CustomBlockData createExtendedCollisionBlock(BoxComponent boxComponent, int extendedCollisionBlock) { private static CustomBlockData createExtendedCollisionBlock(BoxComponent boxComponent, int extendedCollisionBlock) {
return new CustomBlockDataBuilder() return new GeyserCustomBlockData.Builder()
.name("extended_collision_" + extendedCollisionBlock) .name("extended_collision_" + extendedCollisionBlock)
.components( .components(
new CustomBlockComponentsBuilder() new GeyserCustomBlockComponents.Builder()
.collisionBox(boxComponent) .collisionBox(boxComponent)
.selectionBox(BoxComponent.emptyBox()) .selectionBox(BoxComponent.emptyBox())
.materialInstance("*", new MaterialInstanceBuilder() .materialInstance("*", new GeyserMaterialInstance.Builder()
.texture("glass") .texture("glass")
.renderMethod("alpha_test") .renderMethod("alpha_test")
.faceDimming(false) .faceDimming(false)
.ambientOcclusion(false) .ambientOcclusion(false)
.build()) .build())
.lightDampening(0) .lightDampening(0)
.geometry(new GeometryComponentBuilder() .geometry(new GeyserGeometryComponent.Builder()
.identifier("geometry.invisible") .identifier("geometry.invisible")
.build()) .build())
.build()) .build())

Datei anzeigen

@ -34,8 +34,8 @@ import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
import org.geysermc.geyser.api.block.custom.component.TransformationComponent; import org.geysermc.geyser.api.block.custom.component.TransformationComponent;
import org.geysermc.geyser.level.block.GeyserCustomBlockComponents; import org.geysermc.geyser.level.block.GeyserCustomBlockComponents;
import org.geysermc.geyser.level.block.GeyserCustomBlockData; import org.geysermc.geyser.level.block.GeyserCustomBlockData;
import org.geysermc.geyser.level.block.GeyserGeometryComponent.GeometryComponentBuilder; import org.geysermc.geyser.level.block.GeyserGeometryComponent;
import org.geysermc.geyser.level.block.GeyserMaterialInstance.MaterialInstanceBuilder; import org.geysermc.geyser.level.block.GeyserMaterialInstance;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -65,9 +65,9 @@ public class CustomSkull {
public CustomSkull(String skinHash) { public CustomSkull(String skinHash) {
this.skinHash = skinHash; this.skinHash = skinHash;
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder() CustomBlockComponents components = new GeyserCustomBlockComponents.Builder()
.destructibleByMining(1.5f) .destructibleByMining(1.5f)
.materialInstance("*", new MaterialInstanceBuilder() .materialInstance("*", new GeyserMaterialInstance.Builder()
.texture("geyser." + skinHash + "_player_skin") .texture("geyser." + skinHash + "_player_skin")
.renderMethod("alpha_test") .renderMethod("alpha_test")
.faceDimming(true) .faceDimming(true)
@ -82,7 +82,7 @@ public class CustomSkull {
addFloorPermutations(permutations); addFloorPermutations(permutations);
addWallPermutations(permutations); addWallPermutations(permutations);
customBlockData = new GeyserCustomBlockData.CustomBlockDataBuilder() customBlockData = new GeyserCustomBlockData.Builder()
.name("player_skull_" + skinHash) .name("player_skull_" + skinHash)
.components(components) .components(components)
.intProperty(BITS_A_PROPERTY, IntStream.rangeClosed(0, 6).boxed().toList()) // This gives us exactly 21 block states .intProperty(BITS_A_PROPERTY, IntStream.rangeClosed(0, 6).boxed().toList()) // This gives us exactly 21 block states
@ -114,8 +114,8 @@ public class CustomSkull {
} }
private void addDefaultPermutation(List<CustomBlockPermutation> permutations) { private void addDefaultPermutation(List<CustomBlockPermutation> permutations) {
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder() CustomBlockComponents components = new GeyserCustomBlockComponents.Builder()
.geometry(new GeometryComponentBuilder() .geometry(new GeyserGeometryComponent.Builder()
.identifier("geometry.geyser.player_skull_hand") .identifier("geometry.geyser.player_skull_hand")
.build()) .build())
.transformation(new TransformationComponent(0, 180, 0)) .transformation(new TransformationComponent(0, 180, 0))
@ -131,10 +131,10 @@ public class CustomSkull {
for (int quadrant = 0; quadrant < 4; quadrant++) { for (int quadrant = 0; quadrant < 4; quadrant++) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
int floorRotation = 4 * quadrant + i; int floorRotation = 4 * quadrant + i;
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder() CustomBlockComponents components = new GeyserCustomBlockComponents.Builder()
.selectionBox(FLOOR_BOX) .selectionBox(FLOOR_BOX)
.collisionBox(FLOOR_BOX) .collisionBox(FLOOR_BOX)
.geometry(new GeometryComponentBuilder() .geometry(new GeyserGeometryComponent.Builder()
.identifier("geometry.geyser.player_skull_floor_" + quadrantNames[i]) .identifier("geometry.geyser.player_skull_floor_" + quadrantNames[i])
.build()) .build())
.transformation(new TransformationComponent(0, ROTATIONS[quadrant], 0)) .transformation(new TransformationComponent(0, ROTATIONS[quadrant], 0))
@ -150,10 +150,10 @@ public class CustomSkull {
private void addWallPermutations(List<CustomBlockPermutation> permutations) { private void addWallPermutations(List<CustomBlockPermutation> permutations) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder() CustomBlockComponents components = new GeyserCustomBlockComponents.Builder()
.selectionBox(WALL_BOX) .selectionBox(WALL_BOX)
.collisionBox(WALL_BOX) .collisionBox(WALL_BOX)
.geometry(new GeometryComponentBuilder() .geometry(new GeyserGeometryComponent.Builder()
.identifier("geometry.geyser.player_skull_wall") .identifier("geometry.geyser.player_skull_wall")
.build()) .build())
.transformation(new TransformationComponent(0, ROTATIONS[i], 0)) .transformation(new TransformationComponent(0, ROTATIONS[i], 0))

Datei anzeigen

@ -101,8 +101,10 @@ import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.geyser.Constants; import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.bedrock.camera.CameraData;
import org.geysermc.geyser.api.bedrock.camera.CameraShake; import org.geysermc.geyser.api.bedrock.camera.CameraShake;
import org.geysermc.geyser.api.connection.GeyserConnection; 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.GeyserEntity;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent;
@ -110,10 +112,13 @@ import org.geysermc.geyser.api.event.bedrock.SessionLoginEvent;
import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.api.network.RemoteServer;
import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.impl.camera.CameraDefinitions;
import org.geysermc.geyser.impl.camera.GeyserCameraData;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.GeyserEntityData;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.entity.type.ItemFrameEntity;
@ -151,7 +156,16 @@ import java.net.ConnectException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
import java.util.*; 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.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
@ -550,11 +564,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter @Setter
private boolean waitingForStatistics = false; private boolean waitingForStatistics = false;
/**
* All fog effects that are currently applied to the client.
*/
private final Set<String> appliedFog = new HashSet<>();
private final Set<UUID> emotes; private final Set<UUID> emotes;
/** /**
@ -586,6 +595,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
*/ */
private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>(); private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>();
private final GeyserCameraData cameraData;
private final GeyserEntityData entityData;
private MinecraftProtocol protocol; private MinecraftProtocol protocol;
public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop eventLoop) { public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop eventLoop) {
@ -607,6 +620,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.skullCache = new SkullCache(this); this.skullCache = new SkullCache(this);
this.tagCache = new TagCache(); this.tagCache = new TagCache();
this.worldCache = new WorldCache(this); this.worldCache = new WorldCache(this);
this.cameraData = new GeyserCameraData(this);
this.entityData = new GeyserEntityData(this);
this.worldBorder = new WorldBorder(this); this.worldBorder = new WorldBorder(this);
@ -667,6 +682,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
entityPacket.setIdentifiers(Registries.BEDROCK_ENTITY_IDENTIFIERS.get()); entityPacket.setIdentifiers(Registries.BEDROCK_ENTITY_IDENTIFIERS.get());
upstream.sendPacket(entityPacket); upstream.sendPacket(entityPacket);
CameraPresetsPacket cameraPresetsPacket = new CameraPresetsPacket();
cameraPresetsPacket.getPresets().addAll(CameraDefinitions.CAMERA_PRESETS);
upstream.sendPacket(cameraPresetsPacket);
CreativeContentPacket creativePacket = new CreativeContentPacket(); CreativeContentPacket creativePacket = new CreativeContentPacket();
creativePacket.setContents(this.itemMappings.getCreativeItems()); creativePacket.setContents(this.itemMappings.getCreativeItems());
upstream.sendPacket(creativePacket); upstream.sendPacket(creativePacket);
@ -1182,12 +1201,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
// Set the mood // Set the mood
if (shouldShowFog && !isInWorldBorderWarningArea) { if (shouldShowFog && !isInWorldBorderWarningArea) {
isInWorldBorderWarningArea = true; isInWorldBorderWarningArea = true;
sendFog("minecraft:fog_crimson_forest"); camera().sendFog("minecraft:fog_crimson_forest");
} }
} }
if (!shouldShowFog && isInWorldBorderWarningArea) { if (!shouldShowFog && isInWorldBorderWarningArea) {
// Clear fog as we are outside the world border now // Clear fog as we are outside the world border now
removeFog("minecraft:fog_crimson_forest"); camera().removeFog("minecraft:fog_crimson_forest");
isInWorldBorderWarningArea = false; isInWorldBorderWarningArea = false;
} }
@ -1480,6 +1499,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private void startGame() { private void startGame() {
this.upstream.getCodecHelper().setItemDefinitions(this.itemMappings); this.upstream.getCodecHelper().setItemDefinitions(this.itemMappings);
this.upstream.getCodecHelper().setBlockDefinitions((DefinitionRegistry) this.blockMappings); //FIXME this.upstream.getCodecHelper().setBlockDefinitions((DefinitionRegistry) this.blockMappings); //FIXME
this.upstream.getCodecHelper().setCameraPresetDefinitions(CameraDefinitions.CAMERA_DEFINITIONS);
StartGamePacket startGamePacket = new StartGamePacket(); StartGamePacket startGamePacket = new StartGamePacket();
startGamePacket.setUniqueEntityId(playerEntity.getGeyserId()); startGamePacket.setUniqueEntityId(playerEntity.getGeyserId());
@ -1980,72 +2000,66 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Override @Override
public @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId) { public @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId) {
CompletableFuture<GeyserEntity> future = new CompletableFuture<>(); return entities().entityByJavaId(javaId);
ensureInEventLoop(() -> future.complete(this.entityCache.getEntityByJavaId(javaId)));
return future;
} }
@Override @Override
public void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId) { public void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId) {
Entity entity = (Entity) emoter; entities().showEmote(emoter, emoteId);
if (entity.getSession() != this) {
throw new IllegalStateException("Given entity must be from this session!");
} }
EmotePacket packet = new EmotePacket(); public void lockInputs(boolean camera, boolean movement) {
packet.setRuntimeEntityId(entity.getGeyserId()); UpdateClientInputLocksPacket packet = new UpdateClientInputLocksPacket();
packet.setXuid(""); final int cameraOffset = 1 << 1;
packet.setPlatformId(""); // BDS sends empty final int movementOffset = 1 << 2;
packet.setEmoteId(emoteId);
int result = 0;
if (camera) {
result |= cameraOffset;
}
if (movement) {
result |= movementOffset;
}
packet.setLockComponentData(result);
packet.setServerPosition(this.playerEntity.getPosition());
sendUpstreamPacket(packet); sendUpstreamPacket(packet);
} }
@Override
public @NonNull CameraData camera() {
return this.cameraData;
}
@Override
public @NonNull EntityData entities() {
return this.entityData;
}
@Override @Override
public void shakeCamera(float intensity, float duration, @NonNull CameraShake type) { public void shakeCamera(float intensity, float duration, @NonNull CameraShake type) {
CameraShakePacket packet = new CameraShakePacket(); this.cameraData.shakeCamera(intensity, duration, type);
packet.setIntensity(intensity);
packet.setDuration(duration);
packet.setShakeType(type == CameraShake.POSITIONAL ? CameraShakeType.POSITIONAL : CameraShakeType.ROTATIONAL);
packet.setShakeAction(CameraShakeAction.ADD);
sendUpstreamPacket(packet);
} }
@Override @Override
public void stopCameraShake() { public void stopCameraShake() {
CameraShakePacket packet = new CameraShakePacket(); this.cameraData.stopCameraShake();
// 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 @Override
public void sendFog(String... fogNameSpaces) { public void sendFog(String... fogNameSpaces) {
Collections.addAll(this.appliedFog, fogNameSpaces); this.cameraData.sendFog(fogNameSpaces);
PlayerFogPacket packet = new PlayerFogPacket();
packet.getFogStack().addAll(this.appliedFog);
sendUpstreamPacket(packet);
} }
@Override @Override
public void removeFog(String... fogNameSpaces) { public void removeFog(String... fogNameSpaces) {
if (fogNameSpaces.length == 0) { this.cameraData.removeFog(fogNameSpaces);
this.appliedFog.clear();
} else {
for (String id : fogNameSpaces) {
this.appliedFog.remove(id);
}
}
PlayerFogPacket packet = new PlayerFogPacket();
packet.getFogStack().addAll(this.appliedFog);
sendUpstreamPacket(packet);
} }
@Override @Override
public @NonNull Set<String> fogEffects() { public @NonNull Set<String> fogEffects() {
// Use a copy so that sendFog/removeFog can be called while iterating the returned set (avoid CME) return this.cameraData.fogEffects();
return Set.copyOf(this.appliedFog);
} }
public void addCommandEnum(String name, String enums) { public void addCommandEnum(String name, String enums) {

Datei anzeigen

@ -139,7 +139,7 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
DimensionUtils.switchDimension(session, newDimension); DimensionUtils.switchDimension(session, newDimension);
} else if (DimensionUtils.isCustomBedrockNetherId() && newDimension.equalsIgnoreCase(DimensionUtils.NETHER)) { } else if (DimensionUtils.isCustomBedrockNetherId() && newDimension.equalsIgnoreCase(DimensionUtils.NETHER)) {
// If the player is spawning into the "fake" nether, send them some fog // If the player is spawning into the "fake" nether, send them some fog
session.sendFog(DimensionUtils.BEDROCK_FOG_HELL); session.camera().sendFog(DimensionUtils.BEDROCK_FOG_HELL);
} }
ChunkUtils.loadDimension(session); ChunkUtils.loadDimension(session);

Datei anzeigen

@ -143,9 +143,9 @@ public class DimensionUtils {
// thinks they are in the end dimension. // thinks they are in the end dimension.
if (isCustomBedrockNetherId()) { if (isCustomBedrockNetherId()) {
if (NETHER.equals(javaDimension)) { if (NETHER.equals(javaDimension)) {
session.sendFog(BEDROCK_FOG_HELL); session.camera().sendFog(BEDROCK_FOG_HELL);
} else if (NETHER.equals(previousDimension)) { } else if (NETHER.equals(previousDimension)) {
session.removeFog(BEDROCK_FOG_HELL); session.camera().removeFog(BEDROCK_FOG_HELL);
} }
} }
} }

Datei anzeigen

@ -7,5 +7,5 @@ org.gradle.vfs.watch=false
group=org.geysermc group=org.geysermc
id=geyser id=geyser
version=2.2.1-SNAPSHOT version=2.2.2-SNAPSHOT
description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers. description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers.

Datei anzeigen

@ -98,6 +98,8 @@ protocol-common = { group = "org.cloudburstmc.protocol", name = "common", versio
protocol-codec = { group = "org.cloudburstmc.protocol", name = "bedrock-codec", version.ref = "protocol" } protocol-codec = { group = "org.cloudburstmc.protocol", name = "bedrock-codec", version.ref = "protocol" }
protocol-connection = { group = "org.cloudburstmc.protocol", name = "bedrock-connection", version.ref = "protocol-connection" } protocol-connection = { group = "org.cloudburstmc.protocol", name = "bedrock-connection", version.ref = "protocol-connection" }
math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" }
blockstateupdater = { group = "org.cloudburstmc", name = "block-state-updater", version.ref = "blockstateupdater"} blockstateupdater = { group = "org.cloudburstmc", name = "block-state-updater", version.ref = "blockstateupdater"}
[bundles] [bundles]