mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-23 06:50:26 +01:00
Interact with an optional resource pack to add more features (#2176)
See https://github.com/GeyserMC/Geyser/wiki/GeyserOptionalPack
This commit is contained in:
parent
0691bb67b4
commit
95bcd4000f
14 changed files with 330 additions and 73 deletions
|
@ -45,7 +45,8 @@ The following things cannot be fixed without changes to Bedrock. As of now, they
|
|||
- Custom heads in inventories
|
||||
- Clickable links in chat
|
||||
- Glowing effect
|
||||
- Custom armor stand poses
|
||||
|
||||
Do note that some things require the [GeyserOptionalPack](https://github.com/GeyserMC/Geyser/wiki/GeyserOptionalPack) in order to function, such as custom armor stand poses.
|
||||
|
||||
## Compiling
|
||||
1. Clone the repo to your computer
|
||||
|
|
|
@ -36,6 +36,9 @@ public class AbstractArrowEntity extends Entity {
|
|||
public AbstractArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
|
||||
// Set the correct texture if using the resource pack
|
||||
metadata.getFlags().setFlag(EntityFlag.BRIBED, entityType == EntityType.SPECTRAL_ARROW);
|
||||
|
||||
setMotion(motion);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,10 +28,12 @@ package org.geysermc.connector.entity.attribute;
|
|||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public class Attribute {
|
||||
|
||||
private AttributeType type;
|
||||
|
|
|
@ -57,12 +57,12 @@ public enum AttributeType {
|
|||
HUNGER(null, "minecraft:player.hunger", 0f, 20f, 20f),
|
||||
SATURATION(null, "minecraft:player.saturation", 0f, 20f, 20f);
|
||||
|
||||
private String javaIdentifier;
|
||||
private String bedrockIdentifier;
|
||||
private final String javaIdentifier;
|
||||
private final String bedrockIdentifier;
|
||||
|
||||
private float minimum;
|
||||
private float maximum;
|
||||
private float defaultValue;
|
||||
private final float minimum;
|
||||
private final float maximum;
|
||||
private final float defaultValue;
|
||||
|
||||
public Attribute getAttribute(float value) {
|
||||
return getAttribute(value, maximum);
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.connector.entity.living;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Rotation;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
|
@ -157,6 +158,72 @@ public class ArmorStandEntity extends LivingEntity {
|
|||
|
||||
updateSecondEntityStatus(false);
|
||||
}
|
||||
|
||||
// The following values don't do anything on normal Bedrock.
|
||||
// But if given a resource pack, then we can use these values to control armor stand visual properties
|
||||
metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x04) != 0x04); // Has arms
|
||||
metadata.getFlags().setFlag(EntityFlag.ADMIRING, (xd & 0x08) == 0x08); // Has no baseplate
|
||||
} else {
|
||||
EntityData dataLeech = null;
|
||||
EntityFlag negativeXToggle = null;
|
||||
EntityFlag negativeYToggle = null;
|
||||
EntityFlag negativeZToggle = null;
|
||||
switch (entityMetadata.getId()) {
|
||||
case 15: // Head
|
||||
dataLeech = EntityData.MARK_VARIANT;
|
||||
negativeXToggle = EntityFlag.INTERESTED;
|
||||
negativeYToggle = EntityFlag.CHARGED;
|
||||
negativeZToggle = EntityFlag.POWERED;
|
||||
break;
|
||||
case 16: // Body
|
||||
dataLeech = EntityData.VARIANT;
|
||||
negativeXToggle = EntityFlag.IN_LOVE;
|
||||
negativeYToggle = EntityFlag.CELEBRATING;
|
||||
negativeZToggle = EntityFlag.CELEBRATING_SPECIAL;
|
||||
break;
|
||||
case 17: // Left arm
|
||||
dataLeech = EntityData.TRADE_TIER;
|
||||
negativeXToggle = EntityFlag.CHARGING;
|
||||
negativeYToggle = EntityFlag.CRITICAL;
|
||||
negativeZToggle = EntityFlag.DANCING;
|
||||
break;
|
||||
case 18: // Right arm
|
||||
dataLeech = EntityData.MAX_TRADE_TIER;
|
||||
negativeXToggle = EntityFlag.ELDER;
|
||||
negativeYToggle = EntityFlag.EMOTING;
|
||||
negativeZToggle = EntityFlag.IDLING;
|
||||
break;
|
||||
case 19: // Left leg
|
||||
dataLeech = EntityData.SKIN_ID;
|
||||
negativeXToggle = EntityFlag.IS_ILLAGER_CAPTAIN;
|
||||
negativeYToggle = EntityFlag.IS_IN_UI;
|
||||
negativeZToggle = EntityFlag.LINGERING;
|
||||
break;
|
||||
case 20: // Right leg
|
||||
dataLeech = EntityData.HURT_DIRECTION;
|
||||
negativeXToggle = EntityFlag.IS_PREGNANT;
|
||||
negativeYToggle = EntityFlag.SHEARED;
|
||||
negativeZToggle = EntityFlag.STALKING;
|
||||
break;
|
||||
}
|
||||
if (dataLeech != null) {
|
||||
// Indicate that rotation should be checked
|
||||
metadata.getFlags().setFlag(EntityFlag.BRIBED, true);
|
||||
|
||||
Rotation rotation = (Rotation) entityMetadata.getValue();
|
||||
int rotationX = getRotation(rotation.getPitch());
|
||||
int rotationY = getRotation(rotation.getYaw());
|
||||
int rotationZ = getRotation(rotation.getRoll());
|
||||
// The top bit acts like binary and determines if each rotation goes above 100
|
||||
// We don't do this for the negative values out of concerns of the number being too big
|
||||
int topBit = (Math.abs(rotationX) >= 100 ? 4 : 0) + (Math.abs(rotationY) >= 100 ? 2 : 0) + (Math.abs(rotationZ) >= 100 ? 1 : 0);
|
||||
int value = (topBit * 1000000) + ((Math.abs(rotationX) % 100) * 10000) + ((Math.abs(rotationY) % 100) * 100) + (Math.abs(rotationZ) % 100);
|
||||
metadata.put(dataLeech, value);
|
||||
// Set the entity flags if a value is negative
|
||||
metadata.getFlags().setFlag(negativeXToggle, rotationX < 0);
|
||||
metadata.getFlags().setFlag(negativeYToggle, rotationY < 0);
|
||||
metadata.getFlags().setFlag(negativeZToggle, rotationZ < 0);
|
||||
}
|
||||
}
|
||||
if (secondEntity != null) {
|
||||
secondEntity.updateBedrockMetadata(entityMetadata, session);
|
||||
|
@ -302,6 +369,17 @@ public class ArmorStandEntity extends LivingEntity {
|
|||
}
|
||||
}
|
||||
|
||||
private int getRotation(float rotation) {
|
||||
rotation = rotation % 360f;
|
||||
if (rotation < -180f) {
|
||||
rotation += 360f;
|
||||
} else if (rotation >= 180f) {
|
||||
// 181 -> -179
|
||||
rotation = -(180 - (rotation - 180));
|
||||
}
|
||||
return (int) rotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this armor stand is not a marker, set its bounding box size and scale.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 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.connector.entity.living;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class IronGolemEntity extends GolemEntity {
|
||||
|
||||
public IronGolemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
// Indicate that we should show cracks through a resource pack
|
||||
metadata.getFlags().setFlag(EntityFlag.BRIBED, true);
|
||||
// Required, or else the overlay is black
|
||||
metadata.put(EntityData.COLOR_2, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
if (entityMetadata.getId() == 8) {
|
||||
// Required so the resource pack sees the entity health
|
||||
attributes.put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(metadata.getFloat(EntityData.HEALTH), 100f));
|
||||
updateBedrockAttributes(session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockAttributes(GeyserSession session) {
|
||||
if (!valid) return;
|
||||
|
||||
List<AttributeData> attributes = new ArrayList<>();
|
||||
for (Map.Entry<AttributeType, org.geysermc.connector.entity.attribute.Attribute> entry : this.attributes.entrySet()) {
|
||||
if (!entry.getValue().getType().isBedrockAttribute())
|
||||
continue;
|
||||
|
||||
attributes.add(AttributeUtils.getBedrockAttribute(entry.getValue()));
|
||||
}
|
||||
|
||||
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
|
||||
updateAttributesPacket.setRuntimeEntityId(geyserId);
|
||||
updateAttributesPacket.setAttributes(attributes);
|
||||
session.sendUpstreamPacket(updateAttributesPacket);
|
||||
}
|
||||
}
|
|
@ -53,9 +53,12 @@ public class RabbitEntity extends AnimalEntity {
|
|||
int variant = (int) entityMetadata.getValue();
|
||||
|
||||
// Change the killer bunny to display as white since it only exists on Java Edition
|
||||
if (variant == 99) {
|
||||
boolean isKillerBunny = variant == 99;
|
||||
if (isKillerBunny) {
|
||||
variant = 1;
|
||||
}
|
||||
// Allow the resource pack to adjust to the killer bunny
|
||||
metadata.getFlags().setFlag(EntityFlag.BRIBED, isKillerBunny);
|
||||
|
||||
metadata.put(EntityData.VARIANT, variant);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
|||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.connector.entity.living.GolemEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
@ -39,6 +40,8 @@ public class ShulkerEntity extends GolemEntity {
|
|||
|
||||
public ShulkerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
// Indicate that invisibility should be fixed through the resource pack
|
||||
metadata.getFlags().setFlag(EntityFlag.BRIBED, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -39,6 +39,8 @@ public class SpellcasterIllagerEntity extends AbstractIllagerEntity {
|
|||
|
||||
public SpellcasterIllagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
// OptionalPack usage
|
||||
metadata.getFlags().setFlag(EntityFlag.BRIBED, this.entityType == EntityType.ILLUSIONER);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -82,6 +82,9 @@ public class PlayerEntity extends LivingEntity {
|
|||
profile = gameProfile;
|
||||
uuid = gameProfile.getId();
|
||||
username = gameProfile.getName();
|
||||
|
||||
// For the OptionalPack, set all bits as invisible by default as this matches Java Edition behavior
|
||||
metadata.put(EntityData.MARK_VARIANT, 0xff);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -280,6 +283,14 @@ public class PlayerEntity extends LivingEntity {
|
|||
session.sendUpstreamPacket(attributesPacket);
|
||||
}
|
||||
|
||||
if (entityMetadata.getId() == 16) {
|
||||
// OptionalPack usage for toggling skin bits
|
||||
// In Java Edition, a bit being set means that part should be enabled
|
||||
// However, to ensure that the pack still works on other servers, we invert the bit so all values by default
|
||||
// are true (0).
|
||||
metadata.put(EntityData.MARK_VARIANT, ~((byte) entityMetadata.getValue()) & 0xff);
|
||||
}
|
||||
|
||||
// Parrot occupying shoulder
|
||||
if (entityMetadata.getId() == 18 || entityMetadata.getId() == 19) {
|
||||
CompoundTag tag = (CompoundTag) entityMetadata.getValue();
|
||||
|
|
|
@ -55,7 +55,7 @@ public enum EntityType {
|
|||
SQUID(SquidEntity.class, 17, 0.8f),
|
||||
RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f),
|
||||
BAT(BatEntity.class, 19, 0.9f, 0.5f),
|
||||
IRON_GOLEM(GolemEntity.class, 20, 2.7f, 1.4f),
|
||||
IRON_GOLEM(IronGolemEntity.class, 20, 2.7f, 1.4f),
|
||||
SNOW_GOLEM(SnowGolemEntity.class, 21, 1.9f, 0.7f),
|
||||
OCELOT(OcelotEntity.class, 22, 0.35f, 0.3f),
|
||||
HORSE(HorseEntity.class, 23, 1.6f, 1.3965f),
|
||||
|
@ -147,7 +147,7 @@ public enum EntityType {
|
|||
EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"),
|
||||
VEX(VexEntity.class, 105, 0.8f, 0.4f),
|
||||
ICE_BOMB(Entity.class, 106, 0f),
|
||||
BALLOON(Entity.class, 107, 0f), //TODO
|
||||
BALLOON(Entity.class, 107, 0f),
|
||||
PUFFERFISH(PufferFishEntity.class, 108, 0.7f, 0.7f),
|
||||
SALMON(AbstractFishEntity.class, 109, 0.5f, 0.7f),
|
||||
DROWNED(ZombieEntity.class, 110, 1.95f, 0.6f),
|
||||
|
@ -168,9 +168,9 @@ public enum EntityType {
|
|||
ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0),
|
||||
|
||||
/**
|
||||
* Not an entity in Bedrock, so we replace it with a Pillager
|
||||
* Not an entity in Bedrock, so we replace it with an evoker
|
||||
*/
|
||||
ILLUSIONER(AbstractIllagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:pillager"),
|
||||
ILLUSIONER(SpellcasterIllagerEntity.class, 104, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:evocation_illager"),
|
||||
|
||||
/**
|
||||
* Not an entity in Bedrock, but used for the Ender Dragon's multiple hitboxes
|
||||
|
|
|
@ -25,22 +25,27 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityAnimationPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.AnimateEntityPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SpawnParticleEffectPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityAnimationPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
|
||||
@Translator(packet = ServerEntityAnimationPacket.class)
|
||||
public class JavaEntityAnimationTranslator extends PacketTranslator<ServerEntityAnimationPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerEntityAnimationPacket packet, GeyserSession session) {
|
||||
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||
Entity entity;
|
||||
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||
entity = session.getPlayerEntity();
|
||||
} else {
|
||||
entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||
}
|
||||
if (entity == null)
|
||||
return;
|
||||
|
@ -51,11 +56,30 @@ public class JavaEntityAnimationTranslator extends PacketTranslator<ServerEntity
|
|||
case SWING_ARM:
|
||||
animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
|
||||
break;
|
||||
case EAT_FOOD: // ACTUALLY SWING OFF HAND
|
||||
// Use the OptionalPack to trigger the animation
|
||||
AnimateEntityPacket offHandPacket = new AnimateEntityPacket();
|
||||
offHandPacket.setAnimation("animation.player.attack.rotations.offhand");
|
||||
offHandPacket.setNextState("default");
|
||||
offHandPacket.setBlendOutTime(0.0f);
|
||||
offHandPacket.setStopExpression("query.any_animation_finished");
|
||||
offHandPacket.setController("__runtime_controller");
|
||||
offHandPacket.getRuntimeEntityIds().add(entity.getGeyserId());
|
||||
|
||||
session.sendUpstreamPacket(offHandPacket);
|
||||
return;
|
||||
case CRITICAL_HIT:
|
||||
animatePacket.setAction(AnimatePacket.Action.CRITICAL_HIT);
|
||||
break;
|
||||
case ENCHANTMENT_CRITICAL_HIT:
|
||||
animatePacket.setAction(AnimatePacket.Action.MAGIC_CRITICAL_HIT);
|
||||
animatePacket.setAction(AnimatePacket.Action.MAGIC_CRITICAL_HIT); // Unsure if this does anything
|
||||
// Spawn custom particle
|
||||
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
||||
stringPacket.setIdentifier("geyseropt:enchanted_hit_multiple");
|
||||
stringPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension()));
|
||||
stringPacket.setPosition(Vector3f.ZERO);
|
||||
stringPacket.setUniqueEntityId(entity.getGeyserId());
|
||||
session.sendUpstreamPacket(stringPacket);
|
||||
break;
|
||||
case LEAVE_BED:
|
||||
animatePacket.setAction(AnimatePacket.Action.WAKE_UP);
|
||||
|
|
|
@ -26,12 +26,10 @@
|
|||
package org.geysermc.connector.network.translators.java.world;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.particle.BlockParticleData;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.particle.DustParticleData;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.particle.FallingDustParticleData;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.particle.ItemParticleData;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.particle.*;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerSpawnParticlePacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||
|
@ -43,65 +41,117 @@ import org.geysermc.connector.network.translators.effect.EffectRegistry;
|
|||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Translator(packet = ServerSpawnParticlePacket.class)
|
||||
public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnParticlePacket> {
|
||||
private final Random random = new Random();
|
||||
|
||||
@Override
|
||||
public void translate(ServerSpawnParticlePacket packet, GeyserSession session) {
|
||||
LevelEventPacket particle = new LevelEventPacket();
|
||||
switch (packet.getParticle().getType()) {
|
||||
case BLOCK:
|
||||
particle.setType(LevelEventType.PARTICLE_DESTROY_BLOCK_NO_SOUND);
|
||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
particle.setData(session.getBlockTranslator().getBedrockBlockId(((BlockParticleData) packet.getParticle().getData()).getBlockState()));
|
||||
session.sendUpstreamPacket(particle);
|
||||
break;
|
||||
case FALLING_DUST:
|
||||
//In fact, FallingDustParticle should have data like DustParticle,
|
||||
//but in MCProtocol, its data is BlockState(1).
|
||||
particle.setType(LevelEventType.PARTICLE_FALLING_DUST);
|
||||
particle.setData(session.getBlockTranslator().getBedrockBlockId(((FallingDustParticleData)packet.getParticle().getData()).getBlockState()));
|
||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
session.sendUpstreamPacket(particle);
|
||||
break;
|
||||
case ITEM:
|
||||
ItemStack javaItem = ((ItemParticleData)packet.getParticle().getData()).getItemStack();
|
||||
ItemData bedrockItem = ItemTranslator.translateToBedrock(session, javaItem);
|
||||
int id = bedrockItem.getId();
|
||||
int damage = bedrockItem.getDamage();
|
||||
particle.setType(LevelEventType.PARTICLE_ITEM_BREAK);
|
||||
particle.setData(id << 16 | damage);
|
||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
session.sendUpstreamPacket(particle);
|
||||
break;
|
||||
case DUST:
|
||||
DustParticleData data = (DustParticleData)packet.getParticle().getData();
|
||||
int r = (int) (data.getRed()*255);
|
||||
int g = (int) (data.getGreen()*255);
|
||||
int b = (int) (data.getBlue()*255);
|
||||
particle.setType(LevelEventType.PARTICLE_FALLING_DUST);
|
||||
particle.setData(((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff));
|
||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
session.sendUpstreamPacket(particle);
|
||||
break;
|
||||
default:
|
||||
LevelEventType typeParticle = EffectRegistry.getParticleLevelEventType(packet.getParticle().getType());
|
||||
if (typeParticle != null) {
|
||||
particle.setType(typeParticle);
|
||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
session.sendUpstreamPacket(particle);
|
||||
Function<Vector3f, BedrockPacket> particleCreateFunction = createParticle(session, packet.getParticle());
|
||||
if (particleCreateFunction != null) {
|
||||
if (packet.getAmount() == 0) {
|
||||
// 0 means don't apply the offset
|
||||
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
|
||||
session.sendUpstreamPacket(particleCreateFunction.apply(position));
|
||||
} else {
|
||||
String stringParticle = EffectRegistry.getParticleString(packet.getParticle().getType());
|
||||
if (stringParticle != null) {
|
||||
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
||||
stringPacket.setIdentifier(stringParticle);
|
||||
stringPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension()));
|
||||
stringPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||
session.sendUpstreamPacket(stringPacket);
|
||||
for (int i = 0; i < packet.getAmount(); i++) {
|
||||
double offsetX = this.random.nextGaussian() * (double) packet.getOffsetX();
|
||||
double offsetY = this.random.nextGaussian() * (double) packet.getOffsetY();
|
||||
double offsetZ = this.random.nextGaussian() * (double) packet.getOffsetZ();
|
||||
Vector3f position = Vector3f.from(packet.getX() + offsetX, packet.getY() + offsetY, packet.getZ() + offsetZ);
|
||||
|
||||
session.sendUpstreamPacket(particleCreateFunction.apply(position));
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// Null is only returned when no particle of this type is found
|
||||
session.getConnector().getLogger().debug("Unhandled particle packet: " + packet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param session the Bedrock client session.
|
||||
* @param particle the Java particle to translate to a Bedrock equivalent.
|
||||
* @return a function to create a packet with a specified particle, in the event we need to spawn multiple particles
|
||||
* with different offsets.
|
||||
*/
|
||||
private Function<Vector3f, BedrockPacket> createParticle(GeyserSession session, Particle particle) {
|
||||
switch (particle.getType()) {
|
||||
case BLOCK: {
|
||||
int blockState = session.getBlockTranslator().getBedrockBlockId(((BlockParticleData) particle.getData()).getBlockState());
|
||||
return (position) -> {
|
||||
LevelEventPacket packet = new LevelEventPacket();
|
||||
packet.setType(LevelEventType.PARTICLE_CRACK_BLOCK);
|
||||
packet.setPosition(position);
|
||||
packet.setData(blockState);
|
||||
return packet;
|
||||
};
|
||||
}
|
||||
case FALLING_DUST: {
|
||||
int blockState = session.getBlockTranslator().getBedrockBlockId(((FallingDustParticleData) particle.getData()).getBlockState());
|
||||
return (position) -> {
|
||||
LevelEventPacket packet = new LevelEventPacket();
|
||||
// In fact, FallingDustParticle should have data like DustParticle,
|
||||
// but in MCProtocol, its data is BlockState(1).
|
||||
packet.setType(LevelEventType.PARTICLE_FALLING_DUST);
|
||||
packet.setData(blockState);
|
||||
packet.setPosition(position);
|
||||
return packet;
|
||||
};
|
||||
}
|
||||
case ITEM: {
|
||||
ItemStack javaItem = ((ItemParticleData) particle.getData()).getItemStack();
|
||||
ItemData bedrockItem = ItemTranslator.translateToBedrock(session, javaItem);
|
||||
int data = bedrockItem.getId() << 16 | bedrockItem.getDamage();
|
||||
return (position) -> {
|
||||
LevelEventPacket packet = new LevelEventPacket();
|
||||
packet.setType(LevelEventType.PARTICLE_ITEM_BREAK);
|
||||
packet.setData(data);
|
||||
packet.setPosition(position);
|
||||
return packet;
|
||||
};
|
||||
}
|
||||
case DUST: {
|
||||
DustParticleData data = (DustParticleData) particle.getData();
|
||||
int r = (int) (data.getRed() * 255);
|
||||
int g = (int) (data.getGreen() * 255);
|
||||
int b = (int) (data.getBlue() * 255);
|
||||
int rgbData = ((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);
|
||||
return (position) -> {
|
||||
LevelEventPacket packet = new LevelEventPacket();
|
||||
packet.setType(LevelEventType.PARTICLE_FALLING_DUST);
|
||||
packet.setData(rgbData);
|
||||
packet.setPosition(position);
|
||||
return packet;
|
||||
};
|
||||
}
|
||||
default:
|
||||
LevelEventType typeParticle = EffectRegistry.getParticleLevelEventType(particle.getType());
|
||||
if (typeParticle != null) {
|
||||
return (position) -> {
|
||||
LevelEventPacket packet = new LevelEventPacket();
|
||||
packet.setType(typeParticle);
|
||||
packet.setPosition(position);
|
||||
return packet;
|
||||
};
|
||||
} else {
|
||||
String stringParticle = EffectRegistry.getParticleString(particle.getType());
|
||||
if (stringParticle != null) {
|
||||
int dimensionId = DimensionUtils.javaToBedrock(session.getDimension());
|
||||
return (position) -> {
|
||||
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
||||
stringPacket.setIdentifier(stringParticle);
|
||||
stringPacket.setDimensionId(dimensionId);
|
||||
stringPacket.setPosition(position);
|
||||
return stringPacket;
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
Subproject commit c846b8200eb8ebb37207666f7eddb83f2b636c37
|
||||
Subproject commit 53e13b7a0d2ea14df71ed0c9582d29a9b4fb4453
|
Loading…
Reference in a new issue