Skip to content

Mod API

Fxbixn03 edited this page Oct 10, 2026 · 1 revision

BlockFactory has a small public API for other server mods. Through it you can read the factory state of a world (which building sits on a block, which research is done, how many research points a team has, whether the Great Portal is open) and react to events such as finished research. This page is for mod developers; players can skip it.

What is public

Everything you may use lives in the package com.mutigeraffe.blockfactory.api and its subpackage api.event:

Type What it is
BlockFactoryApi Entry point with all queries
PartInfo A building in the world: type, origin block, rotation
api.event.ResearchCompleted Event: a research is done
api.event.CreatureSummoned Event: a Portal of the Ancient summoned a creature
api.event.GreatPortalActivated Event: a team filled the Great Portal's bar

Everything else in the BlockFactory jar, above all the package com.mutigeraffe.blockfactory.internal, is off limits. It changes whenever the mod needs it to, without notice and without a version bump that warns you. If you need something that only internal has, open an issue (see Reporting Bugs) and ask for it in the API.

Depending on BlockFactory

BlockFactory's plugin identifier is group com.mutigeraffe, name blockfactory. Declare it as a dependency in your manifest.json:

"Dependencies": {
    "com.mutigeraffe:blockfactory": "*"
}

You can use a version range instead of *, written like Hytale's own ranges (for example ">=1.0.0 <2.0.0").

The dependency does two things. The server loads BlockFactory first, so its setup is finished before your setup() runs. And your plugin can see BlockFactory's classes at runtime.

To compile, put the BlockFactory jar on your compile classpath only (Gradle: compileOnly). Do not bundle or shade it into your own jar: the classes would then exist twice, and your copy would see an empty API.

Getting the API

Call BlockFactoryApi.get() in your setup() or later. Before BlockFactory is set up it throws an IllegalStateException; with the manifest dependency above that cannot happen.

import com.mutigeraffe.blockfactory.api.BlockFactoryApi;
import java.util.logging.Level;

public final class MyPlugin extends JavaPlugin {

    public MyPlugin(JavaPluginInit init) {
        super(init);
    }

    @Override
    protected void setup() {
        BlockFactoryApi factory = BlockFactoryApi.get();
        getLogger().at(Level.INFO).log("Found BlockFactory %s", factory.version());
    }
}

Queries

All queries read the factory simulation of one world. Call them on that world's thread: from an event handler of that world, or wrapped in world.execute(...). There the state cannot change under your feet.

Method Returns
version() BlockFactory's version, for example "1.0.0"
partAt(world, x, y, z) The building that occupies this block, as Optional<PartInfo>. Works for every block of a large building, not only its origin. Empty if there is none, or in a world without BlockFactory.
completedResearch(world) IDs of all finished research in the world
completedResearch(world, team) IDs of the research a team has finished ("" = players without a team)
isResearchCompleted(world, researchId) Whether one research is done
researchPoints(world, kind) Research points of the world: "Gob" or "Gol"
researchPoints(world, team, kind) Research points of a team: "Gob", "Gol" or "Ancient" (Ancient-Tech)
teamOf(world, playerUuid) The player's team in that world, "" without a team
isGreatPortalOpen(world) Whether any team has opened the Great Portal in that world

PartInfo is a record with type() (the item ID, for example "BF_Stone_Furnace" for the Brick Furnace), x(), y(), z() (origin block) and rotation() (0 to 3, as placed). Research IDs look like "BF_Research_Steam_Power". Both kinds of ID are listed in Items, Machines and Research Tree.

world.execute(() -> {
    BlockFactoryApi factory = BlockFactoryApi.get();
    factory.partAt(world, x, y, z).ifPresent(part ->
            getLogger().at(Level.INFO).log("%s at %d %d %d", part.type(), part.x(), part.y(), part.z()));
    String team = factory.teamOf(world, playerUuid);
    boolean steam = factory.completedResearch(world, team).contains("BF_Research_Steam_Power");
});

Events

BlockFactory posts its events on Hytale's normal event bus. Register for them in your setup(). Each event is sent on the thread of the world it belongs to (getWorld()), outside of entity processing, so you may call the queries above directly in the handler.

Event Getters Sent when
ResearchCompleted getWorld(), getResearchId(), getNumber() A research is finished at an Outlander Test Site (or by an operator command)
CreatureSummoned getWorld(), getX(), getY(), getZ(), getCallId(), getItemId() A Portal of the Ancient summoned a creature. It now lies as a trap stone (getItemId(), for example "BF_Trap_Stone_Goblin") in the portal's output. The position is the portal's origin, getCallId() the call, for example "Goblin".
GreatPortalActivated getWorld(), getTeam(), isOpened() A team filled the Great Portal's bar. isOpened() is true for the first team, which opens the portal for everyone.
import com.mutigeraffe.blockfactory.api.event.CreatureSummoned;
import com.mutigeraffe.blockfactory.api.event.ResearchCompleted;

@Override
protected void setup() {
    getEventRegistry().register(ResearchCompleted.class, event ->
            getLogger().at(Level.INFO).log("%s finished %s", event.getWorld().getName(), event.getResearchId()));
    getEventRegistry().register(CreatureSummoned.class, event -> {
        if (event.getCallId().equals("Goblin")) {
            // a goblin arrived
        }
    });
}

Identify research by its ID, not by getNumber(). The number is the research's place in the order shown in the game, and it can shift when new research is added. New call IDs can appear in later versions, so treat unknown ones gracefully.

Versioning

From version 1.0.0 on, the API follows Semantic Versioning: within 1.x, methods and events are only added, never removed or changed. Anything that breaks existing callers waits for 2.0. Before 1.0.0 the API can still change. This promise covers only the api package, never internal.

See also

Clone this wiki locally