Folia
Folia copied to clipboard
Conversations API results in chat breaking
Expected behavior
Bukkit Conversations API can be used as normal
Observed/Actual behavior
Once a conversation is begin()ed with a player, every chat message they say results in this:
[00:04:52 ERROR]: Chain link failed, continuing to next one
java.lang.UnsupportedOperationException: null
at net.minecraft.server.network.ServerGamePacketListenerImpl.broadcastChatMessage(ServerGamePacketListenerImpl.java:2550) ~[?:?]
at net.minecraft.server.network.ServerGamePacketListenerImpl.lambda$handleChat$11(ServerGamePacketListenerImpl.java:2238) ~[?:?]
at java.util.concurrent.CompletableFuture$UniAccept.tryFire(CompletableFuture.java:718) ~[?:?]
at java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[?:?]
at java.lang.Thread.run(Thread.java:833) ~[?:?]
Steps/models to reproduce
Scala snippet of the code I am using to invoke the conversations API:
private case class PromptState(
promise: Promise[String],
conversation: Conversation,
)
class Prompts(using plugin: Plugin):
private val prompts = scala.collection.concurrent.TrieMap[UUID, PromptState]()
def prompt(player: Player, prompt: String): Future[String] =
if prompts.contains(player.getUniqueId()) then
val state = prompts(player.getUniqueId())
state.promise.failure(Exception("cancelled by another prompt"))
state.conversation.abandon()
prompts.remove(player.getUniqueId())
val promise = Promise[String]()
given ctx: ExecutionContext = EntityExecutionContext(player)
val bukkitPrompt = new StringPrompt:
override def getPromptText(context: ConversationContext): String =
prompt
override def acceptInput(context: ConversationContext, input: String): Prompt =
prompts(player.getUniqueId()).promise.complete(Try(input))
prompts.remove(player.getUniqueId())
Prompt.END_OF_CONVERSATION
val conversation = ConversationFactory(plugin)
.withModality(false)
.withLocalEcho(false)
.withFirstPrompt(bukkitPrompt)
.buildConversation(player)
val state = PromptState(promise, conversation)
Future { player.closeInventory() }
conversation.begin()
prompts(player.getUniqueId()) = state
promise.future
Plugin and Datapack List
Only my own code (findable at https://github.com/BlueMoonVineyard/BallCore/tree/work/jblackquill/folia)
Folia version
> version
[00:09:49 INFO]: Checking version, please wait...
[00:09:49 INFO]: This server is running Folia version git-Folia-"ed7a5c5" (MC: 1.19.4) (Implementing API version 1.19.4-R0.1-SNAPSHOT) (Git: ed7a5c5)
You are 1 version(s) behind
Download the new version at: https://papermc.io/downloads
Other
No response
No plans to resolve
No plans to resolve
@Spottedleaf excuse me? This is unacceptable given the large amount of plugins using the Conversation API. It breaks by merely blocking the input.
I was able to workaround this by using ProtocolLib. Here is a pseudocode:
this.addReceivingListener(PacketType.Play.Client.CHAT, event -> {
String message = event.getPacket().getStrings().read(0);
Player player = event.getPlayer();
if (player.isConversing()) {
player.acceptConversationInput(message);
event.setCancelled(true);
}
});
For anyone interested in reproducing this on Folia here is the full Java code that makes Folia break:
Player player = getPlayer();
player.beginConversation(new Conversation(SimplePlugin.getInstance(), player, new ValidatingPrompt() {
@Override
public String getPromptText(ConversationContext context) {
context.getForWhom().sendRawMessage("Dopice");
return "Type anything";
}
@Override
protected boolean isInputValid(ConversationContext context, String input) {
return false;
}
@Override
protected Prompt acceptValidatedInput(ConversationContext context, String input) {
return Prompt.END_OF_CONVERSATION;
}
}));
No plans to resolve
@Spottedleaf excuse me? This is unacceptable given the large amount of plugins using the Conversation API. It breaks by merely blocking the input.
I was able to workaround this by using ProtocolLib. Here is a pseudocode:
this.addReceivingListener(PacketType.Play.Client.CHAT, event -> { String message = event.getPacket().getStrings().read(0); Player player = event.getPlayer(); if (player.isConversing()) { player.acceptConversationInput(message); event.setCancelled(true); } });
The conversation API will not be implemented at this time as I imagine there is higher priority stuff going on, and there's lots of API that doesn't work. If you know how to implement it, you are free to open a pull request.
Thanks for clarification. I understand now. Sure, if I have time I'll try to make a pull request.