mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-30 20:29:19 +00:00
Merge branch 'master' into feature/networking-api
This commit is contained in:
@@ -15,7 +15,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
|
||||
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
|
||||
|
||||
## Supported Versions
|
||||
Geyser is currently supporting Minecraft Bedrock 1.21.90 - 1.21.114 and Minecraft Java 1.21.9 - 1.21.10. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
|
||||
Geyser is currently supporting Minecraft Bedrock 1.21.90 - 1.21.120 and Minecraft Java 1.21.9 - 1.21.10. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://geysermc.org/wiki/geyser/setup/) for how to set up Geyser.
|
||||
|
||||
@@ -52,7 +52,7 @@ public interface PriorityOption extends ResourcePackOption<Integer> {
|
||||
*/
|
||||
static PriorityOption priority(int priority) {
|
||||
if (priority < -100 || priority > 100) {
|
||||
throw new IllegalArgumentException("Priority must be between 0 and 10 inclusive!");
|
||||
throw new IllegalArgumentException("Priority must be between -100 and 100 inclusive!");
|
||||
}
|
||||
return GeyserApi.api().provider(PriorityOption.class, priority);
|
||||
}
|
||||
|
||||
@@ -216,7 +216,11 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
||||
|
||||
public void initialize() {
|
||||
// Setup encryption early so we don't start if we can't auth
|
||||
EncryptionUtils.getMojangPublicKey();
|
||||
try {
|
||||
EncryptionUtils.getMojangPublicKey();
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Cannot setup authentication! Are you offline? ", e);
|
||||
}
|
||||
|
||||
long startupTime = System.currentTimeMillis();
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, Entit
|
||||
if (identifier == null && type != null) {
|
||||
identifier = "minecraft:" + type.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
GeyserEntityProperties registeredProperties = propertiesBuilder == null ? null : propertiesBuilder.build();
|
||||
GeyserEntityProperties registeredProperties = propertiesBuilder == null ? new GeyserEntityProperties() : propertiesBuilder.build();
|
||||
EntityDefinition<T> definition = new EntityDefinition<>(factory, type, identifier, width, height, offset, registeredProperties, translators);
|
||||
if (register && definition.entityType() != null) {
|
||||
Registries.ENTITY_DEFINITIONS.get().putIfAbsent(definition.entityType(), definition);
|
||||
|
||||
@@ -1272,7 +1272,7 @@ public final class EntityDefinitions {
|
||||
if (propertyId.vanilla()) {
|
||||
throw new IllegalArgumentException("Cannot register custom property in vanilla namespace! " + propertyId);
|
||||
}
|
||||
FloatProperty property = new FloatProperty(propertyId, min, max, defaultValue);
|
||||
FloatProperty property = new FloatProperty(propertyId, max, min, defaultValue);
|
||||
registerProperty(identifier, property);
|
||||
return property;
|
||||
}
|
||||
@@ -1284,7 +1284,7 @@ public final class EntityDefinitions {
|
||||
if (propertyId.vanilla()) {
|
||||
throw new IllegalArgumentException("Cannot register custom property in vanilla namespace! " + propertyId);
|
||||
}
|
||||
IntProperty property = new IntProperty(propertyId, min, max, defaultValue);
|
||||
IntProperty property = new IntProperty(propertyId, max, min, defaultValue);
|
||||
registerProperty(identifier, property);
|
||||
return property;
|
||||
}
|
||||
@@ -1339,7 +1339,7 @@ public final class EntityDefinitions {
|
||||
});
|
||||
|
||||
for (var definition : Registries.ENTITY_DEFINITIONS.get().values()) {
|
||||
if (definition.registeredProperties() != null) {
|
||||
if (!definition.registeredProperties().isEmpty()) {
|
||||
Registries.BEDROCK_ENTITY_PROPERTIES.get().add(definition.registeredProperties().toNbtMap(definition.identifier()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +50,8 @@ public class GeyserEntityProperties {
|
||||
|
||||
private final static Pattern ENTITY_PROPERTY_PATTERN = Pattern.compile("^[a-z0-9_.:-]*:[a-z0-9_.:-]*$");
|
||||
|
||||
private final ObjectArrayList<PropertyType<?, ?>> properties;
|
||||
private final Object2IntMap<String> propertyIndices;
|
||||
|
||||
private GeyserEntityProperties() {
|
||||
this.properties = new ObjectArrayList<>();
|
||||
this.propertyIndices = new Object2IntOpenHashMap<>();
|
||||
}
|
||||
private ObjectArrayList<PropertyType<?, ?>> properties;
|
||||
private Object2IntMap<String> propertyIndices;
|
||||
|
||||
public NbtMap toNbtMap(String entityType) {
|
||||
NbtMapBuilder mapBuilder = NbtMap.builder();
|
||||
@@ -75,6 +70,11 @@ public class GeyserEntityProperties {
|
||||
throw new IllegalStateException("Cannot add properties outside the GeyserDefineEntityProperties event!");
|
||||
}
|
||||
|
||||
if (properties == null || propertyIndices == null) {
|
||||
this.properties = new ObjectArrayList<>(0);
|
||||
this.propertyIndices = new Object2IntOpenHashMap<>(0);
|
||||
}
|
||||
|
||||
if (this.properties.size() > 32) {
|
||||
throw new IllegalArgumentException("Cannot register more than 32 properties for entity type " + entityType);
|
||||
}
|
||||
@@ -94,7 +94,11 @@ public class GeyserEntityProperties {
|
||||
}
|
||||
|
||||
public @NonNull List<PropertyType<?, ?>> getProperties() {
|
||||
return properties;
|
||||
return properties == null ? List.of() : properties;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return properties == null || properties.isEmpty();
|
||||
}
|
||||
|
||||
public int getPropertyIndex(String name) {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.entity.properties.type;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.entity.property.type.GeyserEnumEntityProperty;
|
||||
import org.geysermc.geyser.api.util.Identifier;
|
||||
|
||||
@@ -35,7 +36,7 @@ import java.util.Locale;
|
||||
public record EnumProperty<E extends Enum<E>>(
|
||||
Identifier identifier,
|
||||
Class<E> enumClass,
|
||||
E defaultValue
|
||||
@NonNull E defaultValue
|
||||
) implements AbstractEnumProperty<E>, GeyserEnumEntityProperty<E> {
|
||||
|
||||
public EnumProperty {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.entity.properties.type;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.FloatEntityProperty;
|
||||
import org.geysermc.geyser.api.entity.property.type.GeyserFloatEntityProperty;
|
||||
@@ -34,7 +35,7 @@ public record FloatProperty(
|
||||
Identifier identifier,
|
||||
float max,
|
||||
float min,
|
||||
Float defaultValue
|
||||
@Nullable Float defaultValue
|
||||
) implements PropertyType<Float, FloatEntityProperty>, GeyserFloatEntityProperty {
|
||||
|
||||
public FloatProperty {
|
||||
@@ -42,7 +43,7 @@ public record FloatProperty(
|
||||
throw new IllegalArgumentException("Cannot create float entity property (%s) with a minimum value (%s) greater than maximum (%s)!"
|
||||
.formatted(identifier, min, max));
|
||||
}
|
||||
if (defaultValue < min || defaultValue > max) {
|
||||
if (defaultValue != null && (defaultValue < min || defaultValue > max)) {
|
||||
throw new IllegalArgumentException("Cannot create float entity property (%s) with a default value (%s) outside of the range (%s - %s)!"
|
||||
.formatted(identifier, defaultValue, min, max));
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.entity.properties.type;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.IntEntityProperty;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
@@ -35,7 +36,7 @@ public record IntProperty(
|
||||
Identifier identifier,
|
||||
int max,
|
||||
int min,
|
||||
Integer defaultValue
|
||||
@Nullable Integer defaultValue
|
||||
) implements PropertyType<Integer, IntEntityProperty>, GeyserIntEntityProperty {
|
||||
|
||||
public IntProperty {
|
||||
@@ -43,7 +44,7 @@ public record IntProperty(
|
||||
throw new IllegalArgumentException("Cannot create int entity property (%s) with a minimum value (%s) greater than maximum (%s)!"
|
||||
.formatted(identifier, min, max));
|
||||
}
|
||||
if (defaultValue < min || defaultValue > max) {
|
||||
if (defaultValue != null && (defaultValue < min || defaultValue > max)) {
|
||||
throw new IllegalArgumentException("Cannot create int entity property (%s) with a default value (%s) outside of the range (%s - %s)!"
|
||||
.formatted(identifier, defaultValue, min, max));
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ public class Entity implements GeyserEntity {
|
||||
|
||||
this.valid = false;
|
||||
|
||||
this.propertyManager = definition.registeredProperties() == null ? null : new GeyserEntityPropertyManager(definition.registeredProperties());
|
||||
this.propertyManager = definition.registeredProperties().isEmpty() ? null : new GeyserEntityPropertyManager(definition.registeredProperties());
|
||||
|
||||
setPosition(position);
|
||||
setAirSupply(getMaxAir());
|
||||
|
||||
@@ -210,6 +210,13 @@ public class MinecartEntity extends Entity implements Tickable {
|
||||
return Vector3f.from(0, getYaw(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesJumpDismount() {
|
||||
// This is a little bit misleading because jumping is literally the only way to dismount for Touch users.
|
||||
// Therefore, do this so we won't lock jumping to let Touch user able to dismount.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InteractiveTag testInteraction(Hand hand) {
|
||||
if (definition == EntityDefinitions.CHEST_MINECART || definition == EntityDefinitions.HOPPER_MINECART) {
|
||||
|
||||
@@ -162,7 +162,7 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||
* a block to hold the inventory that's wildly out of range.
|
||||
*/
|
||||
protected boolean checkInteractionPosition(GeyserSession session) {
|
||||
return session.getLastInteractionPlayerPosition().equals(session.getPlayerEntity().getPosition());
|
||||
return session.getLastInteractionPlayerPosition().distance(session.getPlayerEntity().getPosition()) < 2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ package org.geysermc.geyser.item.hashing;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.KeybindComponent;
|
||||
import net.kyori.adventure.text.NBTComponent;
|
||||
import net.kyori.adventure.text.ObjectComponent;
|
||||
import net.kyori.adventure.text.ScoreComponent;
|
||||
import net.kyori.adventure.text.SelectorComponent;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
@@ -39,6 +40,9 @@ import net.kyori.adventure.text.format.ShadowColor;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.object.ObjectContents;
|
||||
import net.kyori.adventure.text.object.PlayerHeadObjectContents;
|
||||
import org.geysermc.geyser.item.hashing.data.ObjectContentsType;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -54,6 +58,17 @@ public interface ComponentHasher {
|
||||
}
|
||||
});
|
||||
|
||||
MinecraftHasher<PlayerHeadObjectContents.ProfileProperty> PROFILE_PROPERTY = MinecraftHasher.mapBuilder(builder -> builder
|
||||
.accept("name", MinecraftHasher.STRING, PlayerHeadObjectContents.ProfileProperty::name)
|
||||
.accept("value", MinecraftHasher.STRING, PlayerHeadObjectContents.ProfileProperty::value)
|
||||
.accept("signature", MinecraftHasher.STRING, PlayerHeadObjectContents.ProfileProperty::signature));
|
||||
|
||||
MinecraftHasher<PlayerHeadObjectContents> RESOLVABLE_PROFILE = MinecraftHasher.mapBuilder(builder -> builder
|
||||
.optionalNullable("name", MinecraftHasher.STRING, PlayerHeadObjectContents::name)
|
||||
.optionalNullable("id", MinecraftHasher.UUID, PlayerHeadObjectContents::id)
|
||||
.optionalList("properties", PROFILE_PROPERTY, PlayerHeadObjectContents::profileProperties)
|
||||
.optionalNullable("texture", MinecraftHasher.KEY, PlayerHeadObjectContents::texture));
|
||||
|
||||
MinecraftHasher<NamedTextColor> NAMED_COLOR = MinecraftHasher.STRING.cast(NamedTextColor::toString);
|
||||
|
||||
MinecraftHasher<TextColor> DIRECT_COLOR = MinecraftHasher.STRING.cast(TextColor::asHexString);
|
||||
@@ -151,6 +166,13 @@ public interface ComponentHasher {
|
||||
.optional("interpret", MinecraftHasher.BOOL, NBTComponent::interpret, false)
|
||||
.optionalNullable("separator", COMPONENT, NBTComponent::separator)); // TODO source key, needs kyori update?
|
||||
|
||||
MinecraftHasher<ObjectContentsType> OBJECT_CONTENTS_TYPE = MinecraftHasher.fromEnum(ObjectContentsType::getName);
|
||||
|
||||
MapBuilder<ObjectContents> OBJECT_CONTENTS = MapBuilder.dispatch("object", OBJECT_CONTENTS_TYPE, ObjectContentsType::fromContents, ObjectContentsType::mapBuilder);
|
||||
|
||||
MinecraftHasher<ObjectComponent> OBJECT_COMPONENT = component(builder -> builder
|
||||
.accept(OBJECT_CONTENTS, ObjectComponent::contents));
|
||||
|
||||
MinecraftHasher<Component> ACTUAL_COMPONENT = (component, encoder) -> {
|
||||
if (component instanceof TextComponent text) {
|
||||
return TEXT_COMPONENT.hash(text, encoder);
|
||||
@@ -164,8 +186,10 @@ public interface ComponentHasher {
|
||||
return SELECTOR_COMPONENT.hash(selector, encoder);
|
||||
} else if (component instanceof NBTComponent<?,?> nbt) {
|
||||
return NBT_COMPONENT.hash(nbt, encoder);
|
||||
} else if (component instanceof ObjectComponent object) {
|
||||
return OBJECT_COMPONENT.hash(object, encoder);
|
||||
}
|
||||
throw new IllegalStateException("Unimplemented component hasher: " + component);
|
||||
throw new UnsupportedOperationException("Unimplemented component hasher: " + component);
|
||||
};
|
||||
|
||||
private static <T extends Component> MinecraftHasher<T> component(MapBuilder<T> componentBuilder) {
|
||||
|
||||
@@ -57,6 +57,34 @@ public interface MapBuilder<Type> extends UnaryOperator<MapHasher<Type>> {
|
||||
return builder -> builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link MapBuilder#dispatch(String, MinecraftHasher, Function, Function)}, uses {@code "type"} as the {@code typeKey}.
|
||||
*
|
||||
* @see MapBuilder#dispatch(String, MinecraftHasher, Function, Function)
|
||||
*/
|
||||
static <Type, Dispatched> MapBuilder<Dispatched> dispatch(MinecraftHasher<Type> typeHasher, Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> hashDispatch) {
|
||||
return dispatch("type", typeHasher, typeExtractor, hashDispatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a map builder that dispatches a {@link Type} from a {@link Dispatched} using {@code typeExtractor}, puts this as the {@code typeKey} key in the map using the given {@code typeHasher},
|
||||
* and uses a {@link MapBuilder} provided by {@code mapDispatch} to build the rest of the map.
|
||||
*
|
||||
* <p>This can be used to create map builders that build an abstract type or interface into a map with different keys depending on the type.</p>
|
||||
*
|
||||
* @param typeKey the key to store the {@link Type} in.
|
||||
* @param typeHasher the hasher used to encode the {@link Type}.
|
||||
* @param typeExtractor the function that extracts a {@link Type} from a {@link Dispatched}.
|
||||
* @param mapDispatch the function that provides a {@link MapBuilder} based on a {@link Type}.
|
||||
* @param <Type> the type of the {@code typeKey}.
|
||||
* @param <Dispatched> the type of the new map builder.
|
||||
*/
|
||||
static <Type, Dispatched> MapBuilder<Dispatched> dispatch(String typeKey, MinecraftHasher<Type> typeHasher, Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> mapDispatch) {
|
||||
return builder -> builder
|
||||
.accept(typeKey, typeHasher, typeExtractor)
|
||||
.accept(typeExtractor, mapDispatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that creates a map builder from an NBT map. The builder simply adds all keys from the NBT map.
|
||||
*
|
||||
|
||||
@@ -216,6 +216,8 @@ public interface MinecraftHasher<Type> {
|
||||
* Delegates to {@link MinecraftHasher#dispatch(String, Function, Function)}, uses {@code "type"} as the {@code typeKey}.
|
||||
*
|
||||
* @see MinecraftHasher#dispatch(String, Function, Function)
|
||||
* @see MapBuilder#dispatch(MinecraftHasher, Function, Function)
|
||||
* @see MapBuilder#dispatch(String, MinecraftHasher, Function, Function)
|
||||
*/
|
||||
default <Dispatched> MinecraftHasher<Dispatched> dispatch(Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> hashDispatch) {
|
||||
return dispatch("type", typeExtractor, hashDispatch);
|
||||
@@ -227,15 +229,17 @@ public interface MinecraftHasher<Type> {
|
||||
*
|
||||
* <p>This can be used to create hashers that hash an abstract type or interface into a map with different keys depending on the type.</p>
|
||||
*
|
||||
* <p>Internally this simply delegates to and wraps {@link MapBuilder#dispatch(String, MinecraftHasher, Function, Function)} in {@link MinecraftHasher#mapBuilder(MapBuilder)},
|
||||
* using {@code this} as {@code typeHasher}.</p>
|
||||
*
|
||||
* @param typeKey the key to store the {@link Type} in.
|
||||
* @param typeExtractor the function that extracts a {@link Type} from a {@link Dispatched}.
|
||||
* @param mapDispatch the function that provides a {@link MapBuilder} based on a {@link Type}.
|
||||
* @param <Dispatched> the type of the new hasher.
|
||||
* @see MapBuilder#dispatch(String, MinecraftHasher, Function, Function)
|
||||
*/
|
||||
default <Dispatched> MinecraftHasher<Dispatched> dispatch(String typeKey, Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> mapDispatch) {
|
||||
return mapBuilder(builder -> builder
|
||||
.accept(typeKey, this, typeExtractor)
|
||||
.accept(typeExtractor, mapDispatch));
|
||||
return mapBuilder(MapBuilder.dispatch(typeKey, this, typeExtractor, mapDispatch));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -304,7 +304,7 @@ public interface RegistryHasher<DirectType> extends MinecraftHasher<Integer> {
|
||||
|
||||
MinecraftHasher<ConsumeEffectType> CONSUME_EFFECT_TYPE = enumRegistry();
|
||||
|
||||
MinecraftHasher<ConsumeEffect> CONSUME_EFFECT = CONSUME_EFFECT_TYPE.dispatch(ConsumeEffectType::fromEffect, type -> type.getBuilder().cast());
|
||||
MinecraftHasher<ConsumeEffect> CONSUME_EFFECT = CONSUME_EFFECT_TYPE.dispatch(ConsumeEffectType::fromEffect, ConsumeEffectType::mapBuilder);
|
||||
|
||||
MinecraftHasher<SuspiciousStewEffect> SUSPICIOUS_STEW_EFFECT = MinecraftHasher.mapBuilder(builder -> builder
|
||||
.accept("id", EFFECT_ID, SuspiciousStewEffect::getMobEffectId)
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
package org.geysermc.geyser.item.hashing.data;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.geyser.item.hashing.MapBuilder;
|
||||
import org.geysermc.geyser.item.hashing.MinecraftHasher;
|
||||
import org.geysermc.geyser.item.hashing.RegistryHasher;
|
||||
@@ -44,7 +43,6 @@ public enum ConsumeEffectType {
|
||||
.accept("sound", RegistryHasher.SOUND_EVENT, ConsumeEffect.PlaySound::sound));
|
||||
|
||||
private final Class<? extends ConsumeEffect> clazz;
|
||||
@Getter
|
||||
private final MapBuilder<? extends ConsumeEffect> builder;
|
||||
|
||||
<T extends ConsumeEffect> ConsumeEffectType(Class<T> clazz) {
|
||||
@@ -57,6 +55,10 @@ public enum ConsumeEffectType {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public MapBuilder<ConsumeEffect> mapBuilder() {
|
||||
return builder.cast();
|
||||
}
|
||||
|
||||
public static ConsumeEffectType fromEffect(ConsumeEffect effect) {
|
||||
Class<? extends ConsumeEffect> clazz = effect.getClass();
|
||||
for (ConsumeEffectType type : values()) {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.item.hashing.data;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.text.object.ObjectContents;
|
||||
import net.kyori.adventure.text.object.PlayerHeadObjectContents;
|
||||
import net.kyori.adventure.text.object.SpriteObjectContents;
|
||||
import org.geysermc.geyser.item.hashing.ComponentHasher;
|
||||
import org.geysermc.geyser.item.hashing.MapBuilder;
|
||||
import org.geysermc.geyser.item.hashing.MinecraftHasher;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public enum ObjectContentsType {
|
||||
ATLAS("atlas", SpriteObjectContents.class,
|
||||
builder -> builder
|
||||
.optional("atlas", MinecraftHasher.KEY, SpriteObjectContents::atlas, SpriteObjectContents.DEFAULT_ATLAS)
|
||||
.accept("sprite", MinecraftHasher.KEY, SpriteObjectContents::sprite)),
|
||||
PLAYER("player", PlayerHeadObjectContents.class,
|
||||
builder -> builder
|
||||
.accept("player", ComponentHasher.RESOLVABLE_PROFILE, Function.identity())
|
||||
.optional("hat", MinecraftHasher.BOOL, PlayerHeadObjectContents::hat, true));
|
||||
|
||||
@Getter
|
||||
private final String name;
|
||||
private final MapBuilder<? extends ObjectContents> builder;
|
||||
|
||||
@SuppressWarnings("unused") // So Java knows what T we are talking about
|
||||
<T extends ObjectContents> ObjectContentsType(String name, Class<T> clazz, MapBuilder<T> builder) {
|
||||
this.name = name;
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public MapBuilder<ObjectContents> mapBuilder() {
|
||||
return builder.cast();
|
||||
}
|
||||
|
||||
public static ObjectContentsType fromContents(ObjectContents contents) {
|
||||
if (contents instanceof SpriteObjectContents) {
|
||||
return ATLAS;
|
||||
} else if (contents instanceof PlayerHeadObjectContents) {
|
||||
return PLAYER;
|
||||
}
|
||||
throw new UnsupportedOperationException("Don't know how to hash object contents of type " + contents.getClass());
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,6 @@ import java.util.function.Function;
|
||||
*/
|
||||
// Lots of unchecked casting happens here. It should all be handled properly.
|
||||
@SuppressWarnings("unchecked")
|
||||
// TODO only log some things once (like was done in vault translator)
|
||||
public final class ItemStackParser {
|
||||
private static final Map<DataComponentType<?>, DataComponentParser<?, ?>> PARSERS = new Reference2ObjectOpenHashMap<>();
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ public class CollisionManager {
|
||||
return vector.getX() * vector.getX() + vector.getZ() * vector.getZ();
|
||||
}
|
||||
|
||||
private Vector3d correctMovementForCollisions(Vector3d movement, BoundingBox boundingBox, boolean checkWorld, boolean walkOnLava) {
|
||||
public Vector3d correctMovementForCollisions(Vector3d movement, BoundingBox boundingBox, boolean checkWorld, boolean walkOnLava) {
|
||||
double movementX = movement.getX();
|
||||
double movementY = movement.getY();
|
||||
double movementZ = movement.getZ();
|
||||
|
||||
@@ -42,6 +42,11 @@ import org.cloudburstmc.protocol.bedrock.codec.v712.serializer.MobArmorEquipment
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v748.serializer.InventoryContentSerializer_v748;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v748.serializer.InventorySlotSerializer_v748;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v776.serializer.BossEventSerializer_v776;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v818.serializer.LoginSerializer_v818;
|
||||
import org.cloudburstmc.protocol.bedrock.data.auth.AuthPayload;
|
||||
import org.cloudburstmc.protocol.bedrock.data.auth.AuthType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.auth.CertificateChainPayload;
|
||||
import org.cloudburstmc.protocol.bedrock.data.auth.TokenPayload;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AnvilDamagePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BossEventPacket;
|
||||
@@ -59,6 +64,7 @@ import org.cloudburstmc.protocol.bedrock.packet.GameTestRequestPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LabTablePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MapCreateLockedCopyPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MapInfoRequestPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MobArmorEquipmentPacket;
|
||||
@@ -83,7 +89,13 @@ import org.cloudburstmc.protocol.bedrock.packet.SettingsCommandPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SimpleEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SubChunkRequestPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SubClientLoginPacket;
|
||||
import org.cloudburstmc.protocol.common.util.Preconditions;
|
||||
import org.cloudburstmc.protocol.common.util.VarInts;
|
||||
import org.jose4j.json.JsonUtil;
|
||||
import org.jose4j.lang.JoseException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Processes the Bedrock codec to remove or modify unused or unsafe packets and fields.
|
||||
@@ -242,6 +254,38 @@ class CodecProcessor {
|
||||
}
|
||||
};
|
||||
|
||||
private static final BedrockPacketSerializer<LoginPacket> LOGIN_PACKET_BEDROCK_PACKET_SERIALIZER = new LoginSerializer_v818() {
|
||||
|
||||
@Override
|
||||
protected AuthPayload readAuthJwt(String authJwt) {
|
||||
try {
|
||||
Map<String, Object> payload = JsonUtil.parseJson(authJwt);
|
||||
Preconditions.checkArgument(payload.containsKey("AuthenticationType"), "Missing AuthenticationType in JWT");
|
||||
int authTypeOrdinal = ((Number)payload.get("AuthenticationType")).intValue();
|
||||
if (authTypeOrdinal >= 0 && authTypeOrdinal < AuthType.values().length - 1) {
|
||||
AuthType authType = AuthType.values()[authTypeOrdinal + 1];
|
||||
if (payload.containsKey("Certificate") && payload.get("Certificate") instanceof String certJson && !certJson.isEmpty()) {
|
||||
Map<String, Object> certData = JsonUtil.parseJson(certJson);
|
||||
if (certData.containsKey("chain") && certData.get("chain") instanceof List) {
|
||||
List<String> chain = (List)certData.get("chain");
|
||||
return new CertificateChainPayload(chain, authType);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid Certificate chain in JWT");
|
||||
}
|
||||
} else if (payload.containsKey("Token") && payload.get("Token") instanceof String token && !token.isEmpty()) {
|
||||
return new TokenPayload(token, authType);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid AuthPayload in JWT");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid AuthenticationType ordinal: " + authTypeOrdinal);
|
||||
}
|
||||
} catch (JoseException e) {
|
||||
throw new IllegalArgumentException("Failed to parse auth payload", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static BedrockCodec processCodec(BedrockCodec codec) {
|
||||
BedrockPacketSerializer<BossEventPacket> bossEventSerializer;
|
||||
@@ -305,6 +349,11 @@ class CodecProcessor {
|
||||
.updateSerializer(PlayerInputPacket.class, ILLEGAL_SERIALIZER);
|
||||
}
|
||||
|
||||
// TODO remove once global api is updated
|
||||
if (codec.getProtocolVersion() >= 818) {
|
||||
codecBuilder.updateSerializer(LoginPacket.class, LOGIN_PACKET_BEDROCK_PACKET_SERIALIZER);
|
||||
}
|
||||
|
||||
if (!Boolean.getBoolean("Geyser.ReceiptPackets")) {
|
||||
codecBuilder.updateSerializer(RefreshEntitlementsPacket.class, IGNORED_SERIALIZER);
|
||||
codecBuilder.updateSerializer(PurchaseReceiptPacket.class, IGNORED_SERIALIZER);
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.cloudburstmc.protocol.bedrock.codec.v818.Bedrock_v818;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v819.Bedrock_v819;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v827.Bedrock_v827;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v844.Bedrock_v844;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v859.Bedrock_v859;
|
||||
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
|
||||
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||
import org.geysermc.geyser.impl.MinecraftVersionImpl;
|
||||
@@ -87,6 +88,7 @@ public final class GameProtocol {
|
||||
register(Bedrock_v819.CODEC, "1.21.93", "1.21.94");
|
||||
register(Bedrock_v827.CODEC, "1.21.100", "1.21.101");
|
||||
register(Bedrock_v844.CODEC, "1.21.111", "1.21.112", "1.21.113", "1.21.114");
|
||||
register(Bedrock_v859.CODEC, "1.21.120");
|
||||
|
||||
MinecraftVersion latestBedrock = SUPPORTED_BEDROCK_VERSIONS.get(SUPPORTED_BEDROCK_VERSIONS.size() - 1);
|
||||
DEFAULT_BEDROCK_VERSION = latestBedrock.versionString();
|
||||
|
||||
@@ -125,11 +125,6 @@ public class LoggingPacketHandler implements BedrockPacketHandler {
|
||||
return defaultHandler(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSignal handle(CraftingEventPacket packet) {
|
||||
return defaultHandler(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSignal handle(EntityEventPacket packet) {
|
||||
return defaultHandler(packet);
|
||||
@@ -165,11 +160,6 @@ public class LoggingPacketHandler implements BedrockPacketHandler {
|
||||
return defaultHandler(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSignal handle(ItemFrameDropItemPacket packet) {
|
||||
return defaultHandler(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketSignal handle(LabTablePacket packet) {
|
||||
return defaultHandler(packet);
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.common.collect.Interners;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
@@ -47,6 +48,7 @@ import org.cloudburstmc.protocol.bedrock.codec.v818.Bedrock_v818;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v819.Bedrock_v819;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v827.Bedrock_v827;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v844.Bedrock_v844;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v859.Bedrock_v859;
|
||||
import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
@@ -124,6 +126,7 @@ public final class BlockRegistryPopulator {
|
||||
.put(ObjectIntPair.of("1_21_90", Bedrock_v819.CODEC.getProtocolVersion()), Conversion827_819::remapBlock)
|
||||
.put(ObjectIntPair.of("1_21_100", Bedrock_v827.CODEC.getProtocolVersion()), Conversion844_827::remapBlock)
|
||||
.put(ObjectIntPair.of("1_21_110", Bedrock_v844.CODEC.getProtocolVersion()), tag -> tag)
|
||||
.put(ObjectIntPair.of("1_21_110", Bedrock_v859.CODEC.getProtocolVersion()), tag -> tag)
|
||||
.build();
|
||||
|
||||
// We can keep this strong as nothing should be garbage collected
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.cloudburstmc.protocol.bedrock.codec.v818.Bedrock_v818;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v819.Bedrock_v819;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v827.Bedrock_v827;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v844.Bedrock_v844;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v859.Bedrock_v859;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition;
|
||||
@@ -196,6 +197,7 @@ public class ItemRegistryPopulator {
|
||||
paletteVersions.add(new PaletteVersion("1_21_93", Bedrock_v819.CODEC.getProtocolVersion(), eightOneNineFallbacks, Conversion844_827::remapItem));
|
||||
paletteVersions.add(new PaletteVersion("1_21_100", Bedrock_v827.CODEC.getProtocolVersion(), eightTwoSevenFallbacks, Conversion844_827::remapItem));
|
||||
paletteVersions.add(new PaletteVersion("1_21_110", Bedrock_v844.CODEC.getProtocolVersion()));
|
||||
paletteVersions.add(new PaletteVersion("1_21_120", Bedrock_v859.CODEC.getProtocolVersion()));
|
||||
|
||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||
|
||||
@@ -209,13 +211,6 @@ public class ItemRegistryPopulator {
|
||||
throw new AssertionError("Unable to load Java runtime item IDs", e);
|
||||
}
|
||||
|
||||
NbtMap vanillaComponents;
|
||||
try (InputStream stream = bootstrap.getResourceOrThrow("bedrock/item_components.nbt")) {
|
||||
vanillaComponents = (NbtMap) NbtUtils.createGZIPReader(stream, true, true).readTag();
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to load Bedrock item components", e);
|
||||
}
|
||||
|
||||
boolean customItemsAllowed = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems();
|
||||
|
||||
// List values here is important compared to HashSet - we need to preserve the order of what's given to us
|
||||
@@ -242,6 +237,13 @@ public class ItemRegistryPopulator {
|
||||
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
|
||||
}
|
||||
|
||||
NbtMap vanillaComponents;
|
||||
try (InputStream stream = bootstrap.getResourceOrThrow("bedrock/item_components.%s.nbt".formatted(palette.version()))) {
|
||||
vanillaComponents = (NbtMap) NbtUtils.createGZIPReader(stream, true, true).readTag();
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to load Bedrock item components", e);
|
||||
}
|
||||
|
||||
// Used for custom items
|
||||
int nextFreeBedrockId = 0;
|
||||
Int2ObjectMap<ItemDefinition> registry = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@@ -276,6 +276,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
*/
|
||||
@Setter
|
||||
private List<String> certChainData;
|
||||
@Setter
|
||||
private String token;
|
||||
|
||||
@NonNull
|
||||
@Setter
|
||||
|
||||
@@ -515,7 +515,7 @@ public class BlockBreakHandler {
|
||||
|
||||
protected boolean mayBreak(float progress, boolean bedrockDestroyed) {
|
||||
// We're tolerant here to account for e.g. obsidian breaking speeds not matching 1:1 :(
|
||||
return (serverSideBlockBreaking && progress >= 1.0F) || (bedrockDestroyed && progress >= 0.7F);
|
||||
return (serverSideBlockBreaking && progress >= 1.0F) || (bedrockDestroyed && progress >= 0.65F);
|
||||
}
|
||||
|
||||
protected void destroyBlock(BlockState state, Vector3i vector, Direction direction, boolean instamine) {
|
||||
|
||||
@@ -190,15 +190,22 @@ public final class FloodgateSkinUploader {
|
||||
};
|
||||
}
|
||||
|
||||
public void uploadSkin(List<String> chainData, String clientData) {
|
||||
if (chainData == null || clientData == null) {
|
||||
public void uploadSkin(GeyserSession session) {
|
||||
List<String> chainData = session.getCertChainData();
|
||||
String token = session.getToken();
|
||||
String clientData = session.getClientData().getOriginalString();
|
||||
if ((chainData == null && token == null) || clientData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectNode node = JACKSON.createObjectNode();
|
||||
ArrayNode chainDataNode = JACKSON.createArrayNode();
|
||||
chainData.forEach(chainDataNode::add);
|
||||
node.set("chain_data", chainDataNode);
|
||||
if (chainData != null) {
|
||||
ArrayNode chainDataNode = JACKSON.createArrayNode();
|
||||
chainData.forEach(chainDataNode::add);
|
||||
node.set("chain_data", chainDataNode);
|
||||
} else {
|
||||
node.put("token", token);
|
||||
}
|
||||
node.put("client_data", clientData);
|
||||
|
||||
// The reason why I don't like Jackson
|
||||
@@ -218,7 +225,7 @@ public final class FloodgateSkinUploader {
|
||||
}
|
||||
|
||||
private void reconnectLater(GeyserImpl geyser) {
|
||||
// we ca only reconnect when the thread pool is open
|
||||
// we can only reconnect when the thread pool is open
|
||||
if (geyser.getScheduledThread().isShutdown() || closed) {
|
||||
logger.info("The skin uploader has been closed");
|
||||
return;
|
||||
@@ -241,4 +248,4 @@ public final class FloodgateSkinUploader {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,10 +530,10 @@ public final class ItemTranslator {
|
||||
}
|
||||
|
||||
if (mapping.getJavaItem().equals(Items.PLAYER_HEAD)) {
|
||||
/*CustomSkull customSkull = getCustomSkull(itemStack.getComponent(DataComponentTypes.PROFILE));
|
||||
CustomSkull customSkull = getCustomSkull(itemStack.getComponent(DataComponentTypes.PROFILE));
|
||||
if (customSkull != null) {
|
||||
itemDefinition = session.getItemMappings().getCustomBlockItemDefinitions().get(customSkull.getCustomBlockData());
|
||||
}*/ // TODO
|
||||
}
|
||||
}
|
||||
|
||||
ItemDefinition definition = CustomItemTranslator.getCustomItem(itemStack.getComponents(), mapping);
|
||||
|
||||
@@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.protocol.bedrock.entity.player.input;
|
||||
|
||||
import org.cloudburstmc.math.GenericMath;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.InputMode;
|
||||
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
|
||||
@@ -40,6 +41,7 @@ import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity;
|
||||
import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
@@ -270,11 +272,31 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
|
||||
}
|
||||
|
||||
if (sendMovement) {
|
||||
vehicle.setOnGround(packet.getInputData().contains(PlayerAuthInputData.VERTICAL_COLLISION) && session.getPlayerEntity().getLastTickEndVelocity().getY() < 0);
|
||||
// We only need to determine onGround status this way for client predicted vehicles.
|
||||
// For other vehicle, Geyser already handle it in VehicleComponent or the Java server handle it.
|
||||
if (packet.getInputData().contains(PlayerAuthInputData.IN_CLIENT_PREDICTED_IN_VEHICLE)) {
|
||||
Vector3f position = vehicle.getPosition();
|
||||
|
||||
if (vehicle instanceof BoatEntity) {
|
||||
position = position.down(vehicle.getDefinition().offset());
|
||||
}
|
||||
|
||||
final BoundingBox box = new BoundingBox(
|
||||
position.up(vehicle.getBoundingBoxHeight() / 2f).toDouble(),
|
||||
vehicle.getBoundingBoxWidth(), vehicle.getBoundingBoxHeight(), vehicle.getBoundingBoxWidth()
|
||||
);
|
||||
|
||||
// Manually calculate the vertical collision ourselves, the VERTICAL_COLLISION input data is inaccurate inside a vehicle!
|
||||
Vector3d movement = session.getPlayerEntity().getLastTickEndVelocity().toDouble();
|
||||
Vector3d correctedMovement = session.getCollisionManager().correctMovementForCollisions(movement, box, true, false);
|
||||
|
||||
vehicle.setOnGround(correctedMovement.getY() != movement.getY() && session.getPlayerEntity().getLastTickEndVelocity().getY() < 0);
|
||||
}
|
||||
|
||||
Vector3f vehiclePosition = packet.getPosition();
|
||||
Vector2f vehicleRotation = packet.getVehicleRotation();
|
||||
if (vehicleRotation == null) {
|
||||
return; // If the client just got in or out of a vehicle for example.
|
||||
return; // If the client just got in or out of a vehicle for example. Or if this vehicle isn't client predicted.
|
||||
}
|
||||
|
||||
if (session.getWorldBorder().isPassingIntoBorderBoundaries(vehiclePosition, false)) {
|
||||
|
||||
@@ -66,11 +66,12 @@ public class JavaLoginFinishedTranslator extends PacketTranslator<ClientboundLog
|
||||
// because otherwise the global server returns the data too fast.
|
||||
// We upload it after we know for sure that the target server
|
||||
// is ready to handle the result of the global server.
|
||||
session.getGeyser().getSkinUploader().uploadSkin(session.getCertChainData(), session.getClientData().getOriginalString());
|
||||
session.getGeyser().getSkinUploader().uploadSkin(session);
|
||||
}
|
||||
|
||||
// We no longer need these variables; they're just taking up space in memory now
|
||||
session.setCertChainData(null);
|
||||
session.setToken(null);
|
||||
session.getClientData().setOriginalString(null);
|
||||
|
||||
// configuration phase stuff that the vanilla client replies with after receiving the GameProfilePacket
|
||||
|
||||
@@ -85,8 +85,12 @@ public class JavaAnimateTranslator extends PacketTranslator<ClientboundAnimatePa
|
||||
session.sendUpstreamPacket(offHandPacket);
|
||||
return;
|
||||
}
|
||||
case CRITICAL_HIT -> animatePacket.setAction(AnimatePacket.Action.CRITICAL_HIT);
|
||||
case CRITICAL_HIT -> {
|
||||
animatePacket.setData(55);
|
||||
animatePacket.setAction(AnimatePacket.Action.CRITICAL_HIT);
|
||||
}
|
||||
case ENCHANTMENT_CRITICAL_HIT -> {
|
||||
animatePacket.setData(15);
|
||||
animatePacket.setAction(AnimatePacket.Action.MAGIC_CRITICAL_HIT); // Unsure if this does anything
|
||||
|
||||
// Spawn custom particle
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import net.raphimc.minecraftauth.step.msa.StepMsaDeviceCode;
|
||||
import org.cloudburstmc.protocol.bedrock.data.auth.AuthPayload;
|
||||
import org.cloudburstmc.protocol.bedrock.data.auth.CertificateChainPayload;
|
||||
import org.cloudburstmc.protocol.bedrock.data.auth.TokenPayload;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.ServerToClientHandshakePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.util.ChainValidationResult;
|
||||
@@ -51,7 +52,6 @@ import org.geysermc.geyser.text.GeyserLocale;
|
||||
import javax.crypto.SecretKey;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class LoginEncryptionUtils {
|
||||
@@ -81,13 +81,13 @@ public class LoginEncryptionUtils {
|
||||
long issuedAt = rawIssuedAt != null ? rawIssuedAt : -1;
|
||||
|
||||
IdentityData extraData = result.identityClaims().extraData;
|
||||
// TODO!!! identity won't persist
|
||||
session.setAuthData(new AuthData(extraData.displayName, extraData.identity, extraData.xuid, issuedAt));
|
||||
if (authPayload instanceof CertificateChainPayload certificateChainPayload) {
|
||||
if (authPayload instanceof TokenPayload tokenPayload) {
|
||||
session.setToken(tokenPayload.getToken());
|
||||
} else if (authPayload instanceof CertificateChainPayload certificateChainPayload) {
|
||||
session.setCertChainData(certificateChainPayload.getChain());
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().warning("Received new auth payload!");
|
||||
session.setCertChainData(List.of());
|
||||
GeyserImpl.getInstance().getLogger().warning("Unknown auth payload! Skin uploading will not work");
|
||||
}
|
||||
|
||||
PublicKey identityPublicKey = result.identityClaims().parsedIdentityPublicKey();
|
||||
|
||||
@@ -76,7 +76,7 @@ public class WebUtils {
|
||||
con.setRequestProperty("User-Agent", getUserAgent()); // Otherwise Java 8 fails on checking updates
|
||||
con.setConnectTimeout(10000);
|
||||
con.setReadTimeout(10000);
|
||||
|
||||
checkResponseCode(con);
|
||||
return connectionToString(con);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new IllegalStateException("Unable to resolve requested url (%s)! Are you offline?".formatted(reqURL), e);
|
||||
@@ -94,6 +94,7 @@ public class WebUtils {
|
||||
con.setRequestProperty("User-Agent", getUserAgent());
|
||||
con.setConnectTimeout(10000);
|
||||
con.setReadTimeout(10000);
|
||||
checkResponseCode(con);
|
||||
return GeyserImpl.JSON_MAPPER.readTree(con.getInputStream());
|
||||
}
|
||||
|
||||
@@ -107,6 +108,7 @@ public class WebUtils {
|
||||
try {
|
||||
HttpURLConnection con = (HttpURLConnection) new URL(reqURL).openConnection();
|
||||
con.setRequestProperty("User-Agent", getUserAgent());
|
||||
checkResponseCode(con);
|
||||
InputStream in = con.getInputStream();
|
||||
Files.copy(in, Paths.get(fileLocation), StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (Exception e) {
|
||||
@@ -270,24 +272,38 @@ public class WebUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string output from the passed {@link HttpURLConnection}
|
||||
*
|
||||
* @param con The connection to get the string from
|
||||
* @return The body of the returned page
|
||||
* @throws IOException If the request fails
|
||||
* Gets the string output from the passed {@link HttpURLConnection},
|
||||
* or logs the error message.
|
||||
*/
|
||||
private static String connectionToString(HttpURLConnection con) throws IOException {
|
||||
checkResponseCode(con);
|
||||
return inputStreamToString(con.getInputStream(), con::disconnect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if there is an error stream to avoid further issues
|
||||
*/
|
||||
private static void checkResponseCode(HttpURLConnection con) throws IOException {
|
||||
// Send the request (we dont use this but its required for getErrorStream() to work)
|
||||
con.getResponseCode();
|
||||
|
||||
// Read the error message if there is one if not just read normally
|
||||
InputStream inputStream = con.getErrorStream();
|
||||
if (inputStream == null) {
|
||||
inputStream = con.getInputStream();
|
||||
InputStream errorStream = con.getErrorStream();
|
||||
if (errorStream != null) {
|
||||
throw new IOException(inputStreamToString(errorStream, null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string output from the passed {@link InputStream}
|
||||
*
|
||||
* @param stream The input stream to get the string from
|
||||
* @return The body of the returned page
|
||||
* @throws IOException If the request fails
|
||||
*/
|
||||
private static String inputStreamToString(InputStream stream, @Nullable Runnable onFinish) throws IOException {
|
||||
StringBuilder content = new StringBuilder();
|
||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(stream))) {
|
||||
String inputLine;
|
||||
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
@@ -295,7 +311,9 @@ public class WebUtils {
|
||||
content.append("\n");
|
||||
}
|
||||
|
||||
con.disconnect();
|
||||
if (onFinish != null) {
|
||||
onFinish.run();
|
||||
}
|
||||
}
|
||||
|
||||
return content.toString();
|
||||
|
||||
9516
core/src/main/resources/bedrock/creative_items.1_21_120.json
Normal file
9516
core/src/main/resources/bedrock/creative_items.1_21_120.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
core/src/main/resources/bedrock/item_components.1_21_110.nbt
Normal file
BIN
core/src/main/resources/bedrock/item_components.1_21_110.nbt
Normal file
Binary file not shown.
BIN
core/src/main/resources/bedrock/item_components.1_21_120.nbt
Normal file
BIN
core/src/main/resources/bedrock/item_components.1_21_120.nbt
Normal file
Binary file not shown.
BIN
core/src/main/resources/bedrock/item_components.1_21_90.nbt
Normal file
BIN
core/src/main/resources/bedrock/item_components.1_21_90.nbt
Normal file
Binary file not shown.
BIN
core/src/main/resources/bedrock/item_components.1_21_93.nbt
Normal file
BIN
core/src/main/resources/bedrock/item_components.1_21_93.nbt
Normal file
Binary file not shown.
11330
core/src/main/resources/bedrock/runtime_item_states.1_21_120.json
Normal file
11330
core/src/main/resources/bedrock/runtime_item_states.1_21_120.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,14 +9,14 @@ netty = "4.2.7.Final"
|
||||
guava = "29.0-jre"
|
||||
gson = "2.3.1" # Provided by Spigot 1.8.8
|
||||
websocket = "1.5.1"
|
||||
protocol-connection = "3.0.0.Beta8-20250929.213851-8"
|
||||
protocol-common = "3.0.0.Beta8-20250929.213851-8"
|
||||
protocol-codec = "3.0.0.Beta8-20250929.213851-8"
|
||||
protocol-connection = "3.0.0.Beta10-20251014.180344-2"
|
||||
protocol-common = "3.0.0.Beta10-20251014.180344-2"
|
||||
protocol-codec = "3.0.0.Beta10-20251014.180344-2"
|
||||
raknet = "1.0.0.CR3-20250811.214335-20"
|
||||
minecraftauth = "4.1.1"
|
||||
mcprotocollib = "1.21.9-20251020.140136-16"
|
||||
adventure = "4.24.0"
|
||||
adventure-platform = "4.3.0"
|
||||
mcprotocollib = "1.21.9-20251029.184056-18"
|
||||
adventure = "4.25.0"
|
||||
adventure-platform = "4.4.1"
|
||||
junit = "5.9.2"
|
||||
checkerframework = "3.19.0"
|
||||
log4j = "2.20.0"
|
||||
|
||||
Reference in New Issue
Block a user