From a0c095ae76fe6a0d215c8107798577aafc9841d7 Mon Sep 17 00:00:00 2001 From: SOFe Date: Tue, 15 Mar 2022 23:04:55 +0800 Subject: [PATCH 1/3] tmp commit --- .php-cs-fixer.php | 2 +- CapiTrade/plugin.yml | 10 ++++ CapiTrade/resources/mysql/init.sql | 35 ++++++++++++ CapiTrade/resources/sqlite/init.sql | 35 ++++++++++++ CapiTrade/src/SOFe/CapiTrade/CapiTrade.php | 57 +++++++++++++++++++ .../SOFe/CapiTrade/Database/DatabaseUtils.php | 28 +++++++++ CapiTrade/src/SOFe/CapiTrade/Database/Mod.php | 16 ++++++ .../src/SOFe/CapiTrade/Loader/Loader.php | 22 +++++++ CapiTrade/src/SOFe/CapiTrade/Mod.php | 16 ++++++ .../src/SOFe/CapiTrade/Plugin/MainClass.php | 16 ++++++ Makefile | 17 ++++-- composer.json | 3 +- phpstan.neon.dist | 1 + src/SOFe/Capital/Di/Mod.php | 11 ++++ src/SOFe/Capital/Loader/Loader.php | 19 ++++++- 15 files changed, 280 insertions(+), 8 deletions(-) create mode 100644 CapiTrade/plugin.yml create mode 100644 CapiTrade/resources/mysql/init.sql create mode 100644 CapiTrade/resources/sqlite/init.sql create mode 100644 CapiTrade/src/SOFe/CapiTrade/CapiTrade.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/Database/DatabaseUtils.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/Database/Mod.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/Loader/Loader.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/Mod.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/Plugin/MainClass.php create mode 100644 src/SOFe/Capital/Di/Mod.php diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index e227b64..8f98168 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -7,7 +7,7 @@ $finder = Finder::create(); foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__ . "/src")) as $file) { $file = realpath($file); - if($file === __DIR__ . "/src/SOFe/Capital/Database/RawQueries.php") { + if(substr($file, strrpos($file, "/")) === "/RawQueries.php") { continue; } diff --git a/CapiTrade/plugin.yml b/CapiTrade/plugin.yml new file mode 100644 index 0000000..5602fba --- /dev/null +++ b/CapiTrade/plugin.yml @@ -0,0 +1,10 @@ +name: CapiTrade +version: 0.1.2 +api: 4.0.0 +main: SOFe\CapiTrade\Plugin\MainClass +author: SOFe +load: STARTUP +depend: + - Capital +loadbefore: + - SuiteTester diff --git a/CapiTrade/resources/mysql/init.sql b/CapiTrade/resources/mysql/init.sql new file mode 100644 index 0000000..54f5afb --- /dev/null +++ b/CapiTrade/resources/mysql/init.sql @@ -0,0 +1,35 @@ +-- #!mysql +-- #{ capitrade.init +CREATE TABLE IF NOT EXISTS capitrade_shop ( + id INT PRIMARY KEY AUTO_INCREMENT, + price BIGINT NOT NULL +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_product ( + id INT PRIMARY KEY, + shop_id INT, + item TEXT, + cnt INT, + from_customer BOOL, + FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_access ( + id INT PRIMARY KEY, + shop_id INT, + FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_access_block ( + access_id INT PRIMARY KEY, + shop_id INT, + x INT, + y INT, + z INT, + world VARCHAR(255), + server_id VARCHAR(255), + delete_permission VARCHAR(255), + FOREIGN KEY(access_id) REFERENCES capitrade_shop(access_id), + FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) +); +-- #} diff --git a/CapiTrade/resources/sqlite/init.sql b/CapiTrade/resources/sqlite/init.sql new file mode 100644 index 0000000..eea111a --- /dev/null +++ b/CapiTrade/resources/sqlite/init.sql @@ -0,0 +1,35 @@ +-- #!sqlite +-- #{ capitrade.init +CREATE TABLE IF NOT EXISTS capitrade_shop ( + shop_id INTEGER PRIMARY KEY AUTOINCREMENT, + price INTEGER NOT NULL +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_product_item ( + id INTEGER PRIMARY KEY, + shop_id INTEGER, + item TEXT, + cnt INTEGER, + from_customer INTEGER, + FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_access ( + access_id INTEGER PRIMARY KEY, + shop_id INTEGER, + FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_access_block ( + access_id INTEGER PRIMARY KEY, + shop_id INTEGER, + x INTEGER, + y INTEGER, + z INTEGER, + world TEXT, + server_id TEXT, + delete_permission TEXT, + FOREIGN KEY(access_id) REFERENCES capitrade_shop(access_id), + FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) +); +-- #} diff --git a/CapiTrade/src/SOFe/CapiTrade/CapiTrade.php b/CapiTrade/src/SOFe/CapiTrade/CapiTrade.php new file mode 100644 index 0000000..03d833b --- /dev/null +++ b/CapiTrade/src/SOFe/CapiTrade/CapiTrade.php @@ -0,0 +1,57 @@ +db->getLabels($shopId); + $event = new ShopUseEvent($shopId, $labels, $customer); + $event->call(); + yield from $event->getWaitGroup()->wait(); + + $ok = false; + try { + $rejection = $event->getRejection(); + if($rejection !== null) { + throw new CapitalException(CapitalException::EVENT_CANCELLED, $rejection); + } + + yield from $this->capital->transact(); + + foreach($event->getProducts() as $product) { + $product->execute(); + } + + $ok = true; + } finally { + if(!$ok) { + foreach($event->getProducts() as $product) { + $product->cancel(); + } + } + } + } + + public function deleteShop(int $shopId, int $accessId) : Generator { + yield from $this->db->deleteAccess($accessId); + yield from $this->db->deleteIfNoAccess($shopId); + } +} diff --git a/CapiTrade/src/SOFe/CapiTrade/Database/DatabaseUtils.php b/CapiTrade/src/SOFe/CapiTrade/Database/DatabaseUtils.php new file mode 100644 index 0000000..2d238fc --- /dev/null +++ b/CapiTrade/src/SOFe/CapiTrade/Database/DatabaseUtils.php @@ -0,0 +1,28 @@ +db->getDataConnector()->executeGeneric() + } + + public static function fromSingletonArgs(Database $db) : Generator{ + $self = new self($db); + + yield from $self->init(); + + return new self; + } +} diff --git a/CapiTrade/src/SOFe/CapiTrade/Database/Mod.php b/CapiTrade/src/SOFe/CapiTrade/Database/Mod.php new file mode 100644 index 0000000..6a162dd --- /dev/null +++ b/CapiTrade/src/SOFe/CapiTrade/Database/Mod.php @@ -0,0 +1,16 @@ + phpstan-baseline.neon -phpstan-baseline.neon/regenerate: src/SOFe/Capital/Database/RawQueries.php vendor +phpstan-baseline.neon/regenerate: src/SOFe/Capital/Database/RawQueries.php CapiTrade/src/SOFe/CapiTrade/Database/RawQueries.php vendor $(PHP) vendor/bin/phpstan analyze --generate-baseline fmt: $(shell find src -type f) .php-cs-fixer.php vendor @@ -33,9 +33,17 @@ dev/Capital.phar: $(CAPITAL_SOURCE_FILES) dev/ConsoleScript.php $(CAPITAL_VIRION for file in $(CAPITAL_VIRIONS); do $(PHP) $$file $@ SOFe\\Capital\\Virions\\$$(tr -dc A-Za-z |null, mixed, void>" ignoreErrors: diff --git a/src/SOFe/Capital/Di/Mod.php b/src/SOFe/Capital/Di/Mod.php new file mode 100644 index 0000000..1c3a70d --- /dev/null +++ b/src/SOFe/Capital/Di/Mod.php @@ -0,0 +1,11 @@ +fetchClass($entryPoint); + } + return new self; } } From 98c1af0b1aff3bbb0a32697e3a25a07b66e77c6b Mon Sep 17 00:00:00 2001 From: SOFe Date: Sat, 19 Mar 2022 13:21:17 +0800 Subject: [PATCH 2/3] Updated dev.md --- dev.md | 70 ++++++++++++++++++++++++++++++ src/SOFe/Capital/Loader/Loader.php | 5 ++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/dev.md b/dev.md index a064027..05685ee 100644 --- a/dev.md +++ b/dev.md @@ -496,6 +496,76 @@ with the name equal to the `displayLabels` label is returned in the output for d +### CapiTrade + +CapiTrade is the shops implementation for Capital. +It exposes a generic event-based API, +so in a sense, using a CapiTrade shop is like "buying an event". + +#### Shop labels + +Shop data can be stored in labels, similar to account/transaction labels. +In the case of shops, labels are mostly intended for technical use +and it is not recommended to expose shop labels to the user interface. + +If there are special requirements, +shop data can still be stored on external tables or storages, e.g. entity/items, +but in general, shop labels are better for storage +because they allow synchronous shop admission in shop event handlers. + +#### Shop accessors + +The same shop can be accessed with multiple methods. +Each method is called a "shop accessor". +A shop is considered inaccessible when the last accessor is deleted, +in which case the shop itself (along with its labels) also get deleted. + +Examples of shop accessors include: + +- Using certain commands +- Clicking blocks at specific positions +- Using certain items +- Clicking on certain entities +- Using an NPC trading dialog + +#### Shop price + +The shop price is a first-class data value stored in the shop table directly. +Shop prices can be adjusted by control loops in servers +that may depend on e.g. metrics computed from accounts/transactions. + +Shop prices may be positive or negative. +The peer of the shop transaction is stored as +an account label selector in the shop label `capitrade/peer-account-selector`. + +#### Shop executors + +Shop executors are the components that implement the outcome of a shop. +Examples of shop executors include: + +- Adding/removing inventory items (a.k.a. buying/selling items) +- Adding/removing player effects +- Updating a player's kits + +When a player accesses a shop, CapiTrade dispatches a `ShopAccessEvent`. +Shop executors should check the shop labels in the event +to determine the desired effects of this shop. + +Shop executors can admit or deny an access event. +Denial takes place when the prerequisites of the executor are not satisfied. +For example, an item-selling executor denies an access event +if the player does not have the required items in their inventory. + +When a shop executor admits an access event, +they should not execute the outcome directly. +Instead, they should try to *hold* the prerequisites, +i.e. ensure that the checked prerequisites will remain satisfied for a short period +(e.g. deny the player from removing the items from their inventory). +After Capital executes the transaction, +it will notify the executors that the transaction succeeded or failed, +in which case the executors should actually execute the outcome +or release the held prerequisites (e.g. stop denying item removal). + ## Tooling Capital is developed on Linux. diff --git a/src/SOFe/Capital/Loader/Loader.php b/src/SOFe/Capital/Loader/Loader.php index 67e4291..2c148de 100644 --- a/src/SOFe/Capital/Loader/Loader.php +++ b/src/SOFe/Capital/Loader/Loader.php @@ -12,6 +12,7 @@ use SOFe\Capital\Di\SingletonArgs; use SOFe\Capital\Di\SingletonTrait; +use function is_subclass_of; final class Loader implements Singleton, FromContext { use SingletonArgs, SingletonTrait; @@ -21,7 +22,7 @@ final class Loader implements Singleton, FromContext { private static array $extraEntryPoints = []; public static function addEntryPoint(string $class) : void { - if(!is_subclass_of($class, Singleton::class)) { + if (!is_subclass_of($class, Singleton::class)) { throw new InvalidArgumentException("Entry point must be a subclass of " . Singleton::class); } } @@ -35,7 +36,7 @@ public static function fromSingletonArgs( C\Migration\Mod $migration, C\Di\Context $context, ) : Generator { - foreach(self::$extraEntryPoints as $entryPoint) { + foreach (self::$extraEntryPoints as $entryPoint) { yield from $context->fetchClass($entryPoint); } From 2e5521b5aabec34fd005ef00f756aabdbb804b55 Mon Sep 17 00:00:00 2001 From: SOFe Date: Sun, 20 Mar 2022 11:53:21 +0800 Subject: [PATCH 3/3] Implemented ShopExecuteEvent --- .php-cs-fixer.php | 8 +- CapiTrade/resources/mysql/init.sql | 35 --- CapiTrade/resources/mysql/shop.sql | 66 +++++ CapiTrade/resources/sqlite/init.sql | 35 --- CapiTrade/resources/sqlite/shop.sql | 67 +++++ CapiTrade/src/SOFe/CapiTrade/CapiTrade.php | 88 ++++-- .../src/SOFe/CapiTrade/CapiTradeException.php | 31 ++ .../SOFe/CapiTrade/Database/DatabaseUtils.php | 86 +++++- .../SOFe/CapiTrade/Database/RawQueries.php | 65 +++++ .../src/SOFe/CapiTrade/ExecuteHandle.php | 46 +++ .../src/SOFe/CapiTrade/Plugin/MainClass.php | 1 + .../src/SOFe/CapiTrade/ShopExecuteEvent.php | 167 +++++++++++ CapiTrade/src/SOFe/CapiTrade/ShopLabels.php | 13 + CapiTrade/src/SOFe/CapiTrade/ShopRef.php | 18 ++ .../src/SOFe/CapiTrade/ShopRejection.php | 10 + Makefile | 4 +- composer.json | 2 +- composer.lock | 54 ++-- dev.md | 2 +- src/SOFe/Capital/Database/Database.php | 23 +- src/SOFe/Capital/Database/LabelManager.php | 29 +- src/SOFe/Capital/Loader/Loader.php | 12 +- suitetest/plugin/composer.json | 4 +- suitetest/plugin/composer.lock | 270 +++++++++--------- 24 files changed, 852 insertions(+), 284 deletions(-) delete mode 100644 CapiTrade/resources/mysql/init.sql create mode 100644 CapiTrade/resources/mysql/shop.sql delete mode 100644 CapiTrade/resources/sqlite/init.sql create mode 100644 CapiTrade/resources/sqlite/shop.sql create mode 100644 CapiTrade/src/SOFe/CapiTrade/CapiTradeException.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/Database/RawQueries.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/ExecuteHandle.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/ShopExecuteEvent.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/ShopLabels.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/ShopRef.php create mode 100644 CapiTrade/src/SOFe/CapiTrade/ShopRejection.php diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 8f98168..3b274c8 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -5,7 +5,13 @@ return (function() { $finder = Finder::create(); - foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__ . "/src")) as $file) { + + $files = []; + $iter = new AppendIterator(); + $iter->append(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__ . "/src"))); + $iter->append(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__ . "/CapiTrade/src"))); + + foreach($iter as $file) { $file = realpath($file); if(substr($file, strrpos($file, "/")) === "/RawQueries.php") { continue; diff --git a/CapiTrade/resources/mysql/init.sql b/CapiTrade/resources/mysql/init.sql deleted file mode 100644 index 54f5afb..0000000 --- a/CapiTrade/resources/mysql/init.sql +++ /dev/null @@ -1,35 +0,0 @@ --- #!mysql --- #{ capitrade.init -CREATE TABLE IF NOT EXISTS capitrade_shop ( - id INT PRIMARY KEY AUTO_INCREMENT, - price BIGINT NOT NULL -); --- #& -CREATE TABLE IF NOT EXISTS capitrade_shop_product ( - id INT PRIMARY KEY, - shop_id INT, - item TEXT, - cnt INT, - from_customer BOOL, - FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) -); --- #& -CREATE TABLE IF NOT EXISTS capitrade_shop_access ( - id INT PRIMARY KEY, - shop_id INT, - FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) -); --- #& -CREATE TABLE IF NOT EXISTS capitrade_shop_access_block ( - access_id INT PRIMARY KEY, - shop_id INT, - x INT, - y INT, - z INT, - world VARCHAR(255), - server_id VARCHAR(255), - delete_permission VARCHAR(255), - FOREIGN KEY(access_id) REFERENCES capitrade_shop(access_id), - FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) -); --- #} diff --git a/CapiTrade/resources/mysql/shop.sql b/CapiTrade/resources/mysql/shop.sql new file mode 100644 index 0000000..a4b4fab --- /dev/null +++ b/CapiTrade/resources/mysql/shop.sql @@ -0,0 +1,66 @@ +-- #!mysql +-- #{ capitrade +-- # { init +CREATE TABLE IF NOT EXISTS capitrade_shop ( + shop_id CHAR(36) PRIMARY KEY, + price BIGINT NOT NULL +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_schema ( + shop_id CHAR(36) PRIMARY KEY, + config TEXT NOT NULL, + + PRIMARY KEY(shop_id), + FOREIGN KEY (shop_id) REFERENCES capitrade_shop(shop_id) ON DELETE CASCADE +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_acc_sel ( + shop_id CHAR(36) NOT NULL, + name VARCHAR(255) NOT NULL, + value TEXT NOT NULL, + + PRIMARY KEY(shop_id, name), + FOREIGN KEY (shop_id) REFERENCES capitrade_shop(shop_id) ON DELETE CASCADE +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_label ( + shop_id CHAR(36) NOT NULL, + name VARCHAR(255) NOT NULL, + value TEXT NOT NULL, + + PRIMARY KEY (shop_id, name), + KEY (name, value), + FOREIGN KEY (shop_id) REFERENCES capitrade_shop(shop_id) ON DELETE CASCADE +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_access ( + id INT PRIMARY KEY, + shop_id INT, + FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) ON DELETE CASCADE +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_access_block ( + access_id INT PRIMARY KEY, + shop_id INT, + x INT, + y INT, + z INT, + world VARCHAR(255), + server_id VARCHAR(255), + delete_permission VARCHAR(255), + FOREIGN KEY(access_id) REFERENCES capitrade_shop(access_id) ON DELETE CASCADE +); +-- # } +-- # { get_price +-- # :shop_id string +SELECT price FROM capitrade_shop WHERE shop_id = :shop_id; +-- # } +-- # { get_shop_account_selector +-- # :shop_id string +SELECT name, value FROM capitrade_shop_acc_sel WHERE shop_id = :shop_id; +-- # } +-- # { get_shop_schema_config +-- # :shop_id string +SELECT config FROM capitrade_shop_schema WHERE shop_id = :shop_id; +-- # } +-- #} diff --git a/CapiTrade/resources/sqlite/init.sql b/CapiTrade/resources/sqlite/init.sql deleted file mode 100644 index eea111a..0000000 --- a/CapiTrade/resources/sqlite/init.sql +++ /dev/null @@ -1,35 +0,0 @@ --- #!sqlite --- #{ capitrade.init -CREATE TABLE IF NOT EXISTS capitrade_shop ( - shop_id INTEGER PRIMARY KEY AUTOINCREMENT, - price INTEGER NOT NULL -); --- #& -CREATE TABLE IF NOT EXISTS capitrade_shop_product_item ( - id INTEGER PRIMARY KEY, - shop_id INTEGER, - item TEXT, - cnt INTEGER, - from_customer INTEGER, - FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) -); --- #& -CREATE TABLE IF NOT EXISTS capitrade_shop_access ( - access_id INTEGER PRIMARY KEY, - shop_id INTEGER, - FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) -); --- #& -CREATE TABLE IF NOT EXISTS capitrade_shop_access_block ( - access_id INTEGER PRIMARY KEY, - shop_id INTEGER, - x INTEGER, - y INTEGER, - z INTEGER, - world TEXT, - server_id TEXT, - delete_permission TEXT, - FOREIGN KEY(access_id) REFERENCES capitrade_shop(access_id), - FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) -); --- #} diff --git a/CapiTrade/resources/sqlite/shop.sql b/CapiTrade/resources/sqlite/shop.sql new file mode 100644 index 0000000..b90bf78 --- /dev/null +++ b/CapiTrade/resources/sqlite/shop.sql @@ -0,0 +1,67 @@ +-- #!sqlite +-- #{ capitrade +-- # { init +CREATE TABLE IF NOT EXISTS capitrade_shop ( + shop_id TEXT PRIMARY KEY, + price INTEGER NOT NULL +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_schema ( + shop_id TEXT PRIMARY KEY, + config TEXT NOT NULL, + + PRIMARY KEY(shop_id), + FOREIGN KEY (shop_id) REFERENCES capitrade_shop(shop_id) ON DELETE CASCADE +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_acc_sel ( + shop_id TEXT NOT NULL, + name TEXT NOT NULL, + value TEXT NOT NULL, + + PRIMARY KEY(shop_id, name), + FOREIGN KEY (shop_id) REFERENCES capitrade_shop(shop_id) ON DELETE CASCADE +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_label ( + shop_id TEXT NOT NULL, + name TEXT NOT NULL, + value TEXT NOT NULL, + + PRIMARY KEY (shop_id, name), + FOREIGN KEY (shop_id) REFERENCES capitrade_shop(shop_id) ON DELETE CASCADE +); +-- #& +CREATE INDEX IF NOT EXISTS capitrade_shop_label_kv ON capitrade_shop_label(name, value); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_access ( + access_id INTEGER PRIMARY KEY, + shop_id TEXT, + FOREIGN KEY(shop_id) REFERENCES capitrade_shop(shop_id) ON DELETE CASCADE +); +-- #& +CREATE TABLE IF NOT EXISTS capitrade_shop_access_block ( + access_id INTEGER PRIMARY KEY, + shop_id TEXT, + x INTEGER, + y INTEGER, + z INTEGER, + world TEXT, + server_id TEXT, + delete_permission TEXT, + FOREIGN KEY(access_id) REFERENCES capitrade_shop(access_id) ON DELETE CASCADE +); +-- # } +-- # { get_price +-- # :shop_id string +SELECT price FROM capitrade_shop WHERE shop_id = :shop_id; +-- # } +-- # { get_shop_account_selector +-- # :shop_id string +SELECT name, value FROM capitrade_shop_acc_sel WHERE shop_id = :shop_id; +-- # } +-- # { get_shop_schema_config +-- # :shop_id string +SELECT config FROM capitrade_shop_schema WHERE shop_id = :shop_id; +-- # } +-- #} diff --git a/CapiTrade/src/SOFe/CapiTrade/CapiTrade.php b/CapiTrade/src/SOFe/CapiTrade/CapiTrade.php index 03d833b..f950a6e 100644 --- a/CapiTrade/src/SOFe/CapiTrade/CapiTrade.php +++ b/CapiTrade/src/SOFe/CapiTrade/CapiTrade.php @@ -6,12 +6,19 @@ use Generator; use pocketmine\player\Player; +use SOFe\AwaitGenerator\Await; use SOFe\Capital\Capital; use SOFe\Capital\CapitalException; use SOFe\Capital\Di\FromContext; use SOFe\Capital\Di\Singleton; use SOFe\Capital\Di\SingletonArgs; use SOFe\Capital\Di\SingletonTrait; +use SOFe\Capital\LabelSet; +use SOFe\Capital\ParameterizedLabelSelector; +use SOFe\Capital\ParameterizedLabelSet; +use SOFe\InfoAPI\PlayerInfo; +use function count; +use function json_decode; final class CapiTrade implements Singleton, FromContext { use SingletonArgs, SingletonTrait; @@ -21,32 +28,71 @@ final class CapiTrade implements Singleton, FromContext { public function __construct(private Database\DatabaseUtils $db, private Capital $capital) { } - public function executeShop(int $shopId, Player $customer) : Generator { - $labels = yield from $this->db->getLabels($shopId); - $event = new ShopUseEvent($shopId, $labels, $customer); - $event->call(); - yield from $event->getWaitGroup()->wait(); + public function executeShop(ShopRef $shopId, Player $customer) : Generator { + /** + * @var array $labels + * @var int $price + * @var array $shopAccSelectorRaw + * @var array $schemaConfig + */ + [$labels, $price, $shopAccSelectorRaw, $schemaConfig] = yield from Await::all([ + $this->db->shopLabels()->getAll($shopId->getId()), + $this->db->getPrice($shopId->getId()), + $this->db->getShopAccountSelector($shopId->getId()), + $this->db->getShopSchemaConfig($shopId->getId()), + ]); - $ok = false; - try { - $rejection = $event->getRejection(); - if($rejection !== null) { - throw new CapitalException(CapitalException::EVENT_CANCELLED, $rejection); - } + $shopAccSelector = new ParameterizedLabelSelector($shopAccSelectorRaw); + $shopAccSelector = $shopAccSelector->transform(new PlayerInfo($customer)); - yield from $this->capital->transact(); + $shopAccs = yield from $this->capital->findAccounts($shopAccSelector); + if (count($shopAccs) === 0) { + throw new CapiTradeException(CapiTradeException::SHOP_HAS_NO_ACCOUNT); + } + $shopAcc = $shopAccs[0]; - foreach($event->getProducts() as $product) { - $product->execute(); - } + $schema = $this->capital->completeConfig($schemaConfig); + $customerAccs = yield from $this->capital->findAccountsComplete($customer, $schema); + $customerAcc = $customerAccs[0]; - $ok = true; - } finally { - if(!$ok) { - foreach($event->getProducts() as $product) { - $product->cancel(); - } + if ($price > 0) { + $src = $customerAcc; + $dest = $shopAcc; + } else { + $src = $shopAcc; + $dest = $customerAcc; + $price *= -1; + } + + if (isset($labels[ShopLabels::TRANSACTION_LABELS])) { + $transactionLabels = json_decode($labels[ShopLabels::TRANSACTION_LABELS], true); + $transactionLabelSet = new ParameterizedLabelSet($transactionLabels); + $transactionLabelSet = $transactionLabelSet->transform(new PlayerInfo($customer)); + } else { + $transactionLabelSet = new LabelSet([]); + } + + $transactionException = null; + $event = new ShopExecuteEvent($shopId, $labels, $customer, function() use ($src, $dest, $price, $transactionLabelSet, $customer, &$transactionException) : Generator { + try { + yield from $this->capital->transact($src, $dest, $price, $transactionLabelSet, [$customer], true); + return true; + } catch (CapitalException $ex) { + $transactionException = $ex; + return false; } + }); + $event->call(); + + yield from $event->waitDone(); + + $rejection = $event->getRejection(); + if ($rejection !== null) { + throw new CapiTradeException(CapiTradeException::EVENT_CANCELLED, $rejection); + } + + if ($transactionException !== null) { + throw new CapiTradeException(CapiTradeException::TRANSACTION_FAILED, $transactionException); } } diff --git a/CapiTrade/src/SOFe/CapiTrade/CapiTradeException.php b/CapiTrade/src/SOFe/CapiTrade/CapiTradeException.php new file mode 100644 index 0000000..0cea62f --- /dev/null +++ b/CapiTrade/src/SOFe/CapiTrade/CapiTradeException.php @@ -0,0 +1,31 @@ + "The shop does not exist", + self::SHOP_LABEL_ALREADY_EXISTS => "The shop already has this label", + self::SHOP_LABEL_DOES_NOT_EXIST => "The shop does not have this label", + self::EVENT_CANCELLED => "Event has been cancelled", + self::SHOP_HAS_NO_ACCOUNT => "The shop is currently unavailable", + self::TRANSACTION_FAILED => "The transaction failed", + }; + parent::__construct($message, $code, $previous); + } +} diff --git a/CapiTrade/src/SOFe/CapiTrade/Database/DatabaseUtils.php b/CapiTrade/src/SOFe/CapiTrade/Database/DatabaseUtils.php index 2d238fc..7599ee0 100644 --- a/CapiTrade/src/SOFe/CapiTrade/Database/DatabaseUtils.php +++ b/CapiTrade/src/SOFe/CapiTrade/Database/DatabaseUtils.php @@ -5,24 +5,100 @@ namespace SOFe\CapiTrade\Database; use Generator; +use Ramsey\Uuid\UuidInterface; +use RuntimeException; use SOFe\Capital\Database\Database; +use SOFe\Capital\Database\LabelManager; use SOFe\Capital\Di\FromContext; use SOFe\Capital\Di\Singleton; use SOFe\Capital\Di\SingletonArgs; use SOFe\Capital\Di\SingletonTrait; +use SOFe\CapiTrade\CapiTradeException; +use SOFe\CapiTrade\Plugin\MainClass; +use function count; +use function json_decode; final class DatabaseUtils implements Singleton, FromContext { use SingletonArgs, SingletonTrait; - public function __construct(private Database $db) { - $this->db->getDataConnector()->executeGeneric() + private RawQueries $raw; + + public function __construct(private Database $db, MainClass $plugin) { + $conn = $db->getDataConnector(); + foreach (["sqlite/shop.sql", "mysql/shop.sql"] as $sql) { + $fh = $plugin->getResource($sql); + if ($fh === null) { + throw new RuntimeException("Resource $sql is missing"); + } + $conn->loadQueryFile($fh); + } + $this->raw = new RawQueries($conn); + } + + public function init() : Generator { + yield from $this->raw->init(); + } + + /** + * @return Generator + */ + public function getPrice(UuidInterface $shop) : Generator { + $rows = yield from $this->raw->getPrice($shop->toString()); + + if (count($rows) === 0) { + throw new CapiTradeException(CapiTradeException::NO_SUCH_SHOP); + } + + return $rows[0]["price"]; + } + + /** + * @return Generator> + */ + public function getShopAccountSelector(UuidInterface $shop) : Generator { + $rows = yield from $this->raw->getShopAccountSelector($shop->toString()); + + $output = []; + foreach ($rows as $row) { + /** @var string $name */ + $name = $row["name"]; + /** @var string $value */ + $value = $row["value"]; + $output[$name] = $value; + } + + return $output; + } + + /** + * @return Generator> + */ + public function getShopSchemaConfig(UuidInterface $shop) : Generator { + $rows = yield from $this->raw->getShopSchemaConfig($shop->toString()); + $json = $rows[0]["config"]; + $config = json_decode($json, true); + + return $config; + } + + /** + * @return LabelManager + */ + public function shopLabels() : LabelManager { + return new LabelManager( + database: $this->db, + labelTable: "capitrade_shop_label", + labelAlreadyExistsErrorCode: CapiTradeException::SHOP_LABEL_ALREADY_EXISTS, + labelDoesNotExistErrorCode: CapiTradeException::SHOP_LABEL_DOES_NOT_EXIST, + throw: fn($code) => new CapiTradeException($code), + ); } - public static function fromSingletonArgs(Database $db) : Generator{ - $self = new self($db); + public static function fromSingletonArgs(Database $db, MainClass $plugin) : Generator { + $self = new self($db, $plugin); yield from $self->init(); - return new self; + return $self; } } diff --git a/CapiTrade/src/SOFe/CapiTrade/Database/RawQueries.php b/CapiTrade/src/SOFe/CapiTrade/Database/RawQueries.php new file mode 100644 index 0000000..8b5931d --- /dev/null +++ b/CapiTrade/src/SOFe/CapiTrade/Database/RawQueries.php @@ -0,0 +1,65 @@ +Declared in: + * - CapiTrade/resources/mysql/shop.sql:57 + * - CapiTrade/resources/sqlite/shop.sql:58 + * @param string $shop_id + * @return Generator|null, mixed, list>> + */ + public function getPrice(string $shop_id, ) : Generator { + $this->conn->executeSelect("capitrade.get_price", ["shop_id" => $shop_id, ], yield Await::RESOLVE, yield Await::REJECT); + return yield Await::ONCE; + } + + /** + *

Declared in:

+ * - CapiTrade/resources/mysql/shop.sql:61 + * - CapiTrade/resources/sqlite/shop.sql:62 + * @param string $shop_id + * @return Generator|null, mixed, list>> + */ + public function getShopAccountSelector(string $shop_id, ) : Generator { + $this->conn->executeSelect("capitrade.get_shop_account_selector", ["shop_id" => $shop_id, ], yield Await::RESOLVE, yield Await::REJECT); + return yield Await::ONCE; + } + + /** + *

Declared in:

+ * - CapiTrade/resources/mysql/shop.sql:65 + * - CapiTrade/resources/sqlite/shop.sql:66 + * @param string $shop_id + * @return Generator|null, mixed, list>> + */ + public function getShopSchemaConfig(string $shop_id, ) : Generator { + $this->conn->executeSelect("capitrade.get_shop_schema_config", ["shop_id" => $shop_id, ], yield Await::RESOLVE, yield Await::REJECT); + return yield Await::ONCE; + } + + /** + *

Declared in:

+ * - CapiTrade/resources/mysql/shop.sql:53 + * - CapiTrade/resources/sqlite/shop.sql:54 + * @return Generator|null, mixed, int> + */ + public function init() : Generator { + $this->conn->executeChange("capitrade.init", [], yield Await::RESOLVE, yield Await::REJECT); + return yield Await::ONCE; + } +} diff --git a/CapiTrade/src/SOFe/CapiTrade/ExecuteHandle.php b/CapiTrade/src/SOFe/CapiTrade/ExecuteHandle.php new file mode 100644 index 0000000..0d73fbf --- /dev/null +++ b/CapiTrade/src/SOFe/CapiTrade/ExecuteHandle.php @@ -0,0 +1,46 @@ + + */ + public function admit() : Generator { + if ($this->used) { + throw new RuntimeException("Cannot admit/reject the same ExecuteHandle twice"); + } + + return yield from $this->event->internalAdmit(); + } + + public function reject(ShopRejection $rejection) : void { + if ($this->used) { + throw new RuntimeException("Cannot admit/reject the same ExecuteHandle twice"); + } + + $this->event->internalReject($rejection); + } + + public function done() : void { + $this->event->internalDone(); + } +} diff --git a/CapiTrade/src/SOFe/CapiTrade/Plugin/MainClass.php b/CapiTrade/src/SOFe/CapiTrade/Plugin/MainClass.php index 507a881..9a482e4 100644 --- a/CapiTrade/src/SOFe/CapiTrade/Plugin/MainClass.php +++ b/CapiTrade/src/SOFe/CapiTrade/Plugin/MainClass.php @@ -11,6 +11,7 @@ final class MainClass extends PluginBase implements Di\Singleton { use Di\SingletonTrait; protected function onEnable() : void { + \SOFe\Capital\Plugin\MainClass::$context->store($this); \SOFe\Capital\Loader\Loader::addEntryPoint(\SOFe\CapiTrade\Loader\Loader::class); } } diff --git a/CapiTrade/src/SOFe/CapiTrade/ShopExecuteEvent.php b/CapiTrade/src/SOFe/CapiTrade/ShopExecuteEvent.php new file mode 100644 index 0000000..eb0da36 --- /dev/null +++ b/CapiTrade/src/SOFe/CapiTrade/ShopExecuteEvent.php @@ -0,0 +1,167 @@ + Completes when any executor has rejected. */ + private Loading $onReject; + /** @var Closure(ShopRejection): void */ + private Closure $rejectFunc; + + /** @var Loading */ + private Loading $onTransactionComplete; + + /** This WaitGroup completes when all executors have completed after admission confirmation. */ + private WaitGroup $onDone; + + /** + * @param ShopRef $shopId The shop ID + * @param array $labels The shop labels + * @param Player $user The user who executed the shop + * @param Closure(): Generator $execute Execute the shop transaction + */ + public function __construct( + private ShopRef $shopId, + private array $labels, + private Player $user, + Closure $execute, + ) { + $this->onAdmit = new WaitGroup; + $this->onReject = new Loading(function() : Generator { + return yield from Await::promise(fn($resolve) => $this->rejectFunc = $resolve); + }); + $this->onTransactionComplete = new Loading(function() use ($execute) : Generator { + [$rejected,] = yield from Await::race([$this->onAdmit->wait(), $this->onReject->get()]); + if ($rejected === 1) { + return false; + } + + $transactionOk = yield from $execute(); + return $transactionOk; + }); + $this->onDone = new WaitGroup; + } + + public function getShopId() : ShopRef { + return $this->shopId; + } + + /** + * @return array + */ + public function getLabels() : array { + return $this->labels; + } + + public function getUser() : Player { + return $this->user; + } + + /** + * @param Closure(): Generator $hold + * @param Closure(): Generator $commit + * @param Closure(): Generator $rollback + */ + public function add(Closure $hold, Closure $commit, Closure $rollback) : void { + Await::f2c(function() use ($hold, $commit, $rollback) : Generator { + $handle = $this->addExecutor(); + + $rejection = yield from $hold(); + if ($rejection === null) { + $ok = yield from $handle->admit(); + if ($ok) { + yield from $commit(); + } else { + yield from $rollback(); + } + $handle->done(); + } else { + $handle->reject($rejection); + } + }); + } + + /** + * Adds an executor to this event. + * + * This function returns a new `ExecuteHandle`, + * and adds the executor to the admission wait group. + */ + public function addExecutor() : ExecuteHandle { + $this->onAdmit->add(); + return ExecuteHandle::internalInit($this); + } + + public function isCancelled() : bool { + return $this->onReject->getSync(null) !== null; + } + + public function getRejection() : ?ShopRejection { + return $this->onReject->getSync(null); + } + + /** + * @internal + * @return Generator + */ + public function internalAdmit() : Generator { + $this->onAdmit->done(); + [$rejected,] = yield from Await::race([$this->onAdmit->wait(), $this->onReject->get()]); + if ($rejected === 1) { + return false; + } + + return yield from $this->onTransactionComplete->get(); + } + + /** + * @internal + */ + public function internalReject(ShopRejection $rejection) : void { + if ($this->isCancelled()) { + return; + } + ($this->rejectFunc)($rejection); + } + + /** + * @internal + */ + public function internalDone() : void { + $this->onDone->done(); + } + + /** + * Waits for the event to finish. + * Returns null if the shop was executed successfully, + * the ShopRejection if the execution failed. + * + * @return Generator + */ + public function waitDone() : Generator { + [$done, $rejection] = yield from Await::race([ + $this->onReject->get(), + $this->onDone->wait(), + ]); + + if ($done === 0) { + /** @var ShopRejection $rejection */ + return $rejection; + } + + return null; + } +} diff --git a/CapiTrade/src/SOFe/CapiTrade/ShopLabels.php b/CapiTrade/src/SOFe/CapiTrade/ShopLabels.php new file mode 100644 index 0000000..329e59f --- /dev/null +++ b/CapiTrade/src/SOFe/CapiTrade/ShopLabels.php @@ -0,0 +1,13 @@ +id; + } +} diff --git a/CapiTrade/src/SOFe/CapiTrade/ShopRejection.php b/CapiTrade/src/SOFe/CapiTrade/ShopRejection.php new file mode 100644 index 0000000..de08145 --- /dev/null +++ b/CapiTrade/src/SOFe/CapiTrade/ShopRejection.php @@ -0,0 +1,10 @@ + + */ public function accountLabels() : LabelManager { - return new LabelManager($this, "capital_acc_label", CapitalException::ACCOUNT_LABEL_ALREADY_EXISTS, CapitalException::ACCOUNT_LABEL_DOES_NOT_EXIST); + return new LabelManager( + database: $this, + labelTable: "capital_acc_label", + labelAlreadyExistsErrorCode: CapitalException::ACCOUNT_LABEL_ALREADY_EXISTS, + labelDoesNotExistErrorCode: CapitalException::ACCOUNT_LABEL_DOES_NOT_EXIST, + throw: fn($code, $prev) => new CapitalException($code, $prev), + ); } // Dynamic account label queries @@ -692,9 +701,17 @@ private function doTransaction2Mysql(array $uuid, array $src, array $dest, array }; } - + /** + * @return LabelManager + */ public function transactionLabels() : LabelManager { - return new LabelManager($this, "capital_tran_label", CapitalException::TRANSACTION_LABEL_ALREADY_EXISTS, CapitalException::TRANSACTION_LABEL_DOES_NOT_EXIST); + return new LabelManager( + database: $this, + labelTable: "capital_tran_label", + labelAlreadyExistsErrorCode: CapitalException::TRANSACTION_LABEL_ALREADY_EXISTS, + labelDoesNotExistErrorCode: CapitalException::TRANSACTION_LABEL_DOES_NOT_EXIST, + throw: fn($code, $prev) => new CapitalException($code, $prev), + ); } // Dynamic transaction label queries diff --git a/src/SOFe/Capital/Database/LabelManager.php b/src/SOFe/Capital/Database/LabelManager.php index c734a52..376a2be 100644 --- a/src/SOFe/Capital/Database/LabelManager.php +++ b/src/SOFe/Capital/Database/LabelManager.php @@ -4,6 +4,8 @@ namespace SOFe\Capital\Database; +use Closure; +use Exception; use Generator; use poggit\libasynql\DataConnector; use poggit\libasynql\generic\GenericVariable; @@ -13,23 +15,28 @@ use poggit\libasynql\SqlError; use poggit\libasynql\SqlThread; use Ramsey\Uuid\UuidInterface; -use SOFe\Capital\CapitalException; use function count; +/** + * @template C Exception code for `E` + * @template E of Exception Exception type to throw + */ final class LabelManager { private DataConnector $conn; /** @var SqlDialect::* */ private string $dialect; /** - * @param CapitalException::* $labelAlreadyExistsErrorCode - * @param CapitalException::* $labelDoesNotExistErrorCode + * @param C $labelAlreadyExistsErrorCode + * @param C $labelDoesNotExistErrorCode + * @param Closure(C, Exception|null): E $throw returns an exception to throw. */ public function __construct( Database $database, private string $labelTable, - private int $labelAlreadyExistsErrorCode, - private int $labelDoesNotExistErrorCode + private $labelAlreadyExistsErrorCode, + private $labelDoesNotExistErrorCode, + private Closure $throw, ) { $this->conn = $database->getDataConnector(); $this->dialect = $database->dialect; @@ -37,7 +44,7 @@ public function __construct( /** * @return VoidPromise - * @throws CapitalException if the object already has this label + * @throws E if the object already has this label */ public function add(UuidInterface $id, string $name, string $value) : Generator { try { @@ -48,7 +55,7 @@ public function add(UuidInterface $id, string $name, string $value) : Generator ->addParam("value", GenericVariable::TYPE_STRING, $value) ->execute($this->conn, $this->dialect); } catch (SqlError $error) { - throw new CapitalException($this->labelAlreadyExistsErrorCode, $error); + throw ($this->throw)($this->labelAlreadyExistsErrorCode, $error); } } @@ -56,7 +63,7 @@ public function add(UuidInterface $id, string $name, string $value) : Generator * Updates a label. * * @return VoidPromise - * @throws CapitalException if the object does not have this label + * @throws E if the object does not have this label */ public function update(UuidInterface $id, string $name, string $value) : Generator { /** @var SqlChangeResult $result */ @@ -67,7 +74,7 @@ public function update(UuidInterface $id, string $name, string $value) : Generat ->addParam("value", GenericVariable::TYPE_STRING, $value) ->execute($this->conn, $this->dialect); if ($result->getAffectedRows() === 0) { - throw new CapitalException($this->labelDoesNotExistErrorCode); + throw ($this->throw)($this->labelDoesNotExistErrorCode, null); } } @@ -91,7 +98,7 @@ public function set(UuidInterface $id, string $name, string $value) : Generator /** * @return Generator - * @throws CapitalException if the object does not have this label + * @throws E if the object does not have this label */ public function get(UuidInterface $id, string $name) : Generator { /** @var SqlSelectResult $result */ @@ -104,7 +111,7 @@ public function get(UuidInterface $id, string $name) : Generator { if (count($rows) > 0) { return $rows[0]["value"]; } - throw new CapitalException($this->labelDoesNotExistErrorCode); + throw ($this->throw)($this->labelDoesNotExistErrorCode, null); } /** diff --git a/src/SOFe/Capital/Loader/Loader.php b/src/SOFe/Capital/Loader/Loader.php index 2c148de..5b9a7a3 100644 --- a/src/SOFe/Capital/Loader/Loader.php +++ b/src/SOFe/Capital/Loader/Loader.php @@ -6,6 +6,7 @@ use Generator; use InvalidArgumentException; +use SOFe\AwaitGenerator\Await; use SOFe\Capital as C; use SOFe\Capital\Di\FromContext; use SOFe\Capital\Di\Singleton; @@ -19,12 +20,17 @@ final class Loader implements Singleton, FromContext { public const API_VERSION = "0.2.0"; + /** @var array, true> */ private static array $extraEntryPoints = []; + /** + * @param class-string $class + */ public static function addEntryPoint(string $class) : void { if (!is_subclass_of($class, Singleton::class)) { throw new InvalidArgumentException("Entry point must be a subclass of " . Singleton::class); } + self::$extraEntryPoints[$class] = true; } public static function fromSingletonArgs( @@ -36,9 +42,11 @@ public static function fromSingletonArgs( C\Migration\Mod $migration, C\Di\Context $context, ) : Generator { - foreach (self::$extraEntryPoints as $entryPoint) { - yield from $context->fetchClass($entryPoint); + $all = []; + foreach (self::$extraEntryPoints as $entryPoint => $_) { + $all[] = $entryPoint::get($context); } + yield from Await::all($all); return new self; } diff --git a/suitetest/plugin/composer.json b/suitetest/plugin/composer.json index c5ed915..6d7b690 100644 --- a/suitetest/plugin/composer.json +++ b/suitetest/plugin/composer.json @@ -5,7 +5,7 @@ "require": { "pocketmine/pocketmine-mp": "4.0.0", "sof3/await-generator": "^3.1.0", - "sof3/await-std": "dev-event-rewrite" + "sof3/await-std": "dev-master" }, "license": "Apache-2.0", "autoload": { @@ -19,6 +19,6 @@ } }, "require-dev": { - "phpstan/phpstan": "1.2.x-dev" + "phpstan/phpstan": "^1.2" } } diff --git a/suitetest/plugin/composer.lock b/suitetest/plugin/composer.lock index 1e77e7e..6dd0747 100644 --- a/suitetest/plugin/composer.lock +++ b/suitetest/plugin/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "257934dfcbfc3bcd1a98e7c6d4d35326", + "content-hash": "952ec7f773c834da7f77c872945b50a1", "packages": [ { "name": "adhocore/json-comment", @@ -123,24 +123,24 @@ }, { "name": "fgrosse/phpasn1", - "version": "v2.3.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/fgrosse/PHPASN1.git", - "reference": "20299033c35f4300eb656e7e8e88cf52d1d6694e" + "reference": "eef488991d53e58e60c9554b09b1201ca5ba9296" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/20299033c35f4300eb656e7e8e88cf52d1d6694e", - "reference": "20299033c35f4300eb656e7e8e88cf52d1d6694e", + "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/eef488991d53e58e60c9554b09b1201ca5ba9296", + "reference": "eef488991d53e58e60c9554b09b1201ca5ba9296", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": "~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0" }, "require-dev": { - "phpunit/phpunit": "~6.3", - "satooshi/php-coveralls": "~2.0" + "php-coveralls/php-coveralls": "~2.0", + "phpunit/phpunit": "^6.3 || ^7.0 || ^8.0" }, "suggest": { "ext-bcmath": "BCmath is the fallback extension for big integer calculations", @@ -192,9 +192,9 @@ ], "support": { "issues": "https://github.com/fgrosse/PHPASN1/issues", - "source": "https://github.com/fgrosse/PHPASN1/tree/v2.3.0" + "source": "https://github.com/fgrosse/PHPASN1/tree/v2.4.0" }, - "time": "2021-04-24T19:01:55+00:00" + "time": "2021-12-11T12:41:06+00:00" }, { "name": "netresearch/jsonmapper", @@ -249,16 +249,16 @@ }, { "name": "pocketmine/bedrock-data", - "version": "1.4.0+bedrock-1.17.40", + "version": "1.6.0+bedrock-1.18.10", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "f29b7be8fa3046d2ee4c6421485b97b3f5b07774" + "reference": "e98c511584a7bd58a95986374d2df4b04c6a2ba0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/f29b7be8fa3046d2ee4c6421485b97b3f5b07774", - "reference": "f29b7be8fa3046d2ee4c6421485b97b3f5b07774", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e98c511584a7bd58a95986374d2df4b04c6a2ba0", + "reference": "e98c511584a7bd58a95986374d2df4b04c6a2ba0", "shasum": "" }, "type": "library", @@ -269,22 +269,22 @@ "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.17.40" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.18.10" }, - "time": "2021-10-19T16:55:41+00:00" + "time": "2022-02-08T19:13:47+00:00" }, { "name": "pocketmine/bedrock-protocol", - "version": "5.1.3+bedrock-1.17.40", + "version": "7.3.1+bedrock-1.18.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "f56aefc60a00d1440e309edb89a8d7e4925fa3bc" + "reference": "c2667453b03ca08a8c54cd89a1fd45cb29196aeb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/f56aefc60a00d1440e309edb89a8d7e4925fa3bc", - "reference": "f56aefc60a00d1440e309edb89a8d7e4925fa3bc", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/c2667453b03ca08a8c54cd89a1fd45cb29196aeb", + "reference": "c2667453b03ca08a8c54cd89a1fd45cb29196aeb", "shasum": "" }, "require": { @@ -298,7 +298,7 @@ "ramsey/uuid": "^4.1" }, "require-dev": { - "phpstan/phpstan": "1.1.1", + "phpstan/phpstan": "1.4.2", "phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0", "phpunit/phpunit": "^9.5" @@ -316,22 +316,22 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/5.1.3+bedrock-1.17.40" + "source": "https://github.com/pmmp/BedrockProtocol/tree/7.3.1+bedrock-1.18.0" }, - "time": "2021-11-14T02:25:53+00:00" + "time": "2022-01-26T21:14:23+00:00" }, { "name": "pocketmine/binaryutils", - "version": "0.2.2", + "version": "0.2.4", "source": { "type": "git", "url": "https://github.com/pmmp/BinaryUtils.git", - "reference": "f883e1cf9099ed6a757a10a2f75b3333eeb2cdf9" + "reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/f883e1cf9099ed6a757a10a2f75b3333eeb2cdf9", - "reference": "f883e1cf9099ed6a757a10a2f75b3333eeb2cdf9", + "url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a", + "reference": "5ac7eea91afbad8dc498f5ce34ce6297d5e6ea9a", "shasum": "" }, "require": { @@ -340,8 +340,10 @@ }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.99", - "phpstan/phpstan-strict-rules": "^0.12.4" + "phpstan/phpstan": "1.3.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0.0", + "phpunit/phpunit": "^9.5" }, "type": "library", "autoload": { @@ -356,9 +358,9 @@ "description": "Classes and methods for conveniently handling binary data", "support": { "issues": "https://github.com/pmmp/BinaryUtils/issues", - "source": "https://github.com/pmmp/BinaryUtils/tree/0.2.2" + "source": "https://github.com/pmmp/BinaryUtils/tree/0.2.4" }, - "time": "2021-10-22T19:54:16+00:00" + "time": "2022-01-12T18:06:33+00:00" }, { "name": "pocketmine/callback-validator", @@ -533,16 +535,16 @@ }, { "name": "pocketmine/locale-data", - "version": "1.1.6", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "216b49b87e20332f0b39d1717e1e2012a40074cc" + "reference": "4d0b081f1a79407e087968ea76aaf330db6ea2b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/216b49b87e20332f0b39d1717e1e2012a40074cc", - "reference": "216b49b87e20332f0b39d1717e1e2012a40074cc", + "url": "https://api.github.com/repos/pmmp/Language/zipball/4d0b081f1a79407e087968ea76aaf330db6ea2b5", + "reference": "4d0b081f1a79407e087968ea76aaf330db6ea2b5", "shasum": "" }, "type": "library", @@ -550,9 +552,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/1.1.6" + "source": "https://github.com/pmmp/Language/tree/2.4.3" }, - "time": "2021-11-07T14:30:46+00:00" + "time": "2022-01-25T23:18:24+00:00" }, { "name": "pocketmine/log", @@ -641,16 +643,16 @@ }, { "name": "pocketmine/math", - "version": "0.4.0", + "version": "0.4.2", "source": { "type": "git", "url": "https://github.com/pmmp/Math.git", - "reference": "6d64e2555bd2e95ed024574f75d1cefc135c89fc" + "reference": "aacc3759a508a69dfa5bc4dfa770ab733c5c94bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Math/zipball/6d64e2555bd2e95ed024574f75d1cefc135c89fc", - "reference": "6d64e2555bd2e95ed024574f75d1cefc135c89fc", + "url": "https://api.github.com/repos/pmmp/Math/zipball/aacc3759a508a69dfa5bc4dfa770ab733c5c94bf", + "reference": "aacc3759a508a69dfa5bc4dfa770ab733c5c94bf", "shasum": "" }, "require": { @@ -658,10 +660,10 @@ "php-64bit": "*" }, "require-dev": { - "irstea/phpunit-shim": "^8.5 || ^9.5", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.99", - "phpstan/phpstan-strict-rules": "^0.12.4" + "phpstan/phpstan": "1.2.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^8.5 || ^9.5" }, "type": "library", "autoload": { @@ -676,22 +678,22 @@ "description": "PHP library containing math related code used in PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Math/issues", - "source": "https://github.com/pmmp/Math/tree/0.4.0" + "source": "https://github.com/pmmp/Math/tree/0.4.2" }, - "time": "2021-10-29T20:33:10+00:00" + "time": "2021-12-05T01:15:17+00:00" }, { "name": "pocketmine/nbt", - "version": "0.3.0", + "version": "0.3.2", "source": { "type": "git", "url": "https://github.com/pmmp/NBT.git", - "reference": "98c4a04b55a915e18f83d3b0c9beb24a71abcd31" + "reference": "3e0d9ef6b6c5fb45e3745a121296e75631b3eefe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/NBT/zipball/98c4a04b55a915e18f83d3b0c9beb24a71abcd31", - "reference": "98c4a04b55a915e18f83d3b0c9beb24a71abcd31", + "url": "https://api.github.com/repos/pmmp/NBT/zipball/3e0d9ef6b6c5fb45e3745a121296e75631b3eefe", + "reference": "3e0d9ef6b6c5fb45e3745a121296e75631b3eefe", "shasum": "" }, "require": { @@ -700,10 +702,10 @@ "pocketmine/binaryutils": "^0.2.0" }, "require-dev": { - "irstea/phpunit-shim": "^9.5", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.85", - "phpstan/phpstan-strict-rules": "^0.12.4" + "phpstan/phpstan": "1.2.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5" }, "type": "library", "autoload": { @@ -718,28 +720,28 @@ "description": "PHP library for working with Named Binary Tags", "support": { "issues": "https://github.com/pmmp/NBT/issues", - "source": "https://github.com/pmmp/NBT/tree/0.3.0" + "source": "https://github.com/pmmp/NBT/tree/0.3.2" }, - "time": "2021-05-18T15:46:33+00:00" + "time": "2021-12-16T01:02:37+00:00" }, { "name": "pocketmine/pocketmine-mp", - "version": "4.0.0-BETA12", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/pmmp/PocketMine-MP.git", - "reference": "635a9143de62e69c6811ef4bc9cb65933b166cd7" + "reference": "468faa464b2bc5c97f23fafbb71ea61035f6f218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/PocketMine-MP/zipball/635a9143de62e69c6811ef4bc9cb65933b166cd7", - "reference": "635a9143de62e69c6811ef4bc9cb65933b166cd7", + "url": "https://api.github.com/repos/pmmp/PocketMine-MP/zipball/468faa464b2bc5c97f23fafbb71ea61035f6f218", + "reference": "468faa464b2bc5c97f23fafbb71ea61035f6f218", "shasum": "" }, "require": { "adhocore/json-comment": "^1.1", "composer-runtime-api": "^2.0", - "ext-chunkutils2": "^0.3.0", + "ext-chunkutils2": "^0.3.1", "ext-crypto": "^0.3.1", "ext-ctype": "*", "ext-curl": "*", @@ -766,14 +768,14 @@ "netresearch/jsonmapper": "^4.0", "php": "^8.0", "php-64bit": "*", - "pocketmine/bedrock-data": "^1.4.0+bedrock-1.17.40", - "pocketmine/bedrock-protocol": "^5.0.0+bedrock-1.17.40", + "pocketmine/bedrock-data": "^1.5.0+bedrock-1.18.0", + "pocketmine/bedrock-protocol": "^7.0.0+bedrock-1.18.0", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/classloader": "^0.2.0", "pocketmine/color": "^0.2.0", "pocketmine/errorhandler": "^0.3.0", - "pocketmine/locale-data": "^1.1.4", + "pocketmine/locale-data": "^2.0.16", "pocketmine/log": "^0.4.0", "pocketmine/log-pthreads": "^0.4.0", "pocketmine/math": "^0.4.0", @@ -785,19 +787,19 @@ "webmozart/path-util": "^2.3" }, "require-dev": { - "phpstan/phpstan": "1.1.1", + "phpstan/phpstan": "1.2.0", "phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0", "phpunit/phpunit": "^9.2" }, "type": "project", "autoload": { - "psr-4": { - "pocketmine\\": "src/" - }, "files": [ "src/CoreConstants.php" - ] + ], + "psr-4": { + "pocketmine\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -807,7 +809,7 @@ "homepage": "https://pmmp.io", "support": { "issues": "https://github.com/pmmp/PocketMine-MP/issues", - "source": "https://github.com/pmmp/PocketMine-MP/tree/4.0.0-BETA12" + "source": "https://github.com/pmmp/PocketMine-MP/tree/4.0.0" }, "funding": [ { @@ -819,20 +821,20 @@ "type": "patreon" } ], - "time": "2021-11-09T16:50:42+00:00" + "time": "2021-12-01T22:33:52+00:00" }, { "name": "pocketmine/raklib", - "version": "0.14.2", + "version": "0.14.3", "source": { "type": "git", "url": "https://github.com/pmmp/RakLib.git", - "reference": "e3a861187470e1facc6625040128f447ebbcbaec" + "reference": "4798576fec0364266dce23b368a7fec5e5de7927" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/RakLib/zipball/e3a861187470e1facc6625040128f447ebbcbaec", - "reference": "e3a861187470e1facc6625040128f447ebbcbaec", + "url": "https://api.github.com/repos/pmmp/RakLib/zipball/4798576fec0364266dce23b368a7fec5e5de7927", + "reference": "4798576fec0364266dce23b368a7fec5e5de7927", "shasum": "" }, "require": { @@ -844,8 +846,8 @@ "pocketmine/log": "^0.3.0 || ^0.4.0" }, "require-dev": { - "phpstan/phpstan": "0.12.99", - "phpstan/phpstan-strict-rules": "^0.12.2" + "phpstan/phpstan": "1.3.3", + "phpstan/phpstan-strict-rules": "^1.0" }, "type": "library", "autoload": { @@ -860,9 +862,9 @@ "description": "A RakNet server implementation written in PHP", "support": { "issues": "https://github.com/pmmp/RakLib/issues", - "source": "https://github.com/pmmp/RakLib/tree/0.14.2" + "source": "https://github.com/pmmp/RakLib/tree/0.14.3" }, - "time": "2021-10-04T20:39:11+00:00" + "time": "2022-01-10T21:29:48+00:00" }, { "name": "pocketmine/raklib-ipc", @@ -1030,12 +1032,12 @@ "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "cd372a7b47a54734cbd491df40b5a48614d8d374" + "reference": "e3e9179f275a44b8e462816d1c65958ff90c4c96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/cd372a7b47a54734cbd491df40b5a48614d8d374", - "reference": "cd372a7b47a54734cbd491df40b5a48614d8d374", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/e3e9179f275a44b8e462816d1c65958ff90c4c96", + "reference": "e3e9179f275a44b8e462816d1c65958ff90c4c96", "shasum": "" }, "require": { @@ -1090,12 +1092,12 @@ } }, "autoload": { - "psr-4": { - "Ramsey\\Uuid\\": "src/" - }, "files": [ "src/functions.php" - ] + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1121,29 +1123,29 @@ "type": "tidelift" } ], - "time": "2021-10-13T18:44:27+00:00" + "time": "2022-01-29T19:05:59+00:00" }, { "name": "sof3/await-generator", - "version": "3.1.0", + "version": "3.4.1", "source": { "type": "git", "url": "https://github.com/SOF3/await-generator.git", - "reference": "c7d99f6c5a9338d9215af0f83880c9a11df83ca3" + "reference": "9e290d34fd627584d00f0b3105b7335c67ff5494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SOF3/await-generator/zipball/c7d99f6c5a9338d9215af0f83880c9a11df83ca3", - "reference": "c7d99f6c5a9338d9215af0f83880c9a11df83ca3", + "url": "https://api.github.com/repos/SOF3/await-generator/zipball/9e290d34fd627584d00f0b3105b7335c67ff5494", + "reference": "9e290d34fd627584d00f0b3105b7335c67ff5494", "shasum": "" }, "require": { - "php": "^7.3 || ^8.0" + "php": "^8.0" }, "require-dev": { "composer/package-versions-deprecated": "1.11.99.1", "infection/infection": "^0.18.2 || ^0.20.2", - "phpstan/phpstan": "^0.12.20", + "phpstan/phpstan": "^0.12.84", "phpunit/phpunit": "^9" }, "type": "library", @@ -1165,31 +1167,32 @@ "description": "Use async/await in PHP using generators", "support": { "issues": "https://github.com/SOF3/await-generator/issues", - "source": "https://github.com/SOF3/await-generator/tree/3.1.0" + "source": "https://github.com/SOF3/await-generator/tree/3.4.1" }, - "time": "2021-03-06T06:16:22+00:00" + "time": "2022-03-19T09:38:28+00:00" }, { "name": "sof3/await-std", - "version": "dev-event-rewrite", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/SOF3/await-std.git", - "reference": "ba865a6e23d4879f88058ffa439295e1bb0e43db" + "reference": "b2141db2917cb1e9e5ad91df05020e91460578fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SOF3/await-std/zipball/ba865a6e23d4879f88058ffa439295e1bb0e43db", - "reference": "ba865a6e23d4879f88058ffa439295e1bb0e43db", + "url": "https://api.github.com/repos/SOF3/await-std/zipball/b2141db2917cb1e9e5ad91df05020e91460578fa", + "reference": "b2141db2917cb1e9e5ad91df05020e91460578fa", "shasum": "" }, "require": { - "pocketmine/pocketmine-mp": "4.0.0-BETA12", - "sof3/await-generator": "^3.1.0" + "pocketmine/pocketmine-mp": "^4.0.0", + "sof3/await-generator": "^3.1.1" }, "require-dev": { "phpstan/phpstan": "^0.12.84" }, + "default-branch": true, "type": "library", "autoload": { "psr-0": { @@ -1203,13 +1206,13 @@ "description": "Writing user-friendly PocketMine plugins fluently with await-generator style", "support": { "issues": "https://github.com/SOF3/await-std/issues", - "source": "https://github.com/SOF3/await-std/tree/event-rewrite" + "source": "https://github.com/SOF3/await-std/tree/master" }, - "time": "2021-11-23T15:00:47+00:00" + "time": "2022-03-05T09:19:43+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "dev-main", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -1230,7 +1233,6 @@ "suggest": { "ext-ctype": "For best performance" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -1242,12 +1244,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1272,7 +1274,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/main" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" }, "funding": [ { @@ -1292,22 +1294,21 @@ }, { "name": "symfony/polyfill-php80", - "version": "dev-main", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9" + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", "shasum": "" }, "require": { "php": ">=7.1" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -1319,12 +1320,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -1356,7 +1357,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/main" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" }, "funding": [ { @@ -1372,11 +1373,11 @@ "type": "tidelift" } ], - "time": "2021-09-13T13:58:33+00:00" + "time": "2022-03-04T08:16:47+00:00" }, { "name": "symfony/polyfill-php81", - "version": "dev-main", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -1391,7 +1392,6 @@ "require": { "php": ">=7.1" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -1403,12 +1403,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -1436,7 +1436,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/main" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" }, "funding": [ { @@ -1568,16 +1568,16 @@ "packages-dev": [ { "name": "phpstan/phpstan", - "version": "dev-master", + "version": "1.5.x-dev", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "43f31e54efdec2bc98d922981b239fec9d3eb86c" + "reference": "86b96ca2d5b7d89096f8edcf4bb14f202f272b77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/43f31e54efdec2bc98d922981b239fec9d3eb86c", - "reference": "43f31e54efdec2bc98d922981b239fec9d3eb86c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/86b96ca2d5b7d89096f8edcf4bb14f202f272b77", + "reference": "86b96ca2d5b7d89096f8edcf4bb14f202f272b77", "shasum": "" }, "require": { @@ -1592,11 +1592,6 @@ "phpstan.phar" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, "autoload": { "files": [ "bootstrap.php" @@ -1609,7 +1604,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/master" + "source": "https://github.com/phpstan/phpstan/tree/1.5.x" }, "funding": [ { @@ -1629,14 +1624,13 @@ "type": "tidelift" } ], - "time": "2021-11-23T08:12:47+00:00" + "time": "2022-03-19T08:19:07+00:00" } ], "aliases": [], "minimum-stability": "dev", "stability-flags": { - "sof3/await-std": 20, - "phpstan/phpstan": 20 + "sof3/await-std": 20 }, "prefer-stable": false, "prefer-lowest": false,