Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interact with an optional resource pack to add more features #2176

Merged
merged 28 commits into from
May 10, 2021
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0a35b93
Initial work on optional pack
Camotoy Mar 1, 2021
ce89d5c
Fix integer overflows
Camotoy Mar 2, 2021
afa910b
Merge branch 'master' of https://github.com/GeyserMC/Geyser into opti…
Camotoy Mar 3, 2021
9bc9210
Support iron golem and shulker features
Camotoy Mar 4, 2021
310cb8b
Add support for spectral arrow entity texture
Camotoy Mar 4, 2021
c1f0489
Send COLOR_2 metadata and set AttributeType fields to final
Camotoy Mar 6, 2021
2ef7e3d
Make iron golem health more consistent
Camotoy Mar 6, 2021
7e2c70c
Don't print
Camotoy Mar 6, 2021
4eba0c7
Add killer bunny support
Camotoy Mar 7, 2021
b134dca
Spawn custom enchanted hit particle
Camotoy Mar 7, 2021
7c6a839
Add height offset
Camotoy Mar 7, 2021
8d509f9
Point enchanted hit animation to target entity
Camotoy Mar 8, 2021
089f4b5
Merge branch 'master' of https://github.com/GeyserMC/Geyser into opti…
Camotoy Mar 9, 2021
c14aa1b
Merge branch 'master' of https://github.com/GeyserMC/Geyser into opti…
Camotoy Mar 21, 2021
96ca375
Merge branch 'master' of https://github.com/GeyserMC/Geyser into opti…
Camotoy Mar 30, 2021
7ad4aff
Merge branch 'master' of https://github.com/GeyserMC/Geyser into opti…
Camotoy Apr 2, 2021
6fd87e8
Send multiple particles when the server tells us to spawn multiple
Camotoy Apr 3, 2021
24b36db
Merge branch 'master' of https://github.com/GeyserMC/Geyser into opti…
Camotoy Apr 13, 2021
db35faf
Change block fall mappings to be more accurate
Camotoy Apr 14, 2021
fc57ea0
Merge branch 'master' of https://github.com/GeyserMC/Geyser into opti…
Camotoy Apr 20, 2021
c6ade99
Add visual illusioner support
Camotoy Apr 25, 2021
abe2ac7
Add offhand animation
Camotoy Apr 26, 2021
8a76cbf
Merge branch 'master' of https://github.com/GeyserMC/Geyser into opti…
Camotoy Apr 27, 2021
a1384af
Update mappings
Camotoy Apr 27, 2021
a32fe7b
Mention the OptionalPack in the README
Camotoy Apr 27, 2021
4ab4edf
Merge branch 'master' of https://github.com/GeyserMC/Geyser into opti…
Camotoy May 3, 2021
a5b2116
Add skin part visibility toggling support
Camotoy May 4, 2021
7555d59
Update README to point to the wiki
Camotoy May 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/GeyserOptionalPack) in order to function, such as custom armor stand poses.

## Compiling
1. Clone the repo to your computer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@AllArgsConstructor
@ToString
public class Attribute {

private AttributeType type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

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;
Expand Down Expand Up @@ -157,6 +158,72 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s

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);
Expand Down Expand Up @@ -302,6 +369,17 @@ private void updateSecondEntityStatus(boolean sendMetadata) {
}
}

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.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,12 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vecto
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
Expand Down Expand Up @@ -280,6 +283,14 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -51,11 +56,30 @@ public void translate(ServerEntityAnimationPacket packet, GeyserSession session)
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);
Expand Down
Loading