{"id":21182785,"url":"https://github.com/tetratau/dataaddons","last_synced_at":"2026-05-17T13:36:34.578Z","repository":{"id":111506252,"uuid":"466923589","full_name":"TetraTau/DataAddons","owner":"TetraTau","description":"(Anti) Pattern for making additions over already existing data.","archived":false,"fork":false,"pushed_at":"2022-09-08T17:22:58.000Z","size":115,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-21T14:45:47.944Z","etag":null,"topics":["minecraft-library","minecraft-plugin","paper-plugin","papermc","papermc-plugin"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TetraTau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-07T03:03:05.000Z","updated_at":"2022-03-08T23:13:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"8d9cee66-d65e-4110-9717-12e8c140e4d1","html_url":"https://github.com/TetraTau/DataAddons","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TetraTau%2FDataAddons","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TetraTau%2FDataAddons/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TetraTau%2FDataAddons/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TetraTau%2FDataAddons/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TetraTau","download_url":"https://codeload.github.com/TetraTau/DataAddons/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243652707,"owners_count":20325611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["minecraft-library","minecraft-plugin","paper-plugin","papermc","papermc-plugin"],"created_at":"2024-11-20T17:58:05.023Z","updated_at":"2026-05-17T13:36:29.540Z","avatar_url":"https://github.com/TetraTau.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DataAddons\n## This library is currently maintained at the [TokyLib](https://github.com/maestro-denery/TokyLib) repository as a subproject, please check it if you want a maintained version!\n### Have a question or want to discuss this library? Join [our discord server](https://discord.gg/upTtNyvkNf)\nDataAddons is a library (or framework?) created for Minecraft providing comfortable abstractions making additions over already existing data, generally, **it is anti-pattern** ans **YOU SHOULDN'T USE IT** in normal programms. \\\nSo why is it needed? It is needed for loading, storing, registering and processing of *data addons* and composing them out of different \"sources\" of data.\n### Downloading\nFor now this library has no public repository artifact, so the only way you can use it is via jar.\n```kotlin\ndependencies {\n    compileOnly(files(\"DataAddons-vX.X.jar\"))\n}\n```\nLater We'll publish an artifact to a public repository.\n\n### Main concepts\nLet's call types which have some data along with Minecraft's one *data addons*. These types have both *native* (Minecraft's) data and *custom* (plugin dev's) data.\nUsually native data serializes when Minecraft server stops, so every developer who has ever made \"customX\" class makes the same pattern every time:\n1. On server startup:\n    * Execute a service looking for \"native\" data which has some tag distinguishes it from usual native data.\n    * Execute service looking for \"custom\" data from its storage. (DB, serialized object, etc.)\n    * Compose \"native\" and \"custom\" data into objects and \"hold\" these objects somewhere for further processing.\n2. While server running:\n    * Process \"held\" objects.\n    * Make new \"custom\" data from \"native\" if needed.\n3. On server shutdown:\n    * Mark \"custom\" data generated from \"native\" while server running with \"tag\" which saves along with native data. (generally - NBT tag)\n    * Save \"custom\" data to its storage. (DBs, serialization etc.)\n\nAs you probably noticed \"these objects\" which we construct, hold, process and store are *data addons*.\n\n### Quick Start.\nIn your JavaPlugin class make bootstrap.\n```java\npublic final class ExamplePlugin extends JavaPlugin {\n    final DataAddonBootstrap bootstrap = new DataAddonBootstrap();\n    \n    @Override\n    public void onEnable() {\n        bootstrap.setContainer(GlobalGroupContainer.getInstance()); // 1.\n        bootstrap.bootstrapRegistries(\"com.example.registries\"); \n        bootstrap.bootstrapDataAddons(\"com.example.addons\");\n    }\n\n    @Override\n    public void onDisable() {\n        logger.info(\"Disabling Tablight Entities plugin...\");\n    }\n}\n```\n1. It uses static `GlobalGroupContainer`, so you can interact with other registered types of other users, \\\nFor non-static `GroupContainer` write `new GroupContainer();`.\n\nWhat is `com.example.registries`?\nWe call \"registries\" `TypeRegistry`, `TypeHolder` and `StoreLoadController` classes, what each registry do we'll discuss later.\nIn `com.example.registries` you should define classes as:\n```java\n@Registry(\"example-group\")\npublic class ExampleTypeRegistry extends DefaultTypeRegistry {\n}\n```\n```java\n@Holder(\"example-group\")\npublic class ExampleTypeHolder extends ConcurrentTypeHolder {\n}\n```\n```java\n@Controller(\"example-group\")\npublic class ExampleController extends DefaultStoreLoadController {\n}\n```\nNote that for proper connection these classes should have the same \"group tag\".\nAnd You can override their methods to define your own behaviour for \"pre-processing\" and \"post-processing\".\n\nWhat is `com.example.addons`? \nIt is our data addons classes, we use them in \"registries\". You should define data addon class as:\n```java\n@DataAddon(\n\t\tidentifier = \"example\", // 1.\n\t\tgroupTag = \"example-group\", // 2.\n\t\tnativeClass = NativeExampleClass.class, // 3.\n\t\tlookup = ExampleDataAddonLookup.class\n)\npublic class ExampleDataAddon {\n\tprivate String someString;\n\tprivate String someNativeStringData;\n\n\tpublic String getSomeString() {\n\t\treturn someString;\n\t}\n\n\tpublic String getSomeNativeStringData() {\n\t\treturn someNativeStringData;\n\t}\n\n\tpublic void setSomeNativeStringData(String someNativeStringData) {\n\t\tthis.someNativeStringData = someNativeStringData;\n\t}\n\n\t@Store // 4\n\tpublic void store() {\n\t\tsomeString = \"store\";\n\t}\n\n\t@Load // 5\n\tpublic void load() {\n\t\tsomeString = \"load\";\n\t}\n}\n```\n1. `identifier` is a unique ID in group in which this data addon registered.\n2. `groupTag` is the same group tag as you registered \"registries\" before, \nit tells a bootstrap that this data addon processes in \"registries\" which have the same `groupTag` in their annotations.\n3. `nativeClass` is a class which have \"native\" data.\n4. `@Store` annotation identifies method which stores \"custom\" data to its storage.\n5. `@Load` annotation is the same as `@Store` but loads \"custom\" data from its storage.\n\nWhat is `lookup`?\n`lookup` is a class which looks for a \"native\" data (in Minecraft server) marked with some tag, and instantiates data addon with **ONLY** with \"native\" data,\nand puts this instance into a `TypeHolder`. \\\nLet's take a look at this example:\n```java\npublic class NativeExample {\n    private final String someNativeString;\n    public NativeDummy(String nativeString) {\n        this.someNativeString = nativeString;\n    } \n\n    public String getSomeNativeString() {\n        return someNativeString;\n    }\n}\n```\n```java\npublic class ExampleDataAddonLookup implements StoreLoadLookup\u003cExampleDataAddon, NativeExample\u003e {\n\n    private final Collection\u003cNativeExample\u003e nativeExamples = Lists.newArrayList( // we use a collection as an example, in plugins we use server data.\n            new NativeExample(\"native1\")\n    );\n\n    @Override\n    public Supplier\u003cCollection\u003cExampleDataAddon\u003e\u003e lookup() { // We return \"lazy\" Supplier returning collection instead of collection itself.\n        return () -\u003e nativeExamples.stream().map(nativeExamples -\u003e {\n            var dummy = new ExampleDataAddon();\n            dummy.setSomeNativeStringData(nativeExamples.getSomeNativeString());\n            return dummy;\n        }).toList();\n    }\n\n    @Override\n    public Stream\u003cNativeExample\u003e getNatives() { // We return \"lazy\" Stream instead of usual Collection.\n        return nativeDummiesContainer.stream();\n    }\n}\n```\nI've written it all, how do I use it? \\\nNow, let's back to `DataAddonBootstrap`, it has everything configured, look at this example:\n```java\npublic final class ExamplePlugin extends JavaPlugin {\n    @Override\n    public void onEnable() {\n        //...\n        var typeReg = bootstrap.getRegistry(ExampleTypeRegistry.class);\n        var typeHolder = bootstrap.getRegistry(ExampleTypeHolder.class);\n        var controller = bootstrap.getRegistry(ExampleController.class);\n        \n        controller.lookupAndLoad(ExampleDataAddon.class); // it looks for this class using its lookup and loads \"custom\" data into \"looked up\" objects instances.\n        Collection\u003cExampleDataAddon\u003e heldExamples = typeHolder.getHeld(ExampleDataAddon.class); // You can obtain all configured data addons using holder.\n        heldExamples.forEach(heldExample -\u003e {\n            System.out.println(\"Handling: \" + heldExample.toString()); // And you handle them.\n        });\n        controller.store(ExampleDataAddon.class); // And you store it back.\n    }\n}\n```\nThat's all! For further reading, read the java docs in files. This Library has more functional I didn't show in this quick start, \nsuch as LMAX Disruptor events in TypeHolder, or manual bootstrapping without annotations.\n\nContributing\n----\nContributions are welcome! But you need to understand that your code will be used on TabLight's servers, so very slow or very hacky contributions will be denied.  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftetratau%2Fdataaddons","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftetratau%2Fdataaddons","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftetratau%2Fdataaddons/lists"}