mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-12 16:27:36 +00:00
Add channel desc and channel indicator item. Its meaning and development examples has been added to overview page in javadoc. Also removes gt_no_hatch and show_error special button from GUI as they are now a candidate in the dropdown menu. one day we will move this to MUI2, but for now let's stick to existing infra that is known to work https://github.com/user-attachments/assets/814edbb6-c4b4-4309-8798-787d9ed1aeef Note: channels in demo video are from GT5. if you boot the pack without the modified gt5 you won't see most of the channels. --------- Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>
367 lines
17 KiB
HTML
367 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
<HTML>
|
|
<HEAD>
|
|
<TITLE>API Overview</TITLE>
|
|
</HEAD>
|
|
<BODY>
|
|
StructureLib API documentation.
|
|
|
|
<h1>Key API class outline</h1>
|
|
<p>
|
|
Here we outline a few key API classes/interfaces.
|
|
</p>
|
|
|
|
<h2> {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition}</h2>
|
|
<p>
|
|
This is the structure definition of your multi.
|
|
You will have one `IStructureDefinition` for each multi.
|
|
</p>
|
|
|
|
<h2> {@link com.gtnewhorizon.structurelib.structure.IStructureElement}</h2>
|
|
<p>
|
|
These are how you define which block will be accepted.
|
|
</p>
|
|
|
|
<h2> {@link com.gtnewhorizon.structurelib.structure.StructureUtility}</h2>
|
|
<p>
|
|
This is the standard library of {@link com.gtnewhorizon.structurelib.structure.IStructureElement
|
|
IStructureElement}s.
|
|
The Standard Library contains structure elements that handle basic block element and elements that allow composing
|
|
these basic elements.
|
|
</p>
|
|
|
|
<h2> {@link com.gtnewhorizon.structurelib.alignment.constructable.IConstructable}</h2>
|
|
<p>
|
|
This interface should be implemented by tile entities that support creative autoplacing and hint particle emitting.
|
|
You will usually want to have a backing {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition
|
|
IStructureDefinition}
|
|
and call its {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition#build(Object, ItemStack, String,
|
|
World, ExtendedFacing, int, int, int, int, int, int) build()}
|
|
or {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition#hints(Object, ItemStack, String, World,
|
|
ExtendedFacing, int, int, int, int, int, int) hints()} method here.
|
|
</p>
|
|
|
|
<h2> {@link com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable}</h2>
|
|
<p>
|
|
This interface is an enhancement over {@link com.gtnewhorizon.structurelib.alignment.constructable.IConstructable
|
|
IConstructable}.
|
|
If your tile entities also support survival autoplacing then you should implement this.
|
|
You will usually want to have a backing {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition
|
|
IStructureDefinition}
|
|
and call its {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition#survivalBuild(Object, ItemStack,
|
|
String, World, ExtendedFacing, int, int, int, int, int, int, int, IItemSource, EntityPlayerMP, boolean)
|
|
survivalBuild()}.
|
|
</p>
|
|
|
|
<h2>{@link com.gtnewhorizon.structurelib.alignment.constructable.IConstructableProvider}</h2>
|
|
<p>
|
|
This interface should be implemented on tile entities that will delegate all constructable or survival construable
|
|
functionality to some other class.
|
|
This is mostly only to support gregtech meta tile entity system.
|
|
</p>
|
|
<p>
|
|
Return null if the delegate does not exist.
|
|
</p>
|
|
|
|
<h2>{@link com.gtnewhorizon.structurelib.util.InventoryUtility}</h2>
|
|
<p>
|
|
This class backs the standard implementations of {@link com.gtnewhorizon.structurelib.structure.IItemSource}.
|
|
You might want to register new inventory provider or inventory extractor there to facilitate survival auto place.
|
|
</p>
|
|
|
|
<h2>{@link com.gtnewhorizon.structurelib.alignment.IAlignmentProvider}, {@link
|
|
com.gtnewhorizon.structurelib.alignment.IAlignment} and {@link
|
|
com.gtnewhorizon.structurelib.alignment.IAlignmentLimits}</h2>
|
|
<p>
|
|
These interfaces will be used to implement rotation.
|
|
Provide a framework to rotate the block.
|
|
You can skip this if you store ExtendedFacing and do rotation in other means.
|
|
</p>
|
|
|
|
<h2>{@link com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor}</h2>
|
|
<p>
|
|
This class contains the accessors for channel data held on a trigger item. See <a href="#channels">below</a> for more
|
|
information.
|
|
</p>
|
|
|
|
<h2>{@link com.gtnewhorizon.structurelib.StructureLibAPI}</h2>
|
|
<p>
|
|
This class contains the low level utility used for implementing new {@link
|
|
com.gtnewhorizon.structurelib.structure.IStructureElement}.
|
|
You will also need methods in this class to while implementing rotation.
|
|
</p>
|
|
<p>
|
|
<b>IF THERE IS A SAME NAMED METHOD IN OTHER CLASS, ONLY USE METHODS FOUND IN THIS CLASS, NOT OTHER CLASS.</b>
|
|
This abstraction is necessary so we can change how underlying code work (e.g. how hint particles are rendered)
|
|
without
|
|
breaking client code.
|
|
</p>
|
|
|
|
<h2>{@link com.gtnewhorizon.structurelib.StructureEvent}</h2>
|
|
<p>
|
|
This class is the base class of all events that will be posted while a IStructureDefinition are visited.
|
|
These events are disabled by default, and can be enabled by calling {@link com.gtnewhorizon.structurelib.StructureLibAPI#enableInstrument(Object)}.
|
|
To receive these events, register an event handler to {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}.
|
|
Handlers should filter the events received by {@link com.gtnewhorizon.structurelib.StructureEvent#getInstrumentIdentifier()}.
|
|
</p>
|
|
<p>
|
|
<b>These events are intended for third party tools to inspect structure details.</b> Normal users should not use
|
|
these at all.
|
|
</p>
|
|
|
|
<h1>Important Concepts</h1>
|
|
<h2>Context Object</h2>
|
|
<p>
|
|
Context Object are what you pass to {@link
|
|
com.gtnewhorizon.structurelib.structure.IStructureDefinition#check(Object, String, World, ExtendedFacing, int, int,
|
|
int, int, int, int, boolean)} and alike as first parameter. It will hold the necessary states for {@link
|
|
com.gtnewhorizon.structurelib.structure.IStructureElement}. It's usually, but not necessarily, the multiblock controller.
|
|
</p>
|
|
<p>
|
|
Standard structure element library offers method to switch context objects if needed.
|
|
</p>
|
|
|
|
<h2>Trigger Item</h2>
|
|
<p>
|
|
Trigger Item is the item used to trigger hint spawn, creative build and survival auto place.
|
|
The trigger item will be passed to relevant classes as an indicator of structure tier.
|
|
The item type and stack tag compound of this item stack are opaque objects that should not be parsed.
|
|
</p>
|
|
<p>
|
|
StructureLib comes with a trigger item implementation.
|
|
It does register it to the game registries, but does not come with a recipe.
|
|
Usually there is no reason to reimplement this on your own.
|
|
</p>
|
|
|
|
<h2><a href="#channels" id="channels">Channels</a></h2>
|
|
|
|
<p>
|
|
Trigger item carries the player's choice of structure tier by its stack size.
|
|
When one multi have multiple tiered component that can have interleaving tier, this becomes an obnoxious
|
|
restriction, as one ItemStack cannot have two stack sizes at the same time.
|
|
Here we use the <i>channels</i> to bypass this restriction.
|
|
</p>
|
|
|
|
<p>
|
|
Basically, channels are information stored on the trigger item's NBT tags.
|
|
One trigger item can carry multiple channels.
|
|
Each channel can be identified with <b>lower case</b> strings and has the value being one positive Java int.
|
|
There will be one and only one master channel and 0 or more sub channel.
|
|
When a non-existent sub channel is queried, master channel will be used instead to fill its place.
|
|
</p>
|
|
|
|
<p>
|
|
Structure Definition Author and/or Structure Element Author can choose whether they use a channel and which channel
|
|
to use.
|
|
</p>
|
|
|
|
<p>
|
|
Structure Element Author will usually not need to consider channels, i.e. proceed as if this concept does not exist,
|
|
unless it must take input from multiple channels.
|
|
</p>
|
|
|
|
<p>
|
|
Structure Definition Author will consider how to map each channel to each tiered component.
|
|
He will be using `com.gtnewhorizon.structurelib.structure.StructureUtility.withChannel` to direct structure element
|
|
on which channel to use.
|
|
Conceptually, it will return an ItemStack with the master channel being the value of selected channel.
|
|
In implementation, withChannel will return a new ItemStack with same item, nbt but different stack size.
|
|
</p>
|
|
|
|
<p>
|
|
It's worth mentioning that the returned new ItemStack will still have all the channel data on it, so a multi-channel
|
|
structure element can still query the channel it wants just fine. It is however impossible to recover the original
|
|
master channel.
|
|
</p>
|
|
|
|
<h3>why is this called channel?</h3>
|
|
|
|
Just give me a better name and I'd do the renaming.
|
|
|
|
<h3>Channel Descriptions</h3>
|
|
<p>
|
|
Each channel can have one description associated with it per mod.
|
|
Channel description should briefly describe how a specific channel is used for inside a specific mod.
|
|
They will be displayed to player in channel editor GUI that comes with StructureLib.
|
|
The registration process can be done either via direct method call to {@link com.gtnewhorizon.structurelib.StructureLibAPI#registerChannelDescription(String, String, String)},
|
|
or by sending an IMC message.
|
|
After registration, the channel will also show up as autocomplete options when players are editing channels.
|
|
</p>
|
|
|
|
<p>
|
|
Here is an example of sending an IMC message to register two channels and their description.
|
|
In this example, we assume your modid is <code>my_mod</code> and your mod will ship with localization entries for
|
|
localization key <code>channels.my_mod.channel_1</code> and <code>channels.my_mod.channel_2</code>
|
|
</p>
|
|
|
|
<pre>
|
|
NBTTagCompound command = new NBTTagCompound();
|
|
NBTTagList tags = new NBTTagList();
|
|
String[] channels = {"channel_1", "channel_2"};
|
|
for (String channel : channels) {
|
|
NBTTagCompound tag = new NBTTagCompound();
|
|
tag.setString("Channel", channel);
|
|
tag.setString("Description", "channels.my_mod." + channel);
|
|
tags.appendTag(tag);
|
|
}
|
|
command.setTag("Channels", tags);
|
|
FMLInterModComms.sendMessage("structurelib", "register_channel", command);
|
|
</pre>
|
|
|
|
<p>
|
|
There is no strict rules over whether you should do direct API method call or send IMC messages.
|
|
You can choose whichever you prefer.
|
|
</p>
|
|
|
|
<h3>Channel Indicator Items</h3>
|
|
<p>
|
|
You can associate an ItemStack with a given (channel, value) pair.
|
|
It is usually (but not required to be) the ItemBlock for the blocks to be placed by this (channel, value) pair.
|
|
Player will be able to drag and drop this item to the builtin channel configuration GUI.
|
|
The registration process can be done either via direct method call to {@link com.gtnewhorizon.structurelib.StructureLibAPI#registerChannelItem(String, String, int, net.minecraft.item.ItemStack)},
|
|
or by sending an IMC message.
|
|
After registration, the channel will also show up as autocomplete options when players are editing channels.
|
|
</p>
|
|
|
|
<p>
|
|
Here is an example of sending an IMC message to register 3 vanilla sandstone as different indicator for channel "sandstone", with block metadata plus 1 as channel value.
|
|
In this example, we assume your modid is <code>my_mod</code>.
|
|
</p>
|
|
|
|
<pre>
|
|
NBTTagCompound command = new NBTTagCompound();
|
|
NBTTagList items = new NBTTagList();
|
|
int[] metas = {0, 1, 2};
|
|
for (int meta : metas) {
|
|
NBTTagCompound tag = new NBTTagCompound();
|
|
tag.setTag("Item", new ItemStack(Blocks.sandstone, 1, meta).writeToNBT(new NBTTagCompound()));
|
|
NBTTagList channels = new NBTTagList();
|
|
NBTTagCompound channel = new NBTTagCompound();
|
|
channel.setString("Channel", "sandstone");
|
|
channel.setInteger("Value", meta + 1);
|
|
channels.appendTag(channel);
|
|
tag.setTag("Channels", channels);
|
|
items.appendTag(tag);
|
|
}
|
|
command.setTag("Items", items);
|
|
FMLInterModComms.sendMessage("structurelib", "register_channel_item", command);
|
|
</pre>
|
|
|
|
<p>
|
|
There is no strict rules over whether you should do direct API method call or send IMC messages.
|
|
You can choose whichever you prefer.
|
|
</p>
|
|
|
|
<h3>Examples</h3>
|
|
|
|
<h4>For Structure Definition Author</h4>
|
|
|
|
<p>GT++ Chemical Plant is a multi with 4 distinctly tiered component.</p>
|
|
|
|
<p>It will use
|
|
<ul>
|
|
<li>master channel for its outer shell</li>
|
|
<li>channel `"casing"` for machine casings</li>
|
|
<li>channel `"pipe"` for pipe casings</li>
|
|
<li>channel `"coil"` for heating coils</li>
|
|
</ul>
|
|
Its structure code will then look like
|
|
<pre>
|
|
.addElement('a', shellStructureElement())
|
|
.addElement('b', withChannel("casing", machineCasingStructureElement()))
|
|
.addElement('c', withChannel("pipe", pipeCasingStructureElement()))
|
|
.addElement('d', withChannel("coil", coilStructureElement()))
|
|
</pre>
|
|
whereas all these structure elements are designed without considering channels whatsoever
|
|
|
|
<h4> Multi channel input Structure Element</h4>
|
|
|
|
<p>
|
|
Suppose your hatch element has a required facing and can accept arbitrary meta tile entity, so it will need both
|
|
meta id
|
|
and facing input.
|
|
</p>
|
|
|
|
<p>
|
|
It will use
|
|
<ul>
|
|
<li>master channel for its facing</li>
|
|
<li>channel `"id"` for meta tile entity id</li>
|
|
</ul>
|
|
<p>
|
|
Its code will then look like
|
|
<pre>
|
|
import com.gtnewhorizon.structurelib.structure.IStructureElement;
|
|
|
|
public class MyHatchElement<T> implements IStructureElement<T> {
|
|
//...
|
|
{@literal @}Override
|
|
public PlaceResult survivalPlaceBlock(
|
|
Object o,
|
|
World world,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
ItemStack trigger,
|
|
IItemSource s,
|
|
EntityPlayerMP actor,
|
|
Consumer chatter) {
|
|
if (shouldSkip(o, world, x, y, z)) return PlaceResult.SKIP;
|
|
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
|
ItemStack taken = s.takeOne(filterByID(ChannelDataAccessor.getChannelData(trigger, "id")).andThen(filterByFacing(trigger.stackSize)));
|
|
if (taken == null) return PlaceResult.REJECT;
|
|
return StructureUtility.survivalPlaceBlock(taken, ItemStackPredicate.NBTMode.IGNORE, null, true, world, x, y, z, s, actor);
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
The code for placeBlock() and spawnHint() will be left as an exercise for the reader.
|
|
|
|
<h2>Instrumenting StructureLib</h2>
|
|
<p>
|
|
StructureLib support third party tools to poke into structure's internals via events. This is done by listening to {@link com.gtnewhorizon.structurelib.StructureEvent}
|
|
and its subclasses on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS forge main event bus}.
|
|
</p>
|
|
<p>
|
|
Please refer to {@link com.gtnewhorizon.structurelib.StructureEvent} to see what events are available.
|
|
</p>
|
|
<h1>Extending Inventory Utility</h1>
|
|
{@link com.gtnewhorizon.structurelib.util.InventoryUtility} is how structurelib survival autoplace pulls item for autoplacing.
|
|
It has two key concepts: inventory providers and stack extractors.
|
|
<p>
|
|
Inventory Providers are classes that retrieve an Iterable of ItemStacks for examination, given an EntityPlayerMP as identifier.
|
|
They are also responsible for marking the relevant item container as dirty, so changes can be commited to disk properly when world save happens.
|
|
Examples include: main player inventory, vanilla ender chest, baubles inventory. StructureLib comes with main player inventory and vanilla ender chest built in.
|
|
</p>
|
|
<p>
|
|
Stack Extractors are classes that retrieve items from another item.
|
|
Examples include: Forestry backpacks, ME network over ME Wireless Terminals.
|
|
</p>
|
|
<p>
|
|
Both of these are managed internally by {@link com.gtnewhorizon.structurelib.SortedRegistry}.
|
|
By default, elements registered will be sorted in lexicographical order in ascending order.
|
|
However, facilities in StructureLib will allow an end user to tweak these registries as their wish.
|
|
They can disable, reorder, reenable any element within the registry, either in game, at main menu or with the game stopped,
|
|
Their changes will be synced to server as well, should they be playing on a dedicated server.
|
|
</p>
|
|
<p>
|
|
This has the following important implications
|
|
</p>
|
|
<ul>
|
|
<li>Not all inventory providers or stack extractors may be called during a survival autoplace session</li>
|
|
<li>One player could have stack extractor 001 ordered before 002, while the other having a completely reversed order</li>
|
|
</ul>
|
|
Therefore, if your stack extractor can somehow correlate with aonther one, make sure it still works if the calling order is inverted.
|
|
At the very least, inform the player about this ordering constraint.
|
|
A future StructrueLib refactor to specify this dependency info is possible, but the chance is low.
|
|
<h2>Localization</h2>
|
|
Both of these would show up in config GUI of StructureLib. Structurelib uses the following localization keys in that GUI.
|
|
<ul>
|
|
<li><code>structurelib.sortedregistry.inventoryproviders.<registry_key></code>: This is the name shown on the button for inventory providers. If this is not provided, then registry_key will be used instead.</li>
|
|
<li><code>structurelib.sortedregistry.inventoryproviders.<registry_key>.tooltip</code>: This is the tooltip shown when user hover over the button for inventory providers. If this is not provided, then no explicit tooltip will be shown.</li>
|
|
<li><code>structurelib.sortedregistry.stackextractors.<registry_key></code>: This is the name shown on the button for stack extractors. If this is not provided, then registry_key will be used instead.</li>
|
|
<li><code>structurelib.sortedregistry.stackextractors.<registry_key>.tooltip</code>: This is the tooltip shown when user hover over the button for stack extractors. If this is not provided, then no explicit tooltip will be shown.</li>
|
|
</ul>
|
|
</BODY>
|
|
</HTML>
|