mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00: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
|
- Custom heads in inventories
|
||||||
- Clickable links in chat
|
- Clickable links in chat
|
||||||
- Glowing effect
|
- 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
|
## Compiling
|
||||||
1. Clone the repo to your computer
|
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) {
|
public AbstractArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, 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);
|
setMotion(motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,12 @@ package org.geysermc.connector.entity.attribute;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@ToString
|
||||||
public class Attribute {
|
public class Attribute {
|
||||||
|
|
||||||
private AttributeType type;
|
private AttributeType type;
|
||||||
|
|
|
@ -57,12 +57,12 @@ public enum AttributeType {
|
||||||
HUNGER(null, "minecraft:player.hunger", 0f, 20f, 20f),
|
HUNGER(null, "minecraft:player.hunger", 0f, 20f, 20f),
|
||||||
SATURATION(null, "minecraft:player.saturation", 0f, 20f, 20f);
|
SATURATION(null, "minecraft:player.saturation", 0f, 20f, 20f);
|
||||||
|
|
||||||
private String javaIdentifier;
|
private final String javaIdentifier;
|
||||||
private String bedrockIdentifier;
|
private final String bedrockIdentifier;
|
||||||
|
|
||||||
private float minimum;
|
private final float minimum;
|
||||||
private float maximum;
|
private final float maximum;
|
||||||
private float defaultValue;
|
private final float defaultValue;
|
||||||
|
|
||||||
public Attribute getAttribute(float value) {
|
public Attribute getAttribute(float value) {
|
||||||
return getAttribute(value, maximum);
|
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.EntityMetadata;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
|
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.math.vector.Vector3f;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
|
@ -157,6 +158,72 @@ public class ArmorStandEntity extends LivingEntity {
|
||||||
|
|
||||||
updateSecondEntityStatus(false);
|
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) {
|
if (secondEntity != null) {
|
||||||
secondEntity.updateBedrockMetadata(entityMetadata, session);
|
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.
|
* 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();
|
int variant = (int) entityMetadata.getValue();
|
||||||
|
|
||||||
// Change the killer bunny to display as white since it only exists on Java Edition
|
// 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;
|
variant = 1;
|
||||||
}
|
}
|
||||||
|
// Allow the resource pack to adjust to the killer bunny
|
||||||
|
metadata.getFlags().setFlag(EntityFlag.BRIBED, isKillerBunny);
|
||||||
|
|
||||||
metadata.put(EntityData.VARIANT, variant);
|
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.Vector3f;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
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.living.GolemEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
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) {
|
public ShulkerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, 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
|
@Override
|
||||||
|
|
|
@ -39,6 +39,8 @@ public class SpellcasterIllagerEntity extends AbstractIllagerEntity {
|
||||||
|
|
||||||
public SpellcasterIllagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public SpellcasterIllagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
// OptionalPack usage
|
||||||
|
metadata.getFlags().setFlag(EntityFlag.BRIBED, this.entityType == EntityType.ILLUSIONER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -82,6 +82,9 @@ public class PlayerEntity extends LivingEntity {
|
||||||
profile = gameProfile;
|
profile = gameProfile;
|
||||||
uuid = gameProfile.getId();
|
uuid = gameProfile.getId();
|
||||||
username = gameProfile.getName();
|
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
|
@Override
|
||||||
|
@ -280,6 +283,14 @@ public class PlayerEntity extends LivingEntity {
|
||||||
session.sendUpstreamPacket(attributesPacket);
|
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
|
// Parrot occupying shoulder
|
||||||
if (entityMetadata.getId() == 18 || entityMetadata.getId() == 19) {
|
if (entityMetadata.getId() == 18 || entityMetadata.getId() == 19) {
|
||||||
CompoundTag tag = (CompoundTag) entityMetadata.getValue();
|
CompoundTag tag = (CompoundTag) entityMetadata.getValue();
|
||||||
|
|
|
@ -55,7 +55,7 @@ public enum EntityType {
|
||||||
SQUID(SquidEntity.class, 17, 0.8f),
|
SQUID(SquidEntity.class, 17, 0.8f),
|
||||||
RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f),
|
RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f),
|
||||||
BAT(BatEntity.class, 19, 0.9f, 0.5f),
|
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),
|
SNOW_GOLEM(SnowGolemEntity.class, 21, 1.9f, 0.7f),
|
||||||
OCELOT(OcelotEntity.class, 22, 0.35f, 0.3f),
|
OCELOT(OcelotEntity.class, 22, 0.35f, 0.3f),
|
||||||
HORSE(HorseEntity.class, 23, 1.6f, 1.3965f),
|
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"),
|
EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"),
|
||||||
VEX(VexEntity.class, 105, 0.8f, 0.4f),
|
VEX(VexEntity.class, 105, 0.8f, 0.4f),
|
||||||
ICE_BOMB(Entity.class, 106, 0f),
|
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),
|
PUFFERFISH(PufferFishEntity.class, 108, 0.7f, 0.7f),
|
||||||
SALMON(AbstractFishEntity.class, 109, 0.5f, 0.7f),
|
SALMON(AbstractFishEntity.class, 109, 0.5f, 0.7f),
|
||||||
DROWNED(ZombieEntity.class, 110, 1.95f, 0.6f),
|
DROWNED(ZombieEntity.class, 110, 1.95f, 0.6f),
|
||||||
|
@ -168,9 +168,9 @@ public enum EntityType {
|
||||||
ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0),
|
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
|
* 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;
|
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.entity.Entity;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
import org.geysermc.connector.utils.DimensionUtils;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityAnimationPacket;
|
|
||||||
import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
|
|
||||||
|
|
||||||
@Translator(packet = ServerEntityAnimationPacket.class)
|
@Translator(packet = ServerEntityAnimationPacket.class)
|
||||||
public class JavaEntityAnimationTranslator extends PacketTranslator<ServerEntityAnimationPacket> {
|
public class JavaEntityAnimationTranslator extends PacketTranslator<ServerEntityAnimationPacket> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerEntityAnimationPacket packet, GeyserSession session) {
|
public void translate(ServerEntityAnimationPacket packet, GeyserSession session) {
|
||||||
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
Entity entity;
|
||||||
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||||
entity = session.getPlayerEntity();
|
entity = session.getPlayerEntity();
|
||||||
|
} else {
|
||||||
|
entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||||
}
|
}
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
return;
|
return;
|
||||||
|
@ -51,11 +56,30 @@ public class JavaEntityAnimationTranslator extends PacketTranslator<ServerEntity
|
||||||
case SWING_ARM:
|
case SWING_ARM:
|
||||||
animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
|
animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
|
||||||
break;
|
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:
|
case CRITICAL_HIT:
|
||||||
animatePacket.setAction(AnimatePacket.Action.CRITICAL_HIT);
|
animatePacket.setAction(AnimatePacket.Action.CRITICAL_HIT);
|
||||||
break;
|
break;
|
||||||
case ENCHANTMENT_CRITICAL_HIT:
|
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;
|
break;
|
||||||
case LEAVE_BED:
|
case LEAVE_BED:
|
||||||
animatePacket.setAction(AnimatePacket.Action.WAKE_UP);
|
animatePacket.setAction(AnimatePacket.Action.WAKE_UP);
|
||||||
|
|
|
@ -26,12 +26,10 @@
|
||||||
package org.geysermc.connector.network.translators.java.world;
|
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.entity.metadata.ItemStack;
|
||||||
import com.github.steveice10.mc.protocol.data.game.world.particle.BlockParticleData;
|
import com.github.steveice10.mc.protocol.data.game.world.particle.*;
|
||||||
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.packet.ingame.server.world.ServerSpawnParticlePacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerSpawnParticlePacket;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
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.LevelEventType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
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.network.translators.item.ItemTranslator;
|
||||||
import org.geysermc.connector.utils.DimensionUtils;
|
import org.geysermc.connector.utils.DimensionUtils;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
@Translator(packet = ServerSpawnParticlePacket.class)
|
@Translator(packet = ServerSpawnParticlePacket.class)
|
||||||
public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnParticlePacket> {
|
public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnParticlePacket> {
|
||||||
|
private final Random random = new Random();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerSpawnParticlePacket packet, GeyserSession session) {
|
public void translate(ServerSpawnParticlePacket packet, GeyserSession session) {
|
||||||
LevelEventPacket particle = new LevelEventPacket();
|
Function<Vector3f, BedrockPacket> particleCreateFunction = createParticle(session, packet.getParticle());
|
||||||
switch (packet.getParticle().getType()) {
|
if (particleCreateFunction != null) {
|
||||||
case BLOCK:
|
if (packet.getAmount() == 0) {
|
||||||
particle.setType(LevelEventType.PARTICLE_DESTROY_BLOCK_NO_SOUND);
|
// 0 means don't apply the offset
|
||||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
|
||||||
particle.setData(session.getBlockTranslator().getBedrockBlockId(((BlockParticleData) packet.getParticle().getData()).getBlockState()));
|
session.sendUpstreamPacket(particleCreateFunction.apply(position));
|
||||||
session.sendUpstreamPacket(particle);
|
} else {
|
||||||
break;
|
for (int i = 0; i < packet.getAmount(); i++) {
|
||||||
case FALLING_DUST:
|
double offsetX = this.random.nextGaussian() * (double) packet.getOffsetX();
|
||||||
//In fact, FallingDustParticle should have data like DustParticle,
|
double offsetY = this.random.nextGaussian() * (double) packet.getOffsetY();
|
||||||
//but in MCProtocol, its data is BlockState(1).
|
double offsetZ = this.random.nextGaussian() * (double) packet.getOffsetZ();
|
||||||
particle.setType(LevelEventType.PARTICLE_FALLING_DUST);
|
Vector3f position = Vector3f.from(packet.getX() + offsetX, packet.getY() + offsetY, packet.getZ() + offsetZ);
|
||||||
particle.setData(session.getBlockTranslator().getBedrockBlockId(((FallingDustParticleData)packet.getParticle().getData()).getBlockState()));
|
|
||||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
session.sendUpstreamPacket(particleCreateFunction.apply(position));
|
||||||
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);
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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