Giter VIP home page Giter VIP logo

infoapi's Introduction

InfoAPI

Extensible templating for PocketMine plugins.

In a nutshell, InfoAPI provides a simple API to register placeholders between plugins. But it is more powerful than just that:

  • Object-oriented placeholder expressions
  • Continuously update a template when variables change
  • Parametric infos — mathematical operations on info expressions

Developer guide: Templating

If you let users customize messages in a config, you can consider formatting the message with InfoAPI.

Pass the config message into InfoAPI:

use SOFe\InfoAPI;

// $this is the plugin main
$player->sendMessage(InfoAPI::render($this, $this->getConfig()->get("format"), [
    "arg" => $arg,
], $player));
  • "message" is the config key for the message template
  • The args array are the base variables for the template. The types of the variables must be one of the default types or provided by another plugin through InfoAPI::addKind.
  • $player is optional. It tells InfoAPI how to localize the message better, e.g. by formatting for the player's language.

Advanced: Continuous templating

You can create a template and watch for changes using the renderContinuous API:

use SOFe\AwaitGenerator\Await;
use SOFe\InfoAPI;

Await::f2c(function() use($player) {
    $traverser = InfoAPI::renderContinuous($this, $this->getConfig()->get("format"), [
        "arg" => $arg,
    ], $player);

    while(yield from $traverser->next($message)) {
        $player->sendPopup($message);
    }
});

Developer guide: Register mapping

A mapping converts one info to another, e.g. money converts a player to the amount of money in {player money}. You can register your own mappings so that your plugin as well as other plugins using InfoAPI can use this info in the template.

For example, to provide the money of an online player:

InfoAPI::addMapping(
    $this, "myplugin.money",
    fn(Player $player) : ?int => $this->getMoney($player),
);

The source and return types must be a default or InfoAPI::addKind types.

Advanced: Register continuous mapping

You can additionally provide a watchChanges closure, which returns a traverser that yields a value when a change is detected. The pmevent library may help with building traversers from events:

InfoAPI::addMapping(
    $this, "myplugin.money",
    fn(Player $player) : ?int => $this->getMoney($player),
    watchChanges: fn(Player $player) => Events::watch(
        $this, MoneyChangeEvent::class, $player->getName(),
        fn(MoneyChangeEvent $event) => $event->getPlayer()->getName(),
    )->asGenerator(),
);

Developer guide: Install InfoAPI

If you are not developing a plugin, you do not need to install InfoAPI yourself. Plugins should have included InfoAPI in their phar release.

InfoAPI v2 is a virion library using virion 3.1. Virion 3.1 uses composer to install libraries:

  1. Include the InfoAPI virion by adding sof3/infoapi in your composer.json:
{
  "require": {
    "sof3/infoapi": "^2"
  }
}

You can place this file next to your plugin.yml. Installing composer is recommended but not required.

  1. Build your plugin with the InfoAPI virion using pharynx. You can test it on a server using the custom start.cmd/start.sh provided by pharynx.

  2. Use the pharynx GitHub action to integrate with Poggit. Remember to gitignore your vendor directory so that you don't push it to GitHub.

User guide: Writing a template

InfoAPI replaces expressions inside {} with variables. For example, if a chat plugin provides two variables:

  • sender: the player who sent chat (SOFe)
  • message: the chat message the following will become something like <SOFe> Hello world if the plugin provides sender and message ("Hello world")for the template:
<{sender}> {message}

Color codes are default variables. Instead of writing §1 §b etc, you could also write:

{aqua}<{sender}> {white}{message}

You can get more detailed info for a variable. For example, to get the coordinates of a player player:

{player} is at ({player x}, {player y}, {player z}).

Writing { directly will cause error without a matching }. If you want to write a {/} that is not part of an expression, write twice instead:

hello {{world}}.

This will become

hello {world}.

infoapi's People

Contributors

aboshxm2 avatar endermanbugzjfc avatar javierleon9966 avatar jaxkdev avatar mcbeany avatar nerahikada avatar poggit-bot avatar sof3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

infoapi's Issues

Anonymous context does not fallback to base context.

Reproduce

  1. Use an arbitrary plugin that contains InfoAPI::render(). E.g. ProfanityFilter
  2. Compose a template which involves the base context.

Expectation: everything would be rendered.
Reality: the anonymous info gets rendered while the base context info remains unchanged.

Attachments

config.yml (portion)

block-warning-message: "&e{player_name} {baseContext.darkBlue} {infoapi.baseContext.playerCount} {infoapi.worldCount} &cwas blocked message contains profanity message."

Log (portion)

[19:46:40.887] [Server thread/WARNING]: [ProfanityFilter] BlahCoast30765 {baseContext.darkBlue} {infoapi.baseContext.playerCount} {infoapi.worldCount} was blocked message contains profanity message.

Corresponding code executed

https://github.com/ReinfyTeam/ProfanityFilter/blob/21f20fd74eb4c5ece7e54f69088c24d34fedfba1/src/EventListener.php#L76-L79

Environment

InfoAPI: 2.0.0-beta1
PocketMine-MP: 5.4.3

Cannot detect integer and boolean types

Code:

$this->getLogger()->info(InfoAPI::render($this, "test {arg}", ["arg" => true]));

Crash:
Mon_Nov_27-07.21.29-UTC_2023.log

After debugging the code it looks like when the ReflectUtil tring to get the type of the clousre parameter for int/bool parameters, it gives different value than gettype function used here.

$reflect = new ReflectionFunction(fn(int $p1, bool $p2) => "whatever");

$closureParams = $reflect->getParameters();

echo($closureParams[0]->getType());// int
echo "\n";
echo(gettype(123));// integer
echo "\n";
echo($closureParams[1]->getType());// bool
echo "\n";
echo(gettype(true));// boolean

Sorry for bad desigging the issue.

Compilation of dev.md

What should the document do

  • Explain the differences between parts and paths.
  • Explain the Graph namespace.

DateTimeInfo and DurationInfo

DateTime

Wraps a timestamp

Name Output type Description Example
infoapi.time.year Number The year part of a date 2006
infoapi.time.month Number The month part of a date 1
infoapi.time.date Number The date part of a date 2
infoapi.time.weekday Text The weekday part of a date Mon
infoapi.time.hour Number The hour part of a time 15
infoapi.time.minute Number The minute part of a time 4
infoapi.time.second Number The second part of a time 5
infoapi.time.elapsed Duration The time since this timestamp
infoapi.time.remaining Duration The time until this timestamp (negative of elapsed)

Duration

Wraps a duration

Name Output type Description Example
infoapi.duration.days Number Number of days (rounded down) 3
infoapi.duration.rawDays Number Number of days (not rounded) 3.5
infoapi.duration.hours Number Number of hours (rounded down, excludes days) 12
infoapi.duration.rawHours Number Number of hours (not rounded, includes days) 84
infoapi.duration.minutes Number Number of minutes (rounded down, excludes hours) 0
infoapi.duration.rawMnutes Number Number of minutes (not rounded, includes hours) 5040
infoapi.duration.seconds Number Number of seconds (rounded down, excludes minutes) 0
infoapi.duration.rawSeconds Number Number of seconds (not rounded, includes minutes) 30240
infoapi.duration.later DateTime Time for duration after now
infoapi.duration.ago DateTime Time for duration before now

Sign info and writable / written book info

They should be convertible between item / block info, right? At least from item / block info to sign / book info so the new logic can be implemented without editing existing PHP code.

What they contain

text string info.

Off-topic

Got the idea when attempting and failing to implement sign shop with Mineflow. However, tbh, sign should should never be implemented with Mineflow. But a plugin instead. Time to upgrade EconomyCShop.

Type's not matching.

foreach($registry->listMinifiedDetails($parent) as $name => $detail){
$optional = substr($detail, 0, strlen($detail) - strlen($name));
/** @var Info $child */
$child = $detail->getClosure()($parent);
$value = $child->toString();
$details[] = ["optional" => $optional, "required" => $name, "info" => $child, "value" => $value];
}

Plugin Version: Jaxks branch latest build (all reproduce this)
pmmp Version: 4.0.0-dev (commit e388ac9c8b133fbf0382ef56f6ab75f04249a670 | e388ac9)
Virions: All latest dev build from poggit as of now.

Im not sure what its actually trying to get at the moment so i left it alone, but it does need looking at.
(After this error i get detail->getClosure() doesnt exist)
Dumping both $detail and $name is:

name - string(4) "name"
detail - object(SOFe\InfoAPI\StringInfo)#24001 (1) {
["string":"SOFe\InfoAPI\StringInfo":private]=>
string(13) "Jackthehaxk21"}

Error Stack Trace:

[09:34:48.158] [Server thread/WARNING]: [InfoAPI] ErrorException: strlen() expects parameter 1 to be string, object given in C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\plugi
ns\InfoAPI\src\SOFe\InfoAPI\ui\BrowseInfosCommand.php:87
Stack trace:
#0 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\plugins\InfoAPI\src\SOFe\InfoAPI\ui\BrowseInfosCommand.php(87): ErrorUtils::errorExceptionHandler(2, 'strlen() expect...', 'C:\
\Users\\Jack\\D...', 87, Array)
#1 [internal function]: SOFe\InfoAPI\ui\BrowseInfosCommand->showInfo(Object(SOFe\InfoAPI\InfoRegistry), Array, Object(SOFe\InfoAPI\PlayerInfo), Object(pocketmine\command\ConsoleCommandSender))
#2 phar://C:/Users/Jack/Documents/PocketMine/PocketMine-MP-FC-Src 4.0.0/virions/await-generator_dev-19.phar/src/SOFe/AwaitGenerator/Await.php(133): Generator->rewind()
#3 phar://C:/Users/Jack/Documents/PocketMine/PocketMine-MP-FC-Src 4.0.0/virions/await-generator_dev-19.phar/src/SOFe/AwaitGenerator/Await.php(97): SOFe\AwaitGenerator\Await->wakeup(Array)
#4 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\plugins\InfoAPI\src\SOFe\InfoAPI\ui\BrowseInfosCommand.php(72): SOFe\AwaitGenerator\Await::g2c(Object(Generator), NULL, Array)
#5 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\command\SimpleCommandMap.php(268): SOFe\InfoAPI\ui\BrowseInfosCommand->execute(Object(pocketmine\command\Console
CommandSender), 'info', Array)
#6 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\Server.php(1551): pocketmine\command\SimpleCommandMap->dispatch(Object(pocketmine\command\ConsoleCommandSender),
 'info j')
#7 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\Server.php(1301): pocketmine\Server->dispatchCommand(Object(pocketmine\command\ConsoleCommandSender), 'info j')
#8 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\vendor\pocketmine\snooze\src\SleeperHandler.php(123): pocketmine\Server->pocketmine\{closure}()
#9 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\vendor\pocketmine\snooze\src\SleeperHandler.php(85): pocketmine\snooze\SleeperHandler->processNotifications()
#10 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\Server.php(1776): pocketmine\snooze\SleeperHandler->sleepUntil(1563788088.1057)
#11 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\Server.php(1307): pocketmine\Server->tickProcessor()
#12 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\PocketMine.php(275): pocketmine\Server->__construct(Object(BaseClassLoader), Object(pocketmine\utils\MainLogger
), 'C:\\Users\\Jack\\D...', 'C:\\Users\\Jack\\D...')
#13 Internal(0): Corrected generator stack trace()
#14 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\plugins\InfoAPI\src\SOFe\InfoAPI\ui\BrowseInfosCommand.php(80): showInfo()
#15 {main}

Next SOFe\AwaitGenerator\AwaitException: Unhandled async exception in phar://C:/Users/Jack/Documents/PocketMine/PocketMine-MP-FC-Src 4.0.0/virions/await-generator_dev-19.phar/src/SOFe/AwaitGen
erator/Await.php:358
Stack trace:
#0 phar://C:/Users/Jack/Documents/PocketMine/PocketMine-MP-FC-Src 4.0.0/virions/await-generator_dev-19.phar/src/SOFe/AwaitGenerator/Await.php(135): SOFe\AwaitGenerator\Await->reject(Object(Err
orException))
#1 phar://C:/Users/Jack/Documents/PocketMine/PocketMine-MP-FC-Src 4.0.0/virions/await-generator_dev-19.phar/src/SOFe/AwaitGenerator/Await.php(97): SOFe\AwaitGenerator\Await->wakeup(Array)
#2 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\plugins\InfoAPI\src\SOFe\InfoAPI\ui\BrowseInfosCommand.php(72): SOFe\AwaitGenerator\Await::g2c(Object(Generator), NULL, Array)
#3 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\command\SimpleCommandMap.php(268): SOFe\InfoAPI\ui\BrowseInfosCommand->execute(Object(pocketmine\command\Console
CommandSender), 'info', Array)
#4 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\Server.php(1551): pocketmine\command\SimpleCommandMap->dispatch(Object(pocketmine\command\ConsoleCommandSender),
 'info j')
#5 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\Server.php(1301): pocketmine\Server->dispatchCommand(Object(pocketmine\command\ConsoleCommandSender), 'info j')
#6 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\vendor\pocketmine\snooze\src\SleeperHandler.php(123): pocketmine\Server->pocketmine\{closure}()
#7 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\vendor\pocketmine\snooze\src\SleeperHandler.php(85): pocketmine\snooze\SleeperHandler->processNotifications()
#8 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\Server.php(1776): pocketmine\snooze\SleeperHandler->sleepUntil(1563788088.1057)
#9 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\Server.php(1307): pocketmine\Server->tickProcessor()
#10 C:\Users\Jack\Documents\PocketMine\PocketMine-MP-FC-Src 4.0.0\src\pocketmine\PocketMine.php(275): pocketmine\Server->__construct(Object(BaseClassLoader), Object(pocketmine\utils\MainLogger
), 'C:\\Users\\Jack\\D...', 'C:\\Users\\Jack\\D...')
#11 {main}

Parameterized Infos

Motivation

It is useful to be able to pass simple parameters, such as string and number literals, into the info resolution path. For example, {number add(1)} comes handy in many cases. This also allows to implement new info types like {stringList join(", ")}.

Goals

  • Provide a way such that infos can take in extra, simple parameters
  • Perform minimal modification on the syntax to support parsing parameters, named or unnamed

Non-goals

  • This issue does not propose dynamic infos. That is, we do not intend to allow arbitrary info names. Info paths should still be statically analyzable so that Graph::pathFind continues to work. This also means return type of an info is fixed, invariant of the input parameters.
  • This issue does not intend to implement complex ASTs that could confuse readers. There are no plans to add recursive infos yet.
  • While this proposal allows lists to be implemented, there are no plans to implement generic infos.

Design details

Parsing

Currently, the template syntax inside {} is defined as

template := "{" path ("|" path)* "}"
path := qualified_name (" " qualified_name)*
qualified_name := (TOKEN ".")* TOKEN

It is proposed that qualified_name be modified to

qualified_name := (TOKEN ".")* TOKEN parameter_list?
parameter_list := "(" (named_params | unnamed_params) ")"
named_params := TOKEN ":" param_value ("," TOKEN ":" param_value)*
unnamed_params := param_value ("," param_value)*
param_value := JSON_STRING_LITERAL | JSON_NUMBER_LITERAL

In particular, the parser must properly support ) and | inside string literals.

API

Add a new method provideParameterizedInfo, which takes a closure Closure(Info, Arguments): Info. Arguments is one of NamedArguments providing get(string $key) or UnnamedArguments providing get(int $index).

Possible enhancements

Use parameterized infos to implement the following new infos:

  • NumberInfo add(number) -> NumberInfo
  • NumberInfo sub(number) -> NumberInfo
  • NumberInfo mul(number) -> NumberInfo
  • NumberInfo div(number) -> NumberInfo
  • NumberInfo reciprocal -> NumberInfo (such that {a reciprocal mul(b)} gives b / a)
  • NumberInfo mod(number) -> NumberInfo
  • NumberInfo imod(number) -> NumberInfo (mod with arguments flipped)
  • StringInfo wrapIfNonEmpty(prefix?: string, suffix?: string) -> StringInfo

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.