Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,30 @@ Developers can easily tap into the Mirage API by simply adding the Mirage depend
```xml
<dependencies>
<dependency>
<groupId>org.ipvp/groupId>
<artifactId>mirage/artifactId>
<version>2.0.0-SNAPSHOT</version>
<groupId>org.ipvp</groupId>
<artifactId>mirage</artifactId>
<version>3.0.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
```

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.
Expand Down
22 changes: 13 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>org.ipvp</groupId>
<artifactId>mirage</artifactId>
<version>2.2.0-SNAPSHOT</version>
<version>3.0.0-SNAPSHOT</version>

<name>Mirage</name>
<description>An API used to send fake blocks to players</description>
Expand Down Expand Up @@ -53,28 +53,32 @@

<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
<id>paper-repo</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>http://repo.dmulloy2.net/content/groups/public/</url>
</repository>
</repositories>

<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8-R0.1-SNAPSHOT</version>
<groupId>com.destroystokyo.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.13.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>3.6.5-SNAPSHOT</version>
<artifactId>ProtocolLib-API</artifactId>
<version>4.4.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.comphenix.packetwrapper</groupId>
<artifactId>PacketWrapper</artifactId>
<version>1.7.10-R0.1-SNAPSHOT</version>
<version>1.13-R0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
Expand Down
91 changes: 33 additions & 58 deletions src/main/java/org/ipvp/mirage/PlayerBlockSender.java
Original file line number Diff line number Diff line change
@@ -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 {

Expand All @@ -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
Expand Down Expand Up @@ -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());
}
}

Expand All @@ -103,7 +100,7 @@ public void clearBlocks(Predicate<FakeBlock> 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());
Expand All @@ -116,9 +113,9 @@ public void clearBlocks(Predicate<FakeBlock> 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
Expand All @@ -134,7 +131,7 @@ private void handleBlockChanges(Collection<FakeBlock> input) {

SetMultimap<Chunk, FakeBlock> 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)) {
Expand All @@ -154,49 +151,27 @@ private void handleBlockChanges(Collection<FakeBlock> 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<FakeBlock> 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);
}
Expand Down
15 changes: 13 additions & 2 deletions src/main/java/org/ipvp/mirage/block/FakeBlock.java
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -26,7 +28,7 @@ interface Data {
*
* @return the data
*/
byte getData();
BlockData getData();
}

/**
Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/ipvp/mirage/block/FakeBlockImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public World getWorld() {
}

@Override
public Vector getLocation() {
public Vector getVector() {
return position;
}

Expand Down
15 changes: 7 additions & 8 deletions src/main/java/org/ipvp/mirage/block/SimpleBlockData.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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) {
Expand Down
Loading