{"id":17016669,"url":"https://github.com/kneelawk/common-events","last_synced_at":"2026-04-29T08:31:43.716Z","repository":{"id":232849305,"uuid":"784530704","full_name":"Kneelawk/Common-Events","owner":"Kneelawk","description":"Cross-platform Minecraft event library","archived":false,"fork":false,"pushed_at":"2025-04-26T23:05:20.000Z","size":491,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"1.x-1.21","last_synced_at":"2026-01-13T04:49:14.101Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kneelawk.png","metadata":{"files":{"readme":"README.md","changelog":"changelogs/changelog-v0.1.0+1.20.5.md","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":"2024-04-10T03:02:28.000Z","updated_at":"2025-04-26T23:00:46.000Z","dependencies_parsed_at":"2024-04-21T21:52:43.539Z","dependency_job_id":"43e784af-e89e-43bd-8a3e-c4e61f433299","html_url":"https://github.com/Kneelawk/Common-Events","commit_stats":null,"previous_names":["kneelawk/common-events"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/Kneelawk/Common-Events","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kneelawk%2FCommon-Events","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kneelawk%2FCommon-Events/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kneelawk%2FCommon-Events/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kneelawk%2FCommon-Events/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kneelawk","download_url":"https://codeload.github.com/Kneelawk/Common-Events/tar.gz/refs/heads/1.x-1.21","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kneelawk%2FCommon-Events/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32417399,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T06:29:02.080Z","status":"ssl_error","status_checked_at":"2026-04-29T06:29:00.631Z","response_time":110,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-10-14T06:34:07.113Z","updated_at":"2026-04-29T08:31:43.709Z","avatar_url":"https://github.com/Kneelawk.png","language":"Java","funding_links":["https://ko-fi.com/kneelawk"],"categories":[],"sub_categories":[],"readme":"# Common-Events\n\n[![Github Release Status]][Github Release] [![Maven Status]][Maven] [![Javadoc Badge]][Javadoc] [![Discord Badge]][Discord] [![Ko-fi Badge]][Ko-fi]\n\n[Github Release Status]: https://img.shields.io/github/v/release/Kneelawk/Common-Events?include_prereleases\u0026sort=semver\u0026style=flat-square\u0026logo=github\n\n[Github Release]: https://github.com/Kneelawk/Common-Events/releases/latest\n\n[Maven Status]: https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fmaven.kneelawk.com%2Freleases%2Fcom%2Fkneelawk%2Fcommon-events%2Fcommon-events-xplat-intermediary%2Fmaven-metadata.xml\u0026style=flat-square\u0026logo=apachemaven\u0026logoColor=blue\n\n[Maven]: https://maven.kneelawk.com/#/releases/com/kneelawk/common-events\n\n[Javadoc Badge]: https://img.shields.io/badge/-javadoc-green?style=flat-square\n\n[Javadoc]: https://maven.kneelawk.com/javadoc/releases/com/kneelawk/common-events/common-events-xplat-intermediary/latest\n\n[Discord Badge]: https://img.shields.io/discord/988299232731607110?style=flat-square\u0026logo=discord\n\n[Discord]: https://discord.gg/6vgpHcKmxg\n\n[Ko-fi Badge]: https://img.shields.io/badge/ko--fi-donate-blue?style=flat-square\u0026logo=kofi\n\n[Ko-fi]: https://ko-fi.com/kneelawk\n\nFully-featured, cross-platform Minecraft event library\n\nThis library is based on [QSL]'s event system, but makes use of class scanning like forge uses, to allow for easy\nregistration of event listeners.\n\n[QSL]: https://github.com/QuiltMC/quilt-standard-libraries\n\n## Getting Common-Events\n\nYou can get Common-Events by adding the following to your `build.gradle` file:\n\n```groovy\nrepositories {\n    maven {\n        name = \"Kneelawk\"\n        url = \"https://maven.kneelawk.com/releases/\"\n    }\n}\n\ndependencies {\n    // If using loom:\n    modImplementation \"com.kneelawk.common-events:common-events-\u003cplatform\u003e:\u003cversion\u003e\"\n    include \"com.kneelawk.common-events:common-events-\u003cplatform\u003e:\u003cversion\u003e\"\n\n    // If using userdev:\n    implementation \"com.kneelawk.common-events:common-events-\u003cplatform\u003e:\u003cversion\u003e\"\n    jarJar \"com.kneelawk.common-events:common-events-\u003cplatform\u003e:\u003cversion\u003e\"\n}\n```\n\n## Example\n\nImagine a callback interface like so:\n\n```java\n@FunctionalInterface\npublic interface MyCallback {\n    void doThing(String str);\n}\n```\n\nYou can create an event for this interface like so:\n\n```java\npublic static final Event\u003cMyCallback\u003e MY_CALLBACK_EVENT = Event.create(MyCallback.class, callbacks -\u003e str -\u003e {\n    for (MyCallback callback : callbacks) {\n        callback.doThing(str);\n    }\n});\n```\n\nThen, potentially in a different mod entirely, you could add a listener for this callback:\n\n```java\n@Scan\npublic class MyListener {\n    @Listen(MyCallback.class)\n    public static void onDoThing(String str) {\n        System.out.println(\"Doing thing: \" + str + \"!\");\n    }\n}\n```\n\nSince version `1.2.0`, scanned listener methods are not required to have all or any of the arguments in the callback\ninterface's method. This means you can create simple event listeners like so:\n\n```java\n@Scan\npublic class MyOtherListener {\n    @Listen(MyCallback.class)\n    public static void onDoThing() {\n        System.out.println(\"Doing thing without arguments!\");\n    }\n}\n```\n\nThis will insert the `onDoThing` method into the `MY_CALLBACK_EVENT` event as a lambda method reference. Note that the\nlistener method does not have to have the same name as the callback interface method.\n\nYou can then invoke the event by getting the event's invoker and calling your callback method on it:\n\n```java\npublic static void invokeMyCallback() {\n    MY_CALLBACK_EVENT.invoker().doThing(\"test\");\n}\n```\n\n## How to Register Listeners\n\nLike in Fabric and Quilt's event systems, callback listeners can be registered directly on event objects. However,\ncallback listeners can also be registered via annotations, ensuring that callbacks are called without having to worry\nabout manual registration.\n\n* First, create a `common-events.json` file in your mod's root directory with the following content:\n    ```json\n    {\n        \"scan\": true\n    }\n    ```\n* Then annotate a class within your mod with `@Scan`. This will allow that class to be searched for listening\n  methods.\n* Finally, annotate a `public static` method with `@Listen(\u003ccallback-interface\u003e.class)`. The listening method *must*\n  have the same signature as the callback interface's single method.\n\n### Class-Loading and Static Initializers\n\nCommon-Events will scan all mods at the earliest possible occasion. However, this will not cause class-loading\nof `@Scan`-annotated classes. These classes will only be loaded and statically initialized when an event is created\nwith a callback-interface type that one of the class's methods listens for, unless the classes are loaded by something\nelse sooner. This allows `@Scan`-annotated classes to safely listen for client-sided events without having to worry\nabout accidentally getting loaded on dedicated servers.\n\n### Only Scanning Specific Classes\n\nThe `scan` field of the `common-events.json` file can instead contain an array of classes to scan for event listeners,\nlike so:\n\n```json\n{\n    \"scan\": [\n        \"com.kneelawk.example.ExampleListener\",\n        \"com.kneelawk.example.client.ExampleClientListener\"\n    ]\n}\n```\n\nClasses referenced here also have the same kind of delayed initialization as classes annotated with `@Scan`. Classes\nhere can additionally be annotated with `@Scan` in order to limit them to being loaded only on the client or the server.\n\n## Creating Events\n\nNot all callback interfaces have to have a single method or even have to be interfaces. However, having your callback\ninterfaces be functional interfaces does make event implementation easier.\n\nFurthermore, if your event simply needs to invoke all listeners with the given arguments, then you can construct the\nevent using `createSimple`, without even having to define an implementation, as one is generated for you:\n\n```java\npublic static final Event\u003cMyCallback\u003e MY_CALLBACK_EVENT = Event.createSimple(MyCallback.class);\n```\n\nThe simple generated implementation does not support fancy things like event cancellation or return values.\n\n### Callback Argument Recommendation\n\nWhen creating a new callback interface, it is recommended that you use a custom type as your callback method's single\nargument, then have each actual argument you want to pass to listeners be available through methods on that custom type.\nThis allows you to add new arguments without breaking existing listeners. And if you need to remove an argument,\nlisteners will get a compile-time error instead of a runtime error.\n\n```java\n@FunctionalInterface\npublic interface MyCallback {\n    void onCallback(Context ctx);\n\n    interface Context {\n        String actualArg1();\n\n        long actualArg2();\n    }\n}\n```\n\nThen a listener would look like:\n\n```java\n@Scan\npublic class MyListener {\n    @Listen(MyCallback.class)\n    public static void onMyCallback(MyCallback.Context ctx) {\n        // use ctx.actualArg1() and ctx.actualArg2() here\n    }\n}\n```\n\n**Note:** since version `1.2.0`, events will accept scanned listener methods that have fewer arguments than the callback\ninterface method, meaning that you can add arguments to your callback interface without breaking scanned listener\nmethods, but it will still break manually-registered listeners.\n\n## Event Buses\n\n`EventBus`es are a convenience collection of `Event`s. They allow you to register things to several events at once.\nEvent buses have two sets of listener registration methods: `registerListener` and `registerListeners`. Note that the\nsecond set of methods registers multiple listeners at once.\n\nThe `registerListener` methods require the callback interface class the listener is being registered for, the listener\nimplementation itself, and a few optional arguments like qualifier, phase, and key.\n\nThe `registerListeners` methods require just a listener object and an optional key. If the listener object is an\ninstance of a class, then that instance is scanned for public instance methods annotated with `@Listen` and then those\nmethods are registered as method-references to their associated events. This registers all annotated methods, including\nthose from super-classes. If the listener object is a `Class` itself, then that class is scanned for public static\nmethods annotated with `@Listen` and then those methods are registered as method-references to their associated events.\nUnlike with instance registration, this does **not** register static methods in super-classes.\n\nAn event bus can be created like so:\n\n```java\npublic static final EventBus MY_BUS =\n        EventBus.builder(ResourceLocation.fromNamespaceAndPath(\"mod_id\", \"bus_name\")).build();\n```\n\n### Adding Events\n\nThere are a couple ways to add events to event buses. The first is a straightforward call to `addEvent`:\n\n```java\npublic static final Event\u003cMyCallback\u003e MY_EVENT = Event.createSimple(MyCallback.class);\n\nstatic {\n    MY_BUS.addEvent(MY_EVENT);\n}\n```\n\nHowever, this is only useful if we can be certain that the `MY_EVENT` is added to `MY_BUS` before any listeners are\nregistered to `MY_BUS`. This is because `EventBus`es only register listeners to events that are currently in the bus.\nCurrently, if an event is added to the bus after a listener is registered to that bus, then that listener will not be\nregistered to the newly added event. Though this may change in future versions of Common Events.\n\nEvent buses will, by default, fire an event when they are created. This can be used to make sure that the bus contains\nan event by the time listeners get registed to it:\n\n```java\n@Listen(EventBus.Created)\npublic static void onBusCreated(EventBus bus) {\n    if (bus.getName().equals(ResourceLocation.fromNamespaceAndPath(\"mod_id\", \"bus_name\"))) {\n        bus.addEvent(MY_EVENT);\n    }\n}\n```\n\nThe other way of adding events to event buses is via the `@BusEvent` annotation. This annotation will automatically\nregister the annotated event to the listed buses. This annotation can be used to register an event to multiple buses at\nonce.\n\n```java\n@BusEvent({\"mod_id:bus_name\", \"other_mod:other_bus\"})\npublic static final Event\u003cMyCallback\u003e MY_EVENT = Event.createSimple(MyCallback.class);\n```\n\nThis method of adding an event to an event bus is the most efficient, but it is also the least configurable.\n\n### The Main Bus\n\nCommon Events supplies an existing main bus. This main bus can be used by adding a dependency on the following:\n\n```groovy\nmodImplementation \"com.kneelawk.common-events:common-events-main-bus-\u003cplatform\u003e:\u003cversion\u003e\"\ninclude \"com.kneelawk.common-events:common-events-main-bus-\u003cplatform\u003e:\u003cversion\u003e\"\n```\n\nYou can add events to the main bus by annotating them like so:\n\n```java\n@BusEvent(CommonEventsMainBus.NAME)\npublic static final Event\u003cMyCallback\u003e MY_CALLBACK_EVENT = Event.createSimple(MyCallback.class);\n```\n\nUsing the `CommonEventsMainBus.NAME` constant here will not cause class-loading, so using it in an annotation is fine,\nbecause its value gets baked into the annotation at compile-time.\n\n## Unregistering Listeners\n\nWhen a listener is registered, it can optionally be registered with a key object. This key object is what is used to\nfind the listener when you want to unregister the listener. There can only be one of each key object registered in each\nevent.\n\n```java\npublic static void registerMyListener() {\n    MY_EVENT.registerKeyed(myKeyObject, ctx -\u003e {\n        // ...\n    });\n}\n\npublic static void unregisterMyListener() {\n    MY_EVENT.unregister(myKeyObject);\n}\n```\n\nThis also works for event buses.\n\n## Kotlin Adapter\n\nThe kotlin adapter can be used by adding a dependency on the following:\n\n```groovy\nmodImplementation \"com.kneelawk.common-events:common-events-kotlin-\u003cplatform\u003e:\u003cversion\u003e\"\ninclude \"com.kneelawk.common-events:common-events-kotlin-\u003cplatform\u003e:\u003cversion\u003e\"\n```\n\nAnd by adding an `adapter` statement to your `common-events.json` like so:\n\n```json\n{\n    \"adapter\": \"kotlin\",\n    \"scan\": true\n}\n```\n\nThe kotlin adapter allows you to mark `object`s with the `@Scan` annotation without having to mark your `@Listen`\n-annotated methods with `@JvmStatic`. This also allows `@BusEvent`-annotated kotlin fields to be discovered, which is\nimpossible with the java adapter.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkneelawk%2Fcommon-events","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkneelawk%2Fcommon-events","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkneelawk%2Fcommon-events/lists"}