diff --git a/README.md b/README.md
index df979d5..656e895 100644
--- a/README.md
+++ b/README.md
@@ -19,20 +19,30 @@ Developers can easily tap into the Mirage API by simply adding the Mirage depend
```xml
- org.ipvp/groupId>
- mirage/artifactId>
- 2.0.0-SNAPSHOT
+ org.ipvp
+ mirage
+ 3.0.0-SNAPSHOT
provided
```
+Make sure to register the packet adapters in your main class:
+
+```java
+@Override
+public void onEnable() {
+ ProtocolLibrary.getProtocolManager().addPacketListener(new BlockDigAdapter(this));
+ ProtocolLibrary.getProtocolManager().addPacketListener(new BlockClickAdapter(this));
+}
+```
+
### Sending Blocks ###
Sending blocks is made simple through the usage of the ```BlockGenerator``` and ```FakeBlockSender``` interfaces. To begin, we will need to create a `BlockGenerator` and a `FakeBlockSender` instance for our own usage:
```java
-private BlockGenerator generator = new SingleBlockGenerator(Material.WOOL, (byte) 14);
+private BlockGenerator generator = new SingleBlockGenerator(Material.PURPLE_STAINED_GLASS);
private FakeBlockSender fakeBlockSender = new PlayerBlockSender(player);
```
In this example, we will be sending fake blocks of red wool to players.
diff --git a/pom.xml b/pom.xml
index 2a23b5e..f7bb230 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
org.ipvp
mirage
- 2.2.0-SNAPSHOT
+ 3.0.0-SNAPSHOT
Mirage
An API used to send fake blocks to players
@@ -53,28 +53,32 @@
- spigot-repo
- https://hub.spigotmc.org/nexus/content/groups/public/
+ paper-repo
+ https://papermc.io/repo/repository/maven-public/
+
+
+ dmulloy2-repo
+ http://repo.dmulloy2.net/content/groups/public/
- org.spigotmc
- spigot-api
- 1.8-R0.1-SNAPSHOT
+ com.destroystokyo.paper
+ paper-api
+ 1.13.1-R0.1-SNAPSHOT
provided
com.comphenix.protocol
- ProtocolLib
- 3.6.5-SNAPSHOT
+ ProtocolLib-API
+ 4.4.0
provided
com.comphenix.packetwrapper
PacketWrapper
- 1.7.10-R0.1-SNAPSHOT
+ 1.13-R0.1-SNAPSHOT
compile
diff --git a/src/main/java/org/ipvp/mirage/PlayerBlockSender.java b/src/main/java/org/ipvp/mirage/PlayerBlockSender.java
index 79bbbaf..78518fb 100644
--- a/src/main/java/org/ipvp/mirage/PlayerBlockSender.java
+++ b/src/main/java/org/ipvp/mirage/PlayerBlockSender.java
@@ -1,36 +1,31 @@
package org.ipvp.mirage;
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Predicate;
-
+import com.comphenix.packetwrapper.WrapperPlayServerMultiBlockChange;
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.ProtocolLibrary;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
+import com.comphenix.protocol.wrappers.MultiBlockChangeInfo;
+import com.comphenix.protocol.wrappers.WrappedBlockData;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
+import org.bukkit.metadata.FixedMetadataValue;
+import org.bukkit.plugin.Plugin;
import org.bukkit.util.Vector;
import org.ipvp.mirage.block.FakeBlock;
import org.ipvp.mirage.block.FakeBlockImpl;
import org.ipvp.mirage.block.SimpleBlockData;
import org.ipvp.mirage.generator.BlockGenerator;
-import com.comphenix.protocol.PacketType;
-import com.comphenix.protocol.ProtocolLibrary;
-import com.comphenix.protocol.events.PacketContainer;
-import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
-import com.comphenix.protocol.wrappers.MultiBlockChangeInfo;
-import com.comphenix.protocol.wrappers.WrappedBlockData;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.SetMultimap;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
public class PlayerBlockSender implements FakeBlockSender {
@@ -40,11 +35,13 @@ public class PlayerBlockSender implements FakeBlockSender {
/**
* Initializes a fake block sender for a specific player
*
- * @param player Player to recieve fake blocks
+ * @param player Player to receive fake blocks
*/
- public PlayerBlockSender(Player player) {
+ public PlayerBlockSender(Plugin plugin, Player player) {
Objects.requireNonNull(player, "Player cannot be null");
this.who = player;
+
+ player.setMetadata(BLOCK_SENDER_META, new FixedMetadataValue(plugin, this));
}
@Override
@@ -88,7 +85,7 @@ public void clearBlockAt(Vector location) {
if (block != null) {
Location loc = new Location(who.getWorld(), location.getBlockX(), location.getBlockY(), location.getBlockZ());
Block current = loc.getBlock();
- who.sendBlockChange(loc, current.getType(), current.getData());
+ who.sendBlockChange(loc, current.getBlockData());
}
}
@@ -103,7 +100,7 @@ public void clearBlocks(Predicate test) {
sentBlocks.values().stream()
.filter(test) // Filter unwanted
.forEach(fakeBlock -> {
- Vector fakeLocation = fakeBlock.getLocation();
+ Vector fakeLocation = fakeBlock.getVector();
if (fakeBlock.getWorld().isChunkLoaded(fakeLocation.getBlockX() >> 4, fakeLocation.getBlockZ() >> 4)) {
FakeBlock.Data data = getCurrentData(fakeBlock);
FakeBlock current = new FakeBlockImpl(data, fakeBlock.getWorld(), fakeLocation, fakeBlock.getGenerator());
@@ -116,9 +113,9 @@ public void clearBlocks(Predicate test) {
// Gets an updated block data for a fake block
private FakeBlock.Data getCurrentData(FakeBlock block) {
- Vector loc = block.getLocation();
+ Vector loc = block.getVector();
Block current = block.getWorld().getBlockAt(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
- return new SimpleBlockData(current.getType(), current.getData());
+ return new SimpleBlockData(current.getBlockData());
}
// Handle block changes for a player
@@ -134,7 +131,7 @@ private void handleBlockChanges(Collection input) {
SetMultimap pairs = HashMultimap.create();
input.forEach(block -> {
- Vector location = block.getLocation();
+ Vector location = block.getVector();
int chunkX = location.getBlockX() >> 4;
int chunkZ = location.getBlockZ() >> 4;
if (block.getWorld().isChunkLoaded(chunkX, chunkZ)) {
@@ -154,49 +151,27 @@ private void handleBlockChanges(Collection input) {
// Sends a single block change to a player
private void sendSingleBlockChange(FakeBlock block) {
- Vector location = block.getLocation();
- Location loc = new Location(who.getWorld(), location.getBlockX(), location.getBlockY(), location.getBlockZ());
- who.sendBlockChange(loc, block.getData().getType(), block.getData().getData());
+ Location loc = block.getLocation();
+ who.sendBlockChange(loc, block.getData().getData());
}
// Sends a bulk block change in a chunk to a player
private void sendBulkBlockChange(Chunk chunk, Set blocksToSend) throws IOException {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.MULTI_BLOCK_CHANGE);
- ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(blocksToSend.size());
- DataOutputStream dataOutputStream = new DataOutputStream(byteOutputStream);
+ WrapperPlayServerMultiBlockChange wrapper = new WrapperPlayServerMultiBlockChange(packet);
- short[] ashort = new short[blocksToSend.size()];
- int[] blocks = new int[blocksToSend.size()];
+ MultiBlockChangeInfo[] records = new MultiBlockChangeInfo[blocksToSend.size()];
int i = 0;
for (FakeBlock block : blocksToSend) {
- Vector location = block.getLocation();
- int blockID = block.getData().getType().getId();
- int data = block.getData().getData();
- // data = SpigotDebreakifier.getCorrectedData(blockID, data);
-
- blocks[i] = ((blockID & 0xFFF) << 4 | data & 0xF);
- ashort[i] = ((short) ((location.getBlockX() & 0xF) << 12 | (location.getBlockZ() & 0xF) << 8 | location.getBlockY()));
-
- dataOutputStream.writeShort(ashort[i]);
- dataOutputStream.writeShort(blocks[i]);
- i++;
+ records[i++] = new MultiBlockChangeInfo(block.getLocation(), WrappedBlockData.createData(block.getData().getData()));
}
- int expectedSize = blocksToSend.size() * 4;
- byte[] bulk = byteOutputStream.toByteArray();
- if (bulk.length != expectedSize) {
- throw new IOException("Expected length: '" + expectedSize + "' doesn't match the generated length: '" + bulk.length + "'");
- }
+ wrapper.setChunk(new ChunkCoordIntPair(chunk.getX(), chunk.getZ()));
+ wrapper.setRecords(records);
- // Write the data to the packet
- packet.getChunkCoordIntPairs().write(0, new ChunkCoordIntPair(chunk.getX(), chunk.getZ()));
- packet.getByteArrays().write(0, bulk);
- packet.getIntegers().write(0, blocksToSend.size());
- packet.getSpecificModifier(short[].class).write(0, ashort);
- packet.getIntegerArrays().write(0, blocks);
try {
- ProtocolLibrary.getProtocolManager().sendServerPacket(getPlayer(), packet);
+ ProtocolLibrary.getProtocolManager().sendServerPacket(getPlayer(), wrapper.getHandle());
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
diff --git a/src/main/java/org/ipvp/mirage/block/FakeBlock.java b/src/main/java/org/ipvp/mirage/block/FakeBlock.java
index 2a72399..cba5749 100644
--- a/src/main/java/org/ipvp/mirage/block/FakeBlock.java
+++ b/src/main/java/org/ipvp/mirage/block/FakeBlock.java
@@ -1,7 +1,9 @@
package org.ipvp.mirage.block;
+import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
+import org.bukkit.block.data.BlockData;
import org.bukkit.util.Vector;
import org.ipvp.mirage.generator.BlockGenerator;
@@ -26,7 +28,7 @@ interface Data {
*
* @return the data
*/
- byte getData();
+ BlockData getData();
}
/**
@@ -43,12 +45,21 @@ interface Data {
*/
World getWorld();
+ /**
+ * Returns the location of this block.
+ *
+ * @return the location, as a vector
+ */
+ Vector getVector();
+
/**
* Returns the location of this block.
*
* @return the location
*/
- Vector getLocation();
+ default Location getLocation() {
+ return getVector().toLocation(getWorld());
+ }
/**
* Returns the block generator that was used to create this block.
diff --git a/src/main/java/org/ipvp/mirage/block/FakeBlockImpl.java b/src/main/java/org/ipvp/mirage/block/FakeBlockImpl.java
index 720191b..cdb7345 100644
--- a/src/main/java/org/ipvp/mirage/block/FakeBlockImpl.java
+++ b/src/main/java/org/ipvp/mirage/block/FakeBlockImpl.java
@@ -29,7 +29,7 @@ public World getWorld() {
}
@Override
- public Vector getLocation() {
+ public Vector getVector() {
return position;
}
diff --git a/src/main/java/org/ipvp/mirage/block/SimpleBlockData.java b/src/main/java/org/ipvp/mirage/block/SimpleBlockData.java
index 4124cd6..58e11b0 100644
--- a/src/main/java/org/ipvp/mirage/block/SimpleBlockData.java
+++ b/src/main/java/org/ipvp/mirage/block/SimpleBlockData.java
@@ -2,29 +2,28 @@
import com.google.common.base.Preconditions;
import org.bukkit.Material;
+import org.bukkit.block.data.BlockData;
public class SimpleBlockData implements FakeBlock.Data {
- private final Material type;
- private final byte data;
+ private final BlockData data;
public SimpleBlockData(Material type) {
- this(type, (byte) 0);
+ this(type.createBlockData());
}
- public SimpleBlockData(Material type, byte data) {
- Preconditions.checkNotNull(type, "Type cannot be null");
- this.type = type;
+ public SimpleBlockData(BlockData data) {
+ Preconditions.checkNotNull(data, "Data cannot be null");
this.data = data;
}
@Override
public Material getType() {
- return type;
+ return data.getMaterial();
}
@Override
- public byte getData() {
+ public BlockData getData() {
return data;
}
}
diff --git a/src/main/java/org/ipvp/mirage/generator/SingleBlockGenerator.java b/src/main/java/org/ipvp/mirage/generator/SingleBlockGenerator.java
index e677c67..3308bae 100644
--- a/src/main/java/org/ipvp/mirage/generator/SingleBlockGenerator.java
+++ b/src/main/java/org/ipvp/mirage/generator/SingleBlockGenerator.java
@@ -1,6 +1,7 @@
package org.ipvp.mirage.generator;
import org.bukkit.Material;
+import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import org.ipvp.mirage.block.FakeBlock.Data;
@@ -10,8 +11,12 @@ public class SingleBlockGenerator implements BlockGenerator {
private final Data data;
- public SingleBlockGenerator(Material type, byte data) {
- this(new SimpleBlockData(type, data));
+ public SingleBlockGenerator(Material type) {
+ this(new SimpleBlockData(type));
+ }
+
+ public SingleBlockGenerator(BlockData data) {
+ this(new SimpleBlockData(data));
}
public SingleBlockGenerator(Data data) {
diff --git a/src/main/java/org/ipvp/mirage/protocol/BlockClickAdapter.java b/src/main/java/org/ipvp/mirage/protocol/BlockClickAdapter.java
new file mode 100644
index 0000000..5899c0f
--- /dev/null
+++ b/src/main/java/org/ipvp/mirage/protocol/BlockClickAdapter.java
@@ -0,0 +1,64 @@
+package org.ipvp.mirage.protocol;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.events.ListenerPriority;
+import com.comphenix.protocol.events.PacketAdapter;
+import com.comphenix.protocol.events.PacketEvent;
+import com.comphenix.protocol.reflect.StructureModifier;
+import com.comphenix.protocol.wrappers.BlockPosition;
+import com.comphenix.protocol.wrappers.EnumWrappers;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.ipvp.mirage.FakeBlockSender;
+import org.ipvp.mirage.block.FakeBlock;
+
+import java.util.Optional;
+
+public class BlockClickAdapter extends PacketAdapter {
+
+ private final BlockFace[] FACES_TO_CHECK = new BlockFace[] {BlockFace.SELF, BlockFace.UP, BlockFace.DOWN, BlockFace.EAST,
+ BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH};
+
+ public BlockClickAdapter(JavaPlugin plugin) {
+ super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.USE_ITEM);
+ }
+
+ @Override
+ public void onPacketReceiving(PacketEvent event) {
+ StructureModifier positions = event.getPacket().getBlockPositionModifier();
+ StructureModifier directions = event.getPacket().getDirections();
+
+
+ Player player = event.getPlayer();
+ BlockPosition position = positions.read(0);
+ int x = position.getX(), y = position.getY(), z = position.getZ();
+ Location location = new Location(player.getWorld(), x, y, z);
+
+ Optional sender = FakeBlockSender.getFrom(player);
+ if (!sender.isPresent()) {
+ return;
+ }
+
+ for (BlockFace face : FACES_TO_CHECK) {
+ Block block = location.getBlock().getRelative(face);
+ FakeBlock visualBlock = sender.get().getBlockAt(block.getLocation().toVector());
+ if (visualBlock != null) {
+ event.setCancelled(true);
+ player.sendBlockChange(location, visualBlock.getData().getData());
+ player.updateInventory();
+ }
+ }
+
+ if (event.isCancelled()) {
+ EnumWrappers.Direction direction = directions.read(0);
+ BlockFace bukkit = BlockFace.valueOf(direction.name());
+ Block block = location.getBlock().getRelative(bukkit);
+
+ player.sendBlockChange(block.getLocation(), Material.AIR.createBlockData());
+ }
+ }
+}
diff --git a/src/main/java/org/ipvp/mirage/protocol/BlockDigAdapter.java b/src/main/java/org/ipvp/mirage/protocol/BlockDigAdapter.java
index 6a1deb5..11ab41b 100644
--- a/src/main/java/org/ipvp/mirage/protocol/BlockDigAdapter.java
+++ b/src/main/java/org/ipvp/mirage/protocol/BlockDigAdapter.java
@@ -2,6 +2,9 @@
import java.util.Optional;
+import com.comphenix.protocol.wrappers.BlockPosition;
+import com.comphenix.protocol.wrappers.ChunkPosition;
+import com.comphenix.protocol.wrappers.EnumWrappers;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -18,22 +21,21 @@
public class BlockDigAdapter extends PacketAdapter {
- private static final int STARTED_DIGGING = 0;
- private static final int FINISHED_DIGGING = 2;
-
public BlockDigAdapter(JavaPlugin plugin) {
super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.BLOCK_DIG);
}
@Override
public void onPacketReceiving(PacketEvent event) {
- StructureModifier modifier = event.getPacket().getIntegers();
+ StructureModifier digTypes = event.getPacket().getPlayerDigTypes();
+ StructureModifier positions = event.getPacket().getBlockPositionModifier();
try {
- int status = modifier.read(4);
- if (status == STARTED_DIGGING || status == FINISHED_DIGGING) {
+ EnumWrappers.PlayerDigType status = digTypes.read(0);
+ if (status == EnumWrappers.PlayerDigType.START_DESTROY_BLOCK || status == EnumWrappers.PlayerDigType.STOP_DESTROY_BLOCK) {
Player player = event.getPlayer();
- int x = modifier.read(0), y = modifier.read(1), z = modifier.read(2);
+ BlockPosition position = positions.read(0);
+ int x = position.getX(), y = position.getY(), z = position.getZ();
Location location = new Location(player.getWorld(), x, y, z);
Optional sender = FakeBlockSender.getFrom(player);
@@ -45,8 +47,8 @@ public void onPacketReceiving(PacketEvent event) {
if (visualBlock != null) {
event.setCancelled(true);
FakeBlock.Data data = visualBlock.getData();
- if (status == FINISHED_DIGGING || player.getGameMode() == GameMode.CREATIVE) {
- player.sendBlockChange(location, data.getType(), data.getData());
+ if (status == EnumWrappers.PlayerDigType.STOP_DESTROY_BLOCK || player.getGameMode() == GameMode.CREATIVE) {
+ player.sendBlockChange(location, data.getData());
}
}
}
diff --git a/src/main/java/org/ipvp/mirage/protocol/BlockPlaceAdapter.java b/src/main/java/org/ipvp/mirage/protocol/BlockPlaceAdapter.java
deleted file mode 100644
index c94170e..0000000
--- a/src/main/java/org/ipvp/mirage/protocol/BlockPlaceAdapter.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.ipvp.mirage.protocol;
-
-import java.util.Optional;
-
-import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.entity.Player;
-import org.bukkit.plugin.java.JavaPlugin;
-import org.ipvp.mirage.FakeBlockSender;
-
-import com.comphenix.protocol.PacketType;
-import com.comphenix.protocol.events.ListenerPriority;
-import com.comphenix.protocol.events.PacketAdapter;
-import com.comphenix.protocol.events.PacketEvent;
-import com.comphenix.protocol.reflect.FieldAccessException;
-import com.comphenix.protocol.reflect.StructureModifier;
-import org.ipvp.mirage.block.FakeBlock;
-
-public class BlockPlaceAdapter extends PacketAdapter {
-
- public BlockPlaceAdapter(JavaPlugin plugin) {
- super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.BLOCK_PLACE);
- }
-
- @Override
- public void onPacketReceiving(PacketEvent event) {
- StructureModifier modifier = event.getPacket().getIntegers();
- Player player = event.getPlayer();
-
- try {
- int face = modifier.read(3);
- if (face == 255) {
- return;
- }
-
- int x = modifier.read(0);
- int y = modifier.read(1);
- int z = modifier.read(2);
- Location clickedBlock = new Location(player.getWorld(), x, y, z);
- clickedBlock = clickedBlock.getBlock().getLocation();
-
- Optional opt = FakeBlockSender.getFrom(player);
- if (!opt.isPresent()) {
- return;
- }
-
- FakeBlockSender sender = opt.get();
- FakeBlock block = sender.getBlockAt(clickedBlock.toVector());
-
- if (block == null) {
- return;
- }
-
- Location placedLocation = clickedBlock.clone();
- switch (face) {
- case 2:
- placedLocation.add(0, 0, -1);
- break;
- case 3:
- placedLocation.add(0, 0, 1);
- break;
- case 4:
- placedLocation.add(-1, 0, 0);
- break;
- case 5:
- placedLocation.add(1, 0, 0);
- break;
- default:
- return;
- }
-
- event.setCancelled(true);
-
- // Revert the block placing for the player
- if (sender.getBlockAt(placedLocation.toVector()) == null) {
- player.sendBlockChange(placedLocation, Material.AIR, (byte) 0);
- player.updateInventory();
- }
- } catch (FieldAccessException ex) {
- ex.printStackTrace();
- }
- }
-}