-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Forms API #1476
+1,402
−1
Closed
Forms API #1476
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
96cba44
Some base work for forms API
dktapps 9f963dd
Implemented ListForm, refactored some stuff
dktapps 8ce7cb0
fix inspections
dktapps 8f40482
Change namespace to 'form' because @SOF3 is fussy
dktapps 383cb7c
Move "title" property up to `Form` base class
dktapps d0690b8
ModalForm: fix typo
dktapps fa7eadb
Added some documentation
dktapps ecd51b2
Some changes to how response data is handled to make it consistent ac…
dktapps ca107a6
Refactor ListForm -> MenuForm and Button -> MenuOption
dktapps 5b84b43
Added CustomForm and its components
dktapps a7df2ca
Added ServerSettingsForm class, refactor icon handling
dktapps 4877f96
fix some issues
dktapps a0a9eb9
don't allow re-using form objects
dktapps 04965a1
refactor some confusing shit
dktapps 17ad96f
change some exception messages to be more clear
dktapps 66b854d
Implemented form queuing
SOF3 f87b23b
Changed the argument order of some constructors
SOF3 9c72600
Merge branch 'master' into forms-api
dktapps cd5667c
fixed returning forms
dktapps e4c409a
Fixed client barfing on forms when assoc arrays are used
dktapps 845c3fc
Merge branch 'master' into forms-api
dktapps 3b6ac5f
add constant visibility
dktapps a449406
Merge branch 'master' into forms-api
dktapps 6d5e8ad
Change hasBeenQueued() to isInUse()
dktapps 7a916fd
Fixed returning $this in onSubmit() or onClose()
dktapps 1ab98db
add some phpdoc
dktapps 5f8599a
Revert "Fixed returning $this in onSubmit() or onClose()"
dktapps a835179
Fixed returning already-used forms, rev. 2
dktapps 5a76b34
Beware exception throws messing up the queue
dktapps dfdafd2
Don't block the player, it didn't do anything wrong
dktapps b0f00b0
Don't break the form queue when DataPacketSendEvent is cancelled
dktapps 402acf5
Added debug for form ID mismatch
dktapps eadc44d
Merge branch 'master' into forms-api
dktapps File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<?php | ||
|
||
/* | ||
* | ||
* ____ _ _ __ __ _ __ __ ____ | ||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ | ||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) | | ||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ | ||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Lesser General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* @author PocketMine Team | ||
* @link http://www.pocketmine.net/ | ||
* | ||
* | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace pocketmine\form; | ||
|
||
use pocketmine\form\element\CustomFormElement; | ||
use pocketmine\Player; | ||
use pocketmine\utils\Utils; | ||
|
||
abstract class CustomForm extends Form{ | ||
|
||
/** @var CustomFormElement[] */ | ||
private $elements; | ||
|
||
/** | ||
* @param string $title | ||
* @param CustomFormElement[] $elements | ||
*/ | ||
public function __construct(string $title, array $elements){ | ||
assert(Utils::validateObjectArray($elements, CustomFormElement::class)); | ||
|
||
parent::__construct($title); | ||
$this->elements = array_values($elements); | ||
} | ||
|
||
/** | ||
* @return string | ||
*/ | ||
public function getType() : string{ | ||
return Form::TYPE_CUSTOM_FORM; | ||
} | ||
|
||
/** | ||
* @param int $index | ||
* | ||
* @return CustomFormElement|null | ||
*/ | ||
public function getElement(int $index) : ?CustomFormElement{ | ||
return $this->elements[$index] ?? null; | ||
} | ||
|
||
/** | ||
* @return CustomFormElement[] | ||
*/ | ||
public function getAllElements() : array{ | ||
return $this->elements; | ||
} | ||
|
||
public function onSubmit(Player $player) : ?Form{ | ||
return null; | ||
} | ||
|
||
/** | ||
* Called when a player closes the form without submitting it. | ||
* @param Player $player | ||
* @return Form|null a form which will be opened immediately (before queued forms) as a response to this form, or null if not applicable. | ||
*/ | ||
public function onClose(Player $player) : ?Form{ | ||
return null; | ||
} | ||
|
||
|
||
public function handleResponse(Player $player, $data) : ?Form{ | ||
if($data === null){ | ||
return $this->onClose($player); | ||
} | ||
|
||
if(is_array($data)){ | ||
/** @var array $data */ | ||
foreach($data as $index => $value){ | ||
$this->elements[$index]->setValue($value); | ||
} | ||
|
||
return $this->onSubmit($player); | ||
} | ||
|
||
throw new \UnexpectedValueException("Expected array or NULL, got " . gettype($data)); | ||
} | ||
|
||
public function serializeFormData() : array{ | ||
return [ | ||
"content" => $this->elements | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
<?php | ||
|
||
/* | ||
* | ||
* ____ _ _ __ __ _ __ __ ____ | ||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \ | ||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) | | ||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/ | ||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_| | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Lesser General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* @author PocketMine Team | ||
* @link http://www.pocketmine.net/ | ||
* | ||
* | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
/** | ||
* API for Minecraft: Bedrock custom UI (forms) | ||
*/ | ||
namespace pocketmine\form; | ||
|
||
use pocketmine\Player; | ||
|
||
/** | ||
* Base class for a custom form. Forms are serialized to JSON data to be sent to clients. | ||
*/ | ||
abstract class Form implements \JsonSerializable{ | ||
|
||
public const TYPE_MODAL = "modal"; | ||
public const TYPE_MENU = "form"; | ||
public const TYPE_CUSTOM_FORM = "custom_form"; | ||
|
||
/** @var string */ | ||
protected $title = ""; | ||
/** @var bool */ | ||
private $inUse = false; | ||
|
||
public function __construct(string $title){ | ||
$this->title = $title; | ||
} | ||
|
||
/** | ||
* Returns the type used to show this form to clients | ||
* @return string | ||
*/ | ||
abstract public function getType() : string; | ||
|
||
/** | ||
* Returns the text shown on the form title-bar. | ||
* @return string | ||
*/ | ||
public function getTitle() : string{ | ||
return $this->title; | ||
} | ||
|
||
/** | ||
* Handles a form response from a player. Plugins should not override this method, override {@link onSubmit} | ||
* instead. | ||
* | ||
* @param Player $player | ||
* @param mixed $data | ||
* | ||
* @return Form|null a form which will be opened immediately (before queued forms) as a response to this form, or null if not applicable. | ||
*/ | ||
abstract public function handleResponse(Player $player, $data) : ?Form; | ||
|
||
/** | ||
* Called when a player submits this form. Each form type usually has its own methods for getting relevant data from | ||
* them. | ||
* | ||
* Plugins should extend the class and override this function and add their own code to handle form responses as | ||
* they wish. | ||
* | ||
* @param Player $player | ||
* @return Form|null a form which will be opened immediately (before queued forms) as a response to this form, or null if not applicable. | ||
*/ | ||
abstract public function onSubmit(Player $player) : ?Form; | ||
|
||
/** | ||
* Returns whether the form has already been sent to a player or not. Note that you cannot send the form again if | ||
* this is true. | ||
* | ||
* @return bool | ||
*/ | ||
public function isInUse() : bool{ | ||
return $this->inUse; | ||
} | ||
|
||
/** | ||
* Called to flag the form as having been sent to prevent it being used again, to avoid concurrency issues. | ||
*/ | ||
public function setInUse() : void{ | ||
if($this->isInUse()){ | ||
throw new \InvalidArgumentException("Form is already in use, create a new one instead"); | ||
} | ||
$this->inUse = true; | ||
} | ||
|
||
/** | ||
* Clears response data from a form, useful if you want to reuse the same form object several times. | ||
*/ | ||
public function clearResponseData() : void{ | ||
|
||
} | ||
|
||
/** | ||
* Serializes the form to JSON for sending to clients. | ||
* | ||
* @return array | ||
*/ | ||
final public function jsonSerialize() : array{ | ||
$jsonBase = [ | ||
"type" => $this->getType(), | ||
"title" => $this->getTitle() | ||
]; | ||
|
||
return array_merge($jsonBase, $this->serializeFormData()); | ||
} | ||
|
||
/** | ||
* Serializes additional data needed to show this form to clients. | ||
* @return array | ||
*/ | ||
abstract protected function serializeFormData() : array; | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is very confusing. It is not called by the server, but relies on the developer to call it.
This leads to inconsistency:
The answer is:
It is particularly tempting to hold a CustomForm instance and use the values there later, since the data cannot be easily extracted. I can foresee this is going to cause a lot of concurrency bugs that can be very difficult to identify. Therefore, it is suggested that, starting from the core, one of either approach ("resend same instance" or "new instance each resend") be adopted to avoid confusion.
clearResponseData()
method after each onSubmit() call.Deciding which approach to adopt should require extensive consultation.