diff --git a/docs/API-Reference.md b/docs/API-Reference.md index 75da1d1..81c458b 100644 --- a/docs/API-Reference.md +++ b/docs/API-Reference.md @@ -50,6 +50,7 @@ Walter Higgins * [Examples](#examples) * [Fireworks Module](#fireworks-module) * [Examples](#examples-1) + * [Asynchronous Input Module](#asynchronous-input-module) * [Http Module](#http-module) * [http.request() function](#httprequest-function) * [sc-mqtt module](#sc-mqtt-module) @@ -819,6 +820,62 @@ location. For example... ![firework example](img/firework.png) +## Asynchronous Input Module + +The `input` module provides a simple way to prompt players for input at the +in-game prompt. In Javascript browser environments the `prompt()` function provides +a way to block execution and ask the user for input. Execution is blocked until the user +provides input using the modal dialog and clicks OK. Unfortunately Minecraft provides no +equivalent modal dialog which can be used to gather player text input. The only way to gather text +input from the player in Minecraft is to do so asynchronously. That is - a prompt message can be +sent to the player but the player is not obliged to provide input immediately, nor does the program +execution block until the player does so. + +So ScriptCraft has no `prompt()` implementation because `prompt()` is a synchronous function and +Minecraft's API provides no equivalent functions or classes which can be used to implement this synchronously. +The Minecraft API does however have a 'Conversation' API which allows for prompting of the player and asynchronously gathering text input from the player. + +This new `input()` function is best illustrated by example. The following code is for a number-guessing game: + +```javascript +var input = require('input'); +exports.numberguess = function(player){ + var randomNumber = Math.ceil(Math.random() * 10); + input( player, 'Think of a number between 1 and 10 (q to quit)', function( guess, repeat ) { + if ( guess == 'q'){ + return; + } + if ( +guess !== randomNumber ) { + if (+guess < randomNumber ) { + player.sendMessage('Too low - guess again'); + } + if (+guess > randomNumber ) { + player.sendMessage('Too high - guess again'); + } + repeat(); + } else { + player.sendMessage('You guessed correctly'); + } + }); +}; +``` + +The `input()` function takes 3 parameters, the player, a prompt message and a callback which will be invoked when the player has entered some text at the in-game command prompt. +The callback is bound to an object which has the following properties: + + * sender : The player who input the text + * value : The value of the text which has been input. + * message: The message prompt. + * repeat: A function which when invoked will repeat the original prompt. (this is for flow control) + +The callback function as well as being bound to an object with the above properties (so you can use this.value inside your callback to get the value which has just been input), can also take the following parameters (in exact order): + + * value + * repeat + * sender + +The `value` parameter will be the same as `this.value`, the `repeat` parameter will be the same as `this.repeat` and so on. + ## Http Module For handling http requests. Not to be confused with the more robust diff --git a/docs/release-notes.md b/docs/release-notes.md index 52f64f7..4e52909 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,3 +1,7 @@ +# 2014 04 13 + +Added asynchronous `input()` function module. + # 2014 03 15 ## Version 2.0.6 diff --git a/src/main/js/modules/input.js b/src/main/js/modules/input.js new file mode 100644 index 0000000..4941085 --- /dev/null +++ b/src/main/js/modules/input.js @@ -0,0 +1,86 @@ +/************************************************************************* +## Asynchronous Input Module + +The `input` module provides a simple way to prompt players for input at the +in-game prompt. In Javascript browser environments the `prompt()` function provides +a way to block execution and ask the user for input. Execution is blocked until the user +provides input using the modal dialog and clicks OK. Unfortunately Minecraft provides no +equivalent modal dialog which can be used to gather player text input. The only way to gather text +input from the player in Minecraft is to do so asynchronously. That is - a prompt message can be +sent to the player but the player is not obliged to provide input immediately, nor does the program +execution block until the player does so. + +So ScriptCraft has no `prompt()` implementation because `prompt()` is a synchronous function and +Minecraft's API provides no equivalent functions or classes which can be used to implement this synchronously. +The Minecraft API does however have a 'Conversation' API which allows for prompting of the player and asynchronously gathering text input from the player. + +This new `input()` function is best illustrated by example. The following code is for a number-guessing game: + +```javascript +var input = require('input'); +exports.numberguess = function(player){ + var randomNumber = Math.ceil(Math.random() * 10); + input( player, 'Think of a number between 1 and 10 (q to quit)', function( guess, repeat ) { + if ( guess == 'q'){ + return; + } + if ( +guess !== randomNumber ) { + if (+guess < randomNumber ) { + player.sendMessage('Too low - guess again'); + } + if (+guess > randomNumber ) { + player.sendMessage('Too high - guess again'); + } + repeat(); + } else { + player.sendMessage('You guessed correctly'); + } + }); +}; +``` + +The `input()` function takes 3 parameters, the player, a prompt message and a callback which will be invoked when the player has entered some text at the in-game command prompt. +The callback is bound to an object which has the following properties: + + * sender : The player who input the text + * value : The value of the text which has been input. + * message: The message prompt. + * repeat: A function which when invoked will repeat the original prompt. (this is for flow control) + +The callback function as well as being bound to an object with the above properties (so you can use this.value inside your callback to get the value which has just been input), can also take the following parameters (in exact order): + + * value + * repeat + * sender + +The `value` parameter will be the same as `this.value`, the `repeat` parameter will be the same as `this.repeat` and so on. + +***/ + +var bkPrompt = org.bukkit.conversations.Prompt, + bkConversationFactory = org.bukkit.conversations.ConversationFactory; + +function asyncInput( sender, promptMesg, callback) { + var repeat = function(){ + asyncInput( sender, promptMesg, callback); + }; + var prompt = new bkPrompt( ) { + getPromptText: function( ctx ) { + return promptMesg; + }, + acceptInput: function( ctx, value ) { + callback.apply( { repeat: repeat, sender: sender, message: promptMesg, value: value }, + [value, repeat, sender]); + return null; + }, + blocksForInput: function( ctx ) { + return true; + } + }; + new bkConversationFactory( __plugin ) + .withModality( true ) + .withFirstPrompt( prompt ) + .buildConversation( sender ) + .begin( ); +} +module.exports = asyncInput;