Skip to content

Instantly share code, notes, and snippets.

@keepoff07
Last active March 18, 2021 23:36
Show Gist options
  • Save keepoff07/38a827d0ea06c8b1d727 to your computer and use it in GitHub Desktop.
Save keepoff07/38a827d0ea06c8b1d727 to your computer and use it in GitHub Desktop.
[Bukkit] BookOpen: 記入済みの本の画面を開かせます。1.8.x用です。
/*
* @author keepoff07
* @license MIT License
* @copyright Copyright keepoff07 2015
*/
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
public class BookOpen {
private static String PackageName;
private static boolean v1_8 = false;
static {
String version = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
PackageName = "net.minecraft.server."+version;
if(version.startsWith("v1_8")){
v1_8 = true;
}
}
@SuppressWarnings("deprecation")
public static boolean openBook(Player player, String[] book) {
if(!v1_8) return false;
ItemStack item = player.getItemInHand().clone();
try {
player.setItemInHand(createWriteBook(book));
player.updateInventory();
Object CustomPayloadPacket = getCraftClass("PacketPlayOutCustomPayload").newInstance();
for (Field field : CustomPayloadPacket.getClass().getDeclaredFields()) {
field.setAccessible(true);
if (field.getName().equals("a")) {
field.set(CustomPayloadPacket, String.valueOf("MC|BOpen"));
} else if (field.getName().equals("b")) {
field.set(CustomPayloadPacket, getPacketDataSerializer());
}
}
sendPacket(player, CustomPayloadPacket);
return true;
}catch(Throwable e) {
return false;
} finally {
player.setItemInHand(item);
player.updateInventory();
}
}
private static ItemStack createWriteBook(String[] book) {
ItemStack item = new ItemStack(Material.WRITTEN_BOOK);
BookMeta meta = (BookMeta)item.getItemMeta();
meta.addPage(book);
item.setItemMeta(meta);
return item;
}
private static Class<?> getCraftClass(String s) throws Exception {
Class<?> craftclass = Class.forName(PackageName+"."+s);
return craftclass;
}
private static Object getPacketDataSerializer() throws Exception {
Class<?> a = getCraftClass("PacketDataSerializer");
Constructor<?> b = a.getConstructor(new Class<?>[]{ByteBuf.class});
Object PacketDataSerializer = b.newInstance(new Object[]{Unpooled.buffer()});
return PacketDataSerializer;
}
private static void sendPacket(Player player, Object packet) throws Exception {
Method PlayerHandle = player.getClass().getMethod("getHandle");
Object EntityPlayer = PlayerHandle.invoke(player);
Object PlayerConnection = null;
for(Field field : EntityPlayer.getClass().getDeclaredFields()){
if(field.getName().equals("playerConnection")){
PlayerConnection = field.get(EntityPlayer);
break;
}
}
Method sendPacket = null;
for(Method m : PlayerConnection.getClass().getDeclaredMethods()){
if(m.getName().equals("sendPacket")){
sendPacket = m;
break;
}
}
sendPacket.invoke(PlayerConnection, packet);
}
}

openBook(Player player, String[] book) - static boolean

メソッドの概要

第一引数で指定したプレイヤーに、第二引数で指定した内容の書かれた著書の画面を開かせます。
返り値はbooleanで、例外なく開かせることができればtrue、それ以外はfalseを返します。
第二引数のStringの配列は、ひとつの要素で1ページを意味します。
{"page1","page2","page3"}…のような感じです。
文字装飾や改行はこちらでは対応してないでの各自でやってください。


参考

PacketPlayOutCustomPayloadを使って本を開かせる場合、手に著書を持っている必要があるようです。
その為、一度著書をもたせ、最後に元々持っていたアイテムを持たせる、ということをやっています。
また、(Player)updateInventory()も使わないと更新されないので使用しています。

ソースの主要となる部分は

PacketPlayOutCustomPayload packet = new PacketPlayOutCustomPayload("MC|BOpen", new PacketDataSerializer(Unpooled.buffer()));  
((CraftPlayer)player).getHandle().playerConnection.sendPacket(packet);`

を基にしてリフレクションを使用した方法にしてるだけです。
EntityPlayerクラス内のopenBookメソッドを参考にしました。

1.7以下でも近い方法で試してみましたが、ダメっぽいですね。
誰か分かったら教えてください。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment