Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Shanwer committed Feb 13, 2025
2 parents cc58339 + d5b5712 commit cc97750
Show file tree
Hide file tree
Showing 16 changed files with 301 additions and 60 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,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.40 - 1.21.50 and Minecraft Java 1.21.4. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
Geyser is currently supporting Minecraft Bedrock 1.21.40 - 1.21.60 and Minecraft Java 1.21.4. 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,40 @@

package org.geysermc.geyser.entity.type;

import org.cloudburstmc.math.vector.Vector3d;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.MinecartStep;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundMoveMinecartPacket;

import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

public class MinecartEntity extends Entity {
public class MinecartEntity extends Entity implements Tickable {
private static final int POS_ROT_LERP_TICKS = 3;

private final List<MinecartStep> lerpSteps = new LinkedList<>();
private final List<MinecartStep> currentLerpSteps = new LinkedList<>();

private MinecartStep lastCompletedStep = new MinecartStep(Vector3d.ZERO, Vector3d. ZERO, 0.0F, 0.0F, 0.0F);
private float currentStepsTotalWeight = 0.0F;
private int lerpDelay = 0;

private PartialStep cachedPartialStep;
private int cachedStepDelay;
private float cachedDelta;

public MinecartEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position.add(0d, definition.offset(), 0d), motion, yaw, pitch, headYaw);
Expand All @@ -58,6 +78,131 @@ public void setShowCustomBlock(BooleanEntityMetadata entityMetadata) {
dirtyMetadata.put(EntityDataTypes.CUSTOM_DISPLAY, (byte) (entityMetadata.getPrimitiveValue() ? 1 : 0));
}

@Override
public void tick() {
if (!session.isUsingExperimentalMinecartLogic()) {
return;
}

// All minecart lerp code here and in the methods below has been based off of the code in the Java NewMinecartBehavior class
lerpDelay--;
if (lerpDelay <= 0) {
updateCompletedStep();
currentLerpSteps.clear();
if (!lerpSteps.isEmpty()) {
currentLerpSteps.addAll(lerpSteps);
lerpSteps.clear();
currentStepsTotalWeight = 0.0F;

for (MinecartStep step : currentLerpSteps) {
currentStepsTotalWeight += step.weight();
}

lerpDelay = currentStepsTotalWeight == 0.0F ? 0 : POS_ROT_LERP_TICKS;
}
}

if (isLerping()) {
float delta = 1.0F; // This is always 1, maybe it should be removed

Vector3f position = getCurrentLerpPosition(delta).toFloat();
Vector3f movement = getCurrentLerpMovement(delta).toFloat();
setPosition(position);
setMotion(movement);

setYaw(180.0F - getCurrentLerpYaw(delta));
setPitch(getCurrentLerpPitch(delta));

MoveEntityDeltaPacket moveEntityPacket = new MoveEntityDeltaPacket();
moveEntityPacket.setRuntimeEntityId(geyserId);

moveEntityPacket.setX(position.getX());
moveEntityPacket.setY(position.getY() + definition.offset());
moveEntityPacket.setZ(position.getZ());
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X);
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y);
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z);

moveEntityPacket.setYaw(getYaw());
moveEntityPacket.setPitch(getPitch());
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW);
moveEntityPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);

SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
entityMotionPacket.setRuntimeEntityId(geyserId);
entityMotionPacket.setMotion(movement);

session.sendUpstreamPacket(moveEntityPacket);
session.sendUpstreamPacket(entityMotionPacket);
}
}

public void handleMinecartMovePacket(ClientboundMoveMinecartPacket packet) {
lerpSteps.addAll(packet.getLerpSteps());
}

private boolean isLerping() {
return !currentLerpSteps.isEmpty();
}

private float getCurrentLerpPitch(float delta) {
PartialStep partialStep = getCurrentLerpStep(delta);
return lerpRotation(partialStep.delta, partialStep.previousStep.xRot(), partialStep.currentStep.xRot());
}

private float getCurrentLerpYaw(float delta) {
PartialStep partialStep = getCurrentLerpStep(delta);
return lerpRotation(partialStep.delta, partialStep.previousStep.yRot(), partialStep.currentStep.yRot());
}

private Vector3d getCurrentLerpPosition(float delta) {
PartialStep partialStep = getCurrentLerpStep(delta);
return lerp(partialStep.delta, partialStep.previousStep.position(), partialStep.currentStep.position());
}

private Vector3d getCurrentLerpMovement(float delta) {
PartialStep partialStep = getCurrentLerpStep(delta);
return lerp(partialStep.delta, partialStep.previousStep.movement(), partialStep.currentStep.movement());
}

private PartialStep getCurrentLerpStep(float delta) {
if (cachedDelta != delta || lerpDelay != cachedStepDelay || cachedPartialStep == null) {
float g = ((POS_ROT_LERP_TICKS - lerpDelay) + delta) / POS_ROT_LERP_TICKS;
float totalWeight = 0.0F;
float stepDelta = 1.0F;
boolean foundStep = false;

int step;
for (step = 0; step < currentLerpSteps.size(); step++) {
float currentWeight = currentLerpSteps.get(step).weight();
if (!(currentWeight <= 0.0F)) {
totalWeight += currentWeight;
if ((double) totalWeight >= currentStepsTotalWeight * (double) g) {
float h = totalWeight - currentWeight;
stepDelta = (g * currentStepsTotalWeight - h) / currentWeight;
foundStep = true;
break;
}
}
}

if (!foundStep) {
step = currentLerpSteps.size() - 1;
}

MinecartStep currentStep = currentLerpSteps.get(step);
MinecartStep previousStep = step > 0 ? currentLerpSteps.get(step - 1) : lastCompletedStep;
cachedPartialStep = new PartialStep(stepDelta, currentStep, previousStep);
cachedStepDelay = lerpDelay;
cachedDelta = delta;
}
return cachedPartialStep;
}

private void updateCompletedStep() {
lastCompletedStep = new MinecartStep(position.toDouble(), motion.toDouble(), yaw, pitch, 0.0F);
}

@Override
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
super.moveAbsolute(position.add(0d, this.definition.offset(), 0d), yaw, pitch, headYaw, isOnGround, teleported);
Expand Down Expand Up @@ -103,4 +248,19 @@ public InteractionResult interact(Hand hand) {
}
}
}

private static Vector3d lerp(double delta, Vector3d start, Vector3d end) {
return Vector3d.from(lerp(delta, start.getX(), end.getX()), lerp(delta, start.getY(), end.getY()), lerp(delta, start.getZ(), end.getZ()));
}

public static double lerp(double delta, double start, double end) {
return start + delta * (end - start);
}

private static float lerpRotation(float delta, float start, float end) {
return start + delta * MathUtils.wrapDegrees(end - start);
}

private record PartialStep(float delta, MinecartStep currentStep, MinecartStep previousStep) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package org.geysermc.geyser.item.components;

import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull;

@Getter
public enum Rarity {
Expand All @@ -44,8 +45,7 @@ public enum Rarity {

private static final Rarity[] VALUES = values();

public static Rarity fromId(int id) {
public static @NonNull Rarity fromId(Integer id) {
return VALUES.length > id ? VALUES[id] : VALUES[0];
}

}
5 changes: 0 additions & 5 deletions core/src/main/java/org/geysermc/geyser/item/type/Item.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.components.Rarity;
import org.geysermc.geyser.item.enchantment.Enchantment;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.registry.Registries;
Expand Down Expand Up @@ -95,10 +94,6 @@ public int defaultMaxStackSize() {
return baseComponents.getOrDefault(DataComponentType.MAX_STACK_SIZE, 1);
}

public Rarity defaultRarity() {
return Rarity.fromId(baseComponents.getOrDefault(DataComponentType.RARITY, 0));
}

/**
* Returns an unmodifiable {@link DataComponents} view containing known data components.
* Optionally, additional components can be provided to replace (or add to)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@

package org.geysermc.geyser.item.type;

import org.geysermc.mcprotocollib.auth.GameProfile;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.item.components.Rarity;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.auth.GameProfile;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;

Expand All @@ -44,24 +45,21 @@ public PlayerHeadItem(Builder builder, Block block, Block... otherBlocks) {
public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) {
super.translateComponentsToBedrock(session, components, builder);

// TODO verify
// Also - ChatColor.YELLOW + ChatColor.ITALIC + MessageTranslator.convertMessageLenient(nameTag.getValue(), session.locale())) this code existed if a custom name was already present.
// But I think we would always overwrite that because translateDisplayProperties runs after this method.
String customName = builder.getCustomName();
if (customName == null) {
GameProfile profile = components.get(DataComponentType.PROFILE);
if (profile != null) {
String name = profile.getName();
if (name != null) {
// Add correct name of player skull
String displayName = ChatColor.RESET + ChatColor.YELLOW +
MinecraftLocale.getLocaleString("block.minecraft.player_head.named", session.locale()).replace("%s", name);
builder.setCustomName(displayName);
} else {
// No name found so default to "Player Head"
builder.setCustomName(ChatColor.RESET + ChatColor.YELLOW +
MinecraftLocale.getLocaleString("block.minecraft.player_head", session.locale()));
}
// Use the correct color, determined by the rarity of the item
char rarity = Rarity.fromId(components.get(DataComponentType.RARITY)).getColor();

GameProfile profile = components.get(DataComponentType.PROFILE);
if (profile != null) {
String name = profile.getName();
if (name != null) {
// Add correct name of player skull
String displayName = ChatColor.RESET + ChatColor.ESCAPE + rarity +
MinecraftLocale.getLocaleString("block.minecraft.player_head.named", session.locale()).replace("%s", name);
builder.setCustomName(displayName);
} else {
// No name found so default to "Player Head"
builder.setCustomName(ChatColor.RESET + ChatColor.ESCAPE + rarity +
MinecraftLocale.getLocaleString("block.minecraft.player_head", session.locale()));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemVersion;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.item.custom.CustomItemData;
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
Expand Down Expand Up @@ -106,7 +107,7 @@ public boolean register(@NonNull NonVanillaCustomItemData customItemData) {

public static GeyserCustomMappingData registerCustomItem(String customItemName, Item javaItem, GeyserMappingItem mapping, CustomItemData customItemData, int bedrockId, int protocolVersion) {
NbtMapBuilder builder = createComponentNbt(customItemData, javaItem, mapping, customItemName, bedrockId, protocolVersion);
ItemDefinition itemDefinition = new SimpleItemDefinition(customItemName, bedrockId, 1, true, builder.build());
ItemDefinition itemDefinition = new SimpleItemDefinition(customItemName, bedrockId, ItemVersion.DATA_DRIVEN, true, builder.build());

return new GeyserCustomMappingData(itemDefinition, customItemName, bedrockId);
}
Expand Down Expand Up @@ -143,7 +144,7 @@ public static NonVanillaItemRegistration registerCustomItem(NonVanillaCustomItem
customItemData.isHat(), customItemData.displayHandheld(), protocolVersion);
ItemMapping customItemMapping = ItemMapping.builder()
.bedrockIdentifier(customIdentifier)
.bedrockDefinition(new SimpleItemDefinition(customIdentifier, customItemId, 1, true, builder.build()))
.bedrockDefinition(new SimpleItemDefinition(customIdentifier, customItemId, ItemVersion.DATA_DRIVEN, true, builder.build()))
.bedrockData(0)
.bedrockBlockDefinition(null)
.toolType(customItemData.toolType())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
import java.util.List;
import java.util.Map;

/**
* Loads default item components for all Java items.
*/
public final class DataComponentRegistryPopulator {

public static void populate() {
Expand Down
Loading

0 comments on commit cc97750

Please sign in to comment.