Giter Club home page Giter Club logo

capital's Introduction

Capital

CI

An extensible economy plugin for PocketMine-MP.

How is Capital different from other economy plugins?

As a core API for economy, Capital supports different styles of account management:

  • You can have the old, simple one-account-per-player mechanism.
  • Or do you like currencies? You can add new currencies to config.yml and other plugins will let you configure which currency to use in each case.
  • Or are currencies too complicated for you? What about just having one account per world? You don't need any special configuration in other plugins!
  • Are commands and form UI boring for you? Maybe use banknote/wallet items so that players lose money when they drop the item? (Capital itself does not support banknote/wallet items, but it is the only economy API where both simple accounts and item payment can be used from other plugins without writing code twice)
  • Or maybe sometimes the money goes to the faction bank account instead of player account?
  • Capital is extensible for other plugins to include new account management styles, and it will work automatically with all plugins!

Other cool features include:

  • Powerful analytics commands. How much active capital is there? How is wealth distributed on the server? Which industries are the most active? What are the biggest transactions in the server yesterday? Capital can help you answer these questions with label-based analytics.
  • Is editing the config file too confusing for you? Capital supports self-healing configuration. Your config file will be automatically regenerated if something is wrong, and Capital will try its best to guess what you really wanted.
  • Supports migration from other economy plugins, including:
    • EconomyAPI
  • Uses async database access, supporting both SQLite and MySQL. Capital will not lag your server.
  • Safe for multiple servers. Transactions are strictly atomic. Players cannot duplicate money by joining multiple servers.

Setting up

After running the server with Capital the first time, Capital generates config.yml and db.yml, which you can edit to configure Capital.

db.yml is used for configuring the database used by Capital. You can use sqlite or mysql here. The configuration is same as most other plugins.

config.yml is a large config that allows you to change almost everything in Capital. Read the comments in config.yml for more information. Text after '# xxx: are comments. If you edit config.yml incorrectly, Capital will try to fix the config.yml and save the old one as config.yml.old so that you can refer to it if Capital fixed it incorrectly.

Default commands

All commands in Capital can be configured in config.yml. Try searching them in the config file to find out the correct place. The following commands come from the default config:

Player commands:

  • /pay <player> <amount> [account...]: Pay money to another player with your own account.
  • /checkmoney: Check your own wealth.
  • /richest: View the richest players on the server.

Admin commands:

  • /addmoney <player> <amount> [account...]: Add money to a player's account.
  • /takemoney <player> <amount> [account...]: Remove money from a player's account.
  • /checkmoney <player>: Check the wealth of another player.

[account...] can be used to select the account (e.g. currency) if you change the schema in config.yml. (You can still disable these arguments by setting up selector in config.yml)

You can create many other useful commands by editing config.yml, e.g. check how much money was paid by /pay command! Check out the comments in config.yml for more information.

Community, Contact & Contributing

If you want to get help, share your awesome config setup or show off your cool plugin that uses Capital, create a discussion on GitHub.

To report bugs, create an issue on GitHub.

If you want to help with developing Capital, see dev.md for a comprehensive walkthrough of the internals.

capital's People

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  avatar

Watchers

 avatar  avatar  avatar  avatar

capital's Issues

why when access selector becomes null

Code:

use SOFe\Capital{Capital, CapitalException, LabelSet};

class Main extends PluginBase {
private $selector;

protected function onEnable() : void {
Capital::api("0.1.0", function(Capital $api) {
$this->selector = $api->completeConfig($this->getConfig()->get("selector"));
});
}
}

and if i var_dump($this->selector);
returned null
but if i var_dump in Capital completeConfig it returned Object

Spread transfer request over multiple accounts matching a selector

For example, if we have a database like this:

ID Balance Labels
X01 15 name=X, valueMin=0
X02 15 name=X, valueMin=0
Y03 0 name=Y, valueMax=100

We want to transfer 20 from name=X to name=Y. This is not possible using a single account, but this is possible by splitting the transfer into multiple transactions:

Source Destination Delta
X01 Y03 15
X02 Y03 5

Alternatively:

Source Destination Delta
X01 Y03 10
X02 Y03 10

Whichever strategy we want to use, this is not possible with the current SQL procedures.
To implement this with atomic transactions on MySQL, we may need to create a table storing a temporary list of accounts matching a label selector, then perform operations based on this temporary list.

Large volume of transactions causes memory depletion

If the database server is slow and takes a long time to process transactions, attempting to process a large number of transactions will create a large number of connections and cause the server to crash due to lack of memory.

Using slow database, you can reproduce it with code like this

use SOFe\Capital\{Capital, CapitalException, LabelSet};

Capital::api("0.1.0", function(Capital $api){
    $this->selector = $api->completeConfig(["currency" => "money"]);
});

$this->getScheduler()->scheduleRepeatingTask(new ClosureTask(function(){
    foreach($this->getServer()->getOnlinePlayers() as $player){
        Capital::api("0.1.0", function(Capital $api) use ($player){
            yield from $api->addMoney($this->getName(), $player, $this->selector, 1, new LabelSet([]));
        });
    }
}), 1);

Add a method where you can use the player’s balance

Currently there is no way to make Player A pay Player B depending on its balance (ex: a percentage of Player A) without causing a race condition. I would suggest having some parameter where it accepts a promise and that in the first argument it has the player A balance. Example:

$promise = function(int $balance) use($percentage){
    return $balance * $percentage / 100;
};
yield from $capital->payWithBalance(
    "SellHeads",
    $player1,
    $player2,
    $this->selector,
    $promise, //This could also be async just like `Capital::api()`
    new LabelSet(["reason" => "Claimed head"])
);

Monitor for unusual activities

Temporarily block the amount in the account if there are abnormal signs such as: a sudden increase in the amount, a negative amount, a continuous increase or decrease in the amount, ..

Account per world

As requested by @reyyan987 in poggit-orphanage/EconomyS#76

This should involve creating a schema called "world", with the following config:

schema:
  '#type': The type of schema. Possible values are "basic", "currency", "world".
  type: world
  '##': In the world schema, each player has one account for each world.

There are several possible configurations, but I am not sure if they are actually useful:

  • World groups: Worlds are grouped such that player uses the same account in the worlds in the same group
  • World whitelist/blacklist: Player cannot use any accounts at all in these worlds

Question

Can you open dm in discord i have something to tell you - Necessary -

Question

When Shop/Bazaar Will Be Implemented?

Readme mistakes

the smallest issue in this repo lol

i think not And, its and
Screenshot_2022-08-25-12-39-15-44
no slashes
Screenshot_2022-08-25-12-40-20-13
issue, not isuse
Screenshot_2022-08-25-12-41-00-75

Vague purpose of dev.md

dev.md holds >99% of the official API docs (outside Poggit). I remember one day I spent so long trying to find an official API doc of this plugin. But end up giving up because I omitted dev.md after getting told its purpose.

I originally wanted to complain the purpose, "This document is for developers who want to contribute to this project" is misleading. However, while writing this issue, I realized that making (peripheral) plugins based on the Capital API can be seen as a contribution to the "project". Therefore, I say its purpose is vague instead of misleading.

TL;DR

I do not want to open Poggit to look up the official API doc. But I thought dev.md is for internal dev so I did not read it while looking up on GitHub.

Capital does not work with libasynql 4.1.1

The development build from the 156th in poggit does not work

 [Server thread/CRITICAL]: SOFe\Capital\Plugin\_58655cc4\SOFe\AwaitGenerator\AwaitException: "Unhandled async exception: SOFe\Capital\Loader\Loader took more than 5 seconds to initialize (blocking classes: SOFe\Capital\Loader\Loader, SOFe\Capital\Transfer\Mod, SOFe\Capital\Capital, SOFe\Capital\Database\Database, SOFe\Capital\Analytics\Mod, SOFe\Capital\Analytics\Single\Mod, SOFe\Capital\Analytics\Top\Mod, SOFe\Capital\Analytics\Top\DatabaseUtils, SOFe\Capital\Migration\Mod, SOFe\Capital\Migration\DatabaseUtils)" (EXCEPTION) in "plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_58655cc4/SOFe/AwaitGenerator/Await" at line 497
--- Stack trace ---
  #0 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_58655cc4/SOFe/AwaitGenerator/Await(244): SOFe\Capital\Plugin\_58655cc4\SOFe\AwaitGenerator\Await->reject(object RuntimeException#26410)
  #1 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_58655cc4/SOFe/AwaitGenerator/Await(227): SOFe\Capital\Plugin\_58655cc4\SOFe\AwaitGenerator\Await->wakeup(object Closure#26400)
  #2 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_58655cc4/SOFe/AwaitGenerator/Await(454): SOFe\Capital\Plugin\_58655cc4\SOFe\AwaitGenerator\Await->wakeupFlat(object Closure#26400)
  #3 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_58655cc4/SOFe/AwaitGenerator/AwaitChild(63): SOFe\Capital\Plugin\_58655cc4\SOFe\AwaitGenerator\Await->recheckPromiseQueue(object SOFe\Capital\Plugin\_58655cc4\SOFe\AwaitGenerator\AwaitChild#26327)
  #4 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/Mutex(42): SOFe\Capital\Plugin\_58655cc4\SOFe\AwaitGenerator\AwaitChild->reject(object RuntimeException#26410)
  #5 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/Await(461): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\Mutex->SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\{closure}(object RuntimeException#26410)
  #6 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/Await(217): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\Await->reject(object RuntimeException#26410)
  #7 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/Await(200): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\Await->wakeup(object Closure#21895)
  #8 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/Await(403): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\Await->wakeupFlat(object Closure#21895)
  #9 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/AwaitChild(52): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\Await->recheckPromiseQueue(object SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\AwaitChild#26414)
  #10 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(189): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\AwaitChild->resolve(array[2])
  #11 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(472): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await::SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\{closure}(null)
  #12 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(247): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await->resolve(null)
  #13 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(224): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await->wakeup(object Closure#25171)
  #14 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(456): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await->wakeupFlat(object Closure#25171)
  #15 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/AwaitChild(52): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await->recheckPromiseQueue(object SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\AwaitChild#26420)
  #16 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/AwaitStd(38): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\AwaitChild->resolve()
  #17 pmsrc/src/scheduler/ClosureTask(57): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\AwaitStd->SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\{closure}()
  #18 pmsrc/src/scheduler/TaskHandler(123): pocketmine\scheduler\ClosureTask->onRun()
  #19 pmsrc/src/scheduler/TaskScheduler(141): pocketmine\scheduler\TaskHandler->run()
  #20 pmsrc/src/plugin/PluginManager(498): pocketmine\scheduler\TaskScheduler->mainThreadHeartbeat(int 101)
  #21 pmsrc/src/Server(1815): pocketmine\plugin\PluginManager->tickSchedulers(int 101)
  #22 pmsrc/src/Server(1704): pocketmine\Server->tick()
  #23 pmsrc/src/Server(1065): pocketmine\Server->tickProcessor()
  #24 pmsrc/src/PocketMine(305): pocketmine\Server->__construct(object BaseClassLoader#2, object pocketmine\utils\MainLogger#3, string[8] C:\pmmp\, string[16] C:\pmmp\plugins\)
  #25 pmsrc/src/PocketMine(328): pocketmine\server()
  #26 pmsrc(11): require(string[52] phar://C:/pmmp/PocketMine-MP.phar/src/PocketMine.)
--- Previous ---
RuntimeException: "SOFe\Capital\Loader\Loader took more than 5 seconds to initialize (blocking classes: SOFe\Capital\Loader\Loader, SOFe\Capital\Transfer\Mod, SOFe\Capital\Capital, SOFe\Capital\Database\Database, SOFe\Capital\Analytics\Mod, SOFe\Capital\Analytics\Single\Mod, SOFe\Capital\Analytics\Top\Mod, SOFe\Capital\Analytics\Top\DatabaseUtils, SOFe\Capital\Migration\Mod, SOFe\Capital\Migration\DatabaseUtils)" (EXCEPTION) in "plugins/Capital_dev-156.phar/src/SOFe/Capital/Di/Context" at line 78
  #0 (): SOFe\Capital\Di\Context->SOFe\Capital\Di\{closure}()
  #1 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/Await(402): Generator->send(array[2])
  #2 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/Await(215): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\Await->SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\{closure}()
  #3 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/Await(200): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\Await->wakeup(object Closure#21895)
  #4 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/Await(403): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\Await->wakeupFlat(object Closure#21895)
  #5 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_469338e7/SOFe/RwLock/_29f5ac4d/SOFe/AwaitGenerator/AwaitChild(52): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\Await->recheckPromiseQueue(object SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\AwaitChild#26414)
  #6 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(189): SOFe\Capital\Plugin\_469338e7\SOFe\RwLock\_29f5ac4d\SOFe\AwaitGenerator\AwaitChild->resolve(array[2])
  #7 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(472): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await::SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\{closure}(null)
  #8 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(247): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await->resolve(null)
  #9 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(224): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await->wakeup(object Closure#25171)
  #10 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/Await(456): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await->wakeupFlat(object Closure#25171)
  #11 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/_7b46c874/SOFe/AwaitGenerator/AwaitChild(52): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\Await->recheckPromiseQueue(object SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\AwaitChild#26420)
  #12 plugins/Capital_dev-156.phar/src/SOFe/Capital/Plugin/_a903796d/SOFe/AwaitStd/AwaitStd(38): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\_7b46c874\SOFe\AwaitGenerator\AwaitChild->resolve()
  #13 pmsrc/src/scheduler/ClosureTask(57): SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\AwaitStd->SOFe\Capital\Plugin\_a903796d\SOFe\AwaitStd\{closure}()
  #14 pmsrc/src/scheduler/TaskHandler(123): pocketmine\scheduler\ClosureTask->onRun()
  #15 pmsrc/src/scheduler/TaskScheduler(141): pocketmine\scheduler\TaskHandler->run()
  #16 pmsrc/src/plugin/PluginManager(498): pocketmine\scheduler\TaskScheduler->mainThreadHeartbeat(int 101)
  #17 pmsrc/src/Server(1815): pocketmine\plugin\PluginManager->tickSchedulers(int 101)
  #18 pmsrc/src/Server(1704): pocketmine\Server->tick()
  #19 pmsrc/src/Server(1065): pocketmine\Server->tickProcessor()
  #20 pmsrc/src/PocketMine(305): pocketmine\Server->__construct(object BaseClassLoader#2, object pocketmine\utils\MainLogger#3, string[8] C:\pmmp\, string[16] C:\pmmp\plugins\)
  #21 pmsrc/src/PocketMine(328): pocketmine\server()
  #22 pmsrc(11): require(string[52] phar://C:/pmmp/PocketMine-MP.phar/src/PocketMine.)
--- End of exception information ---

Contact you

Can you give me your discord please i want private plugin

/richest does not work with SQLite

In the SQLite database, the metrics displayed in the rankings are not updated after the first account update.
(If recompute-frequency is set to 0, the metrics will be updated.)

Shops

  • A shop consists of an Item Type, Item Source, an Item Sink, a Money Source, a Money Sink and a parameterized transaction label set.
  • A shop is first created by a Shop Creator, then interacts with the Customer.
  • A shop can only have one item type.
  • Item sources/sinks:
    • Infinity
    • Inventory of the customer
    • Chests in a world
    • A "cloud chest" which is backed by an account where 1 value = 1 item
  • Money source and sink are account label selectors, partially parameterized at the time of shop creation and fully parameterized at the time of shop interaction.
  • Methods to interact with a shop are independent of the shop definitions. A shop may have zero or more methods of interaction, such as:
    • Commands
    • Block interaction

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.