Skip to content

Porting a Fabric mod by hand

Status: Implemented as a mapping to the API that exists; rows marked planned name CLight features that aren’t in the game yet.

Celestial does not run, convert or translate Fabric or Quilt mods, and there is no conversion tool. This guide maps Fabric concepts to CLight so you can rewrite your own mod. Read Porting existing mods to CLight: overview first.

Fabric mods are often small, data-heavy and mixin-based: the data carries over as-is, and many mixins map to an event, a behaviour or a CLight mixin.

Project and metadata

Fabric CLight
fabric.mod.json clight.mod.json (Writing the manifest)
id, version id (keep it: it’s your namespace), version (SemVer)
environment: *, client, server side: both, client, server; plus sync
entrypoints.main / client / server One entry point; lifecycle.common_setup, client_setup, server_setup
depends, recommends, breaks depends, optional, breaks
mixins config files "mixins" in clight.mod.json or cl.mixin.inject / CL_MIXIN, on named game targets
accessWidener CLight accessors where the game has them
Gradle + Loom clight build (C/C++), nothing (Lua)
src/main/resources/assets, data assets/<ns>/, datapack/<ns>/
runClient clight run

Entry points and lifecycle

Fabric CLight
ModInitializer.onInitialize The entry point (subscriptions, mixins) plus lifecycle.register for content
ClientModInitializer.onInitializeClient lifecycle.client_setup
DedicatedServerModInitializer lifecycle.server_setup
Server lifecycle events lifecycle.server_starting, server_started, server_stopping, server_stopped
World load and unload lifecycle.level_load, level_unload
Data pack reload lifecycle.data_reloaded

Registration happens only during lifecycle.register.

Registration

Fabric CLight
Registry.register(Registries.BLOCK, ...) cl_block_register / cl.blocks.register{...} (vanilla-backed)
Block settings (strength, sounds, luminance, slipperiness, velocityMultiplier, jumpVelocityMultiplier) destroy_time, sound_type, light_emission, friction, speed_factor, jump_factor; shape and tool from the base block
A Block subclass with overridden methods A behaviour table: use_without_item, attack, random_tick (more entries planned)
Block states and properties planned (one state per block today)
BlockItem item in the block descriptor
Item settings (maxCount) max_stack_size; other components come from the base item (components from code planned)
Item use cl_item_behavior.use
Item groups creative_tab on the item (a vanilla tab; the item goes after the vanilla items). Custom tabs planned
BlockEntityType, EntityType, ScreenHandlerType, SoundEvent, ParticleType, data components planned (sounds work through sounds.json; menus through vanilla chest menus)

Events (callbacks)

Fabric callback (concept) CLight
Server / world / client tick tick.server_pre/post, tick.level_pre/post, tick.client_pre/post
Use block player.interact_block, or your block’s use_without_item
Use item player.interact_item, or your item’s use
Attack entity player.attack_entity
Player block break block.break (cancellable; xp, drop_items mutable)
Block place block.place
Join / disconnect player.join, player.leave
After respawn player.respawn
Living damage, death entity.damage (amount mutable), entity.death
Command registration server.command_register
Chat player.chat, client.chat_received, client.chat_send
HUD render callback cl_hud_layer_register
Screen open client.screen_open
Loot table modification, tooltip callback, world render events planned

The full list: Complete event list.

Mixins

Ask what each mixin changes, then use the table in Porting existing mods to CLight: overview. Many become an event with cancel or a mutable field; the rest become CLight mixins on the game’s named targets (head, return, value, call, wrap, overwrite), which compose by priority.

Networking

Fabric CLight
Custom payload types + registry cl_payload_register (an id, a direction, byte buffers)
Send to a player / to the server cl_net_send_to_player, cl_net_send_to_server
Receivers The handler runs on the receiving side’s tick
Send to players tracking an entity, “can send” checks, configuration tasks planned

Client side

Fabric CLight
Block render layer map From the base block today (render_layer is stored)
Key binding helper cl_keybind_register (in Controls, rebindable)
Screens cl_screen_open with vanilla widgets, or RmlUi documents
HUD cl_hud_layer_register
Colour providers, entity and block entity renderers, particle factories, baked models planned

Data and config

Fabric CLight
Data attachments, persistent state typed attachments (cl.attachment, cl_attachment_register) and saved data; files in data/ through cl.fs / cl_fs_*
Config libraries + Mod Menu cl_config_define_* and the Mods screen’s Config page
Biome modifications, custom features planned; data-pack worldgen JSON for vanilla features

Common conventions

  • The shared c: tag namespace works the same way; keep using it.
  • Keep your lang keys (item.<ns>.<path>, block.<ns>.<path>).

A porting example, in words

A Fabric mod “Gem Shop” has: a gem item, a shop block that opens a chest GUI, a /gems command, a HUD counter and a config.

  1. Copy assets/ into assets/gem_shop/ and the data into datapack/gem_shop/.
  2. Register the gem item (base minecraft:emerald) and the shop block (base minecraft:barrel) in lifecycle.register.
  3. The shop block’s use_without_item opens a locked chest menu with cl_menu_open; clicks take and give items (A shop block (block + menu)).
  4. /gems becomes a command tree (A command with arguments and permissions); the HUD counter a HUD layer that counts on the client tick.
  5. The config becomes cl.config.open("common") with the Config page in the Mods screen.
  6. side: both, sync: optional: vanilla clients see a barrel and an emerald, and the shop still works for them.