Skip to content

Commit

Permalink
Fluix Mana Pool (#9)
Browse files Browse the repository at this point in the history
A channel device which aggregates all Mana attached to a system, while acting as a Mana pool able to do infusion recipes. Can be used as a read-write view of all the system's attached Mana

Texture and model by @Sea-Kerman
  • Loading branch information
ramidzkh authored Jul 18, 2022
1 parent fad4296 commit 7ad2c73
Show file tree
Hide file tree
Showing 23 changed files with 866 additions and 11 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,10 @@ tasks.withType(JavaCompile) {

processResources {
inputs.property("version", project.version)
setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE)

from("LICENSE")
exclude(".cache")

filesMatching("fabric.mod.json") {
expand("version": project.version)
Expand Down
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
minecraft_version=1.18.2
loader_version=0.13.3
ae2_version=11.0.0
botania_version=431-FABRIC-SNAPSHOT
loader_version=0.14.8
ae2_version=11.1.4
botania_version=434-FABRIC-SNAPSHOT

# Temp fix for Spotless / Remove Unused Imports:
# https://github.com/diffplug/spotless/issues/834
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/appbot/ABBlocks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package appbot;

import static appbot.AppliedBotanics.id;

import net.minecraft.core.Registry;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;

import appbot.block.FluixPool;
import vazkii.botania.common.block.ModBlocks;
import vazkii.botania.common.block.mana.BlockPool;

public class ABBlocks {

public static final Block FLUIX_MANA_POOL = Registry.register(Registry.BLOCK, id("fluix_mana_pool"),
new FluixPool(BlockPool.Variant.FABULOUS, BlockBehaviour.Properties.copy(ModBlocks.fabulousPool)));

public static void register() {
}
}
9 changes: 5 additions & 4 deletions src/main/java/appbot/ABItems.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
import net.minecraft.Util;
import net.minecraft.core.Registry;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
import net.minecraft.world.item.*;
import net.minecraft.world.level.block.entity.BlockEntity;

import appbot.ae2.ManaKeyType;
import appbot.ae2.ManaP2PTunnelPart;
import vazkii.botania.common.item.ModItems;

import appeng.api.client.StorageCellModels;
import appeng.api.implementations.blockentities.IChestOrDrive;
Expand Down Expand Up @@ -42,6 +40,9 @@ private static Item.Properties properties() {
return new Item.Properties().tab(CREATIVE_TAB);
}

public static final Item FLUIX_MANA_POOL = Registry.register(Registry.ITEM, id("fluix_mana_pool"),
new BlockItem(ABBlocks.FLUIX_MANA_POOL, ModItems.defaultBuilder().tab(CREATIVE_TAB)));

public static final Item MANA_CELL_HOUSING = Registry.register(Registry.ITEM, id("mana_cell_housing"),
new Item(properties()));

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/appbot/AppliedBotanics.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import vazkii.botania.common.integration.corporea.CorporeaNodeDetectors;

import appeng.api.behaviors.ContainerItemStrategy;
import appeng.api.behaviors.GenericInternalInventory;
import appeng.api.behaviors.GenericSlotCapacities;
import appeng.api.features.P2PTunnelAttunement;
import appeng.api.inventories.PartApiLookup;
import appeng.api.stacks.AEKeyTypes;
import appeng.helpers.externalstorage.GenericStackInvStorage;
import appeng.parts.automation.FabricExternalStorageStrategy;
import appeng.parts.automation.StackWorldBehaviors;

Expand All @@ -26,11 +28,21 @@ static ResourceLocation id(String path) {

static void initialize() {
ABMenus.register();
ABBlocks.register();
ABItems.register();

AEKeyTypes.register(ManaKeyType.TYPE);
PartApiLookup.register(Apis.BLOCK, (part, context) -> part.getExposedApi(), ManaP2PTunnelPart.class);

Apis.BLOCK.registerFallback((world, pos, state, blockEntity, direction) -> {
// Fall back to generic inv
var genericInv = GenericInternalInventory.SIDED.find(world, pos, state, blockEntity, direction);
if (genericInv != null) {
return new GenericStackInvStorage<>(ManaVariantConversion.INSTANCE, ManaKeyType.TYPE, genericInv);
}
return null;
});

StackWorldBehaviors.registerImportStrategy(ManaKeyType.TYPE, (level, fromPos, fromSide) -> Reflect
.newStorageImportStrategy(Apis.BLOCK, ManaVariantConversion.INSTANCE, level, fromPos, fromSide));
StackWorldBehaviors.registerExportStrategy(ManaKeyType.TYPE, (level, fromPos, fromSide) -> Reflect
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/appbot/block/FluixPool.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package appbot.block;

import org.jetbrains.annotations.NotNull;

import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;

import vazkii.botania.common.block.mana.BlockPool;

public class FluixPool extends BlockPool {

public FluixPool(Variant v, Properties builder) {
super(v, builder);
}

@NotNull
@Override
public BlockEntity newBlockEntity(@NotNull BlockPos pos, @NotNull BlockState state) {
return new FluixPoolBlockEntity(pos, state);
}
}
240 changes: 240 additions & 0 deletions src/main/java/appbot/block/FluixPoolBlockEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
package appbot.block;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

import com.google.common.primitives.Ints;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.BlockState;

import appbot.ABBlocks;
import appbot.ae2.ManaKey;
import vazkii.botania.common.block.tile.mana.TilePool;

import appeng.api.config.Actionable;
import appeng.api.networking.*;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.StorageHelper;
import appeng.api.util.AECableType;
import appeng.hooks.ticking.TickHandler;
import appeng.me.helpers.BlockEntityNodeListener;
import appeng.me.helpers.IGridConnectedBlockEntity;

public class FluixPoolBlockEntity extends TilePool implements IInWorldGridNodeHost, IGridConnectedBlockEntity {

static {
/*
* We need to defer actually unloading tile entities until the end of the tick, after the chunk has been saved
* to disk. The CHUNK_UNLOAD event runs before the chunk has been saved, and if we disconnect nodes at that
* point, the saved data will be missing information from the node (such as the player id).
*
* From DeferredBlockEntityUnloader
*/
ServerChunkEvents.CHUNK_UNLOAD.register((serverWorld, worldChunk) -> {
List<FluixPoolBlockEntity> entitiesToRemove = new ArrayList<>();

for (var value : worldChunk.getBlockEntities().values()) {
if (value instanceof FluixPoolBlockEntity fluixPoolBlockEntity) {
entitiesToRemove.add(fluixPoolBlockEntity);
}
}

if (!entitiesToRemove.isEmpty()) {
TickHandler.instance().addCallable(serverWorld, (world) -> {
for (var blockEntity : entitiesToRemove) {
blockEntity.onChunkUnloaded();
}
});
}
});
}

private final Accessor mana = (Accessor) this;
private final IManagedGridNode mainNode = GridHelper.createManagedNode(this, BlockEntityNodeListener.INSTANCE)
.setFlags(GridFlags.REQUIRE_CHANNEL)
.setVisualRepresentation(ABBlocks.FLUIX_MANA_POOL)
.setInWorldNode(true)
.setExposedOnSides(EnumSet.complementOf(EnumSet.of(Direction.UP)))
.setTagName("proxy");
private final IActionSource actionSource = IActionSource.ofMachine(mainNode::getNode);

public FluixPoolBlockEntity(@NotNull BlockPos pos, @NotNull BlockState state) {
super(pos, state);
}

@Override
public boolean isFull() {
var grid = getMainNode().getGrid();

if (grid == null || !getMainNode().isActive()) {
return true;
}

return grid.getStorageService().getInventory().extract(ManaKey.KEY, 1, Actionable.SIMULATE, actionSource) == 0;
}

@Override
public void receiveMana(int mana) {
var grid = getMainNode().getGrid();

if (grid == null || !getMainNode().isActive()) {
return;
}

var storage = grid.getStorageService().getInventory();
var changed = false;

if (mana > 0) {
changed = StorageHelper.poweredInsert(grid.getEnergyService(), storage, ManaKey.KEY, mana,
actionSource) != 0;
} else if (mana < 0) {
changed = StorageHelper.poweredExtraction(grid.getEnergyService(), storage, ManaKey.KEY, -mana,
actionSource) != 0;
}

if (changed) {
setChanged();
markDispatchable();
}
}

@Override
public int getCurrentMana() {
var grid = getMainNode().getGrid();

if (grid == null) {
return mana.getMana();
}

if (!getMainNode().isActive()) {
return 0;
}

return (int) grid.getStorageService().getInventory().extract(ManaKey.KEY, Integer.MAX_VALUE,
Actionable.SIMULATE, actionSource);
}

public void recalculateManaCap() {
var grid = getMainNode().getGrid();

if (grid == null) {
return;
}

var oldMana = mana.getMana();
var oldManaCap = manaCap;

if (getMainNode().isActive()) {
var storage = grid.getStorageService().getInventory();
mana.setMana(
Ints.saturatedCast(
storage.extract(ManaKey.KEY, Integer.MAX_VALUE, Actionable.SIMULATE, actionSource)));
manaCap = Ints
.saturatedCast(storage.extract(ManaKey.KEY, Integer.MAX_VALUE, Actionable.SIMULATE, actionSource)
+ storage.insert(ManaKey.KEY, Integer.MAX_VALUE, Actionable.SIMULATE, actionSource));
} else {
mana.setMana(0);
manaCap = 0;
}

if (oldMana != mana.getMana() || oldManaCap != manaCap) {
setChanged();
markDispatchable();
}
}

@Override
public void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
this.getMainNode().saveToNBT(tag);
}

@Override
public void load(@NotNull CompoundTag tag) {
super.load(tag);
this.getMainNode().loadFromNBT(tag);
}

@Nullable
@Override
public IGridNode getGridNode(Direction dir) {
var node = this.getMainNode().getNode();
return node != null && node.isExposedOnSide(dir) ? node : null;
}

@Override
public AECableType getCableConnectionType(Direction dir) {
return AECableType.SMART;
}

@Override
public IManagedGridNode getMainNode() {
return mainNode;
}

@Override
public void securityBreak() {
this.level.destroyBlock(this.worldPosition, true);
}

@Override
public void saveChanges() {
if (this.level == null) {
return;
}

// Clientside is marked immediately as dirty as there is no queue processing
// Serverside is only queued once per tick to avoid costly operations
// TODO: Evaluate if this is still necessary
if (this.level.isClientSide) {
this.setChanged();
} else {
this.level.blockEntityChanged(this.worldPosition);
if (!this.setChangedQueued) {
TickHandler.instance().addCallable(null, this::setChangedAtEndOfTick);
this.setChangedQueued = true;
}
}
}

private boolean setChangedQueued = false;

private void setChangedAtEndOfTick() {
this.setChanged();
this.setChangedQueued = false;
}

public void onChunkUnloaded() {
this.getMainNode().destroy();
}

public void onReady() {
this.getMainNode().create(getLevel(), getBlockPos());
}

@Override
public void setRemoved() {
super.setRemoved();
this.getMainNode().destroy();
}

@Override
public void clearRemoved() {
super.clearRemoved();
GridHelper.onFirstTick(this, FluixPoolBlockEntity::onReady);
}

public interface Accessor {
int getMana();

void setMana(int mana);
}
}
7 changes: 7 additions & 0 deletions src/main/java/appbot/block/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package appbot.block;

import javax.annotation.ParametersAreNonnullByDefault;

import net.minecraft.MethodsReturnNonnullByDefault;
Loading

0 comments on commit 7ad2c73

Please sign in to comment.