<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.mutigeraffe.com/index.php?action=history&amp;feed=atom&amp;title=UtilityConnect%3ARegistering_Nodes</id>
	<title>UtilityConnect:Registering Nodes - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.mutigeraffe.com/index.php?action=history&amp;feed=atom&amp;title=UtilityConnect%3ARegistering_Nodes"/>
	<link rel="alternate" type="text/html" href="https://wiki.mutigeraffe.com/index.php?title=UtilityConnect:Registering_Nodes&amp;action=history"/>
	<updated>2026-10-20T20:00:34Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.11</generator>
	<entry>
		<id>https://wiki.mutigeraffe.com/index.php?title=UtilityConnect:Registering_Nodes&amp;diff=509&amp;oldid=prev</id>
		<title>Maintenance script: UtilityConnect wiki</title>
		<link rel="alternate" type="text/html" href="https://wiki.mutigeraffe.com/index.php?title=UtilityConnect:Registering_Nodes&amp;diff=509&amp;oldid=prev"/>
		<updated>2026-10-19T07:07:06Z</updated>

		<summary type="html">&lt;p&gt;UtilityConnect wiki&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;This page shows how a mod adds its blocks to Utility Connect networks. All types are explained on [[UtilityConnect:Modding API|Modding API]]. The helper methods in the examples (&amp;lt;code&amp;gt;blockIdAt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;isMyBlock&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;isMyPipe&amp;lt;/code&amp;gt;) stand for your own code.&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
# Add the manifest dependency (see [[UtilityConnect:Modding API#Setting up the dependency|Modding API]]).&lt;br /&gt;
# In your plugin&amp;#039;s &amp;lt;code&amp;gt;setup()&amp;lt;/code&amp;gt;, register a &amp;lt;code&amp;gt;NodeKindResolver&amp;lt;/code&amp;gt;.&lt;br /&gt;
# When one of your blocks is placed, call &amp;lt;code&amp;gt;addNode&amp;lt;/code&amp;gt;. When it is broken, call &amp;lt;code&amp;gt;removeNode&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Query networks with &amp;lt;code&amp;gt;networkAt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sameNetwork&amp;lt;/code&amp;gt; and the counters whenever you need them.&lt;br /&gt;
&lt;br /&gt;
Loading and saving are handled by the library. You do not need to re-register nodes after a restart or when a chunk loads.&lt;br /&gt;
&lt;br /&gt;
== 1. Register a resolver ==&lt;br /&gt;
The resolver tells Utility Connect which of your blocks is a device and which is a connector. It is asked on every chunk load and fixes stored roles that are out of date.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import com.cyberpiratepenguin.utilityconnect.api.UtilityConnectAPI;&lt;br /&gt;
import com.cyberpiratepenguin.utilityconnect.api.network.NodeKind;&lt;br /&gt;
&lt;br /&gt;
@Override&lt;br /&gt;
protected void setup() {&lt;br /&gt;
    UtilityConnectAPI.networks().registerKindResolver((world, pos) -&amp;gt; {&lt;br /&gt;
        String blockId = blockIdAt(world, pos);&lt;br /&gt;
        if (blockId == null || !isMyBlock(blockId)) {&lt;br /&gt;
            return null;                    // not my block: do not guess&lt;br /&gt;
        }&lt;br /&gt;
        return isMyPipe(blockId) ? NodeKind.CONNECTOR : NodeKind.DEVICE;&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Always return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; for positions that are not yours. Then the stored role stays, and other mods keep control over their own blocks.&lt;br /&gt;
&lt;br /&gt;
== 2. Add and remove nodes ==&lt;br /&gt;
Call these from your block place and break handlers, on the world thread.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import com.cyberpiratepenguin.utilityconnect.api.UtilityConnectAPI;&lt;br /&gt;
import com.cyberpiratepenguin.utilityconnect.api.network.NodeKind;&lt;br /&gt;
import com.cyberpiratepenguin.utilityconnect.api.network.NodePos;&lt;br /&gt;
&lt;br /&gt;
void onMyBlockPlaced(World world, NodePos pos, String blockId) {&lt;br /&gt;
    NodeKind kind = isMyPipe(blockId) ? NodeKind.CONNECTOR : NodeKind.DEVICE;&lt;br /&gt;
    UtilityConnectAPI.networks().addNode(world, pos, kind);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onMyBlockBroken(World world, NodePos pos) {&lt;br /&gt;
    UtilityConnectAPI.networks().removeNode(world, pos);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use &amp;lt;code&amp;gt;NodePos.from(blockPosition)&amp;lt;/code&amp;gt; to convert a Hytale &amp;lt;code&amp;gt;BlockPosition&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* The new node connects at once. Merging and splitting networks happen automatically.&lt;br /&gt;
* &amp;lt;code&amp;gt;addNode&amp;lt;/code&amp;gt; on a position that is already a node returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;. If you pass a different kind, the node&amp;#039;s role and connections change in memory, but no save is triggered for that call. A registered resolver corrects the stored role on the next chunk load.&lt;br /&gt;
* The chunk containing the node is saved automatically (see [[UtilityConnect:Persistence|Persistence]]).&lt;br /&gt;
&lt;br /&gt;
== 3. Query networks ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Networks networks = UtilityConnectAPI.networks();&lt;br /&gt;
&lt;br /&gt;
// All nodes connected to this block, including itself.&lt;br /&gt;
// Empty if there is no loaded node at that position.&lt;br /&gt;
Set&amp;amp;lt;NodePos&amp;amp;gt; members = networks.networkAt(world, pos);&lt;br /&gt;
&lt;br /&gt;
// Are a terminal and a storage block linked?&lt;br /&gt;
boolean linked = networks.sameNetwork(world, terminalPos, storagePos);&lt;br /&gt;
&lt;br /&gt;
// Statistics for a status command.&lt;br /&gt;
int networkCount = networks.networkCount(world);&lt;br /&gt;
int nodeCount = networks.nodeCount(world);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A typical use is to collect all your device blocks from &amp;lt;code&amp;gt;networkAt&amp;lt;/code&amp;gt; and work on their contents, for example to list every item stored in a network.&lt;br /&gt;
&lt;br /&gt;
== Checking the version at runtime ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (UtilityConnectAPI.isReady()) {&lt;br /&gt;
    String running = UtilityConnectAPI.runtimeVersion();   // installed library&lt;br /&gt;
    String compiled = UtilityConnectAPI.API_VERSION;       // API you compiled against&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;networks()&amp;lt;/code&amp;gt; throws &amp;lt;code&amp;gt;IllegalStateException&amp;lt;/code&amp;gt; while the library is not ready. With a correct hard dependency this does not happen during your &amp;lt;code&amp;gt;setup()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;start()&amp;lt;/code&amp;gt; or normal gameplay. Use &amp;lt;code&amp;gt;isReady()&amp;lt;/code&amp;gt; in code that might run during shutdown.&lt;br /&gt;
&lt;br /&gt;
== Rules to keep in mind ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;World thread only.&amp;#039;&amp;#039;&amp;#039; Never call the API from an async command or another thread.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Loaded chunks only.&amp;#039;&amp;#039;&amp;#039; Queries only see nodes in loaded chunks. A network that runs through an unloaded chunk appears split until the chunk loads.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Devices need connectors.&amp;#039;&amp;#039;&amp;#039; Two &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; nodes side by side do not connect. Give your mod a connector block, or register blocks that should link directly as &amp;lt;code&amp;gt;CONNECTOR&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No diagonals.&amp;#039;&amp;#039;&amp;#039; Only the six face neighbours connect.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Positions inside the world height.&amp;#039;&amp;#039;&amp;#039; Nodes are stored with packed chunk-relative coordinates, and a &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; outside the world height cannot be saved.&lt;br /&gt;
&lt;br /&gt;
{{UtilityConnect Nav}}&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
</feed>