mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-22 22:45:04 +01:00
Feature: Camera/Input locking API (#4332)
Adds API methods to control player cameras - including fancy transitions, color fades, or simple input locks.
This commit is contained in:
parent
07150db592
commit
f555dc0a92
36 changed files with 1523 additions and 149 deletions
|
@ -4,4 +4,5 @@ plugins {
|
|||
|
||||
dependencies {
|
||||
api(libs.base.api)
|
||||
api(libs.math)
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
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 {
|
||||
POSITIONAL,
|
||||
ROTATIONAL
|
||||
|
|
|
@ -29,8 +29,10 @@ import org.checkerframework.checker.index.qual.NonNegative;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||
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.player.GeyserPlayerEntity;
|
||||
|
||||
|
@ -41,10 +43,29 @@ import java.util.concurrent.CompletableFuture;
|
|||
* Represents a player connection used in Geyser.
|
||||
*/
|
||||
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.
|
||||
* @return a {@link GeyserEntity} if present in this connection's entity tracker.
|
||||
* @deprecated Use {@link EntityData#entityByJavaId(int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull
|
||||
CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId);
|
||||
|
||||
|
@ -53,11 +74,14 @@ public interface GeyserConnection extends Connection, CommandSource {
|
|||
*
|
||||
* @param emoter the player entity emoting.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
|
@ -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 duration the time in seconds that the shake will occur for
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Stops all camera shake of any type.
|
||||
*
|
||||
* @deprecated Use {@link CameraData#stopCameraShake()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
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>
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @deprecated Use {@link CameraData#removeFog(String...)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
void removeFog(String... fogNameSpaces);
|
||||
|
||||
/**
|
||||
* Returns an immutable copy of all fog affects currently applied to this client.
|
||||
*
|
||||
* @deprecated Use {@link CameraData#fogEffects()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull
|
||||
Set<String> fogEffects();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -25,7 +25,15 @@
|
|||
|
||||
package org.geysermc.geyser.api.entity.type.player;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
|
||||
public interface GeyserPlayerEntity extends GeyserEntity {
|
||||
|
||||
/**
|
||||
* Gets the position of the player.
|
||||
*
|
||||
* @return the position of the player.
|
||||
*/
|
||||
Vector3f position();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -39,12 +39,20 @@ import net.kyori.adventure.text.Component;
|
|||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
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.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
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.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
|
@ -433,4 +441,9 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
|
|||
public UUID getTabListUuid() {
|
||||
return getUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3f position() {
|
||||
return this.position.clone();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -115,7 +115,7 @@ public class GeyserCustomItemData implements CustomItemData {
|
|||
return tags;
|
||||
}
|
||||
|
||||
public static class CustomItemDataBuilder implements Builder {
|
||||
public static class Builder implements CustomItemData.Builder {
|
||||
protected String name = null;
|
||||
protected CustomItemOptions customItemOptions = null;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public record GeyserCustomItemOptions(TriState unbreakable,
|
|||
boolean defaultItem) implements CustomItemOptions {
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static class CustomItemOptionsBuilder implements CustomItemOptions.Builder {
|
||||
public static class Builder implements CustomItemOptions.Builder {
|
||||
private TriState unbreakable = TriState.NOT_SET;
|
||||
private OptionalInt customModelData = OptionalInt.empty();
|
||||
private OptionalInt damagePredicate = OptionalInt.empty();
|
||||
|
|
|
@ -59,7 +59,7 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
|
|||
private final boolean canAlwaysEat;
|
||||
private final boolean isChargeable;
|
||||
|
||||
public GeyserNonVanillaCustomItemData(NonVanillaCustomItemDataBuilder builder) {
|
||||
public GeyserNonVanillaCustomItemData(Builder builder) {
|
||||
super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand,
|
||||
builder.displayHandheld, builder.textureSize, builder.renderOffsets, builder.tags);
|
||||
|
||||
|
@ -168,7 +168,7 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
|
|||
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 int javaId = -1;
|
||||
|
||||
|
@ -197,49 +197,49 @@ public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData i
|
|||
private boolean chargeable = false;
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder name(@NonNull String name) {
|
||||
return (NonVanillaCustomItemData.Builder) super.name(name);
|
||||
public Builder name(@NonNull String name) {
|
||||
return (Builder) super.name(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) {
|
||||
public Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) {
|
||||
//Do nothing, as that value won't be read
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder allowOffhand(boolean allowOffhand) {
|
||||
return (NonVanillaCustomItemData.Builder) super.allowOffhand(allowOffhand);
|
||||
public Builder allowOffhand(boolean allowOffhand) {
|
||||
return (Builder) super.allowOffhand(allowOffhand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder displayHandheld(boolean displayHandheld) {
|
||||
return (NonVanillaCustomItemData.Builder) super.displayHandheld(displayHandheld);
|
||||
public Builder displayHandheld(boolean displayHandheld) {
|
||||
return (Builder) super.displayHandheld(displayHandheld);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder displayName(@NonNull String displayName) {
|
||||
return (NonVanillaCustomItemData.Builder) super.displayName(displayName);
|
||||
public Builder displayName(@NonNull String displayName) {
|
||||
return (Builder) super.displayName(displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder icon(@NonNull String icon) {
|
||||
return (NonVanillaCustomItemData.Builder) super.icon(icon);
|
||||
public Builder icon(@NonNull String icon) {
|
||||
return (Builder) super.icon(icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder textureSize(int textureSize) {
|
||||
return (NonVanillaCustomItemData.Builder) super.textureSize(textureSize);
|
||||
public Builder textureSize(int textureSize) {
|
||||
return (Builder) super.textureSize(textureSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder renderOffsets(CustomRenderOffsets renderOffsets) {
|
||||
return (NonVanillaCustomItemData.Builder) super.renderOffsets(renderOffsets);
|
||||
public Builder renderOffsets(CustomRenderOffsets renderOffsets) {
|
||||
return (Builder) super.renderOffsets(renderOffsets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomItemData.Builder tags(@Nullable Set<String> tags) {
|
||||
return (NonVanillaCustomItemData.Builder) super.tags(tags);
|
||||
public Builder tags(@Nullable Set<String> tags) {
|
||||
return (Builder) super.tags(tags);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -62,7 +62,7 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
|
|||
boolean placeAir;
|
||||
Set<String> tags;
|
||||
|
||||
private GeyserCustomBlockComponents(CustomBlockComponentsBuilder builder) {
|
||||
private GeyserCustomBlockComponents(Builder builder) {
|
||||
this.selectionBox = builder.selectionBox;
|
||||
this.collisionBox = builder.collisionBox;
|
||||
this.displayName = builder.displayName;
|
||||
|
@ -157,7 +157,7 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
|
|||
return tags;
|
||||
}
|
||||
|
||||
public static class CustomBlockComponentsBuilder implements Builder {
|
||||
public static class Builder implements CustomBlockComponents.Builder {
|
||||
protected BoxComponent selectionBox;
|
||||
protected BoxComponent collisionBox;
|
||||
protected String displayName;
|
||||
|
|
|
@ -54,7 +54,7 @@ public class GeyserCustomBlockData implements CustomBlockData {
|
|||
|
||||
private final Map<String, Object> defaultProperties;
|
||||
|
||||
GeyserCustomBlockData(CustomBlockDataBuilder builder) {
|
||||
GeyserCustomBlockData(Builder builder) {
|
||||
this.name = builder.name;
|
||||
if (name == null) {
|
||||
throw new IllegalStateException("Name must be set");
|
||||
|
@ -141,10 +141,10 @@ public class GeyserCustomBlockData implements CustomBlockData {
|
|||
|
||||
@Override
|
||||
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 boolean includedInCreativeInventory;
|
||||
private CreativeCategory creativeCategory;
|
||||
|
|
|
@ -64,7 +64,7 @@ public class GeyserCustomBlockState implements CustomBlockState {
|
|||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class CustomBlockStateBuilder implements CustomBlockState.Builder {
|
||||
public static class Builder implements CustomBlockState.Builder {
|
||||
private final CustomBlockData blockData;
|
||||
private final Object2ObjectMap<String, Object> properties = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public class GeyserGeometryComponent implements GeometryComponent {
|
|||
private final String identifier;
|
||||
private final Map<String, String> boneVisibility;
|
||||
|
||||
GeyserGeometryComponent(GeometryComponentBuilder builder) {
|
||||
GeyserGeometryComponent(Builder builder) {
|
||||
this.identifier = builder.identifier;
|
||||
this.boneVisibility = builder.boneVisibility;
|
||||
}
|
||||
|
@ -52,18 +52,18 @@ public class GeyserGeometryComponent implements GeometryComponent {
|
|||
return boneVisibility;
|
||||
}
|
||||
|
||||
public static class GeometryComponentBuilder implements Builder {
|
||||
public static class Builder implements GeometryComponent.Builder {
|
||||
private String identifier;
|
||||
private Map<String, String> boneVisibility;
|
||||
|
||||
@Override
|
||||
public GeometryComponent.Builder identifier(@NonNull String identifier) {
|
||||
public Builder identifier(@NonNull String identifier) {
|
||||
this.identifier = identifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeometryComponent.Builder boneVisibility(@Nullable Map<String, String> boneVisibility) {
|
||||
public Builder boneVisibility(@Nullable Map<String, String> boneVisibility) {
|
||||
this.boneVisibility = boneVisibility;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public class GeyserJavaBlockState implements JavaBlockState {
|
|||
String pistonBehavior;
|
||||
boolean hasBlockEntity;
|
||||
|
||||
private GeyserJavaBlockState(JavaBlockStateBuilder builder) {
|
||||
private GeyserJavaBlockState(Builder builder) {
|
||||
this.identifier = builder.identifier;
|
||||
this.javaId = builder.javaId;
|
||||
this.stateGroupId = builder.stateGroupId;
|
||||
|
@ -81,7 +81,7 @@ public class GeyserJavaBlockState implements JavaBlockState {
|
|||
return hasBlockEntity;
|
||||
}
|
||||
|
||||
public static class JavaBlockStateBuilder implements Builder {
|
||||
public static class Builder implements JavaBlockState.Builder {
|
||||
private String identifier;
|
||||
private int javaId;
|
||||
private int stateGroupId;
|
||||
|
|
|
@ -36,7 +36,7 @@ public class GeyserMaterialInstance implements MaterialInstance {
|
|||
private final boolean faceDimming;
|
||||
private final boolean ambientOcclusion;
|
||||
|
||||
GeyserMaterialInstance(MaterialInstanceBuilder builder) {
|
||||
GeyserMaterialInstance(Builder builder) {
|
||||
this.texture = builder.texture;
|
||||
this.renderMethod = builder.renderMethod;
|
||||
this.faceDimming = builder.faceDimming;
|
||||
|
@ -63,7 +63,7 @@ public class GeyserMaterialInstance implements MaterialInstance {
|
|||
return ambientOcclusion;
|
||||
}
|
||||
|
||||
public static class MaterialInstanceBuilder implements Builder {
|
||||
public static class Builder implements MaterialInstance.Builder {
|
||||
private String texture;
|
||||
private String renderMethod;
|
||||
private boolean faceDimming;
|
||||
|
|
|
@ -37,7 +37,7 @@ import java.util.List;
|
|||
public class GeyserNonVanillaCustomBlockData extends GeyserCustomBlockData implements NonVanillaCustomBlockData {
|
||||
private final String namespace;
|
||||
|
||||
GeyserNonVanillaCustomBlockData(NonVanillaCustomBlockDataBuilder builder) {
|
||||
GeyserNonVanillaCustomBlockData(Builder builder) {
|
||||
super(builder);
|
||||
|
||||
this.namespace = builder.namespace;
|
||||
|
@ -56,58 +56,58 @@ public class GeyserNonVanillaCustomBlockData extends GeyserCustomBlockData imple
|
|||
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;
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder namespace(@NonNull String namespace) {
|
||||
public Builder namespace(@NonNull String namespace) {
|
||||
this.namespace = namespace;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder name(@NonNull String name) {
|
||||
return (NonVanillaCustomBlockDataBuilder) super.name(name);
|
||||
public Builder name(@NonNull String name) {
|
||||
return (Builder) super.name(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder includedInCreativeInventory(boolean includedInCreativeInventory) {
|
||||
return (NonVanillaCustomBlockDataBuilder) super.includedInCreativeInventory(includedInCreativeInventory);
|
||||
public Builder includedInCreativeInventory(boolean includedInCreativeInventory) {
|
||||
return (Builder) super.includedInCreativeInventory(includedInCreativeInventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder creativeCategory(@Nullable CreativeCategory creativeCategories) {
|
||||
return (NonVanillaCustomBlockDataBuilder) super.creativeCategory(creativeCategories);
|
||||
public Builder creativeCategory(@Nullable CreativeCategory creativeCategories) {
|
||||
return (Builder) super.creativeCategory(creativeCategories);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder creativeGroup(@Nullable String creativeGroup) {
|
||||
return (NonVanillaCustomBlockDataBuilder) super.creativeGroup(creativeGroup);
|
||||
public Builder creativeGroup(@Nullable String creativeGroup) {
|
||||
return (Builder) super.creativeGroup(creativeGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder components(@NonNull CustomBlockComponents components) {
|
||||
return (NonVanillaCustomBlockDataBuilder) super.components(components);
|
||||
public Builder components(@NonNull CustomBlockComponents components) {
|
||||
return (Builder) super.components(components);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder booleanProperty(@NonNull String propertyName) {
|
||||
return (NonVanillaCustomBlockDataBuilder) super.booleanProperty(propertyName);
|
||||
public Builder booleanProperty(@NonNull String propertyName) {
|
||||
return (Builder) super.booleanProperty(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder intProperty(@NonNull String propertyName, List<Integer> values) {
|
||||
return (NonVanillaCustomBlockDataBuilder) super.intProperty(propertyName, values);
|
||||
public Builder intProperty(@NonNull String propertyName, List<Integer> values) {
|
||||
return (Builder) super.intProperty(propertyName, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder stringProperty(@NonNull String propertyName, List<String> values) {
|
||||
return (NonVanillaCustomBlockDataBuilder) super.stringProperty(propertyName, values);
|
||||
public Builder stringProperty(@NonNull String propertyName, List<String> values) {
|
||||
return (Builder) super.stringProperty(propertyName, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonVanillaCustomBlockDataBuilder permutations(@NonNull List<CustomBlockPermutation> permutations) {
|
||||
return (NonVanillaCustomBlockDataBuilder) super.permutations(permutations);
|
||||
public Builder permutations(@NonNull List<CustomBlockPermutation> permutations) {
|
||||
return (Builder) super.permutations(permutations);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -100,6 +100,16 @@ public class LoggingPacketHandler implements BedrockPacketHandler {
|
|||
return defaultHandler(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSignal handle(CameraPresetsPacket packet) {
|
||||
return defaultHandler(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSignal handle(CameraInstructionPacket packet) {
|
||||
return defaultHandler(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSignal handle(CommandBlockUpdatePacket packet) {
|
||||
return defaultHandler(packet);
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
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.NonVanillaCustomBlockData;
|
||||
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.NonVanillaCustomItemData;
|
||||
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.event.GeyserEventRegistrar;
|
||||
import org.geysermc.geyser.item.GeyserCustomItemData;
|
||||
|
@ -65,20 +69,24 @@ public class ProviderRegistryLoader implements RegistryLoader<Map<Class<?>, Prov
|
|||
// misc
|
||||
providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Extension) args[0]));
|
||||
|
||||
providers.put(CustomBlockComponents.Builder.class, args -> new GeyserCustomBlockComponents.CustomBlockComponentsBuilder());
|
||||
providers.put(CustomBlockData.Builder.class, args -> new GeyserCustomBlockData.CustomBlockDataBuilder());
|
||||
providers.put(JavaBlockState.Builder.class, args -> new GeyserJavaBlockState.JavaBlockStateBuilder());
|
||||
providers.put(NonVanillaCustomBlockData.Builder.class, args -> new GeyserNonVanillaCustomBlockData.NonVanillaCustomBlockDataBuilder());
|
||||
providers.put(MaterialInstance.Builder.class, args -> new GeyserMaterialInstance.MaterialInstanceBuilder());
|
||||
providers.put(GeometryComponent.Builder.class, args -> new GeyserGeometryComponent.GeometryComponentBuilder());
|
||||
providers.put(CustomBlockComponents.Builder.class, args -> new GeyserCustomBlockComponents.Builder());
|
||||
providers.put(CustomBlockData.Builder.class, args -> new GeyserCustomBlockData.Builder());
|
||||
providers.put(JavaBlockState.Builder.class, args -> new GeyserJavaBlockState.Builder());
|
||||
providers.put(NonVanillaCustomBlockData.Builder.class, args -> new GeyserNonVanillaCustomBlockData.Builder());
|
||||
providers.put(MaterialInstance.Builder.class, args -> new GeyserMaterialInstance.Builder());
|
||||
providers.put(GeometryComponent.Builder.class, args -> new GeyserGeometryComponent.Builder());
|
||||
|
||||
providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0]));
|
||||
providers.put(PathPackCodec.class, args -> new GeyserPathPackCodec((Path) args[0]));
|
||||
|
||||
// items
|
||||
providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder());
|
||||
providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder());
|
||||
providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder());
|
||||
providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.Builder());
|
||||
providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.Builder());
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -35,17 +35,22 @@ import org.geysermc.geyser.GeyserImpl;
|
|||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockPermutation;
|
||||
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.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.CustomItemOptions;
|
||||
import org.geysermc.geyser.api.util.CreativeCategory;
|
||||
import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException;
|
||||
import org.geysermc.geyser.level.block.GeyserCustomBlockComponents.CustomBlockComponentsBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserCustomBlockData.CustomBlockDataBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserGeometryComponent.GeometryComponentBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance.MaterialInstanceBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserCustomBlockComponents;
|
||||
import org.geysermc.geyser.level.block.GeyserCustomBlockData;
|
||||
import org.geysermc.geyser.level.block.GeyserGeometryComponent;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
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 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.Function;
|
||||
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();
|
||||
|
||||
// Create the data for the overall block
|
||||
CustomBlockData.Builder customBlockDataBuilder = new CustomBlockDataBuilder()
|
||||
CustomBlockData.Builder customBlockDataBuilder = new GeyserCustomBlockData.Builder()
|
||||
.name(name)
|
||||
.includedInCreativeInventory(includedInCreativeInventory)
|
||||
.creativeCategory(creativeCategory)
|
||||
|
@ -360,7 +372,7 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||
int id = BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(stateKey, -1);
|
||||
BoxComponent boxComponent = createBoxComponent(id);
|
||||
BoxComponent extendedBoxComponent = createExtendedBoxComponent(id);
|
||||
CustomBlockComponents.Builder builder = new CustomBlockComponentsBuilder()
|
||||
CustomBlockComponents.Builder builder = new GeyserCustomBlockComponents.Builder()
|
||||
.collisionBox(boxComponent)
|
||||
.selectionBox(boxComponent);
|
||||
|
||||
|
@ -392,12 +404,12 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||
|
||||
if (node.has("geometry")) {
|
||||
if (node.get("geometry").isTextual()) {
|
||||
builder.geometry(new GeometryComponentBuilder()
|
||||
builder.geometry(new GeyserGeometryComponent.Builder()
|
||||
.identifier(node.get("geometry").asText())
|
||||
.build());
|
||||
} else {
|
||||
JsonNode geometry = node.get("geometry");
|
||||
GeometryComponentBuilder geometryBuilder = new GeometryComponentBuilder();
|
||||
GeometryComponent.Builder geometryBuilder = new GeyserGeometryComponent.Builder();
|
||||
if (geometry.has("identifier")) {
|
||||
geometryBuilder.identifier(geometry.get("identifier").asText());
|
||||
}
|
||||
|
@ -654,7 +666,7 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||
ambientOcclusion = node.get("ambient_occlusion").asBoolean();
|
||||
}
|
||||
|
||||
return new MaterialInstanceBuilder()
|
||||
return new GeyserMaterialInstance.Builder()
|
||||
.texture(texture)
|
||||
.renderMethod(renderMethod)
|
||||
.faceDimming(faceDimming)
|
||||
|
|
|
@ -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.event.lifecycle.GeyserDefineCustomBlocksEvent;
|
||||
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.GeyserCustomBlockComponents.CustomBlockComponentsBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserCustomBlockData.CustomBlockDataBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserGeometryComponent.GeometryComponentBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance.MaterialInstanceBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserGeometryComponent;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.mappings.MappingsConfigReader;
|
||||
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||
|
@ -479,20 +479,20 @@ public class CustomBlockRegistryPopulator {
|
|||
}
|
||||
|
||||
private static CustomBlockData createExtendedCollisionBlock(BoxComponent boxComponent, int extendedCollisionBlock) {
|
||||
return new CustomBlockDataBuilder()
|
||||
return new GeyserCustomBlockData.Builder()
|
||||
.name("extended_collision_" + extendedCollisionBlock)
|
||||
.components(
|
||||
new CustomBlockComponentsBuilder()
|
||||
new GeyserCustomBlockComponents.Builder()
|
||||
.collisionBox(boxComponent)
|
||||
.selectionBox(BoxComponent.emptyBox())
|
||||
.materialInstance("*", new MaterialInstanceBuilder()
|
||||
.materialInstance("*", new GeyserMaterialInstance.Builder()
|
||||
.texture("glass")
|
||||
.renderMethod("alpha_test")
|
||||
.faceDimming(false)
|
||||
.ambientOcclusion(false)
|
||||
.build())
|
||||
.lightDampening(0)
|
||||
.geometry(new GeometryComponentBuilder()
|
||||
.geometry(new GeyserGeometryComponent.Builder()
|
||||
.identifier("geometry.invisible")
|
||||
.build())
|
||||
.build())
|
||||
|
|
|
@ -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.level.block.GeyserCustomBlockComponents;
|
||||
import org.geysermc.geyser.level.block.GeyserCustomBlockData;
|
||||
import org.geysermc.geyser.level.block.GeyserGeometryComponent.GeometryComponentBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance.MaterialInstanceBuilder;
|
||||
import org.geysermc.geyser.level.block.GeyserGeometryComponent;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -65,9 +65,9 @@ public class CustomSkull {
|
|||
public CustomSkull(String skinHash) {
|
||||
this.skinHash = skinHash;
|
||||
|
||||
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder()
|
||||
CustomBlockComponents components = new GeyserCustomBlockComponents.Builder()
|
||||
.destructibleByMining(1.5f)
|
||||
.materialInstance("*", new MaterialInstanceBuilder()
|
||||
.materialInstance("*", new GeyserMaterialInstance.Builder()
|
||||
.texture("geyser." + skinHash + "_player_skin")
|
||||
.renderMethod("alpha_test")
|
||||
.faceDimming(true)
|
||||
|
@ -82,7 +82,7 @@ public class CustomSkull {
|
|||
addFloorPermutations(permutations);
|
||||
addWallPermutations(permutations);
|
||||
|
||||
customBlockData = new GeyserCustomBlockData.CustomBlockDataBuilder()
|
||||
customBlockData = new GeyserCustomBlockData.Builder()
|
||||
.name("player_skull_" + skinHash)
|
||||
.components(components)
|
||||
.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) {
|
||||
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder()
|
||||
.geometry(new GeometryComponentBuilder()
|
||||
CustomBlockComponents components = new GeyserCustomBlockComponents.Builder()
|
||||
.geometry(new GeyserGeometryComponent.Builder()
|
||||
.identifier("geometry.geyser.player_skull_hand")
|
||||
.build())
|
||||
.transformation(new TransformationComponent(0, 180, 0))
|
||||
|
@ -131,10 +131,10 @@ public class CustomSkull {
|
|||
for (int quadrant = 0; quadrant < 4; quadrant++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int floorRotation = 4 * quadrant + i;
|
||||
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder()
|
||||
CustomBlockComponents components = new GeyserCustomBlockComponents.Builder()
|
||||
.selectionBox(FLOOR_BOX)
|
||||
.collisionBox(FLOOR_BOX)
|
||||
.geometry(new GeometryComponentBuilder()
|
||||
.geometry(new GeyserGeometryComponent.Builder()
|
||||
.identifier("geometry.geyser.player_skull_floor_" + quadrantNames[i])
|
||||
.build())
|
||||
.transformation(new TransformationComponent(0, ROTATIONS[quadrant], 0))
|
||||
|
@ -150,10 +150,10 @@ public class CustomSkull {
|
|||
|
||||
private void addWallPermutations(List<CustomBlockPermutation> permutations) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder()
|
||||
CustomBlockComponents components = new GeyserCustomBlockComponents.Builder()
|
||||
.selectionBox(WALL_BOX)
|
||||
.collisionBox(WALL_BOX)
|
||||
.geometry(new GeometryComponentBuilder()
|
||||
.geometry(new GeyserGeometryComponent.Builder()
|
||||
.identifier("geometry.geyser.player_skull_wall")
|
||||
.build())
|
||||
.transformation(new TransformationComponent(0, ROTATIONS[i], 0))
|
||||
|
|
|
@ -101,8 +101,10 @@ import org.geysermc.floodgate.crypto.FloodgateCipher;
|
|||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.entity.EntityData;
|
||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||
import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent;
|
||||
|
@ -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.RemoteServer;
|
||||
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.configuration.EmoteOffhandWorkaroundOption;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.GeyserEntityData;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||
|
@ -151,7 +156,16 @@ import java.net.ConnectException;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
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.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
@ -550,11 +564,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
@Setter
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -586,6 +595,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
*/
|
||||
private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private final GeyserCameraData cameraData;
|
||||
|
||||
private final GeyserEntityData entityData;
|
||||
|
||||
private MinecraftProtocol protocol;
|
||||
|
||||
public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop eventLoop) {
|
||||
|
@ -607,6 +620,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
this.skullCache = new SkullCache(this);
|
||||
this.tagCache = new TagCache();
|
||||
this.worldCache = new WorldCache(this);
|
||||
this.cameraData = new GeyserCameraData(this);
|
||||
this.entityData = new GeyserEntityData(this);
|
||||
|
||||
this.worldBorder = new WorldBorder(this);
|
||||
|
||||
|
@ -667,6 +682,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
entityPacket.setIdentifiers(Registries.BEDROCK_ENTITY_IDENTIFIERS.get());
|
||||
upstream.sendPacket(entityPacket);
|
||||
|
||||
CameraPresetsPacket cameraPresetsPacket = new CameraPresetsPacket();
|
||||
cameraPresetsPacket.getPresets().addAll(CameraDefinitions.CAMERA_PRESETS);
|
||||
upstream.sendPacket(cameraPresetsPacket);
|
||||
|
||||
CreativeContentPacket creativePacket = new CreativeContentPacket();
|
||||
creativePacket.setContents(this.itemMappings.getCreativeItems());
|
||||
upstream.sendPacket(creativePacket);
|
||||
|
@ -1182,12 +1201,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
// Set the mood
|
||||
if (shouldShowFog && !isInWorldBorderWarningArea) {
|
||||
isInWorldBorderWarningArea = true;
|
||||
sendFog("minecraft:fog_crimson_forest");
|
||||
camera().sendFog("minecraft:fog_crimson_forest");
|
||||
}
|
||||
}
|
||||
if (!shouldShowFog && isInWorldBorderWarningArea) {
|
||||
// Clear fog as we are outside the world border now
|
||||
removeFog("minecraft:fog_crimson_forest");
|
||||
camera().removeFog("minecraft:fog_crimson_forest");
|
||||
isInWorldBorderWarningArea = false;
|
||||
}
|
||||
|
||||
|
@ -1480,6 +1499,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
private void startGame() {
|
||||
this.upstream.getCodecHelper().setItemDefinitions(this.itemMappings);
|
||||
this.upstream.getCodecHelper().setBlockDefinitions((DefinitionRegistry) this.blockMappings); //FIXME
|
||||
this.upstream.getCodecHelper().setCameraPresetDefinitions(CameraDefinitions.CAMERA_DEFINITIONS);
|
||||
|
||||
StartGamePacket startGamePacket = new StartGamePacket();
|
||||
startGamePacket.setUniqueEntityId(playerEntity.getGeyserId());
|
||||
|
@ -1980,72 +2000,66 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId) {
|
||||
CompletableFuture<GeyserEntity> future = new CompletableFuture<>();
|
||||
ensureInEventLoop(() -> future.complete(this.entityCache.getEntityByJavaId(javaId)));
|
||||
return future;
|
||||
return entities().entityByJavaId(javaId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId) {
|
||||
Entity entity = (Entity) emoter;
|
||||
if (entity.getSession() != this) {
|
||||
throw new IllegalStateException("Given entity must be from this session!");
|
||||
entities().showEmote(emoter, emoteId);
|
||||
}
|
||||
|
||||
public void lockInputs(boolean camera, boolean movement) {
|
||||
UpdateClientInputLocksPacket packet = new UpdateClientInputLocksPacket();
|
||||
final int cameraOffset = 1 << 1;
|
||||
final int movementOffset = 1 << 2;
|
||||
|
||||
int result = 0;
|
||||
if (camera) {
|
||||
result |= cameraOffset;
|
||||
}
|
||||
if (movement) {
|
||||
result |= movementOffset;
|
||||
}
|
||||
|
||||
EmotePacket packet = new EmotePacket();
|
||||
packet.setRuntimeEntityId(entity.getGeyserId());
|
||||
packet.setXuid("");
|
||||
packet.setPlatformId(""); // BDS sends empty
|
||||
packet.setEmoteId(emoteId);
|
||||
packet.setLockComponentData(result);
|
||||
packet.setServerPosition(this.playerEntity.getPosition());
|
||||
|
||||
sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CameraData camera() {
|
||||
return this.cameraData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull EntityData entities() {
|
||||
return this.entityData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shakeCamera(float intensity, float duration, @NonNull CameraShake type) {
|
||||
CameraShakePacket packet = new CameraShakePacket();
|
||||
packet.setIntensity(intensity);
|
||||
packet.setDuration(duration);
|
||||
packet.setShakeType(type == CameraShake.POSITIONAL ? CameraShakeType.POSITIONAL : CameraShakeType.ROTATIONAL);
|
||||
packet.setShakeAction(CameraShakeAction.ADD);
|
||||
sendUpstreamPacket(packet);
|
||||
this.cameraData.shakeCamera(intensity, duration, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopCameraShake() {
|
||||
CameraShakePacket packet = new CameraShakePacket();
|
||||
// CameraShakeAction.STOP removes all types regardless of the given type, but regardless it can't be null
|
||||
packet.setShakeType(CameraShakeType.POSITIONAL);
|
||||
packet.setShakeAction(CameraShakeAction.STOP);
|
||||
sendUpstreamPacket(packet);
|
||||
this.cameraData.stopCameraShake();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFog(String... fogNameSpaces) {
|
||||
Collections.addAll(this.appliedFog, fogNameSpaces);
|
||||
|
||||
PlayerFogPacket packet = new PlayerFogPacket();
|
||||
packet.getFogStack().addAll(this.appliedFog);
|
||||
sendUpstreamPacket(packet);
|
||||
this.cameraData.sendFog(fogNameSpaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFog(String... fogNameSpaces) {
|
||||
if (fogNameSpaces.length == 0) {
|
||||
this.appliedFog.clear();
|
||||
} else {
|
||||
for (String id : fogNameSpaces) {
|
||||
this.appliedFog.remove(id);
|
||||
}
|
||||
}
|
||||
PlayerFogPacket packet = new PlayerFogPacket();
|
||||
packet.getFogStack().addAll(this.appliedFog);
|
||||
sendUpstreamPacket(packet);
|
||||
this.cameraData.removeFog(fogNameSpaces);
|
||||
}
|
||||
|
||||
@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);
|
||||
return this.cameraData.fogEffects();
|
||||
}
|
||||
|
||||
public void addCommandEnum(String name, String enums) {
|
||||
|
|
|
@ -139,7 +139,7 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
|||
DimensionUtils.switchDimension(session, newDimension);
|
||||
} else if (DimensionUtils.isCustomBedrockNetherId() && newDimension.equalsIgnoreCase(DimensionUtils.NETHER)) {
|
||||
// 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);
|
||||
|
|
|
@ -143,9 +143,9 @@ public class DimensionUtils {
|
|||
// thinks they are in the end dimension.
|
||||
if (isCustomBedrockNetherId()) {
|
||||
if (NETHER.equals(javaDimension)) {
|
||||
session.sendFog(BEDROCK_FOG_HELL);
|
||||
session.camera().sendFog(BEDROCK_FOG_HELL);
|
||||
} else if (NETHER.equals(previousDimension)) {
|
||||
session.removeFog(BEDROCK_FOG_HELL);
|
||||
session.camera().removeFog(BEDROCK_FOG_HELL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,5 @@ org.gradle.vfs.watch=false
|
|||
|
||||
group=org.geysermc
|
||||
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.
|
|
@ -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-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"}
|
||||
|
||||
[bundles]
|
||||
|
|
Loading…
Reference in a new issue