From ebf90fc5a6e4f06173bb2c49b66cd1a3253c1497 Mon Sep 17 00:00:00 2001 From: <> Date: Mon, 20 Nov 2023 12:29:21 +0000 Subject: [PATCH] Deployed bd05764 with MkDocs version: 1.5.3 --- .nojekyll | 0 404.html | 1770 +++++ .../General-Topics/Capabilities/index.html | 1819 +++++ .../Energy-Fluid-Item-Storage/index.html | 2088 +++++ .../General-Topics/Global-Data/index.html | 1892 +++++ .../Handling-Fluid-Bucket-Size/index.html | 1813 +++++ .../General-Topics/Optimization/index.html | 1811 +++++ .../General-Topics/Ore-Generation/index.html | 2055 +++++ .../index.html | 1942 +++++ .../General-Topics/Tick-Updates/index.html | 2033 +++++ .../Annotations/DescSynced/index.html | 1810 +++++ .../SyncData/Annotations/Persisted/index.html | 1810 +++++ .../Annotations/RequireRerender/index.html | 1810 +++++ .../Annotations/UpdateListener/index.html | 1808 +++++ .../SyncData/Using-SyncData/index.html | 1907 +++++ Development/SyncData/index.html | 1802 +++++ Development/_Glossary/index.html | 1957 +++++ Development/index.html | 1802 +++++ Gameplay/Ore-Generation/index.html | 2925 +++++++ Gameplay/index.html | 1796 +++++ Modpacks/KubeJS/Beyond-The-Docs/index.html | 1903 +++++ .../KubeJS/Examples/Alternator/index.html | 1986 +++++ .../KubeJS/Examples/Greenhouse/index.html | 2118 ++++++ .../Adding-and-Removing-Recipes/index.html | 1812 +++++ .../Server-Scripts/Ore-Generation/index.html | 2037 +++++ .../Startup-Scripts/Custom-Coils/index.html | 1880 +++++ .../Custom-Machines/index.html | 1950 +++++ .../Custom-Recipe-Types/index.html | 1891 +++++ .../Element-Creation/index.html | 1871 +++++ .../Materials/Material-Creation/index.html | 1955 +++++ .../Materials/Material-Flags/index.html | 1946 +++++ .../Materials/Material-Icon-Sets/index.html | 1921 +++++ .../Materials/Material-Properties/index.html | 1858 +++++ .../Modifying-Existing-Materials/index.html | 1814 +++++ Modpacks/KubeJS/index.html | 1822 +++++ Modpacks/LDLIB/index.html | 1803 +++++ assets/favicon.png | Bin 0 -> 839 bytes assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.6c14ae12.min.js | 29 + assets/javascripts/bundle.6c14ae12.min.js.map | 7 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.el.min.js | 1 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.he.min.js | 1 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.hy.min.js | 1 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.kn.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + assets/javascripts/lunr/min/lunr.sa.min.js | 1 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.te.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.f886a092.min.js | 42 + .../workers/search.f886a092.min.js.map | 7 + assets/logo.png | Bin 0 -> 700 bytes assets/stylesheets/main.fad675c6.min.css | 1 + assets/stylesheets/main.fad675c6.min.css.map | 1 + assets/stylesheets/palette.356b1318.min.css | 1 + .../stylesheets/palette.356b1318.min.css.map | 1 + index.html | 1874 +++++ search/search_index.json | 1 + sitemap.xml | 3 + sitemap.xml.gz | Bin 0 -> 127 bytes stylesheets/extra.css | 44 + 86 files changed, 76429 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 Development/General-Topics/Capabilities/index.html create mode 100644 Development/General-Topics/Energy-Fluid-Item-Storage/index.html create mode 100644 Development/General-Topics/Global-Data/index.html create mode 100644 Development/General-Topics/Handling-Fluid-Bucket-Size/index.html create mode 100644 Development/General-Topics/Optimization/index.html create mode 100644 Development/General-Topics/Ore-Generation/index.html create mode 100644 Development/General-Topics/Overclocking-And-Parallel-Logic/index.html create mode 100644 Development/General-Topics/Tick-Updates/index.html create mode 100644 Development/SyncData/Annotations/DescSynced/index.html create mode 100644 Development/SyncData/Annotations/Persisted/index.html create mode 100644 Development/SyncData/Annotations/RequireRerender/index.html create mode 100644 Development/SyncData/Annotations/UpdateListener/index.html create mode 100644 Development/SyncData/Using-SyncData/index.html create mode 100644 Development/SyncData/index.html create mode 100644 Development/_Glossary/index.html create mode 100644 Development/index.html create mode 100644 Gameplay/Ore-Generation/index.html create mode 100644 Gameplay/index.html create mode 100644 Modpacks/KubeJS/Beyond-The-Docs/index.html create mode 100644 Modpacks/KubeJS/Examples/Alternator/index.html create mode 100644 Modpacks/KubeJS/Examples/Greenhouse/index.html create mode 100644 Modpacks/KubeJS/Server-Scripts/Adding-and-Removing-Recipes/index.html create mode 100644 Modpacks/KubeJS/Server-Scripts/Ore-Generation/index.html create mode 100644 Modpacks/KubeJS/Startup-Scripts/Custom-Coils/index.html create mode 100644 Modpacks/KubeJS/Startup-Scripts/Custom-Machines/index.html create mode 100644 Modpacks/KubeJS/Startup-Scripts/Custom-Recipe-Types/index.html create mode 100644 Modpacks/KubeJS/Startup-Scripts/Element-Creation/index.html create mode 100644 Modpacks/KubeJS/Startup-Scripts/Materials/Material-Creation/index.html create mode 100644 Modpacks/KubeJS/Startup-Scripts/Materials/Material-Flags/index.html create mode 100644 Modpacks/KubeJS/Startup-Scripts/Materials/Material-Icon-Sets/index.html create mode 100644 Modpacks/KubeJS/Startup-Scripts/Materials/Material-Properties/index.html create mode 100644 Modpacks/KubeJS/Startup-Scripts/Materials/Modifying-Existing-Materials/index.html create mode 100644 Modpacks/KubeJS/index.html create mode 100644 Modpacks/LDLIB/index.html create mode 100644 assets/favicon.png create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.6c14ae12.min.js create mode 100644 assets/javascripts/bundle.6c14ae12.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.el.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.he.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.te.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.f886a092.min.js create mode 100644 assets/javascripts/workers/search.f886a092.min.js.map create mode 100644 assets/logo.png create mode 100644 assets/stylesheets/main.fad675c6.min.css create mode 100644 assets/stylesheets/main.fad675c6.min.css.map create mode 100644 assets/stylesheets/palette.356b1318.min.css create mode 100644 assets/stylesheets/palette.356b1318.min.css.map create mode 100644 index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz create mode 100644 stylesheets/extra.css diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..642c1a7 --- /dev/null +++ b/404.html @@ -0,0 +1,1770 @@ + + + +
+ + + + + + + + + + + + + + +Fabric doesn't have a capability system like Forge does, but you can use several utility methods instead:
+FluidTransferHelper.getFluidTransfer(...);
+ItemTransferHelper.getItemTransfer(...);
+GTCapabilityHelper.getRecipeLogic(...)
+GTCapabilityHelper.getControllable(...)
+GTCapabilityHelper.getCoverable(...)
+GTCapabilityHelper.getToolable(...)
+GTCapabilityHelper.getWorkable(...)
+GTCapabilityHelper.getElectricItem(...)
+GTCapabilityHelper.getEnergyContainer(...)
+
Note
+In general, these containers should be created as final
fields (that's what we need for the
+SyncData system).
+Set their base arguments in the constructor (you can pass args for subclasses to modify).
You can create these containers via one of the following classes:
+NotifiableItemStackHandler
NotifiableFluidTank
NotifiableEnergyContainer
In general, you should prefer these classes over other implementations if possible, as they notify all listeners +of internal changes to improve performance.
+IO constructor parameters:
+handlerIO
: Whether the container is regarded as input or output during recipe processingcapabilityIO
: Whether the player can use hoppers, pipes, cables, etc. to interact with the storageIf you don't need to use the storage for recipe processing and/or providing capabilities, you can just use one of the +following classes, as they are more lightweight:
+ItemStackTransfer
FluidStorage
In some cases, you might need to create a custom implementation for either of these containers.
+To do so, use either of the following interfaces:
IItemTransfer
IFluidTransfer
IEnergyContainer
In case you have special requirements to your containers, you may be able to use one of these implementations in
+conjunction with one or more regular containers.
+They generally act as a proxy to the underlying container(s), while also handling these requirements.
ItemTransferList
FluidTransferList
EnergyContainerList
For proxying multiple containers, limited to a specific IO direction.
+IOItemTransferList
IOFluidTransferList
Not merged yet
Branch: mi-ender-link
If you need to proxy any item or fluid container that needs to be rate limited for insertion and extraction, you can +use either of the following classes:
+LimitingItemTransferProxy
LimitingFluidTransferProxy
The transfer limit passed as a constructor parameter will not renew automatically. Your container will therefore stop +transferring anything once this limit is reached.
+If you want to make this a rate limit instead, you will have to schedule a task that regularly resets the transfer +limit to the maximum value for your task's interval:
+public class MyCover extends CoverBehavior {
+ private LimitingFluidTransferProxy transferProxy;
+ private ConditionalSubscriptionHandler rateLimitSubscription;
+
+ public MyCover(IFluidTransfer myFluidTransfer) {
+ super(/* ... */);
+
+ transferProxy = new LimitingFluidTransferProxy(
+ myFluidTransfer,
+ 0L // Initial limit of 0, will be updated regularly in isRateLimitRefreshActive()
+ );
+ rateLimitSubscription = new ConditionalSubscriptionHandler(
+ this,
+ this::resetTransferRateLimit,
+ this::isRateLimitRefreshActive
+ );
+ }
+
+ @Override
+ public void onLoad() {
+ super.onLoad();
+ rateLimitSubscription.initialize(coverHolder.getLevel());
+ }
+
+ private void resetTransferRateLimit() {
+ if (transferProxy == null)
+ return;
+
+ transferProxy.setRemainingTransfer(transferRate.getMilliBuckets() * 20);
+ }
+
+ private boolean isRateLimitRefreshActive() {
+ // ...
+ }
+}
+
In certain cases (e.g. in a cache that holds all currently loaded instances of a machine), you might need to store data +in a global (static and mutable) variable.
+When doing so, you need to ensure that remote and serverside instances don't get mixed up.
+SideLocal<T>
Not yet merged
Branch: mi-ender-link
To make working with this requirement easier, You can use SideLocal<T>
to store your global data.
+It is similar to Java's ThreadLocal
, but operates on the game's sides instead.
If you are currently on the remote side (LDLib.isRemote()
/ on the client's main
thread), it will return the
+remote side's instance of your data. Otherwise, you will get the server side's instance.
public class MyCache {
+ private static SideLocal<Map<UUID, MyData>> cache = new SideLocal<>(HashMap::new);
+
+ public static void cacheData(UUID id, MyData data) {
+ cache.get().put(id, data);
+ }
+
+ public static MyData getData(UUID id) {
+ return cache.get().get(id);
+ }
+}
+
Alternatively to passing an initializer for both instances to SideLocal
's constructor, you can also supply
+separate instances for the remote and server side.
The fluid systems of Forge and Fabric use different units. +Make sure you use the correct units whenever you're handling fluid amounts.
+To get the size of one bucket, use the following method:
+FluidHelper.getBucket(); // returns 1000 on Forge and 81000 on Fabric.
+
Not yet documented
+Due to Minecraft's worldgen limitations (1), GTCEu's ore vein generation does not use the native worldgen feature system.
+Instead, we have our own system of generating ore veins separately from the actual ore placement,
+so that ores are only ever placed for the currently generating chunk.
+This page roughly describes the process of generating, caching and placing ores.
The generation can be (roughly) split up into three steps:
+This document will cover these steps from the bottom up, starting at the chunk generation mixin (ChunkGeneratorMixin.gtceu$applyBiomeDecoration()
).
The ChunkGeneratorMixin
holds a reference to the OrePlacer
(not to be confused with OreBlockPlacer
) - which is used to place the
+generated veins' blocks into the world, limited to the currently generating chunk.
When trying to generate a chunk, the OrePlacer
will query the OreGenCache
for a list of veins surrounding the current chunk.
The radius for querying the surrounding area is determined by the oreVeinRandomOffset
config option, as well as the largest registered vein size.
+It is therefore automatically compatible with any additional (or changed default) veins registered through either KubeJS, or by an addon.
Of course, the ore gen cache can only hold a limited amount of generated veins at once (see the oreGenerationChunkCacheSize
config option).
Because veins may be removed from the cache before all of their chunks are generated, it is extremely important that the ore generation is fully deterministic!
+This ensures that we do not generate ore veins that are either cut off, or have a mismatch in shape or type across chunk borders.
+It also automatically applies across game restarts, keeping continuity even then.
The only situation where ore veins will differ across chunk borders (other than certain internal changes to the generation, of course), is after +the relevant config options have been changed.
+In our case, that means that the RandomSource
s used for world generation must be completely new for generating each vein, so that its type, shape, offset,
+contents, etc. are not influenced by previous queries to the random generator.
+It is completely and exclusively seeded from the world's seed, as well as the chunk position.
For the random ore vein offset, we also include the vein's world generation layer in the random seed.
+This may need to include an additional component in the future, in case we add support for multiple veins per chunk and worldgen-layer.
Whenever the OreGenCache
cannot find a vein for a specific chunk, it will request a list of that chunk's GeneratedVein
s from the OreGenerator
.
The OreGenerator
is responsible for determining a vein's type, its origin (influenced by the oreVeinRandomOffset
config option), as well as providing the appropriate
+randomness source to the used implementation of VeinGenerator
.
Vein Origin vs Center
+A vein's origin is always the chunk it originates in, regardless of the random offset.
+The actual center of a vein is influenced by the random offset and might not be located at the chunk center - or in the same chunk at all.
Once the relevant VeinGenerator
implementation has finished generating the vein's shape, it will be cached per chunk, inside a GeneratedVein
.
VeinGenerator
and OreBlockPlacer
A vein generator is what will generate the actual shape of the vein.
+It should, however, never try to place any blocks directly. Instead, its generate()
method will only return a map of OreBlockPlacer
s by block position, which
+are responsible for actually placing the blocks in the world, as soon as a chunk generates.
+Each OreBlockPlacer
should only place either a single block, or no block.
OreBlockPlacer
sIn certain situations, the process of actually placing the block requires a randomness source (e.g. to determine the chance of its block being placed).
+To keep the ore generation fully deterministic in this case as well, it is recommended to generate a new seed using the supplied RandomSource
at the time of
+vein shape generation. This seed should be passed into the OreBlockPlacer
returned for the each block position.
Inside the OreBlockPlacer
, you can then simply create a new RandomSource
using the precomputed seed.
public class MyVeinGenerator {
+ public Map<BlockPos, OreBlockPlacer> generate(WorldGenLevel level, RandomSource random, GTOreDefinition entry, BlockPos origin) {
+ Map<BlockPos, OreBlockPlacer> generatedBlocks = new Object2ObjectOpenHashMap<>();
+
+ for (BlockPos pos : determineShapePositions()) {
+ final var randomSeed = random.nextLong(); // Fully deterministic regardless of chunk generation order
+ generatedBlocks.put(pos, (access, section) -> placeBlock(access, section, randomSeed, pos, entry));
+ }
+
+ return generatedBlocks;
+ }
+
+ private void placeBlock(BulkSectionAccess level, LevelChunkSection section, long randomSeed, BlockPos pos, GTOreDefinition entry) {
+ RandomSource rand = new XoroshiroRandomSource(randomSeed);
+
+ // ...
+ }
+}
+
To improve its generality, RecipeLogic has been rewritten in GTCEu Modern to support inputs and outputs other than +EU, items and fluids.
+The new RecipeLogic
no longer handles overclocked and parallel logic, but instead delegates it to the
+machine via IRecipeLogicMachine
:
/**
+ * Override it to modify recipe on the fly e.g. applying overclock,
+ * change chance, etc
+ *
+ * @param recipe recipe from detected from GTRecipeType
+ * @return modified recipe.
+ * null -- this recipe is unavailable
+ */
+@Nullable
+GTRecipe modifyRecipe(GTRecipe recipe);
+
In general, a simple electric overclocking can be done this way. For details, see OverclockingLogic
and RecipeHelper
public @Nullable GTRecipe modifyRecipe(GTRecipe recipe) {
+ if (RecipeHelper.getRecipeEUtTier(recipe) > getTier()) {
+ return null;
+ }
+
+ return RecipeHelper.applyOverclock(
+ getDefinition().getOverclockingLogic(),
+ recipe,
+ getMaxVoltage()
+ );
+}
+
Parallel is also not complicated to implement. Let's take the generator
as an example
public @Nullable GTRecipe modifyRecipe(GTRecipe recipe) {
+ var EUt = RecipeHelper.getOutputEUt(recipe); // get the recipe's EU/t
+
+ if (EUt > 0) {
+ // calculate the max parallel limitation.
+ var maxParallel = (int) (Math.min(
+ energyContainer.getOutputVoltage(),
+ GTValues.V[overclockTier]
+ ) / EUt);
+
+ while (maxParallel > 0) {
+ // copy and apply parallel, it will affect all recipes' contents
+ // and the recipe duration.
+ var copied = recipe.copy(ContentModifier.multiplier(maxParallel));
+
+ // If the machine has enough ingredients, return copied recipe.
+ if (copied.matchRecipe(this)) {
+ copied.duration = copied.duration / maxParallel;
+
+ return copied;
+ }
+
+ // Trying to halve the number of parallelism
+ maxParallel /= 2;
+ }
+ }
+ return null;
+}
+
ITickable
/ update()
The client update is always present and you can override the clientTick()
method, which works just as well as in 1.12.
But for the sake of performance, our machines are no longer always in a tickable state.
+We introduced ITickSubscription
for managed tick logic.
+Understand the basic concept of subscribing to periodic updates when they are needed, and unsubscribe them when
+they are not.
Automatic output of our machine requires periodic output of internal items to an adjacent inventory. +But most of the time this logic doesn't need to be executed if any of the following conditions apply:
+Lets look at how we implement it in QuantumChest
.
QuantumChest
@Getter @Persisted @DescSynced
+protected boolean autoOutputItems;
+@Persisted @DropSaved
+protected final NotifiableItemStackHandler cache; // inner inventory
+protected TickableSubscription autoOutputSubs;
+protected ISubscription exportItemSubs;
+
+// update subscription, subscribe if tick logic subscription is required, unsubscribe otherwise.
+protected void updateAutoOutputSubscription() {
+ var outputFacing = getOutputFacingItems(); // get output facing
+ if ((isAutoOutputItems() && !cache.isEmpty()) // inner item non empty
+ && outputFacing != null // has output facing
+ && ItemTransferHelper.getItemTransfer(getLevel(), getPos().relative(outputFacing), outputFacing.getOpposite()) != null) { // adjacent block has inventory.
+ autoOutputSubs = subscribeServerTick(autoOutputSubs, this::checkAutoOutput); // subscribe tick logic
+ } else if (autoOutputSubs != null) { // unsubscribe tick logic
+ autoOutputSubs.unsubscribe();
+ autoOutputSubs = null;
+ }
+}
+
+// output to nearby block.
+protected void checkAutoOutput() {
+ if (getOffsetTimer() % 5 == 0) {
+ if (isAutoOutputItems() && getOutputFacingItems() != null) {
+ cache.exportToNearby(getOutputFacingItems());
+ }
+ updateAutoOutputSubscription(); // dont foget to check if it's still available
+ }
+}
+
+@Override
+public void onLoad() {
+ super.onLoad();
+ if (getLevel() instanceof ServerLevel serverLevel) {
+ // you cant call ItemTransferHelper.getItemTransfer while chunk is loading, so lets defer it next tick.
+ serverLevel.getServer().tell(new TickTask(0, this::updateAutoOutputSubscription));
+ }
+ // add a listener to listen the changes of inner inventory. (for ex, if inventory not empty anymore, we may need to unpdate logic)
+ exportItemSubs = cache.addChangedListener(this::updateAutoOutputSubscription);
+}
+
+@Override
+public void onUnload() {
+ super.onUnload(); //autoOutputSubs will be released automatically when machine unload
+ if (exportItemSubs != null) { //we should mannually release it.
+ exportItemSubs.unsubscribe();
+ exportItemSubs = null;
+ }
+}
+
+// For any change may affect the logic to invoke updateAutoOutputSubscription at a time
+@Override
+public void setAutoOutputItems(boolean allow) {
+ this.autoOutputItems = allow;
+ updateAutoOutputSubscription();
+}
+
+@Override
+public void setOutputFacingItems(Direction outputFacing) {
+ this.outputFacingItems = outputFacing;
+ updateAutoOutputSubscription();
+}
+
+@Override
+public void onNeighborChanged(Block block, BlockPos fromPos, boolean isMoving) {
+ super.onNeighborChanged(block, fromPos, isMoving);
+ updateAutoOutputSubscription();
+}
+
I know the code is a kinda long, but it's for performance, and thanks to the SyncData system, we've eliminated a lot of +synchronization code, so please sacrifice a little for better performance.
+ConditionalSubscriptionHandler
For ease of use in some situations, it is possible to eliminate some of the boilerplate code and delegate management of
+your subscription to a ConditionalSubscriptionHandler
instead.
Using that class, it is possible to simply provide an update method to run every tick while the subscription is active,
+as well as a Supplier<Boolean>
that determines whether it is active.
Whenever the input of its condition changes, you need to call updateSubscription()
on the handler, so that it can
+re-evaluate it and take the necessary steps if it has changed.
+You should also to call this method after executing your tick logic in most cases, to ensure the subscription doesn't
+stay active any longer than it needs to.
ConditionalSubscriptionHandler
class MyMachine extends MetaMachine implements IControllable {
+ @Persisted @Getter
+ private boolean workingEnabled = true;
+
+ private final ConditionalSubscriptionHandler subscriptionHandler;
+
+ public MyMachine() {
+ super(/* ... */);
+
+ this.subscriptionHandler = new ConditionalSubscriptionHandler(
+ this, this::update, this::isSubscriptionActive
+ );
+ }
+
+ private void update() {
+ // Only run once every second
+ if (getOffsetTimer() % 20 != 0)
+ return;
+
+ // ...
+
+ // Now that the update logic has been executed, update the subscription.
+ // This will internally check if the subscription is still active and
+ // unsubscribe otherwise.
+ subscriptionHandler.updateSubscription();
+ }
+
+ private boolean isSubscriptionActive() {
+ return isWorkingEnabled();
+ }
+
+ @Override
+ public void setWorkingEnabled(boolean workingEnabled) {
+ this.workingEnabled = workingEnabled;
+
+ // Whether the subscription is currently active depends on whether working
+ // is enabled for this machine. As soon as any of the condition inputs changes,
+ // you need to update the subscription.
+ subscriptionHandler.updateSubscription();
+ }
+
+ @Override
+ public void onLoad() {
+ super.onLoad();
+
+ // As soon as you can get a reference to the dimension/level you're in,
+ // you need to initialize your subscription handler.
+ subscriptionHandler.initialize(getLevel());
+ }
+}
+
@DescSynced
@Persisted
@RequireRerender
@UpdateListener
For serializing and synchronizing fields, LDLib's annotation-based SyncData system is used.
+Please also refer to the LDLib Wiki.
+Here is an overview of the most important annotations:
+public class MyMachine {
+ @Persisted // (1)
+ private String myPersistedField;
+
+ @DescSynced // (2)
+ private String myClientsideRelevantField;
+
+ @DescSynced @RequireRerender // (3)
+ private IO io;
+
+ @DescSynced @UpdateListener(methodName = "runAdditionalUpdate") // (4)
+ private int fieldWithAdditionalClientUpdateLogic;
+
+
+ private void runAdditionalUpdate(int newValue, int oldValue) {
+ // Run additional clientside update code here
+ }
+}
+
This field is automatically serialized to and deserialized from NBT data, that will be stored with its container.
+ By default, @Persisted
only applies on the server side.
For fields that need to be available on the client (or more specifically, remote) side, you can annotate them with
+ @DescSynced
to make them available there as well.
+ Any changes made to the field on the server side will automatically be synchronized to the client side.
If a change of a synced field's value requires rerendering the containing block (e.g. for different overlays based
+ on a cover's IO direction), simply add the @RequireRerender
annotation to it.
+ Its renderer's code will then be called again every time the field changes.
In some cases, a field may require some additional code to run on the client/remote side when it has been synced.
+ The @UpdateListener
annotation allows you to define a method to be called in that case.
For serializing and synchronizing fields, LDLib's annotation-based SyncData system is used.
+Using this system, almost all boilerplate code regarding these topics can be omitted.
For more info on the SyncData annotations, please refer to the following chapters, as well as to the +LDLib Wiki.
+ + + + + + +Info
+This is an overview of technical terms that are commonly used in this documentation.
+If you're not sure what something means (or how it applies to the current context), please refer to this page.
The part of the game running on a player's computer.
+It always hosts the Remote Side.
+In singleplayer mode, the Server Side is hosted on the client as well. In multiplayer mode, the client
+connects to a dedicated server instead.
See also: Server Side
+The remote side is the part of the game that is connected to the game's server side.
+It always runs on the client.
This side may not have the same amount of data available to it as the server does (see SyncData if you
+need to automatically synchronize certain data to the remote side).
+It also does not perform any tick update logic.
See also: Remote Side
+The server side is what one or more players connect to.
+In singleplayer mode, it runs on the client. In multiplayer mode, it runs on a dedicated server.
This side usually has all of the world's data available to it and runs tick update logic.
+This is therefore, where TPS impact becomes relevant. In general, use
Short for "ticks per second". Should stay at exactly 20.
+See Tick Updates and Optimization for techniques +on how to reduce performance impact.
+ + + + + + +If you want to contribute to the development of GTCEu Modern, please feel free to submit a +pull request with your changes.
+The following pages describe a few important concepts that you will likely run into when working with our codebase.
+This mod is based on Architectury for compatibility across multiple modding +platforms, as well as the LDLib library for a lot of +commonly used functionalities. Please refer to the documentation for those as well.
+ + + + + + +This is an overview of GTCEu's ore veins and the ore types they contain.
+Please note that these are the default settings and may be different in certain modpacks.
Height range: 10 to 80
+Height range: 10 to 80
+Height range: 10 to 140
+Height range: -10 to 160
+Height range: -15 to 45
+Height range: 30 to 60
+Height range: -10 to 60
+Height range: 0 to 50
+Height range: 10 to 60
+Height range: 15 to 60
+Height range: -10 to 60
+Height range: 30 to 70
+Height range: 30 to 80
+ +Height range: -40 to 10
+Height range: -65 to -30
+Height range: -60 to 10
+Height range: -30 to 0
+Height range: -40 to -10
+Height range: -20 to 10
+Height range: -65 to -10
+Height range: -40 to 0
+Height range: 20 to 40
+Height range: 5 to 30
+Height range: 80 to 120
+Height range: 20 to 30
+Height range: 20 to 50
+Height range: 20 to 40
+Height range: 40 to 80
+Height range: 5 to 40
+Height range: 5 to 45
+Height range: 10 to 30
+Height range: 80 to 120
+Height range: 80 to 120
+Height range: 10 to 80
+Height range: 20 to 80
+Height range: 10 to 90
+Height range: 30 to 60
+Height range: 20 to 60
+Height range: 5 to 50
+ + + + + + +While we try to keep this documentation up to date and as complete as possible, it may not always contain all of the latest information.
+As an additional resource to these docs, you can also reference our KubeJS integration directly in the source code:
+https://github.com/GregTechCEu/GregTech-Modern/tree/1.20.1/common/src/main/java/com/gregtechceu/gtceu/integration/kjs
Continue reading for a few important places you may want to check.
+Builders
+https://github.com/GregTechCEu/GregTech-Modern/tree/1.20.1/common/src/main/java/com/gregtechceu/gtceu/integration/kjs/builders
+If you're not sure what methods and fields are available on one of our builders, you can find all of them in this directory.
+GregTechKubeJSPlugin
+https://github.com/GregTechCEu/GregTech-Modern/blob/1.20.1/common/src/main/java/com/gregtechceu/gtceu/integration/kjs/GregTechKubeJSPlugin.java
+GregTechKubeJSPlugin.registerBindings()
GregTechKubeJSPlugin.registerTypeWrappers()
GTCEuStartupEvents.registry('gtceu:recipe_type', event => {
+ event.create('basic_alternator')
+ .category('multiblock')
+ .setEUIO('out')
+ .setMaxIOSize(0, 0, 0, 0)
+ .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, FillDirection.LEFT_TO_RIGHT)
+ .setSound(GTSoundEntries.ARC)
+ .setMaxTooltips(6)
+})
+
GTCEuStartupEvents.registry('gtceu:machine', event => {
+ event.create('basic_alternator', 'multiblock')
+ .rotationState(RotationState.NON_Y_AXIS)
+ .recipeType('basic_alternator')
+ .pattern(definition => FactoryBlockPattern.start()
+ .aisle("CWC", "CWC", "#W#")
+ .aisle("CWC", "K#E", "CWC")
+ .aisle("CWC", "CWA", "#W#")
+ .where('A', Predicates.controller(Predicates.blocks(definition.get())))
+ .where('W', Predicates.blocks(GTBlocks.COIL_CUPRONICKEL.get()))
+ .where("C", Predicates.blocks(GTBlocks.CASING_STEEL_SOLID.get()))
+ .where('#', Predicates.any())
+ .where('K', Predicates.abilities(PartAbility.INPUT_KINETIC).setExactLimit(1))
+ .where('E', Predicates.abilities(PartAbility.OUTPUT_ENERGY).setExactLimit(1))
+ .build()
+ )
+ .workableCasingRenderer(
+ "gtceu:block/casings/solid/machine_casing_solid_steel",
+ "gtceu:block/multiblock/implosion_compressor", false
+ )
+})
+
{
+ "block.gtceu.basic_alternator": "Basic Alternator",
+ "gtceu.basic_alternator": "Basic Alternator"
+}
+
ServerEvents.recipes(event => {
+ function basic_alt(id, su, rpm, eu){
+ event.recipes.gtceu.basic_alternator(id)
+ .inputStress(su)
+ .rpm(rpm)
+ .duration(2)
+ .EUt(eu)
+ }
+ basic_alt('lv_1_amp', 256, 32, -32)
+})
+
GTCEuStartupEvents.registry('gtceu:recipe_type', event => {
+ event.create('greenhouse')
+ .category('drack')
+ .setEUIO('in')
+ .setMaxIOSize(3, 4, 1, 0)
+ .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, FillDirection.LEFT_TO_RIGHT)
+ .setSound(GTSoundEntries.BATH)
+})
+
GTCEuStartupEvents.registry('gtceu:machine', event => {
+ event.create('greenhouse', 'multiblock')
+ .rotationState(RotationState.NON_Y_AXIS)
+ .recipeType('greenhouse')
+ .appearanceBlock(GTBlocks.CASING_STEEL_SOLID)
+ .pattern(definition => FactoryBlockPattern.start()
+ .aisle('CCC', 'CGC', 'CGC', 'CLC', 'CCC')
+ .aisle('CMC', 'GSG', 'G#G', 'LIL', 'COC')
+ .aisle('CKC', 'CGC', 'CGC', 'CLC', 'CNC')
+ .where('K', Predicates.controller(Predicates.blocks(definition.get())))
+ .where('M', Predicates.blocks('moss_block')
+ .or(Predicates.blocks('dirt'))
+ .or(Predicates.blocks('grass_block'))
+ )
+ .where('G', Predicates.blocks('ae2:quartz_glass'))
+ .where('S', Predicates.blocks('oak_sapling')
+ .or(Predicates.blocks('dark_oak_sapling'))
+ .or(Predicates.blocks('spruce_sapling'))
+ .or(Predicates.blocks('birch_sapling'))
+ .or(Predicates.blocks('jungle_sapling'))
+ .or(Predicates.blocks('acacia_sapling'))
+ .or(Predicates.blocks('azalea'))
+ .or(Predicates.blocks('flowering_azalea'))
+ .or(Predicates.blocks('mangrove_propagule'))
+ .or(Predicates.blocks('gtceu:rubber_sapling'))
+ )
+ .where('I', Predicates.blocks('glowstone'))
+ .where('L', Predicates.blocks(GTBlocks.CASING_GRATE.get()))
+ .where('C', Predicates.blocks(GTBlocks.CASING_STEEL_SOLID.get())
+ .or(Predicates.autoAbilities(definition.getRecipeTypes()))
+ )
+ .where('O', Predicates.abilities(PartAbility.MUFFLER)
+ .setExactLimit(1)
+ )
+ .where('N', Predicates.abilities(PartAbility.MAINTENANCE))
+ .where('#', Predicates.air())
+ .build()
+ )
+ .workableCasingRenderer('gtceu:block/casings/solid/machine_casing_solid_steel', 'gtceu:block/multiblock/implosion_compressor', false)
+})
+
{
+ "block.gtceu.greenhouse": "Greenhouse",
+ "gtceu.greenhouse": "Greenhouse"
+}
+
ServerEvents.recipes(event => {
+
+ ////// Machine Recipe //////
+
+ event.shaped(
+ 'gtceu:greenhouse',
+ ['AWA', 'CSC', 'WCW'],
+ {
+ A: '#forge:circuits/mv',
+ W: 'gtceu:copper_single_cable',
+ C: '#forge:circuits/mv',
+ S: 'gtceu:solid_machine_casing'
+ }
+ ).id('gtceu:shaped/greenhouse')
+
+
+ ////// Greenhouse Recipes //////
+
+ function Greenhouse(id, input, fluid, output, boosted) {
+ if (boosted) {
+ event.recipes.gtceu.greenhouse(id)
+ .circuit(2)
+ .notConsumable(InputItem.of(input))
+ .itemInputs('4x gtceu:fertilizer')
+ .inputFluids(Fluid.of('minecraft:water', fluid))
+ .itemOutputs(output)
+ .duration(320)
+ .EUt(MV)
+ } else {
+ event.recipes.gtceu.greenhouse(id)
+ .circuit(1)
+ .notConsumable(InputItem.of(input))
+ .inputFluids(Fluid.of('minecraft:water', fluid))
+ .itemOutputs(output)
+ .duration(640)
+ .EUt(MV)
+ }
+ }
+
+
+ ////// Trees //////
+
+ // Rubber
+ Greenhouse('rubber_sapling', 'gtceu:rubber_sapling', 1000, ['32x gtceu:rubber_log', '8x gtceu:sticky_resin', '4x gtceu:rubber_sapling'], false)
+ Greenhouse('rubber_sapling_boosted', 'gtceu:rubber_sapling', 1000, ['64x gtceu:rubber_log', '16x gtceu:sticky_resin', '4x gtceu:rubber_sapling'], true)
+
+ // Oak
+ Greenhouse('oak_sapling', 'minecraft:oak_sapling', 1000, ['64x minecraft:oak_log', '4x minecraft:oak_sapling'], false)
+ Greenhouse('oak_sapling_boosted', 'minecraft:oak_sapling', 1000, ['64x minecraft:oak_log', '64x minecraft:oak_log', '4x minecraft:oak_sapling'], true)
+
+ // Dark Oak
+ Greenhouse('dark_oak_sapling', 'minecraft:dark_oak_sapling', 1000, ['64x minecraft:dark_oak_log', '4x minecraft:dark_oak_sapling'], false)
+ Greenhouse('dark_oak_sapling_boosted', 'minecraft:dark_oak_sapling', 1000, ['64x minecraft:dark_oak_log', '64x minecraft:dark_oak_log', '4x minecraft:dark_oak_sapling'], true)
+
+ // Spruce
+ Greenhouse('spruce_sapling', 'minecraft:spruce_sapling', 1000, ['64x minecraft:spruce_log', '4x minecraft:spruce_sapling'], false)
+ Greenhouse('spruce_sapling_boosted', 'minecraft:spruce_sapling', 1000, ['64x minecraft:spruce_log', '64x minecraft:spruce_log', '4x minecraft:spruce_sapling'], true)
+
+ // Birch
+ Greenhouse('birch_sapling', 'minecraft:birch_sapling', 1000, ['64x minecraft:birch_log', '4x minecraft:birch_sapling'], false)
+ Greenhouse('birch_sapling_boosted', 'minecraft:birch_sapling', 1000, ['64x minecraft:birch_log', '64x minecraft:birch_log', '4x minecraft:birch_sapling'], true)
+
+ // Acacia
+ Greenhouse('acacia_sapling', 'minecraft:acacia_sapling', 1000, ['64x minecraft:acacia_log', '4x minecraft:acacia_sapling'], false)
+ Greenhouse('acacia_sapling_boosted', 'minecraft:acacia_sapling', 1000, ['64x minecraft:acacia_log', '64x minecraft:acacia_log', '4x minecraft:acacia_sapling'], true)
+
+ // Jungle
+ Greenhouse('jungle_sapling', 'minecraft:jungle_sapling', 1000, ['64x minecraft:jungle_log', '4x minecraft:jungle_sapling'], false)
+ Greenhouse('jungle_sapling_boosted', 'minecraft:jungle_sapling', 1000, ['64x minecraft:jungle_log', '64x minecraft:jungle_log', '4x minecraft:jungle_sapling'], true)
+
+ // Azalea
+ Greenhouse('azalea_sapling', 'minecraft:azalea', 1000, ['64x minecraft:oak_log', '4x minecraft:azalea'], false)
+ Greenhouse('azalea_boosted', 'minecraft:azalea', 1000, ['64x minecraft:oak_log', '64x minecraft:oak_log', '4x minecraft:azalea'], true)
+
+ // Flowering Azalea
+ Greenhouse('flowering_azalea', 'minecraft:flowering_azalea', 1000, ['64x minecraft:oak_log', '4x minecraft:flowering_azalea'], false)
+ Greenhouse('flowering_azalea_boosted', 'minecraft:flowering_azalea', 1000, ['64x minecraft:oak_log', '64x minecraft:oak_log', '4x minecraft:flowering_azalea'], true)
+
+ // Mangrove
+ Greenhouse('mangrove_propagule', 'minecraft:mangrove_propagule', 1000, ['64x minecraft:mangrove_log', '4x minecraft:mangrove_propagule'], false)
+ Greenhouse('mangrove_propagule_boosted', 'minecraft:mangrove_propagule', 1000, ['64x minecraft:mangrove_log', '64x minecraft:mangrove_log', '4x minecraft:mangrove_propagule'], true)
+
+ ////// Crops //////
+
+ // Sugarcane
+ Greenhouse('sugar_cane', 'minecraft:sugar_cane', 1000, '24x minecraft:sugar_cane', false)
+ Greenhouse('sugar_cane_boosted', 'minecraft:sugar_cane', 1000, '48x minecraft:sugar_cane', true)
+
+ // Kelp
+ Greenhouse('kelp', 'minecraft:kelp', 2000, '24x minecraft:kelp', false)
+ Greenhouse('kelp_boosted', 'minecraft:kelp', 2000, '48x minecraft:kelp', true)
+
+ // Bamboo
+ Greenhouse('bamboo', 'minecraft:bamboo', 1000, '24x minecraft:bamboo', false)
+ Greenhouse('bamboo_boosted', 'minecraft:bamboo', 1000, '48x minecraft:bamboo', true)
+
+ // Cactus
+ Greenhouse('cactus', 'minecraft:cactus', 1000, '24x minecraft:cactus', false)
+ Greenhouse('cactus_boosted', 'minecraft:cactus', 1000, '48x minecraft:cactus', true)
+
+ // Wheat
+ Greenhouse('wheat', 'minecraft:wheat_seeds', 1000, '24x minecraft:wheat', false)
+ Greenhouse('wheat_boosted', 'minecraft:wheat_seeds', 1000, '48x minecraft:wheat', true)
+
+ // Carrot
+ Greenhouse('carrot', 'minecraft:carrot', 1000, '24x minecraft:carrot', false)
+ Greenhouse('carrot_boosted', 'minecraft:carrot', 1000, '48x minecraft:carrot', true)
+
+ // Potato
+ Greenhouse('potato', 'minecraft:potato', 1000, '24x minecraft:potato', false)
+ Greenhouse('potato_boosted', 'minecraft:potato', 1000, '48x minecraft:potato', true)
+
+ // Beetroot
+ Greenhouse('beetroot', 'minecraft:beetroot_seeds', 1000, '24x minecraft:beetroot', false)
+ Greenhouse('beetroot_boosted', 'minecraft:beetroot_seeds', 1000, '48x minecraft:beetroot', true)
+
+ // Mellon
+ Greenhouse('melon', 'minecraft:melon_seeds', 1000, '12x minecraft:melon', false)
+ Greenhouse('melon_boosted', 'minecraft:melon_seeds', 1000, '24x minecraft:melon', true)
+
+ // Pumpkin
+ Greenhouse('pumpkin', 'minecraft:pumpkin_seeds', 1000, '12x minecraft:pumpkin', false)
+ Greenhouse('pumpkin_boosted', 'minecraft:pumpkin_seeds', 1000, '24x minecraft:pumpkin', true)
+
+ // Nether Wart
+ Greenhouse('nether_wart', 'minecraft:nether_wart', 1000, '12x minecraft:nether_wart', false)
+ Greenhouse('nether_wart_boosted', 'minecraft:nether_wart', 1000, '24x minecraft:nether_wart', true)
+
+ // Red Mushroom
+ Greenhouse('red_mushroom', 'minecraft:red_mushroom', 1000, '12x minecraft:red_mushroom', false)
+ Greenhouse('red_mushroom_boosted', 'minecraft:red_mushroom', 1000, '24x minecraft:red_mushroom', true)
+
+ // Brown Mushroom
+ Greenhouse('brown_mushroom', 'minecraft:brown_mushroom', 1000, '12x minecraft:brown_mushroom', false)
+ Greenhouse('brown_mushroom_boosted', 'minecraft:brown_mushroom', 1000, '24x minecraft:brown_mushroom', true)
+})
+
GTCEuServerEvents.oreVeins(event => {
+ event.add("kubejs:custom_vein", vein => {
+ vein.clusterSize(40)
+ vein.weight(200)
+ vein.layer("deepslate")
+ vein.density(0.25)
+
+ vein.addSpawnDimension("minecraft:overworld")
+ vein.addSpawnBiome("#minecraft:is_overworld")
+
+ vein.heightRange({
+ height: {
+ type: "uniform",
+ min_inclusive: {
+ absolute: -60
+ },
+ max_inclusive: {
+ absolute: 20
+ }
+ }
+ })
+ vein.discardChanceOnAirExposure(0)
+ vein.generator({
+ type: "gtceu:layer",
+ layer_patterns: [
+ [
+ {
+ targets: [
+ "gold",
+ "tricalcium_phosphate"
+ ],
+ min_size: 2,
+ max_size: 2,
+ weight: 3
+ },
+ {
+ targets: [
+ "copper",
+ "vanadium_magnetite"
+ ],
+ min_size: 1,
+ max_size: 3,
+ weight: 1
+ }
+ ]
+ ]
+ })
+ })
+})
+
GTCEuServerEvents.oreVeins(event => {
+ event.remove("gtceu:magnetite_over_vein")
+})
+
If you want to remove all predefined ore veins (for example if you want to completely change ore generation +in your modpack), you can use the following code:
+GTCEuServerEvents.oreVeins(event => {
+ GTRegistries.ORE_VEINS.keys().forEach(id => event.remove(id))
+})
+
const $Either = Java.loadClass("com.mojang.datafixers.util.Either")
+GTCEuServerEvents.oreVeins(event => {
+ event.modify("gtceu:cassiterite_vein", vein => {
+ vein.veinGenerator.layerPatterns.get(0).layers.get(0).targets.set(0, $Either.right(GTMaterials.get('diamond')))
+ })//(1)
+})
+
const $GTOreFeatureEntry = Java.loadClass('com.gregtechceu.gtceu.api.data.worldgen.GTOreFeatureEntry')
+
+GTCEuServerEvents.oreVeins(event => {
+ $GTOreFeatureEntry.ALL.keySet().forEach(veinId => {
+ event.modify(veinId, vein => {
+ vein.layer('air')
+ })
+ }) //(1)
+})
+
air
.GTCEuStartupEvents.registry('gtceu:world_gen_layer', event => {
+ event.create('air')
+ .target([])
+})
+
StartupEvents.registry('block', event => {
+ event.create('infinity_coil_block', 'gtceu:coil')
+ .temperature(100)
+ .level(0)
+ .energyDiscount(1) // (1)
+ .tier(10)
+ .coilMaterial(GTMaterials.get('infinity'))
+ .texture('kubejs:block/example_block')
+ .hardness(5)
+ .requiresTool(true)
+ .material('metal')
+})
+
GTCEuStartupEvents.registry('gtceu:machine', event => {
+ event.create('test_simple_steam_machine', 'steam', true) // (1)
+})
+
GTCEuStartupEvents.registry('gtceu:machine', event => {
+ event.create('test_electric', 'simple', GTValues.LV, GTValues.MV, GTValues.HV) // (1)
+ .recipeType('test_recipe_type')
+ .tankScalingFunction(tier => tier * 3200)
+})
+
GTCEuStartupEvents.registry('gtceu:machine', event => {
+
+})
+
GTCEuStartupEvents.registry('gtceu:machine', event => {
+
+})
+
GTCEuStartupEvents.registry('gtceu:recipe_type', event => {
+ event.create('test_recipe_type')
+ .category('test')
+ .setEUIO('in')
+ .setMaxIOSize(3, 3, 3, 3) // (1)
+ .setSlotOverlay(false, false, GuiTextures.SOLIDIFIER_OVERLAY)
+ .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, FillDirection.LEFT_TO_RIGHT)
+ .setSound(GTSoundEntries.COOLING)
+})
+
GTCEuStartupEvents.registry('gtceu:recipe_type', event => {
+ event.create('test_recipe_type')
+ .category('test_kinetic')
+ .setEUIO('in')
+ .setMaxIOSize(3, 3, 3, 3)
+ .setSlotOverlay(false, false, GuiTextures.SOLIDIFIER_OVERLAY)
+ .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, FillDirection.LEFT_TO_RIGHT)
+ .setSound(GTSoundEntries.COOLING)
+ .setMaxTooltip(6) // (1)
+})
+
GTCEuStartupEvents.registry('gtceu:element', event => {
+ event.create('test_element', 27, 177, -1, null, 'test', false) // (1)
+})
+
GTCEuStartupEvents.registry('gtceu:material', event => {
+ event.create('andesite_alloy')
+ .ingot()
+ .components('1x andesite', '1x iron')
+ .color(0x839689).iconSet(GTMaterialIconSet.DULL)
+ .flags(GTMaterialFlags.GENERATE_PLATE, GTMaterialFlags.GENERATE_GEAR, GTMaterialFlags.GENERATE_SMALL_GEAR)
+})
+
GTCEuStartupEvents.registry('gtceu:material', event => {
+ event.create('purple_coal')
+ .gem(2, 4000)
+ .element(GTElements.C)
+ .ore(2, 3)
+ .color(0x7D2DDB).iconSet(GTMaterialIconSet.LIGNITE)
+})
+
GTCEuStartupEvents.registry('gtceu:material', event => {
+ event.create('mysterious_dust')
+ .dust()
+ .cableProperties(GTValues.V[GTValues.LV], 69, 0, true) // (1)
+})
+
GTCEuStartupEvents.registry('gtceu:material', event => {
+ event.create('mysterious_ooze')
+ .fluid()
+ .color(0x500bbf)
+ .fluidTemp(69420)
+})
+
Using material flags, you can specify several properties of each material, which +can influence how the material behaves, as well as which items are generated for it.
+Using material Flags
+GTCEuStartupEvents.registry('gtceu:material', event => {
+ event.create('my_material')
+ // ...
+ .flags(GTMaterialFlags.FLAMMABLE)
+})
+
FORCE_GENERATE_BLOCK
GENERATE_BOLT_SCREW
GENERATE_DENSE
GENERATE_FINE_WIRE
GENERATE_FOIL
GENERATE_FRAME
GENERATE_GEAR
GENERATE_LENS
GENERATE_LONG_ROD
GENERATE_PLATE
GENERATE_RING
GENERATE_ROD
GENERATE_ROTOR
GENERATE_ROUND
GENERATE_SMALL_GEAR
GENERATE_SPRING
GENERATE_SPRING_SMALL
BLAST_FURNACE_CALCITE_DOUBLE
BLAST_FURNACE_CALCITE_TRIPLE
DISABLE_ALLOY_BLAST
DISABLE_ALLOY_PROPERTY
CRYSTALLIZABLE
DECOMPOSITION_BY_CENTRIFUGING
DECOMPOSITION_BY_ELECTROLYZING
DISABLE_DECOMPOSITION
EXCLUDE_BLOCK_CRAFTING_BY_HAND_RECIPES
EXCLUDE_BLOCK_CRAFTING_RECIPES
EXCLUDE_PLATE_COMPRESSOR_RECIPES
EXPLOSIVE
FLAMMABLE
HIGH_SIFTER_OUTPUT
IS_MAGNETIC
MORTAR_GRINDABLE
NO_SMASHING
NO_SMELTING
NO_UNIFICATION
NO_WORKING
SOLDER_MATERIAL
SOLDER_MATERIAL_BAD
SOLDER_MATERIAL_GOOD
STICKY
The material system uses icon sets to determine the textures of generated blocks and items.
+The following icon sets are available by default:
+GTMaterialIconSet.BRIGHT
GTMaterialIconSet.CERTUS
GTMaterialIconSet.DIAMOND
GTMaterialIconSet.DULL
GTMaterialIconSet.EMERALD
GTMaterialIconSet.FINE
GTMaterialIconSet.FLINT
GTMaterialIconSet.FLUID
GTMaterialIconSet.GAS
GTMaterialIconSet.GEM_HORIZONTAL
GTMaterialIconSet.GEM_VERTICAL
GTMaterialIconSet.GLASS
GTMaterialIconSet.LAPIS
GTMaterialIconSet.LIGNITE
GTMaterialIconSet.MAGNETIC
GTMaterialIconSet.METALLIC
GTMaterialIconSet.NETHERSTAR
GTMaterialIconSet.OPAL
GTMaterialIconSet.QUARTZ
GTMaterialIconSet.ROUGH
GTMaterialIconSet.RUBY
GTMaterialIconSet.SAND
GTMaterialIconSet.SHINY
GTMaterialIconSet.WOOD
Custom iconsets can be specified as well, for example GTMatericalIconSet.get('infinity')
.
GTMaterialIconSet.get('')
+
BlastProperty.blastTemp() // (1)
+BlastProperty.gasTier() // (2)
+BlastProperty.durationOverride() // (3)
+BlastProperty.eutOverride() // (4)
+- DustProperty:
+ - .dust() // (5)
+- FluidPipeProperty:
+ - .fluidPipeProperties() // (6)
+- FluidProperty:
+ - .fluid() // (7)
+ - .isGas() // (8)
+ - .hasBlock()
+- GemProperty:
+ - .gem()
+- IngotProperty:
+ - .ingot() // (9)
+ - .smeltInto()
+ - .arcSmeltInto()
+ - .magneticMaterial()
+ - .macerateInto()
+- OreProperty:
+ - .ore() // (10)
+
Sets the Blast Furnace Temperature of the material. If the temperature is below 1000K recipes will be generated in the Primitive Blast Furnace. If above 1750K recipes for the Hot Ingot will be created along with the Vacuum Freezer Recipe to cool the ingot. Example: .blastTemp(2750)
Sets the Gas Tier which determins what GAS EBF recipes will be generated. Example: .gasTier(LOW)
Overrides the EBF's default behaviour for recipe durations.
+Overrides the EBF's default behaviour for EU/t.
+Used for creating a dust material. The haverst level and burn time can be specified in the brackets. Example: .dust(2, 4000)
Creates a fluid pipe out of the material it is added to. The possible values are: Max Fluid Temperature, Throughput, Gas Proof, Acid Proof, Cyro Proof, Plasma Proof,
+ Channels. Example: .fluidPipeProperties(9620, 850, false, false, false, false, 1)
GTCEu Modern offers extensive integration with KubeJS for customizability.
+Refer to this section for information on how to use it, as well as for examples.
While we try to keep this documentation up to date and as complete as possible, it may not always contain all of the latest information.
+Please also check the Beyond the Docs page for additional references.
+ + + + + + +Low Drag Lib provides an in-game UI Editor for creating custom GUI's for
+machines and for Recipe Types. The UI Editor is accessible in-game
+via the command /gtceu ui_editor
.
{"use strict";/*!
+ * escape-html
+ * Copyright(c) 2012-2013 TJ Holowaychuk
+ * Copyright(c) 2015 Andreas Lubbe
+ * Copyright(c) 2015 Tiancheng "Timothy" Gu
+ * MIT Licensed
+ */var Ha=/["'&<>]/;Nn.exports=$a;function $a(e){var t=""+e,r=Ha.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i