玩家是 Minecraft 的主体,没有玩家还谈什么游戏!
这一节我们专门研究玩家,这个类是 Player
接口。
之前我们说过,PlayerXXXEvent
是属于玩家的事件,可以通过 getPlayer
获取涉及到的玩家。
打开 Player
类的 JavaDocs(或者反编译查看源代码),你能看到许多方法。
UUID 是区别不同玩家所使用的标识符,正版账号的 UUID 由 Mojang 提供,盗版账号的 UUID 则由 OfflinePlayer:
+ 玩家的名字并进行哈希计算得出,当然这项工作已经由 Bukkit 为我们完成了。
getUniqueId
获得一个实体的 UUID,因为不仅玩家有 UUID,Minecraft 中的羊、末影龙、火焰弹也是有 UUID 的。
UUID id = Bukkit.getPlayer("ThatRarityEG").getUniqueId();
// 无论在哪个服务端上,上面这个 id 都是相同的
正版玩家的 UUID 是跟随账号的,不会因改名而改变。不过,如果名字相同,盗版玩家的 UUID 也不会改变。因此,我们建议使用 UUID 查找玩家。
Bukkit 提供了通过 UUID 查找玩家的方法重载,签名如下:
@Nullable
public static Player getPlayer(@NotNull UUID id)
另外还可以通过这个方法计算玩家的盗版 UUID:
@Nullable
public static UUID getPlayerUniqueId(@NotNull String playerName)
这个方法不需要玩家在线也能运转,它这样工作:
- 如果服务器在离线模式下,则计算出这个名字对应的盗版 UUID
- 如果服务器在正版模式下,并且玩家没有注册正版账号,则返回
null
。 - 如果服务器在正版模式下,并且玩家注册了正版账号……???
最后一种情况的行为是不确定的,得到的结果不可靠,因此,对于正版玩家,应当等玩家在线时再使用 getUniqueId
来获取 UUID。
kickPlayer
方法用于将玩家踢出服务器。
Bukkit.getPlayer("RarityEG").kickPlayer("信号不好");
就可以踢出名为 RarityEG 的玩家,踢出玩家时需要提供原因,所以这里可以填入诸如 java.lang.NullPointerException
或者「随机轰炸」之类的内容来迷惑玩家。(说真的——不要这样做!)
要给玩家物品,一般使用 getInventory
获得物品栏再进行 addItem
。这里一样是提供 ItemStack
,它们的签名:
@NotNull
PlayerInventory getInventory()
@NotNull
HashMap<Integer, ItemStack> addItem(@NotNull ItemStack... items) throws IllegalArgumentException
addItem
返回的 HashMap
中包含了新物品栏中槽位与物品的对应关系,不过大多数情况下我们不使用这个结果。
ItemStack...
表示「可以传入任意个 ItemStack
类型的参数」,一个可以,两个也可以,几十上百个呢?那可能就像它说的一样,会触发 IllegalArgumentException
异常。
这里的 ItemStack
和上一节中的一样,可以进行各种设置后再交给玩家。
要给玩家经验,直接使用 Player
接口的 giveExp
即可:
default void giveExp(int amount)
它还有一个变种:
void giveExp(int amount, boolean applyMending)
如果将 applyMending
设为 false
,那么这次添加的经验将不会用于「经验修补」附魔的修复,而直接加入了玩家经验条。
如果要单独进行修复呢?
int applyMending(int amount)
返回值是剩余的经验值。至于这些到底有没有加入到玩家的经验条中,要看各个版本的实现。
另外,如果要直接给玩家等级(在附魔时比较常用):
void giveExpLevels(int amount)
你可能会想知道玩家现在的经验等级:
int getLevel()
还有一个不怎么常用的 getExp
方法:
float getExp()
这个方法返回「到下一个等级的进度」,也就是说,如果你刚升到 26 级,那么这个值就接近 0,如果你马上就要 27 级了,那么这个值就接近 1。
如果要计算玩家的具体经验值,就需要先用 getLevel
获得等级,再通过 getExp
的结果获得经验进度,然后用 这张表 进行计算。这个相对而言比较困难,而且通常没有很大的意义(万一哪天 Mojang 改了经验值计算方式你就要重新来)。大多数情况下,有等级就足够了。
展示给玩家的内容有几种:物品栏 GUI、书本视图、计分板、BOSS 血条等。
物品栏 GUI 我们已经说过,这里我们介绍书本视图。
要给玩家展示一本书,可以使用 openBook
方法:
void openBook(@NotNull ItemStack book)
这里只能使用 Material.WRITTEN_BOOK
,不能使用 Material.WRITING_BOOK
!
在调用 openBook
前:
- 先创建一个
ItemStack
,转换为BookMeta
- 确认
ItemStack
已经设置过了标题、作者、内容,三项缺一不可 - 玩家处在一个可以接受信息的状态,玩家如果正在激烈地战斗,把书显示出来显然不合适
玩家的生命值、饱食度、生命上限等都可设置:
Player hacker = Bukkit.getPlayer("Hacker");
hacker.setHealth((double) 1); // 生命值
hacker.setFoodLevel(1); // 饱食度
hacker.setMaxHealth((double) 1); // 生命上限
等等。
需要注意的是有些方法(例如 setHealth
)并没有写在 Player
接口中,而是写在了更通用的 Damageable
接口中,尝试使用 JavaDocs 中的搜索来找出它们吧!
要移动玩家,可以使用 teleport
方法,演示如下:
player.teleport(new Location(player.getWorld(), 10000, 64, 10000)); // x,y,z
Location
是玩家的新位置,它包含四个信息:
- X
- Y(高度)
- Z
- 玩家所属的世界
一般我们都是在当前世界中移动,因此可以通过 player.getWorld
方法获取玩家当前所属的世界。
setResourcePack
用于建议玩家使用一个资源包,这里只是建议,玩家是否接受需要在客户端决定。
void setResourcePack(String url, String hash)
这里的 hash
是资源包的 SHA-1 校验码,通常应该由资源包的制作者向你提供。(当然也可以自己算)
如果想知道玩家到底有没有接受资源包,可以调用这个方法:
@Nullable
PlayerResourcePackStatusEvent.Status getResourcePackStatus()
返回值是 PlayerResourcePackStatusEvent.Status
枚举中的一个:
ACCEPTED
,下载已经开始DECLINED
,玩家拒绝FAILED_DOWNLOAD
,下载失败SUCCESSFULLY_LOADED
,已经加载
所以如果万不得已,需要强制设置玩家资源包时,可以监听 PlayerResourcePackStatusEvent
,并等到 getResourcePackStatus
返回的结果是 PlayerResourcePackStatusEvent.Status.SUCCESSFULLY_LOADED
时再允许玩家做别的事。
!> 请尊重玩家!
也许这样可以让玩家没法卸载资源包,但玩家有权退出服务器!亦请了解很多玩家的机器性能并不好,可能在加载资源包的过程中崩溃!此外,如果你的 CDN 分发速度不快……玩家没有那么好的耐心!请尊重玩家的选择!
这里的移动速度指的是走路速度,不影响玩家的跳跃。也就是说,即使你把速度设置为 0,玩家仍然能够通过跳跃来进行行进。
与此相对的还有一个设置飞行速度,可用于鞘翅的飞行或者创造模式。
两个方法的签名分别是:
void setWalkSpeed(float value) throws IllegalArgumentException
void setFlySpeed(float value) throws IllegalArgumentException
// 只能从 -1 到 1,否则会抛出异常
同样它们还有对应的 getWalkSpeed
与 getFlySpeed
用于检测玩家的当前速度。
另外这里还有几个方法可以检测玩家的移动状态:
boolean isSneaking()
// 是否在潜行
boolean isSprinting()
// 是否在冲刺
boolean isFlying()
// 是否在飞行
// 附赠三个 set 方法
void setSneaking(boolean sneak)
void setSprinting(boolean sprinting)
void setFlying(boolean flying)
另外还有一个不怎么常用的方法,可以允许玩家在生存模式飞行:
void setAllowFlight(boolean flight)
理论上这个可以突破配置文件中 allow-flight
的限制,但笔者没有经过确认。即使可以,很可能也会被某些反作弊插件拦下来,那对玩家就太冤枉了,所以不建议使用。
void setBedSpawnLocation(@Nullable Location location)
这就是重设重生点的方法。要注意的是,在 1.15 及以前的版本中,请不要将玩家的重生点设在下界或末地,否则可能会出现意料之外的后果!
这个方法需要目标位置是一张床,如果不是,可以使用下面这个变种:
void setBedSpawnLocation(@Nullable Location location, boolean force)
第二个参数传入 true
即可强制重设,也不需要有床。
这个我们已经做过了,里面可以使用 ChatColor
设置颜色样式。
void sendMessage(String message)
以及它的批量发送版本:
void sendMessage(String[] messages)
// 又见到数组了
还可以「假装」是某个实体发给该玩家的:
void sendMessage(UUID sender, String message)
// 批量版本
void sendMessage(UUID sender, String[] messages)
由于所有的实体(Entity
)都有 getUniqueId
,因此可用这个方法实现「我和末影龙聊天」这样的效果。
如果我想向玩家发送 §
这样的字符呢?
可以使用:
void sendRawMessage(@NotNull String message)
这会将 message
原封不动地发给玩家,不进行解析或者渲染。
可以使用 Player
接口的这个方法让玩家说话:
void chat(@NotNull String msg)
这当然也可以用来执行命令,但如果要执行命令,还有一个专门的:
void performCommand(@NotNull command)
通过这个方法执行命令不需要输入 /
,笔者也不知道 Bukkit 为什么要设置两个方法,一般用 chat
就好的。
上面我们只是非常简略地介绍了有关 Player
接口的一些实用方法,限于篇幅无法全部介绍,你可以参考 有关 Player
接口的 JavaDocs。