Custom fluids with ProtocolLib
Showcase video:
//!import org.bukkit.Bukkit
//!import org.bukkit.Material
//!import com.comphenix.protocol.wrappers.WrappedBlockData
//!ProtocolLib
const Bukkit = org.bukkit.Bukkit;
const Material = org.bukkit.Material;
const createDataMethod = getMethod(WrappedBlockData, "createData", ["org.bukkit.Material"]);
const fluidBlocks = new Set([
Material.WATER,
Material.LAVA,
Material.FLOWING_WATER,
Material.FLOWING_LAVA
]);
var SpoofedBlock = createDataMethod.invoke(null, [Material.OAK_LOG]);
var IsSpooferActive = false;
var listener = null;
const handler = {
onSend(event) {
const packet = event.getPacket();
const type = event.getPacketType().name();
if (type === "BLOCK_CHANGE") {
const blockData = packet.getBlockData().read(0);
const material = blockData.getType();
if (fluidBlocks.has(material)) {
packet.getBlockData().write(0, SpoofedBlock);
}
} else if (type === "MULTI_BLOCK_CHANGE") {
const blocks = packet.getBlockDataArrays().read(0);
for (let i = 0; i < blocks.length; i++) {
if (fluidBlocks.has(blocks[i].getType())) {
blocks[i] = SpoofedBlock;
}
}
packet.getBlockDataArrays().write(0, blocks);
}
},
onReceive() {
}
};
function loadPacketListener() {
listener = ProtocolLib.registerListener("NORMAL", handler, [
"Play.Server.BLOCK_CHANGE",
"Play.Server.MULTI_BLOCK_CHANGE"
]);
log.info("Block spoof listener active.");
}
function setSpooferActive(Active) {
if (Active === IsSpooferActive) {
return "The block-spoofer is already " + (Active ? "active" : "inactive");
}
if (!Active) {
if (listener) {
ProtocolLib.unregisterListener(listener);
listener = null;
}
} else if (!listener) {
loadPacketListener();
}
IsSpooferActive = Active;
return "Block-spoofer is now " + (Active ? "active" : "inactive");
}
addCommand("blockspoofer", {
onCommand: function(sender, args) {
args = toArray(args); // Convert Java array to JS array
if (args.length === 0) {
sender.sendMessage("§eUsage:");
sender.sendMessage("§e/blockspoofer setblock <material>");
sender.sendMessage("§e/blockspoofer enabled <true|false>");
return;
}
const subcommand = args[0].toLowerCase();
if (subcommand === "setblock") {
if (args.length < 2) {
sender.sendMessage("§cUsage: /blockspoofer setblock <material>");
return;
}
const blockName = args[1].toUpperCase();
const material = Material.matchMaterial(blockName);
if (material === null || !material.isBlock()) {
sender.sendMessage("§cInvalid block: " + blockName);
return;
}
SpoofedBlock = createDataMethod.invoke(null, [material]);
sender.sendMessage("§aSpoofed block set to: §f" + material.name());
}
else if (subcommand === "enabled") {
if (args.length < 2) {
sender.sendMessage("§cUsage: /blockspoofer enabled <true|false>");
return;
}
const val = args[1].toLowerCase();
if (val === "true") {
sender.sendMessage("§a"+setSpooferActive(true));
} else if (val === "false") {
sender.sendMessage("§c"+setSpooferActive(false));
} else {
sender.sendMessage("§cInvalid value: use true or false.");
}
}
else {
sender.sendMessage("§cUnknown subcommand: " + subcommand);
}
},
onTabComplete: function(sender, args) {
args = toArray(args);
if (args.length === 1) {
return toJavaList(["setblock", "enabled"]);
}
const sub = args[0].toLowerCase();
if (args.length === 2 && sub === "enabled") {
return toJavaList(["true", "false"]);
}
if (args.length === 2 && sub === "setblock") {
const prefix = args[1].toLowerCase();
const matches = [];
for (const mat of Material.values()) {
if (mat.isBlock() && mat.name().toLowerCase().startsWith(prefix)) {
matches.push(mat.name().toLowerCase());
}
}
return toJavaList(matches.slice(0, 50)); // limit to 50 results for sanity
}
return toJavaList([]);
}
});
setSpooferActive(true);
Last updated
Was this helpful?