Set entities silent client-side, and more

Add warden entity events. Fix up other things.
This commit is contained in:
Camotoy 2022-05-31 14:25:15 -04:00
parent bf4e1d5be7
commit 196742a597
No known key found for this signature in database
GPG Key ID: 7EEFB66FE798081F
13 changed files with 86 additions and 51 deletions

View File

@ -184,7 +184,7 @@ public final class EntityDefinitions {
.addTranslator(MetadataType.INT, Entity::setAir) // Air/bubbles
.addTranslator(MetadataType.OPTIONAL_CHAT, Entity::setDisplayName)
.addTranslator(MetadataType.BOOLEAN, Entity::setDisplayNameVisible)
.addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.SILENT, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.addTranslator(MetadataType.BOOLEAN, Entity::setSilent)
.addTranslator(MetadataType.BOOLEAN, Entity::setGravity)
.addTranslator(MetadataType.POSE, (entity, entityMetadata) -> entity.setPose(entityMetadata.getValue()))
.addTranslator(MetadataType.INT, Entity::setFreezing)

View File

@ -23,11 +23,11 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.entity;
package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.geyser.entity.type.BoatEntity;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;

View File

@ -94,6 +94,8 @@ public class Entity {
private float boundingBoxWidth;
@Setter(AccessLevel.NONE)
protected String nametag = "";
@Setter(AccessLevel.NONE)
protected boolean silent = false;
/* Metadata end */
protected List<Entity> passengers = Collections.emptyList();
@ -148,6 +150,12 @@ public class Entity {
setFlag(EntityFlag.HAS_COLLISION, true);
setFlag(EntityFlag.CAN_SHOW_NAME, true);
setFlag(EntityFlag.CAN_CLIMB, true);
// Let the Java server (or us) supply all sounds for an entity
setClientSideSilent();
}
protected void setClientSideSilent() {
setFlag(EntityFlag.SILENT, true);
}
public void spawnEntity() {
@ -370,6 +378,10 @@ public class Entity {
dirtyMetadata.put(EntityData.NAMETAG_ALWAYS_SHOW, (byte) (entityMetadata.getPrimitiveValue() ? 1 : 0));
}
public final void setSilent(BooleanEntityMetadata entityMetadata) {
silent = entityMetadata.getPrimitiveValue();
}
public void setGravity(BooleanEntityMetadata entityMetadata) {
setFlag(EntityFlag.HAS_GRAVITY, !entityMetadata.getPrimitiveValue());
}

View File

@ -27,7 +27,6 @@ package org.geysermc.geyser.entity.type;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
@ -58,7 +57,7 @@ public class EvokerFangsEntity extends Entity implements Tickable {
public void setAttackStarted() {
this.attackStarted = true;
if (!getFlag(EntityFlag.SILENT)) {
if (!silent) {
// Play the chomp sound
PlaySoundPacket packet = new PlaySoundPacket();
packet.setPosition(this.position);

View File

@ -28,17 +28,16 @@ package org.geysermc.geyser.entity.type;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
import lombok.Getter;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.translator.collision.BlockCollision;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.level.block.BlockPositionIterator;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.collision.BlockCollision;
import org.geysermc.geyser.util.BlockUtils;
import java.util.UUID;
@ -129,7 +128,7 @@ public class FishingHookEntity extends ThrowableEntity {
}
private void sendSplashSound(GeyserSession session) {
if (!getFlag(EntityFlag.SILENT)) {
if (!silent) {
float volume = (float) (0.2f * Math.sqrt(0.2 * (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) + motion.getY() * motion.getY()));
if (volume > 1) {
volume = 1;

View File

@ -213,7 +213,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
*/
private void effectTick() {
Random random = ThreadLocalRandom.current();
if (!getFlag(EntityFlag.SILENT)) {
if (!silent) {
if (Math.cos(wingPosition * 2f * Math.PI) <= -0.3f && Math.cos(lastWingPosition * 2f * Math.PI) >= -0.3f) {
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
playSoundPacket.setSound("mob.enderdragon.flap");

View File

@ -31,13 +31,19 @@ import com.nukkitx.math.GenericMath;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.Tickable;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.MathUtils;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
public class WardenEntity extends MonsterEntity implements Tickable {
private int heartBeatDelay;
private int tickCount;
public class WardenEntity extends MonsterEntity {
public WardenEntity(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, motion, yaw, pitch, headYaw);
}
@ -53,6 +59,23 @@ public class WardenEntity extends MonsterEntity {
public void setAngerLevel(IntEntityMetadata entityMetadata) {
float anger = (float) entityMetadata.getPrimitiveValue() / 80f;
dirtyMetadata.put(EntityData.HEARTBEAT_INTERVAL_TICKS, 40 - GenericMath.floor(MathUtils.clamp(anger, 0.0F, 1.0F) * 30F));
heartBeatDelay = 40 - GenericMath.floor(MathUtils.clamp(anger, 0.0F, 1.0F) * 30F);
dirtyMetadata.put(EntityData.HEARTBEAT_INTERVAL_TICKS, heartBeatDelay);
}
@Override
public void tick() {
if (++tickCount % heartBeatDelay == 0 && !silent) {
// We have to do these calculations because they're clientside on Java Edition but we mute entities
// to prevent hearing their step sounds
ThreadLocalRandom random = ThreadLocalRandom.current();
PlaySoundPacket packet = new PlaySoundPacket();
packet.setSound("mob.warden.heartbeat");
packet.setPosition(position);
packet.setPitch(1.0f);
packet.setVolume((random.nextFloat() - random.nextFloat()) * 0.2f + 1.0f);
session.sendUpstreamPacket(packet);
}
}
}

View File

@ -75,6 +75,11 @@ public class SessionPlayerEntity extends PlayerEntity {
valid = true;
}
@Override
protected void setClientSideSilent() {
// Do nothing, since we want the session player to hear their own footstep sounds for example.
}
@Override
public void spawnEntity() {
// Already logged in

View File

@ -38,10 +38,10 @@ public class SoundMapping {
public SoundMapping(String java, String bedrock, String playsound, int extraData, String identifier, boolean levelEvent) {
this.java = java;
this.bedrock = bedrock == null || bedrock.equalsIgnoreCase("") ? null : bedrock;
this.playsound = playsound == null || playsound.equalsIgnoreCase("") ? null : playsound;
this.bedrock = bedrock == null || bedrock.isEmpty() ? null : bedrock;
this.playsound = playsound == null || playsound.isEmpty() ? null : playsound;
this.extraData = extraData;
this.identifier = identifier == null || identifier.equalsIgnoreCase("") ? ":" : identifier;
this.identifier = identifier == null || identifier.isEmpty() ? ":" : identifier;
this.levelEvent = levelEvent;
}
}

View File

@ -26,7 +26,6 @@
package org.geysermc.geyser.translator.protocol.java.entity;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundEntityEventPacket;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.SoundEvent;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
@ -42,7 +41,6 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
@Translator(packet = ClientboundEntityEventPacket.class)
@ -50,6 +48,7 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
@Override
public void translate(GeyserSession session, ClientboundEntityEventPacket packet) {
System.out.println(packet);
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
if (entity == null)
return;
@ -180,8 +179,9 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
case IRON_GOLEM_EMPTY_HAND:
entityEventPacket.setType(EntityEventType.GOLEM_FLOWER_WITHDRAW);
break;
case IRON_GOLEM_ATTACK:
if (entity.getDefinition() == EntityDefinitions.IRON_GOLEM || entity.getDefinition() == EntityDefinitions.EVOKER_FANGS) {
case ATTACK:
if (entity.getDefinition() == EntityDefinitions.IRON_GOLEM || entity.getDefinition() == EntityDefinitions.EVOKER_FANGS
|| entity.getDefinition() == EntityDefinitions.WARDEN) {
entityEventPacket.setType(EntityEventType.ATTACK_START);
if (entity.getDefinition() == EntityDefinitions.EVOKER_FANGS) {
((EvokerFangsEntity) entity).setAttackStarted();
@ -236,28 +236,14 @@ public class JavaEntityEventTranslator extends PacketTranslator<ClientboundEntit
break;
case MAKE_POOF_PARTICLES:
if (entity instanceof LivingEntity) {
// Not ideal, but...
// LevelEventType.PARTICLE_DEATH_SMOKE doesn't work (as of 1.18.2 Bedrock)
// EntityEventType.DEATH_SMOKE_CLOUD also plays the entity death noise
// Bedrock sends the particles through EntityEventType.DEATH, but Java despawns the entity
// prematurely so they don't show up.
Vector3f position = entity.getPosition();
float baseX = position.getX();
float baseY = position.getY();
float baseZ = position.getZ();
float height = entity.getBoundingBoxHeight();
float width = entity.getBoundingBoxWidth();
Random random = ThreadLocalRandom.current();
for (int i = 0; i < 20; i++) {
// Reconstruct the Java Edition (1.18.1) logic, but in floats
float x = baseX + width * (2.0f * random.nextFloat() - 1f);
float y = baseY + height * random.nextFloat();
float z = baseZ + width * (2.0f * random.nextFloat() - 1f);
LevelEventPacket levelEventPacket = new LevelEventPacket();
levelEventPacket.setPosition(Vector3f.from(x, y, z));
levelEventPacket.setType(LevelEventType.PARTICLE_EXPLODE);
session.sendUpstreamPacket(levelEventPacket);
}
// Note that this event usually makes noise, but because we set all entities as silent on the
// client end this isn't an issue.
entityEventPacket.setType(EntityEventType.DEATH_SMOKE_CLOUD);
}
break;
case WARDEN_RECEIVE_SIGNAL:
if (entity.getDefinition() == EntityDefinitions.WARDEN) {
entityEventPacket.setType(EntityEventType.VIBRATION_DETECTED);
}
break;
}

View File

@ -40,6 +40,6 @@ public class JavaSoundEntityTranslator extends PacketTranslator<ClientboundSound
if (entity == null) {
return;
}
SoundUtils.playBuiltinSound(session, packet.getSound(), entity.getPosition(), packet.getPitch());
SoundUtils.playBuiltinSound(session, packet.getSound(), entity.getPosition(), packet.getVolume(), packet.getPitch());
}
}

View File

@ -38,6 +38,6 @@ public class JavaSoundTranslator extends PacketTranslator<ClientboundSoundPacket
@Override
public void translate(GeyserSession session, ClientboundSoundPacket packet) {
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
SoundUtils.playBuiltinSound(session, packet.getSound(), position, packet.getPitch());
SoundUtils.playBuiltinSound(session, packet.getSound(), position, packet.getVolume(), packet.getPitch());
}
}

View File

@ -33,6 +33,7 @@ import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.SoundEvent;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.registry.BlockRegistries;
@ -74,10 +75,9 @@ public class SoundUtils {
return "";
}
// Drop the namespace
int colonPos = packetSound.indexOf(":");
if (colonPos != -1) {
packetSound = packetSound.substring(colonPos + 1);
// Drop the Minecraft namespace if applicable
if (packetSound.startsWith("minecraft:")) {
packetSound = packetSound.substring("minecraft:".length());
}
SoundMapping soundMapping = Registries.SOUNDS.get(packetSound);
@ -97,7 +97,7 @@ public class SoundUtils {
* @param position the position
* @param pitch the pitch
*/
public static void playBuiltinSound(GeyserSession session, BuiltinSound javaSound, Vector3f position, float pitch) {
public static void playBuiltinSound(GeyserSession session, BuiltinSound javaSound, Vector3f position, float volume, float pitch) {
String packetSound = javaSound.getName();
SoundMapping soundMapping = Registries.SOUNDS.get(packetSound);
@ -106,6 +106,17 @@ public class SoundUtils {
return;
}
if (soundMapping.getPlaysound() != null) {
// We always prefer the PlaySound mapping because we can control volume and pitch
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
playSoundPacket.setSound(soundMapping.getPlaysound());
playSoundPacket.setPosition(position);
playSoundPacket.setVolume(volume);
playSoundPacket.setPitch(pitch);
session.sendUpstreamPacket(playSoundPacket);
return;
}
if (soundMapping.isLevelEvent()) {
LevelEventPacket levelEventPacket = new LevelEventPacket();
levelEventPacket.setPosition(position);