diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9TO1_8.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9TO1_8.java index 88057bdb2..7ca26205e 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9TO1_8.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/Protocol1_9TO1_8.java @@ -12,10 +12,7 @@ import us.myles.ViaVersion.api.remapper.ValueTransformer; import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.packets.State; import us.myles.ViaVersion.protocols.protocol1_9to1_8.packets.*; -import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider; -import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.EntityIdProvider; -import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.HandItemProvider; -import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.*; import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.*; import us.myles.ViaVersion.util.GsonUtil; @@ -95,6 +92,7 @@ public class Protocol1_9TO1_8 extends Protocol { protected void register(ViaProviders providers) { providers.register(HandItemProvider.class, new HandItemProvider()); providers.register(BulkChunkTranslatorProvider.class, new BulkChunkTranslatorProvider()); + providers.register(CommandBlockProvider.class, new CommandBlockProvider()); providers.register(EntityIdProvider.class, new EntityIdProvider()); providers.require(MovementTransmitterProvider.class); if (Via.getConfig().isStimulatePlayerTick()) { @@ -125,5 +123,7 @@ public class Protocol1_9TO1_8 extends Protocol { userConnection.put(new InventoryTracker(userConnection)); // Place block tracker userConnection.put(new PlaceBlockTracker(userConnection)); + // CommandBlock storage + userConnection.put(new CommandBlockStorage(userConnection)); } } diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/PlayerPackets.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/PlayerPackets.java index aa8559ad0..2b495a185 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/PlayerPackets.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/PlayerPackets.java @@ -18,6 +18,7 @@ import us.myles.ViaVersion.protocols.protocol1_9to1_8.PlayerMovementMapper; import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8; import us.myles.ViaVersion.protocols.protocol1_9to1_8.chat.ChatRewriter; import us.myles.ViaVersion.protocols.protocol1_9to1_8.chat.GameMode; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.CommandBlockProvider; import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks; import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker; @@ -183,6 +184,16 @@ public class PlayerPackets { tracker.setGameMode(GameMode.getById(wrapper.get(Type.UNSIGNED_BYTE, 0))); //Set player gamemode } }); + + // Gotta fake their op + handler(new PacketHandler() { + @Override + public void handle(PacketWrapper wrapper) throws Exception { + CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class); + provider.sendPermission(wrapper.user()); + } + } + ); } }); @@ -323,6 +334,15 @@ public class PlayerPackets { wrapper.user().get(EntityTracker.class).setGameMode(GameMode.getById(gamemode)); } }); + + // Fake permissions to get Commandblocks working + handler(new PacketHandler() { + @Override + public void handle(PacketWrapper wrapper) throws Exception { + CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class); + provider.sendPermission(wrapper.user()); + } + }); } }); diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/WorldPackets.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/WorldPackets.java index b4552defd..ef510dc8f 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/WorldPackets.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/WorldPackets.java @@ -1,5 +1,6 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.packets; +import com.google.common.base.Optional; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.spacehq.opennbt.tag.builtin.CompoundTag; @@ -18,6 +19,7 @@ import us.myles.ViaVersion.protocols.protocol1_9to1_8.ItemRewriter; import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8; import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.Chunk1_9to1_8; import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.CommandBlockProvider; import us.myles.ViaVersion.protocols.protocol1_9to1_8.sounds.Effect; import us.myles.ViaVersion.protocols.protocol1_9to1_8.sounds.SoundEffect; import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks; @@ -121,9 +123,14 @@ public class WorldPackets { public void handle(PacketWrapper wrapper) throws Exception { ClientChunks clientChunks = wrapper.user().get(ClientChunks.class); Chunk1_9to1_8 chunk = (Chunk1_9to1_8) wrapper.passthrough(new ChunkType(clientChunks)); - if (chunk.isUnloadPacket()) + if (chunk.isUnloadPacket()) { wrapper.setId(0x1D); + // Remove commandBlocks on chunk unload + CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class); + provider.unloadChunk(wrapper.user(), chunk.getX(), chunk.getZ()); + } + // eat any other data (Usually happens with unload packets) wrapper.read(Type.REMAINING_BYTES); } @@ -189,6 +196,9 @@ public class WorldPackets { } } if (action == 2) { // Update Command Block + CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class); + provider.addOrUpdateBlock(wrapper.user(), wrapper.get(Type.POSITION, 0), wrapper.get(Type.NBT, 0)); + // To prevent window issues don't send updates wrapper.cancel(); } @@ -388,6 +398,27 @@ public class WorldPackets { } }); + // Handle CommandBlocks + handler(new PacketHandler() { + @Override + public void handle(PacketWrapper wrapper) throws Exception { + CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class); + + Position pos = wrapper.get(Type.POSITION, 0); + Optional tag = provider.get(wrapper.user(), pos); + // Send the Update Block Entity packet if present + if (tag.isPresent()) { + PacketWrapper updateBlockEntity = new PacketWrapper(0x09, null, wrapper.user()); + + updateBlockEntity.write(Type.POSITION, pos); + updateBlockEntity.write(Type.UNSIGNED_BYTE, (short) 2); + updateBlockEntity.write(Type.NBT, tag.get()); + + updateBlockEntity.send(Protocol1_9TO1_8.class); + } + } + }); + } }); } diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/providers/CommandBlockProvider.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/providers/CommandBlockProvider.java new file mode 100644 index 000000000..90381aca1 --- /dev/null +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/providers/CommandBlockProvider.java @@ -0,0 +1,65 @@ +package us.myles.ViaVersion.protocols.protocol1_9to1_8.providers; + +import com.google.common.base.Optional; +import org.spacehq.opennbt.tag.builtin.CompoundTag; +import us.myles.ViaVersion.api.PacketWrapper; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.minecraft.Position; +import us.myles.ViaVersion.api.platform.providers.Provider; +import us.myles.ViaVersion.api.type.Type; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.CommandBlockStorage; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker; + +public class CommandBlockProvider implements Provider { + + public void addOrUpdateBlock(UserConnection user, Position position, CompoundTag tag) throws Exception { + checkPermission(user); + if (isEnabled()) + getStorage(user).addOrUpdateBlock(position, tag); + } + + public Optional get(UserConnection user, Position position) throws Exception { + checkPermission(user); + if (isEnabled()) + return getStorage(user).getCommandBlock(position); + return Optional.absent(); + } + + public void unloadChunk(UserConnection user, int x, int z) throws Exception { + checkPermission(user); + if (isEnabled()) + getStorage(user).unloadChunk(x, z); + } + + private CommandBlockStorage getStorage(UserConnection connection) { + return connection.get(CommandBlockStorage.class); + } + + public void sendPermission(UserConnection user) throws Exception { + if (!isEnabled()) + return; + PacketWrapper wrapper = new PacketWrapper(0x1B, null, user); // Entity status + + wrapper.write(Type.INT, user.get(EntityTracker.class).getProvidedEntityId()); // Entity ID + wrapper.write(Type.BYTE, (byte) 26); // Hardcoded op permission level + + wrapper.send(Protocol1_9TO1_8.class); + + user.get(CommandBlockStorage.class).setPermissions(true); + } + + // Fix for Bungee since the join game is not sent after the first one + private void checkPermission(UserConnection user) throws Exception { + if (!isEnabled()) + return; + CommandBlockStorage storage = getStorage(user); + if (!storage.isPermissions()) { + sendPermission(user); + } + } + + public boolean isEnabled() { + return true; + } +} diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/storage/CommandBlockStorage.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/storage/CommandBlockStorage.java new file mode 100644 index 000000000..6c416e117 --- /dev/null +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/storage/CommandBlockStorage.java @@ -0,0 +1,72 @@ +package us.myles.ViaVersion.protocols.protocol1_9to1_8.storage; + +import com.google.common.base.Optional; +import lombok.Getter; +import lombok.Setter; +import org.spacehq.opennbt.tag.builtin.ByteTag; +import org.spacehq.opennbt.tag.builtin.CompoundTag; +import us.myles.ViaVersion.api.Pair; +import us.myles.ViaVersion.api.data.StoredObject; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.minecraft.Position; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class CommandBlockStorage extends StoredObject { + private Map, Map> storedCommandBlocks = new ConcurrentHashMap<>(); + @Setter + @Getter + private boolean permissions = false; + + public CommandBlockStorage(UserConnection user) { + super(user); + } + + public void unloadChunk(int x, int z) { + Pair chunkPos = new Pair<>(x, z); + if (storedCommandBlocks.containsKey(chunkPos)) + storedCommandBlocks.remove(chunkPos); + } + + public void addOrUpdateBlock(Position position, CompoundTag tag) { + Pair chunkPos = getChunkCoords(position); + + if (!storedCommandBlocks.containsKey(chunkPos)) + storedCommandBlocks.put(chunkPos, new ConcurrentHashMap()); + + Map blocks = storedCommandBlocks.get(chunkPos); + + if (blocks.containsKey(position)) + if (blocks.get(position).equals(tag)) + return; + + blocks.put(position, tag); + } + + private Pair getChunkCoords(Position position) { + int chunkX = (int) Math.floor(position.getX() / 16); + int chunkZ = (int) Math.floor(position.getZ() / 16); + + return new Pair<>(chunkX, chunkZ); + } + + public Optional getCommandBlock(Position position) { + Pair chunkCoords = getChunkCoords(position); + + if (!storedCommandBlocks.containsKey(chunkCoords)) + return Optional.absent(); + + Map blocks = storedCommandBlocks.get(chunkCoords); + + if (!blocks.containsKey(position)) + return Optional.absent(); + + CompoundTag tag = blocks.get(position).clone(); + tag.put(new ByteTag("powered", (byte) 0)); + tag.put(new ByteTag("auto", (byte) 0)); + tag.put(new ByteTag("conditionMet", (byte) 0)); + + return Optional.of(tag); + } +}