Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@

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($file === __DIR__ . "/src/SOFe/Capital/Database/RawQueries.php") {
if(substr($file, strrpos($file, "/")) === "/RawQueries.php") {
continue;
}

Expand Down
10 changes: 10 additions & 0 deletions CapiTrade/plugin.yml
Original file line number Diff line number Diff line change
@@ -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
66 changes: 66 additions & 0 deletions CapiTrade/resources/mysql/shop.sql
Original file line number Diff line number Diff line change
@@ -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;
-- # }
-- #}
67 changes: 67 additions & 0 deletions CapiTrade/resources/sqlite/shop.sql
Original file line number Diff line number Diff line change
@@ -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;
-- # }
-- #}
103 changes: 103 additions & 0 deletions CapiTrade/src/SOFe/CapiTrade/CapiTrade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

declare(strict_types=1);

namespace SOFe\CapiTrade;

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;

public const VERSION = "0.1.0";

public function __construct(private Database\DatabaseUtils $db, private Capital $capital) {
}

public function executeShop(ShopRef $shopId, Player $customer) : Generator {
/**
* @var array<string, string> $labels
* @var int $price
* @var array<string, string> $shopAccSelectorRaw
* @var array<string, mixed> $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()),
]);

$shopAccSelector = new ParameterizedLabelSelector($shopAccSelectorRaw);
$shopAccSelector = $shopAccSelector->transform(new PlayerInfo($customer));

$shopAccs = yield from $this->capital->findAccounts($shopAccSelector);
if (count($shopAccs) === 0) {
throw new CapiTradeException(CapiTradeException::SHOP_HAS_NO_ACCOUNT);
}
$shopAcc = $shopAccs[0];

$schema = $this->capital->completeConfig($schemaConfig);
$customerAccs = yield from $this->capital->findAccountsComplete($customer, $schema);
$customerAcc = $customerAccs[0];

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);
}
}

public function deleteShop(int $shopId, int $accessId) : Generator {
yield from $this->db->deleteAccess($accessId);
yield from $this->db->deleteIfNoAccess($shopId);
}
}
31 changes: 31 additions & 0 deletions CapiTrade/src/SOFe/CapiTrade/CapiTradeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace SOFe\CapiTrade;

use Exception;

final class CapiTradeException extends Exception {
public const NO_SUCH_SHOP = 0;
public const SHOP_LABEL_ALREADY_EXISTS = 1;
public const SHOP_LABEL_DOES_NOT_EXIST = 2;
public const EVENT_CANCELLED = 3;
public const SHOP_HAS_NO_ACCOUNT = 4;
public const TRANSACTION_FAILED = 5;

/**
* @param self::* $code
*/
public function __construct(int $code, ?Exception $previous = null) {
$message = match ($code) {
self::NO_SUCH_SHOP => "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);
}
}
Loading