14问答会话
问答指令:
public class ConversationExecutor implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender instanceof Player) {
Player me = (Player) sender;
MyDefaultConversationFactory.start(me);
return true;
}
sender.sendMessage("只有玩家才嫩使用该指令!");
return true;
}
}
创建并开启会话:
/**
* 创建并开启自定义会话
*/
public class MyDefaultConversationFactory {
/**
* 问答会话 工厂
*/
private static ConversationFactory conversationFactory;
/**
* 初始化问答会话工厂
*/
static {
conversationFactory = new ConversationFactory(MyPlugin.instance)
//factory对象的playerOnlyMessage属性如果非空, 则会阻止控制台发起Conversation会话
.thatExcludesNonPlayersWithMessage("控制台你这次别参与了捏~")
//会话途中退出 用的语句
.withEscapeSequence("quit")
//10秒后没有得到 (玩家|控制台)的回复,则关闭此次会话
.withTimeout(10)
//第一次显示给(玩家|控制台)的问题(Prompt的翻译是提示,我感觉叫成问题更为合适)
.withFirstPrompt(new FirstPrompt())
//设置 每个Prompt问题的前缀
.withPrefix(new MyPromptPrefix())
//是否显示(玩家|控制台)的回复 内容,默认为true
.withLocalEcho(false)
//是否阻塞非问答会话信息的展示(比如其他玩家的聊天、服务器公告等等),默认为true
.withModality(false);
}
/**
* 获取工厂单例
*/
public static ConversationFactory get() {
return conversationFactory;
}
/**
* 给玩家开启一次问答会话
* @param playerOrConsole 玩家
*/
public static void start(Conversable playerOrConsole) {
//Conversation对象创建后就直接使用,不需要实例化后再维护或调用它abandoned方法关闭本地问答会话。服务端会根据超时时间自动关闭会话。
conversationFactory.buildConversation(playerOrConsole)
.begin();
}
}
自定义问答前缀:
/**
* 自定义问答的 前缀
*/
public class MyPromptPrefix implements ConversationPrefix {
@Override
public String getPrefix(ConversationContext context) {
//从上下文中获取信息集合
Map<Object, Object> infoMap = context.getAllSessionData();
if (!infoMap.containsKey("animal")) {
//还不包含 animal时,肯定是第一个问题
return ChatColor.GREEN + "问题一:" + ChatColor.WHITE;
}else if (!infoMap.containsKey("number")) {
return ChatColor.GREEN + "问题二:" + ChatColor.WHITE;
} else if (!infoMap.containsKey("player")) {
return ChatColor.GREEN + "问题三:" + ChatColor.WHITE;
}
return "";
}
}
四个问题的第一个:
/**
* 第一个问题:选择需要生成的生物[COW, PIG, CAT, NONE]
*/
public class FirstPrompt extends FixedSetPrompt {
/**
* 指出需要的 答案
*/
public FirstPrompt() {
//传入Fixed固定的答案
super(EntityType.COW.toString(), EntityType.PIG.toString(), EntityType.CAT.toString(), "NONE");
}
/**
* 输入正确回复 的后处理
* @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文
* @param input 与super()里添加的默认文本相同的input,可惜这里的input是匹配后的,如果想自己来验证,可以Override 父类的isInputValid方法
* @return 下一个问题,如果return null则相当于关闭了会话
*/
@Override
protected Prompt acceptValidatedInput(ConversationContext context, String input) {
/**
* 这里的input只会是 构造方法里传入的那些String,
* 如果想自己来验证input是否匹配构造方法里传入的默认参数,可以Override 父类的isInputValid方法
*/
if (input.equalsIgnoreCase("none")) {
//同等与 return null
return Prompt.END_OF_CONVERSATION;
}
//通过context保存自定义中间信息,方便在以后的问答会话中获取
context.setSessionData("animal", input);
return new SecondPrompt();
}
/**
* 一开始给出的提示
* @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文
* @return 当前问题的提示
*/
@Override
public String getPromptText(ConversationContext context) {
//由于是第一个问题:我们暂时用不到context来获取什么信息
return "请选择需要生成的动物:" + ChatColor.WHITE + this.formatFixedSet();
}
}
第二个:
/**
* 第二个问题:需要生成 X 只 XX 生物?
*/
public class SecondPrompt extends NumericPrompt {
/**
* 自己重载了这个方法,用来手动校验数字是否正确
*
* 不是所有的方法都满足我们的需求,有需求就自己Override或写个自己的方法
* @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文
* @param input 玩家给出的输入
* @return 校验结果,true表示校验通过,false表示校验不通过
*/
@Override
protected boolean isNumberValid(ConversationContext context, Number input) {
//如果玩家输入的数字小于等于0,则认为输入错误
return input.intValue() >= 1 && input.intValue() <= 10;
}
/**
* 输入校验成功 的后处理
* @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文
* @param input 校验成功的数字
* @return 下一个提示对象,返回null则关闭会话
*/
@Override
protected Prompt acceptValidatedInput(ConversationContext context, Number input) {
//存储数字到上下文中
context.setSessionData("number", input.intValue());
return new ThirdPrompt(MyPlugin.instance);
}
/**
* 提示信息
* @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文
* @return 提示信息
*/
@Override
public String getPromptText(ConversationContext context) {
return "你要生成几只" + context.getSessionData("animal") + "呢?(允许数量:[1 ~ 10])";
}
}
第三个:
public class ThirdPrompt extends PlayerNamePrompt {
/**
* 由于需要判断玩家输入的名字能否找到对应的玩家,需要插件对象
* @param plugin 插件对象
*/
public ThirdPrompt(Plugin plugin) {
super(plugin);
}
/**
* 同样的,我又对父类的isInputValid感到不满意了,于是手动@Overrider这个方法,自己对input进行判断
* @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文
* @param input 玩家输入的内容
* @return 校验结果
*/
@Override
protected boolean isInputValid(ConversationContext context, String input) {
Player player = context.getPlugin().getServer().getPlayer(input);
//如果玩家不存在,或玩家不在线,则校验失败
if (Objects.isNull(player) || !player.isOnline()) {
context.getForWhom().sendRawMessage("玩家不存在或不在线!");
return false;
}
return true;
}
/**
* 校验成功,开始处理最终结果,并通过返回null值来结束会话
* @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文
* @param input
* @return
*/
@Override
protected Prompt acceptValidatedInput(ConversationContext context, Player input) {
/**
* 可以在这里直接根据context存储的中间信息处理,然后return null结束会话
*
* 也可以跟我一样再返回一个MessagePrompt,提示个问题(友好信息)后返回null结束会话
*/
context.setSessionData("player", input);
return new FinalFriendMessagePrompt();
// return Prompt.END_OF_CONVERSATION;
}
/**
* 问题提示
*/
@Override
public String getPromptText(ConversationContext context) {
return "请输入玩家的名字来给它生成" + context.getSessionData("number") + "只" + context.getSessionData("animal") + ":";
}
}
最后一个:
/**
* 友好信息提示
*/
public class FinalFriendMessagePrompt extends MessagePrompt {
@Override
protected Prompt getNextPrompt(ConversationContext context) {
//不需要继续提出问题了
return null;
}
@Override
public String getPromptText(ConversationContext context) {
String animal = (String) context.getSessionData("animal");
Integer number = (Integer) context.getSessionData("number");
Player targetPlayer = (Player) context.getSessionData("player");
//给对应玩家生成实体
for (Integer i = 0; i < number; i++) {
targetPlayer.getWorld().spawnEntity(targetPlayer.getLocation(), EntityType.valueOf(animal));
}
//获取问题的回复者
Player forWhom = (Player) context.getForWhom();
forWhom.playSound(forWhom.getLocation(), Sound.BLOCK_ANVIL_BREAK, 1, 1);
return "成功生成了 " + number + "只" + animal + "给" + targetPlayer.getName() + "!";
}
}
效果: