Pyrofab/Cardinal-Components-API


Component API for data-driven development https://minecraft.curseforge.com/projects/318449

Download


Step 1. Add the JitPack repository to your build file

Add it in your root settings.gradle at the end of repositories:

	dependencyResolutionManagement {
		repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
		repositories {
			mavenCentral()
			maven { url 'https://jitpack.io' }
		}
	}

Add it in your settings.gradle.kts at the end of repositories:

	dependencyResolutionManagement {
		repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
		repositories {
			mavenCentral()
			maven { url = uri("https://jitpack.io") }
		}
	}

Add to pom.xml

	<repositories>
		<repository>
		    <id>jitpack.io</id>
		    <url>https://jitpack.io</url>
		</repository>
	</repositories>

Add it in your build.sbt at the end of resolvers:

 
    resolvers += "jitpack" at "https://jitpack.io"
        
    

Add it in your project.clj at the end of repositories:

 
    :repositories [["jitpack" "https://jitpack.io"]]
        
    

Step 2. Add the dependency

	dependencies {
		implementation 'com.github.pyrofab:cardinal-components-api:2.0.1-pre'
	}
	dependencies {
		implementation("com.github.pyrofab:cardinal-components-api:2.0.1-pre")
	}
	<dependency>
	    <groupId>com.github.pyrofab</groupId>
	    <artifactId>cardinal-components-api</artifactId>
	    <version>2.0.1-pre</version>
	</dependency>

                            
    libraryDependencies += "com.github.pyrofab" % "cardinal-components-api" % "2.0.1-pre"
        
        

                            
    :dependencies [[com.github.pyrofab/cardinal-components-api "2.0.1-pre"]]
        
        

Readme


Cardinal-Components-API

A components API for Fabric that is easy, modular, and fast.

Adding the API to your buildscript (loom 0.2.5):

Note: as no 2.0 version of Cardinal Components API is available on the main maven, this fork offers an alternative using Jitpack

repositories {
    maven {
        name = "JitPack"
        url "https://jitpack.io"
    }
}

dependencies {
    // Replace modImplementation with modApi if you expose components in your own API
    modImplementation "com.github.Pyrofab:Cardinal-Components-API:<VERSION>"
}

The include configuration will not work with JitPack and the master jar! See how to include individual modules below.

You can find the current version of the API in the releases tab of the repository on Github.

Cardinal Components API is split into several modules. The main artifact bundles every module, but often all are not required for a project. To depend on a specific module, use the dependency string com.github.Pyrofab.Cardinal-Components-API:<MODULE>:<VERSION>. Module names can be found below.

Example:

// Adds an API dependency on the base cardinal components module
modApi "com.github.Pyrofab.Cardinal-Components-API:cardinal-components-base:<VERSION>"
// Includes the base module as a Jar-in-Jar dependency (optional)
include "com.github.Pyrofab.Cardinal-Components-API:cardinal-components-base:<VERSION>"

Usage

To get started, you only need 2 things: an interface extending Component, and a class implementing this interface.

Minimal code example:

interface IntComponent extends Component {
    int getValue();
}

class RandomIntComponent implements IntComponent {
    private int value = (int) (Math.random() * 20);
    @Override public int getValue() { return this.value; }
    @Override public void fromTag(CompoundTag tag) { this.value = tag.getInt("value"); }
    @Override public CompoundTag toTag(CompoundTag tag) { tag.putInt("value", this.value); return tag; }
}

All that is left is to actually use that component.

Components are provided by various objects through the ComponentProvider interface. To interact with those, you need to register your component type, using ComponentRegistry.registerIfAbsent; the resulting ComponentType instance is used as a key for component providers.

public static final ComponentType<IntComponent> MAGIK = 
        ComponentRegistry.INSTANCE.registerIfAbsent(new Identifier("mymod:magik"), IntComponent.class);

public static void useMagik(ComponentProvider provider) {
    // Retrieve a provided component
    int magik = MAGIK.get(provider).getValue();
    // Or, if the provider is not guaranteed to provide that component:
    int magik = MAGIK.maybeGet(provider).map(IntComponent::getValue).orElse(0);
    // ...
}

Note: a component class can be reused for several component types

Cardinal Components API offers component provider implementations for a few vanilla types, each in its own module:

Entities

Components can be added to entities of any type (modded or vanilla) by registering an EntityComponentCallback. Entity components are saved automatically with the entity. Synchronization must be done either manually or with help of the SyncedComponent and EntitySyncedComponent interfaces. Cardinal Components also provides mechanisms for handling player respawns. By default, components get copied when players return from the End, but mods can customize that behaviour through RespawnCopyStrategy and PlayerCopyCallback to copy all or part of the component data.

Example:

// Add the component to every instance of PlayerEntity
EntityComponentCallback.event(PlayerEntity.class).register((player, components) -> components.put(MAGIK, new RandomIntComponent()));
// Ensure the component's data is copied when keepInventory is enabled (Optional)
EntityComponents.registerRespawnCopyStrat(MAGIK, RespawnCopyStrategy.INVENTORY);

module: cardinal-components-entity

Item Stacks

Components can be added to stacks of any item (modded or vanilla) by registering an ItemComponentCallback. Item stack components are saved and synchronized automatically.

Notes:

  • ItemStack equality: stack equality methods areTagsEqual and isEqualIgnoreDamage are modified to check component equality. If you have issues when attaching components to item stacks, it usually means you forgot to implement a proper equals check on your component.
  • Empty ItemStack: empty item stacks never expose any components, no matter what was originally attached to them.

Example:

// Add the component to every stack of wasted diamonds
ItemComponentCallback.event(Items.DIAMOND_HOE).register((stack, components) -> components.put(MAGIK, new RandomIntComponent()));

module: cardinal-components-item

Worlds

Components can be added to any world by registering a WorldComponentCallback. World components are saved automatically with the world. Synchronization must be done either manually or with help of the SyncedComponent and WorldSyncedComponent interfaces.

Example:

// Add the component to every world
WorldComponentCallback.EVENT.register((world, components) -> components.put(MAGIK, new RandomIntComponent()));

module: cardinal-components-world

Levels

Components can be added to LevelProperties objects by registering a LevelComponentCallback. Level properties are shared between every world in a server, making them useful to store global data. Level components are saved automatically with the global state. Synchronization must be done either manually or with help of the SyncedComponent and LevelSyncedComponent interfaces.

Example:

// Add the component to level properties
LevelComponentCallback.EVENT.register((levelProperties, components) -> components.put(MAGIK, new RandomIntComponent()));

module: cardinal-components-level

Chunks

Components can be added to chunks by registering a ChunkComponentCallback. Chunk components are saved automatically with the chunk. Synchronization must be done either manually or with help of the SyncedComponent and ChunkSyncedComponent interfaces.

Notes:

  • EmptyChunk: empty chunks never expose any components, no matter what was originally attached to them. As such, when chunk components are queried on the client, one should make sure the chunk is loaded, or use ComponentType#maybeGet to retrieve a component.

Example:

// Add the component to every chunk in every world
ChunkComponentCallback.EVENT.register((chunk, components) -> components.put(MAGIK, new RandomIntComponent()));

module: cardinal-components-chunk

Blocks

Blocks actually implement the BlockComponentProvider interface instead of the regular ComponentProvider. Custom blocks may re-implement that interface themselves to provide components independently of the presence of a BlockEntity. Usually the block simply proxies its Block Entity, however the Block Entity does not need to implement BlockComponentProvider if the block already has a custom implementation. Block components can be slightly less convenient to provide as they require their own implementations, but several utility classes are available to help.

Components are entirely compatible with LibBlockAttributes' attributes. Since Component is an interface, any attribute instance can easily implement it. Conversely, making an Attribute for an existing Component is as simple as calling Attributes.create(MyComponent.class).

module: cardinal-components-block

Test Mod

A test mod for the API is available in this repository, under src/testmod. It makes uses of most features from the API. Its code is outlined in a secondary readme.