[Lib] CustomItemLibrary - Custom items without client-side mods

:sunrise_over_mountains: CustomItemLibrary - Custom items without client-side mods

CustomItemLibrary is a library for Sponge plugins, which makes the creation of fake custom blocks and items easy. It generates a resourcepack to be used on your server. If you are not a plugin developer and you need this library for the plugins you use to work, just download it below and install it like you would install any other plugin. This library alone does not provide any gameplay features.

:camera: Preview

A custom material being placed, creating a custom block on the ground as a result.

:open_file_folder: Downloads

Latest

Old

:pencil: About

:pager: Commands and Permissions

  1. /cil
    • Required permission: customitemlibrary.command.customitemlibrary
    • Lists available subcommands
  2. /cil give <Player> <Item ID> [Quantity]
    • Required permission: customitemlibrary.command.customitemlibrary.give
    • Gives the target player the specified custom item
  3. /cil setBlock <Block ID>
    • Required permission: customitemlibrary.command.customitemlibrary.setblock
    • Changes the block you are looking at to the specified custom block
  4. /cil resourcepack
    • Required permission: customitemlibrary.command.customitemlibrary.resourcepack
    • Generates the resourcepack for the players to use; it is located at config/customitemlibrary/resourcepack

:hammer_pick: Integration

In your @Plugin annotation, add a dependency to this library. For example:

@Plugin(
        id = "limefun",
        name = "LimeFun",
        description = "Bring things from mods to a server without mods!",
        authors = {
                "Limeth"
        },
        // This is what you should add:
        dependencies = {
                // Require version 0.2.3 of CustomItemLibrary or newer
                @Dependency(id = "customitemlibrary", version = "[0.2.3,)")
        }
)

Next, add the dependency to your build.gradle:

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

dependencies {
    // ...
    compile 'com.github.Limeth:CustomItemLibrary:v0.2.3'
}

You should now be able to access the CustomItemLibrary classes in your code. In order to get the API class, do the following:

CustomItemService service = Sponge.getServiceManager().provide(CustomItemService.class)
        .orElseThrow(() -> new IllegalStateException("Could not access the CustomItemLibrary service."));

Classes extending CustomFeatureDefinition are like ItemTypes or BlockTypes. They define the behavior, but they themselves don’t do anything. CustomFeatures, on the other hand, are classes that wrap around a DataHolder and a CustomFeatureDefinition in order to pair them together and make manipulation easier.

Definitions may be registered after the GamePostInitializationEvent with Order#AFTER_PRE was called.
Custom features may be created and used after the GameLoadCompleteEvent with Order#DEFAULT was called.

I highly recommend reading the javadocs of CustomItemService.java and all of the CustomFeatureDefinition builders in CustomFeatureDefinition.java.

Feature overview

  • CustomTool
    • Builder: CustomFeatureDefinition#itemToolBuilder()
    • The itemStackSnapshot must be an item with durability
    • The tool does not lose the abilities the itemStackSnapshot has; for example, a custom tool with itemStackSnapshot as ItemTypes.SHEARS#getTemplate() will still be able to harvest cobwebs and leaves
    • Not stackable
    • Item models at assets/%PLUGIN_ID%/models/tools/%MODEL%.json
  • CustomMaterial
    • Builder: CustomFeatureDefinition#itemMaterialBuilder()
    • The type of itemStackSnapshot must be ItemTypes#SKULL
    • Stackable
    • Item textures at assets/%PLUGIN_ID%/textures/materials/%MODEL%.png; a 64x64 skin, or 64x16 with just the head part
    • Places the CustomBlock with the same typeId, by default
  • CustomBlock
    • Builder: CustomFeatureDefinition#simpleBlockBuilder()
    • Represented as a barrier block with an invisible armor stand equipped with an item with the model
    • Custom blocks are not opaque (light shines through them)
    • Minecraft cannot optimize rendering for these custom blocks, so it is highly recommended, that you only create custom blocks which would be placed sparingly – do not define blocks which are intended for building only.
    • Block models at assets/%PLUGIN_ID%/models/blocks/%MODEL%.json
    • When broken, by default drops the CustomMaterial with the same typeId

Example: Creating custom tools

public static CustomToolDefinition magicWandDefinition;

@Listener
public void onGamePostInitializationDefault(GamePostInitializationEvent event) {
    // During this phase, plugins using this library should register their custom item definitions.
    service.register(
            magicWandDefinition = CustomFeatureDefinition.itemToolBuilder()
                    // Required fields:
                    .plugin(plugin)  // The owner plugin
                    .typeId("magic_wand")  // The definition id
                    .itemStackSnapshot(ItemTypes.SHEARS.getTemplate())  // Used to build magic wands
                    .defaultModel("magic_wand_primary")  // Models are located at `assets/%PLUGIN_ID%/models/tools/%MODEL%.json` in the JAR
                    // Optional fields:
                    .additionalModel("magic_wand_secondary")
                    .additionalAsset("textures/tools/magic_wand_texture.png")
                    .build()
    );
}

@Listener
public void onInteractBlock(InteractBlockEvent.Secondary.MainHand event, @Root Player player) {
    CustomTool magicWand = magicWandDefinition.createItem(event.getCause());
    ItemStack magicWandItemStack = magicWand.getDataHolder();

    // Give the player a magic wand when they right-click a block
    player.setItemInHand(HandTypes.MAIN_HAND, magicWandItemStack);
}

@Listener
public void onInteractItem(InteractItemEvent.Primary.MainHand event, @Root Player player) {
    player.getItemInHand(HandTypes.MAIN_HAND).flatMap(service::getItem)
            // Run the following code block if the player is holding a custom item
            .ifPresent(customItem -> {
                CustomItemDefinition definition = customItem.getDefinition();

                // Do not continue unless the player is holding a magic wand
                if(definition != magicWandDefinition)
                    return;

                CustomTool magicWand = (CustomTool) customItem;

                // Change the model and update the inventory
                magicWand.setModel("magic_wand_secondary");
                player.setItemInHand(HandTypes.MAIN_HAND, magicWand.getDataHolder());
            });
}

Example plugin

For a full example, check out the sources of the LimeFun plugin.

Support

If you have any suggestions or problems you came across, or just want to ask about something regarding this plugin, feel free to do so here, or if I’m online, feel free to message me on the Sponge IRC. I’ll be happy to help.

9 Likes

So, how exactly does this even work without client side mods?

EDIT: Resourcepacks, ay?

Yes, resourcepacks. Forgot to make that part more clear.

Neat. Not quite completely custom blocks but pretty damn close. Love it. I had a similar idea to this once but I forgot about it ages ago. Glad to see a good implementation.

1 Like

Is the resource pack sent by the plugin automatically or does each plugin have to send their own resource pack?

As far as I can tell, the library handles sending of resources.

The resources are not sent automatically, I am not aware of that being possible. The administrator has to generate the resourcepack manually using a command and then upload it somewhere and link it in the server.properties file.

@Limeth This is amazing!!! You plan to use it in the mechanisms?
Edit: Already I see :slight_smile:

1 Like

Well… you can send them automatically

Yes but you must provide an URI and probably an http server to host the file.

You can just host the file in the config directory and use the file:\\ protocol… And if I’m not mistaken, Paths can be converted to URI’s.

Edit: yep
Path#toUri

Yes but the client will think that the file is located on his disk and not on the server.

#v0.3.0 changelog

  • Tools now get damaged when breaking a custom block;
  • Generalized harvestingType on custom blocks by replacing it with correctToolPredicate. This lets you hand-pick the range of applicable tools.

v0.4.0 changelog

  • Fixed a bug with the resourcepack model priority, where sometimes invalid models would be displayed
  • Added block damage indicators. You can use the default cubic one, or make CIL generate a proper one for your detailed 3D model using the #generateCustomIndicatorModels(boolean) builder method.
  • Tools are now damaged, decreasing their durability, when breaking a custom block
2 Likes