Add effects support and block break particles/place sounds

Co-authored-by: RednedEpic <redned235@gmail.com>
This commit is contained in:
DoctorMacc 2020-04-22 23:40:49 -05:00 committed by RednedEpic
parent 3e15d21931
commit b0a8b9219a
11 changed files with 455 additions and 4 deletions

View file

@ -30,6 +30,7 @@ import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsExcepti
import com.github.steveice10.mc.auth.exception.request.RequestException; import com.github.steveice10.mc.auth.exception.request.RequestException;
import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.github.steveice10.mc.protocol.MinecraftProtocol;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket; import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
import com.github.steveice10.packetlib.Client; import com.github.steveice10.packetlib.Client;
@ -126,6 +127,12 @@ public class GeyserSession implements CommandSender {
@Setter @Setter
private boolean jumping; private boolean jumping;
@Setter
private BlockState breakingBlock;
@Setter
private Vector3i lastBlockPlacePosition;
@Setter @Setter
private boolean switchingDimension = false; private boolean switchingDimension = false;
private boolean manyDimPackets = false; private boolean manyDimPackets = false;

View file

@ -27,6 +27,10 @@ package org.geysermc.connector.network.translators.bedrock;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
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 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;
@ -41,6 +45,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket; import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket; import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket;
import org.geysermc.connector.network.translators.block.BlockTranslator;
@Translator(packet = PlayerActionPacket.class) @Translator(packet = PlayerActionPacket.class)
public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket> { public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket> {
@ -107,6 +112,11 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
session.getDownstream().getSession().send(startBreakingPacket); session.getDownstream().getSession().send(startBreakingPacket);
break; break;
case CONTINUE_BREAK: case CONTINUE_BREAK:
LevelEventPacket continueBreakPacket = new LevelEventPacket();
continueBreakPacket.setType(LevelEventType.PUNCH_BLOCK);
continueBreakPacket.setData(BlockTranslator.getBedrockBlockId(session.getBreakingBlock() == null ? BlockTranslator.AIR : session.getBreakingBlock()));
continueBreakPacket.setPosition(packet.getBlockPosition().toFloat());
session.getUpstream().sendPacket(continueBreakPacket);
break; break;
case ABORT_BREAK: case ABORT_BREAK:
ClientPlayerActionPacket abortBreakingPacket = new ClientPlayerActionPacket(PlayerAction.CANCEL_DIGGING, new Position(packet.getBlockPosition().getX(), ClientPlayerActionPacket abortBreakingPacket = new ClientPlayerActionPacket(PlayerAction.CANCEL_DIGGING, new Position(packet.getBlockPosition().getX(),

View file

@ -26,12 +26,16 @@
package org.geysermc.connector.network.translators.bedrock; package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
import com.nukkitx.math.vector.Vector3i;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.ItemStackTranslator;
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.network.translators.Translators; import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.block.BlockTranslator;
import org.geysermc.connector.network.translators.item.ItemTranslator;
import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.InventoryUtils;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
@ -73,6 +77,29 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(), packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(),
false); false);
session.getDownstream().getSession().send(blockPacket); session.getDownstream().getSession().send(blockPacket);
Vector3i clickPos = packet.getBlockPosition();
// TODO: Find a better way to do this?
switch (packet.getFace()) {
case 0:
clickPos = clickPos.sub(0, 1, 0);
break;
case 1:
clickPos = clickPos.add(0, 1, 0);
break;
case 2:
clickPos = clickPos.sub(0, 0, 1);
break;
case 3:
clickPos = clickPos.add(0, 0, 1);
break;
case 4:
clickPos = clickPos.sub(1, 0, 0);
break;
case 5:
clickPos = clickPos.add(1, 0, 0);
break;
}
session.setLastBlockPlacePosition(clickPos);
break; break;
case 1: case 1:
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND); ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019-2020 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.network.translators.effect;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
public class Effect {
private String javaName;
private String bedrockName;
private String type;
private int data;
private String identifier;
}

View file

@ -26,6 +26,8 @@
package org.geysermc.connector.network.translators.java.entity.player; package org.geysermc.connector.network.translators.java.entity.player;
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.block.BlockState;
import com.github.steveice10.mc.protocol.data.game.world.particle.BlockParticleData;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerActionAckPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerActionAckPacket;
import com.github.steveice10.opennbt.tag.builtin.*; import com.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
@ -49,9 +51,13 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer
LevelEventPacket levelEvent = new LevelEventPacket(); LevelEventPacket levelEvent = new LevelEventPacket();
switch (packet.getAction()) { switch (packet.getAction()) {
case FINISH_DIGGING: case FINISH_DIGGING:
levelEvent.setType(LevelEventType.DESTROY);
levelEvent.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
levelEvent.setData(BlockTranslator.getBedrockBlockId(session.getBreakingBlock()));
session.getUpstream().sendPacket(levelEvent);
session.setBreakingBlock(null);
ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition()); ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition());
break; break;
case START_DIGGING: case START_DIGGING:
levelEvent.setType(LevelEventType.BLOCK_START_BREAK); levelEvent.setType(LevelEventType.BLOCK_START_BREAK);
levelEvent.setPosition(Vector3f.from( levelEvent.setPosition(Vector3f.from(
@ -70,9 +76,9 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer
} }
double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState().getId(), itemEntry, nbtData, session.getPlayerEntity()) * 20); double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState().getId(), itemEntry, nbtData, session.getPlayerEntity()) * 20);
levelEvent.setData((int) (65535 / breakTime)); levelEvent.setData((int) (65535 / breakTime));
session.setBreakingBlock(packet.getNewState());
session.getUpstream().sendPacket(levelEvent); session.getUpstream().sendPacket(levelEvent);
break; break;
case CANCEL_DIGGING: case CANCEL_DIGGING:
levelEvent.setType(LevelEventType.BLOCK_STOP_BREAK); levelEvent.setType(LevelEventType.BLOCK_STOP_BREAK);
levelEvent.setPosition(Vector3f.from( levelEvent.setPosition(Vector3f.from(
@ -81,6 +87,7 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer
packet.getPosition().getZ() packet.getPosition().getZ()
)); ));
levelEvent.setData(0); levelEvent.setData(0);
session.setBreakingBlock(null);
session.getUpstream().sendPacket(levelEvent); session.getUpstream().sendPacket(levelEvent);
break; break;
} }

View file

@ -25,9 +25,13 @@
package org.geysermc.connector.network.translators.java.world; package org.geysermc.connector.network.translators.java.world;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.SoundEvent;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
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.network.translators.block.BlockTranslator;
import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.ChunkUtils;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockChangePacket; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockChangePacket;
@ -38,5 +42,23 @@ public class JavaBlockChangeTranslator extends PacketTranslator<ServerBlockChang
@Override @Override
public void translate(ServerBlockChangePacket packet, GeyserSession session) { public void translate(ServerBlockChangePacket packet, GeyserSession session) {
ChunkUtils.updateBlock(session, packet.getRecord().getBlock(), packet.getRecord().getPosition()); ChunkUtils.updateBlock(session, packet.getRecord().getBlock(), packet.getRecord().getPosition());
Vector3i lastPlacePos = session.getLastBlockPlacePosition();
if (lastPlacePos == null) {
return;
}
if (lastPlacePos.getX() != packet.getRecord().getPosition().getX()
|| lastPlacePos.getY() != packet.getRecord().getPosition().getY()
|| lastPlacePos.getZ() != packet.getRecord().getPosition().getZ()) {
return;
}
// This is not sent from the server, so we need to send it this way
LevelSoundEventPacket placeBlockSoundPacket = new LevelSoundEventPacket();
placeBlockSoundPacket.setSound(SoundEvent.PLACE);
placeBlockSoundPacket.setPosition(lastPlacePos.toFloat());
placeBlockSoundPacket.setBabySound(false);
placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(packet.getRecord().getBlock()));
placeBlockSoundPacket.setIdentifier(":");
session.getUpstream().sendPacket(placeBlockSoundPacket);
session.setLastBlockPlacePosition(null);
} }
} }

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2019-2020 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.network.translators.java.world;
import com.github.steveice10.mc.protocol.data.game.world.effect.ParticleEffect;
import com.github.steveice10.mc.protocol.data.game.world.effect.*;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerPlayEffectPacket;
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.packet.LevelEventPacket;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.block.BlockTranslator;
import org.geysermc.connector.network.translators.effect.Effect;
import org.geysermc.connector.utils.EffectUtils;
@Translator(packet = ServerPlayEffectPacket.class)
public class JavaPlayEffectTranslator extends PacketTranslator<ServerPlayEffectPacket> {
@Override
public void translate(ServerPlayEffectPacket packet, GeyserSession session) {
LevelEventPacket effect = new LevelEventPacket();
// Some things here are particles, others are not
if (packet.getEffect() instanceof ParticleEffect) {
ParticleEffect particleEffect = (ParticleEffect) packet.getEffect();
Effect geyserEffect = EffectUtils.EFFECTS.get(particleEffect.name());
if (geyserEffect != null) {
String name = geyserEffect.getBedrockName();
effect.setType(LevelEventType.valueOf(name));
} else {
switch (particleEffect) {
// TODO: BREAK_SPLASH_POTION has additional data
// TODO: Block break doesn't work when you're the player.
case BONEMEAL_GROW:
effect.setType(LevelEventType.BONEMEAL);
BonemealGrowEffectData growEffectData = (BonemealGrowEffectData) packet.getData();
effect.setData(growEffectData.getParticleCount());
break;
//TODO: Block break particles when under fire
case BREAK_BLOCK:
effect.setType(LevelEventType.DESTROY);
BreakBlockEffectData breakBlockEffectData = (BreakBlockEffectData) packet.getData();
effect.setData(BlockTranslator.getBedrockBlockId(breakBlockEffectData.getBlockState()));
break;
// TODO: Check these three
case EXPLOSION:
effect.setType(LevelEventType.PARTICLE_EXPLODE);
break;
case MOB_SPAWN:
effect.setType(LevelEventType.ENTITY_SPAWN);
break;
// Done with a dispenser
case SMOKE:
// Might need to be SHOOT
effect.setType(LevelEventType.PARTICLE_SMOKE);
break;
default:
GeyserConnector.getInstance().getLogger().debug("No effect handling for particle effect: " + packet.getEffect());
}
}
effect.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
session.getUpstream().sendPacket(effect);
} else if (packet.getEffect() instanceof SoundEffect) {
SoundEffect soundEffect = (SoundEffect) packet.getEffect();
Effect geyserEffect = EffectUtils.EFFECTS.get(soundEffect.name());
if (geyserEffect != null) {
// Some events are LevelEventTypes, some are SoundEvents.
if (geyserEffect.getType().equals("soundLevel")) {
// TODO: Opening doors also does not work as the player
effect.setType(LevelEventType.valueOf(geyserEffect.getBedrockName()));
} else if (geyserEffect.getType().equals("soundEvent")) {
LevelSoundEvent2Packet soundEvent = new LevelSoundEvent2Packet();
// Separate case since each RecordEffectData in Java is an individual track in Bedrock
if (geyserEffect.getJavaName().equals("RECORD")) {
RecordEffectData recordEffectData = (RecordEffectData) packet.getData();
soundEvent.setSound(EffectUtils.RECORDS.get(recordEffectData.getRecordId()));
} else {
soundEvent.setSound(SoundEvent.valueOf(geyserEffect.getBedrockName()));
}
soundEvent.setExtraData(geyserEffect.getData());
soundEvent.setIdentifier(geyserEffect.getIdentifier());
soundEvent.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
session.getUpstream().sendPacket(soundEvent);
}
} else {
GeyserConnector.getInstance().getLogger().debug("No effect handling for sound effect: " + packet.getEffect());
}
}
if (effect.getType() != null) {
effect.setPosition(Vector3f.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
session.getUpstream().sendPacket(effect);
}
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2019-2020 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.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.*;
import com.nukkitx.protocol.bedrock.data.ItemData;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import com.nukkitx.protocol.bedrock.packet.SpawnParticleEffectPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.Translators;
import org.geysermc.connector.network.translators.block.BlockTranslator;
import org.geysermc.connector.utils.EffectUtils;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerSpawnParticlePacket;
import com.nukkitx.math.vector.Vector3f;
@Translator(packet = ServerSpawnParticlePacket.class)
public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnParticlePacket> {
@Override
public void translate(ServerSpawnParticlePacket packet, GeyserSession session) {
LevelEventPacket particle = new LevelEventPacket();
switch (packet.getParticle().getType()) {
case BLOCK:
particle.setType(LevelEventType.DESTROY);
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
particle.setData(BlockTranslator.getBedrockBlockId(((BlockParticleData) packet.getParticle().getData()).getBlockState()));
session.getUpstream().sendPacket(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(BlockTranslator.getBedrockBlockId(((FallingDustParticleData)packet.getParticle().getData()).getBlockState()));
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
session.getUpstream().sendPacket(particle);
break;
case ITEM:
ItemStack javaItem = ((ItemParticleData)packet.getParticle().getData()).getItemStack();
ItemData bedrockItem = Translators.getItemTranslator().translateToBedrock(session, javaItem);
int id = bedrockItem.getId();
short 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.getUpstream().sendPacket(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.getUpstream().sendPacket(particle);
break;
default:
LevelEventType typeParticle = EffectUtils.getParticleLevelEventType(packet.getParticle().getType());
if (typeParticle != null) {
particle.setType(typeParticle);
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
session.getUpstream().sendPacket(particle);
} else {
String stringParticle = EffectUtils.getParticleString(packet.getParticle().getType());
if (stringParticle != null) {
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
stringPacket.setIdentifier(stringParticle);
stringPacket.setDimensionId(session.getPlayerEntity().getDimension());
stringPacket.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
session.getUpstream().sendPacket(stringPacket);
}
}
break;
}
}
}

View file

@ -0,0 +1,106 @@
package org.geysermc.connector.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.mc.protocol.data.game.world.particle.ParticleType;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.SoundEvent;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.NonNull;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.translators.effect.Effect;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class EffectUtils {
public static final Map<String, Effect> EFFECTS = new HashMap<>();
public static final Int2ObjectMap<SoundEvent> RECORDS = new Int2ObjectOpenHashMap<>();
private static Map<ParticleType, LevelEventType> particleTypeMap = new HashMap<>();
private static Map<ParticleType, String> particleStringMap = new HashMap<>();
public static void init() {
// no-op
}
static {
/* Load particles */
InputStream particleStream = Toolbox.getResource("mappings/particles.json");
JsonNode particleEntries;
try {
particleEntries = Toolbox.JSON_MAPPER.readTree(particleStream);
} catch (Exception e) {
throw new AssertionError("Unable to load particle map", e);
}
Iterator<Map.Entry<String, JsonNode>> particlesIterator = particleEntries.fields();
while (particlesIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = particlesIterator.next();
try {
setIdentifier(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase()));
} catch (IllegalArgumentException e1) {
try {
setIdentifier(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText());
GeyserConnector.getInstance().getLogger().debug("Force to map particle "
+ entry.getKey()
+ "=>"
+ entry.getValue().asText()
+ ", it will take effect.");
} catch (IllegalArgumentException e2){
GeyserConnector.getInstance().getLogger().warning("Fail to map particle " + entry.getKey() + "=>" + entry.getValue().asText());
}
}
}
/* Load effects */
InputStream effectsStream = Toolbox.getResource("mappings/effects.json");
JsonNode effects;
try {
effects = Toolbox.JSON_MAPPER.readTree(effectsStream);
} catch (Exception e) {
throw new AssertionError("Unable to load effects mappings", e);
}
Iterator<Map.Entry<String, JsonNode>> effectsIterator = effects.fields();
while (effectsIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = effectsIterator.next();
// Separate records database since they're handled differently between the two versions
if (entry.getValue().has("records")) {
JsonNode records = entry.getValue().get("records");
Iterator<Map.Entry<String, JsonNode>> recordsIterator = records.fields();
while (recordsIterator.hasNext()) {
Map.Entry<String, JsonNode> recordEntry = recordsIterator.next();
RECORDS.put(Integer.parseInt(recordEntry.getKey()), SoundEvent.valueOf(recordEntry.getValue().asText()));
}
}
String identifier = (entry.getValue().has("identifier")) ? entry.getValue().get("identifier").asText() : "";
int data = (entry.getValue().has("data")) ? entry.getValue().get("data").asInt() : -1;
Effect effect = new Effect(entry.getKey(), entry.getValue().get("name").asText(), entry.getValue().get("type").asText(), data, identifier);
EFFECTS.put(entry.getKey(), effect);
}
}
public static void setIdentifier(ParticleType type, LevelEventType identifier) {
particleTypeMap.put(type, identifier);
}
public static void setIdentifier(ParticleType type, String identifier) {
particleStringMap.put(type, identifier);
}
public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) {
return particleTypeMap.getOrDefault(type, null);
}
public static String getParticleString(@NonNull ParticleType type){
return particleStringMap.getOrDefault(type, null);
}
}

View file

@ -129,6 +129,8 @@ public class Toolbox {
itemIndex++; itemIndex++;
} }
// Load particle/effect mappings
EffectUtils.init();
// Load sound mappings // Load sound mappings
SoundUtils.init(); SoundUtils.init();
// Load the locale data // Load the locale data

@ -1 +1 @@
Subproject commit 9ecd90c71a26423a5f824554cce9b4236e544723 Subproject commit b03f56113199a1a360efc68d2a80b8f706c6f56d