Implement most of it

This commit is contained in:
shedaniel
2021-12-06 15:28:37 +08:00
parent 3e605d90fa
commit fadc321034
32 changed files with 1449 additions and 245 deletions

View File

@@ -20,6 +20,7 @@
package dev.architectury.fluid;
import dev.architectury.hooks.fluid.FluidStackHooks;
import dev.architectury.injectables.annotations.ExpectPlatform;
import dev.architectury.utils.NbtType;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@@ -30,22 +31,56 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
public final class FluidStack {
private static final FluidStack EMPTY = create(Fluids.EMPTY, 0);
private long amount;
@Nullable
private CompoundTag tag;
private Supplier<Fluid> fluid;
private static final FluidStackAdapter ADAPTER = adapt(FluidStack::getValue, FluidStack::new);
private Object value;
private FluidStack(Supplier<Fluid> fluid, long amount, CompoundTag tag) {
this.fluid = Objects.requireNonNull(fluid);
this.amount = amount;
this.tag = tag == null ? null : tag.copy();
this(ADAPTER.create(fluid, amount, tag));
}
private FluidStack(Object value) {
this.value = value;
}
private Object getValue() {
return value;
}
@ExpectPlatform
private static FluidStackAdapter adapt(Function<FluidStack, Object> toValue, Function<Object, FluidStack> fromValue) {
throw new AssertionError();
}
@ApiStatus.Internal
public interface FluidStackAdapter {
Object create(Supplier<Fluid> fluid, long amount, CompoundTag tag);
void check(Object object);
Supplier<Fluid> getRawFluidSupplier(Object object);
Fluid getFluid(Object object);
long getAmount(Object object);
void setAmount(Object object, long amount);
CompoundTag getTag(Object value);
void setTag(Object value, CompoundTag tag);
Object copy(Object value);
int hashCode(Object value);
}
public static FluidStack empty() {
@@ -82,61 +117,66 @@ public final class FluidStack {
@Nullable
public final Fluid getRawFluid() {
return fluid.get();
return ADAPTER.getFluid(value);
}
public final Supplier<Fluid> getRawFluidSupplier() {
return fluid;
return ADAPTER.getRawFluidSupplier(value);
}
public boolean isEmpty() {
return getRawFluid() == Fluids.EMPTY || amount <= 0;
return getRawFluid() == Fluids.EMPTY || ADAPTER.getAmount(value) <= 0;
}
public long getAmount() {
return isEmpty() ? 0 : amount;
return isEmpty() ? 0 : ADAPTER.getAmount(value);
}
public void setAmount(long amount) {
this.amount = amount;
ADAPTER.setAmount(value, amount);
}
public void grow(long amount) {
setAmount(this.amount + amount);
setAmount(getAmount() + amount);
}
public void shrink(long amount) {
setAmount(this.amount - amount);
setAmount(getAmount() - amount);
}
public boolean hasTag() {
return tag != null;
return getTag() != null;
}
@Nullable
public CompoundTag getTag() {
return tag;
return ADAPTER.getTag(value);
}
public void setTag(@Nullable CompoundTag tag) {
this.tag = tag;
ADAPTER.setTag(value, tag);
}
public CompoundTag getOrCreateTag() {
if (tag == null)
setTag(new CompoundTag());
CompoundTag tag = getTag();
if (tag == null) {
tag = new CompoundTag();
setTag(tag);
return tag;
}
return tag;
}
@Nullable
public CompoundTag getChildTag(String childName) {
CompoundTag tag = getTag();
if (tag == null)
return null;
return tag.getCompound(childName);
}
public CompoundTag getOrCreateChildTag(String childName) {
getOrCreateTag();
CompoundTag tag = getOrCreateTag();
var child = tag.getCompound(childName);
if (!tag.contains(childName, Tag.TAG_COMPOUND)) {
tag.put(childName, child);
@@ -145,6 +185,7 @@ public final class FluidStack {
}
public void removeChildTag(String childName) {
CompoundTag tag = getTag();
if (tag != null)
tag.remove(childName);
}
@@ -158,17 +199,12 @@ public final class FluidStack {
}
public FluidStack copy() {
return new FluidStack(fluid, amount, tag);
return new FluidStack(ADAPTER.copy(value));
}
@Override
public final int hashCode() {
var code = 1;
code = 31 * code + getFluid().hashCode();
code = 31 * code + Long.hashCode(amount);
if (tag != null)
code = 31 * code + tag.hashCode();
return code;
return ADAPTER.hashCode(value);
}
@Override
@@ -188,7 +224,9 @@ public final class FluidStack {
}
private boolean isTagEqual(FluidStack other) {
return tag == null ? other.tag == null : other.tag != null && tag.equals(other.tag);
var tag = getTag();
var otherTag = other.getTag();
return tag == null ? otherTag == null : otherTag != null && tag.equals(otherTag);
}
public static FluidStack read(FriendlyByteBuf buf) {
@@ -209,7 +247,7 @@ public final class FluidStack {
public FluidStack copyWithAmount(long amount) {
if (isEmpty()) return this;
return new FluidStack(fluid, amount, tag);
return new FluidStack(getRawFluidSupplier(), amount, getTag());
}
@Environment(EnvType.CLIENT)

View File

@@ -0,0 +1,121 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.impl.transfer.access;
import dev.architectury.transfer.access.BlockLookup;
import dev.architectury.transfer.access.BlockLookupAccess;
import dev.architectury.transfer.access.BlockLookupRegistration;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class BlockLookupAccessImpl<T, C> implements BlockLookupAccess<T, C> {
private final List<BlockLookup<T, C>> lookups = new ArrayList<>();
private final List<BlockLookupRegistration<T, C>> registrationHandlers = new ArrayList<>();
@Override
public void addQueryHandler(BlockLookup<T, C> handler) {
this.lookups.add(0, handler);
}
@Override
public void addRegistrationHandler(BlockLookupRegistration<T, C> registration) {
this.registrationHandlers.add(0, registration);
}
@Override
@Nullable
public T get(Level level, BlockPos pos, C context) {
for (BlockLookup<T, C> handler : lookups) {
T result = handler.get(level, pos, context);
if (result != null) {
return result;
}
}
return null;
}
@Override
@Nullable
public T get(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, C context) {
for (BlockLookup<T, C> handler : lookups) {
T result = handler.get(level, pos, state, blockEntity, context);
if (result != null) {
return result;
}
}
return null;
}
@Override
public boolean register(ResourceLocation id, BlockAccessProvider<Function<C, T>, @Nullable BlockEntity> provider) {
for (BlockLookupRegistration<T, C> handler : registrationHandlers) {
if (handler.register(id, provider)) {
return true;
}
}
return false;
}
@Override
public boolean registerForBlocks(ResourceLocation id, BlockAccessProvider<Function<C, T>, @Nullable BlockEntity> provider, Block... blocks) {
for (BlockLookupRegistration<T, C> handler : registrationHandlers) {
if (handler.registerForBlocks(id, provider, blocks)) {
return true;
}
}
return false;
}
@Override
public <B extends BlockEntity> boolean registerForBlockEntities(ResourceLocation id, BlockAccessProvider<Function<C, T>, B> provider, BlockEntityType<B>... blockEntityTypes) {
for (BlockLookupRegistration<T, C> handler : registrationHandlers) {
if (handler.registerForBlockEntities(id, provider, blockEntityTypes)) {
return true;
}
}
return false;
}
@Override
public <B extends BlockEntity> boolean registerForSelf(ResourceLocation id, BlockEntityType<B>... blockEntityTypes) {
for (BlockLookupRegistration<T, C> handler : registrationHandlers) {
if (handler.registerForSelf(id, blockEntityTypes)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,70 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.impl.transfer.access;
import dev.architectury.transfer.access.ItemLookup;
import dev.architectury.transfer.access.ItemLookupAccess;
import dev.architectury.transfer.access.ItemLookupRegistration;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class ItemLookupAccessImpl<T, C> implements ItemLookupAccess<T, C> {
private final List<ItemLookup<T, C>> lookups = new ArrayList<>();
private final List<ItemLookupRegistration<T, C>> registrationHandlers = new ArrayList<>();
@Override
public void addQueryHandler(ItemLookup<T, C> handler) {
this.lookups.add(handler);
}
@Override
public void addRegistrationHandler(ItemLookupRegistration<T, C> registration) {
this.registrationHandlers.add(registration);
}
@Override
@Nullable
public T get(ItemStack stack, C context) {
for (ItemLookup<T, C> handler : lookups) {
T result = handler.get(stack, context);
if (result != null) {
return result;
}
}
return null;
}
@Override
public boolean register(ResourceLocation id, ItemAccessProvider<Function<C, T>> provider) {
for (ItemLookupRegistration<T, C> handler : registrationHandlers) {
if (handler.register(id, provider)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,48 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer;
/**
* The base interface for all lookup accesses.
*
* @param <T> the type of the API
* @param <H> the type of the query handler
* @param <R> the type of the registration handler
*/
public interface ApiLookupAccess<T, H, R> {
/**
* Adds a handler for querying the api, this will
* only be called by architectury mods.
* <p>
* For adding interop with platform APIs, use the
* register methods for linking and attaching to fabric's
* api lookup or forge's capability system.
*
* @param handler the query handler to add
*/
void addQueryHandler(H handler);
/**
* Adds a lookup registration.
*
* @param registration the registration handler to add
*/
void addRegistrationHandler(R registration);
}

View File

@@ -69,4 +69,14 @@ class EmptyTransferHandler<T> implements TransferHandler<T> {
public T blank() {
return blank.get();
}
@Override
public Object saveState() {
throw new UnsupportedOperationException();
}
@Override
public void loadState(Object state) {
throw new UnsupportedOperationException();
}
}

View File

@@ -19,13 +19,44 @@
package dev.architectury.transfer;
import java.util.function.Predicate;
/**
* Represents an <b>immutable</b> view of a resource.
*
* @param <T> the type of resource
*/
public interface ResourceView<T> {
public interface ResourceView<T> extends TransferView<T> {
/**
* Returns the resource that this view represents.
* The returned resource is <b>immutable</b>.
*
* @return the resource
*/
T getResource();
/**
* Returns the capacity of this view.
*
* @return the capacity
*/
long getCapacity();
/**
* Returns a copy of a resource with the given amount.
*
* @param resource the resource to copy
* @param amount the amount to copy
* @return the copy
*/
T copyWithAmount(T resource, long amount);
@Override
default T extract(Predicate<T> toExtract, long maxAmount, TransferAction action) {
if (toExtract.test(getResource())) {
return extract(copyWithAmount(getResource(), maxAmount), action);
}
return blank();
}
}

View File

@@ -23,7 +23,6 @@ import dev.architectury.fluid.FluidStack;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
@@ -36,7 +35,7 @@ import java.util.stream.Stream;
* @param <T> the type of resource
*/
@ApiStatus.NonExtendable
public interface TransferHandler<T> {
public interface TransferHandler<T> extends TransferView<T> {
/**
* Returns an empty item transfer handler, which does nothing.
*
@@ -91,29 +90,4 @@ public interface TransferHandler<T> {
* @return the amount that was inserted
*/
long insert(T toInsert, TransferAction action);
/**
* Extracts the given resource from the handler, returning the stack that was extracted.
*
* @param toExtract the resource to extract
* @param action whether to simulate or actually extract the resource
* @return the stack that was extracted
*/
T extract(T toExtract, TransferAction action);
/**
* Extracts the given resource from the handler, returning the stack that was extracted.
*
* @param toExtract the predicates to use to filter the resources to extract
* @param action whether to simulate or actually extract the resource
* @return the stack that was extracted
*/
T extract(Predicate<T> toExtract, long maxAmount, TransferAction action);
/**
* Returns a blank resource.
*
* @return a blank resource
*/
T blank();
}

View File

@@ -0,0 +1,74 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer;
import org.jetbrains.annotations.ApiStatus;
import java.util.function.Predicate;
public interface TransferView<T> {
/**
* Extracts the given resource from the handler, returning the stack that was extracted.
*
* @param toExtract the resource to extract
* @param action whether to simulate or actually extract the resource
* @return the stack that was extracted
*/
T extract(T toExtract, TransferAction action);
/**
* Extracts the given resource from the handler, returning the stack that was extracted.
*
* @param toExtract the predicates to use to filter the resources to extract
* @param maxAmount the maximum amount of resources to extract
* @param action whether to simulate or actually extract the resource
* @return the stack that was extracted
*/
T extract(Predicate<T> toExtract, long maxAmount, TransferAction action);
/**
* Returns a blank resource.
*
* @return a blank resource
*/
T blank();
/**
* Returns the saved state of the handler, this method must not be called by the implementation.
* This method is used to provide support for transactions, which is only used if the handler is
* registered with the lookup.
*
* @return the saved state of the handler
* @throws UnsupportedOperationException if the handler is provided by the platform
*/
@ApiStatus.OverrideOnly
Object saveState();
/**
* Loads the saved state of the handler, this method must not be called by the implementation.
* This method is used to provide support for transactions, which is only used if the handler is
* registered with the lookup.
*
* @param state the saved state of the handler
* @throws UnsupportedOperationException if the handler is provided by the platform
*/
@ApiStatus.OverrideOnly
void loadState(Object state);
}

View File

@@ -19,28 +19,36 @@
package dev.architectury.transfer.access;
import dev.architectury.transfer.TransferAccess;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
public interface BlockTransferAccess<T, C> extends TransferAccess<T> {
public interface BlockLookup<T, Context> {
/**
* Queries the api for the given block.
* If you need the block state or block entity, you must query it yourself,
* as this method will not do it for you.
*
* @param level the level
* @param pos the position of the block
* @param context the context
* @return the transfer handler, or null if none was found
*/
@Nullable
T get(Level level, BlockPos pos, C context);
T get(Level level, BlockPos pos, Context context);
/**
* Queries the api for the given block.
*
* @param level the level
* @param pos the position of the block
* @param state the state of the block
* @param blockEntity the block entity, or null if none
* @param context the context
* @return the transfer handler, or null if none was found
*/
@Nullable
T get(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, C context);
void register(ResourceLocation id, BlockAccessProvider<T, C> provider);
@FunctionalInterface
interface BlockAccessProvider<T, C> {
@Nullable
Function<C, T> get(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity);
}
}
T get(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, Context context);
}

View File

@@ -0,0 +1,35 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer.access;
import dev.architectury.impl.transfer.access.BlockLookupAccessImpl;
import dev.architectury.transfer.ApiLookupAccess;
/**
* An API lookup for blocks.
*
* @param <T> the type of the API
* @param <Context> the type of the context
*/
public interface BlockLookupAccess<T, Context> extends ApiLookupAccess<T, BlockLookup<T, Context>, BlockLookupRegistration<T, Context>>, BlockLookup<T, Context>, BlockLookupRegistration<T, Context> {
static <T, Context> BlockLookupAccess<T, Context> create() {
return new BlockLookupAccessImpl<>();
}
}

View File

@@ -0,0 +1,55 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer.access;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
public interface BlockLookupRegistration<T, Context> {
/**
* Registers a lookup registration handler, this is used to provide
* interop with platform apis.
*
* @param id the id of the lookup registration handler
* @param provider the provider of the lookup registration handler
* @return true if the registration was successful
*/
boolean register(ResourceLocation id, BlockAccessProvider<Function<Context, T>, @Nullable BlockEntity> provider);
boolean registerForBlocks(ResourceLocation id, BlockAccessProvider<Function<Context, T>, @Nullable BlockEntity> provider, Block... blocks);
<B extends BlockEntity> boolean registerForBlockEntities(ResourceLocation id, BlockAccessProvider<Function<Context, T>, B> provider, BlockEntityType<B>... blockEntityTypes);
<B extends BlockEntity> boolean registerForSelf(ResourceLocation id, BlockEntityType<B>... blockEntityTypes);
@FunctionalInterface
interface BlockAccessProvider<R, B extends BlockEntity> {
@Nullable
R get(Level level, BlockPos pos, BlockState state, B blockEntity);
}
}

View File

@@ -19,12 +19,17 @@
package dev.architectury.transfer.access;
import dev.architectury.transfer.TransferAccess;
import dev.architectury.transfer.TransferHandler;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
public interface ItemTransferAccess<T, C> extends TransferAccess<T> {
public interface ItemLookup<T, Context> {
/**
* Queries the api for the given item stack.
*
* @param stack the item stack
* @param context the context
* @return the transfer handler, or null if none was found
*/
@Nullable
T get(ItemStack stack, C context);
}
T get(ItemStack stack, Context context);
}

View File

@@ -17,24 +17,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer.forge;
package dev.architectury.transfer.access;
import dev.architectury.transfer.TransferContext;
import dev.architectury.impl.transfer.access.ItemLookupAccessImpl;
import dev.architectury.transfer.ApiLookupAccess;
public enum TransferContextImpl implements TransferContext {
INSTANCE;
public static TransferContext create() {
return TransferContextImpl.INSTANCE;
}
@Override
public int nestingDepth() {
return 0;
}
@Override
public void close() throws Exception {
public interface ItemLookupAccess<T, Context> extends ApiLookupAccess<T, ItemLookup<T, Context>, ItemLookupRegistration<T, Context>>, ItemLookup<T, Context>, ItemLookupRegistration<T, Context> {
static <T, Context> ItemLookupAccess<T, Context> create() {
return new ItemLookupAccessImpl<>();
}
}

View File

@@ -17,7 +17,20 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer;
package dev.architectury.transfer.access;
public interface TransferAccess<T> {
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
public interface ItemLookupRegistration<T, Context> {
boolean register(ResourceLocation id, ItemAccessProvider<Function<Context, T>> provider);
@FunctionalInterface
interface ItemAccessProvider<R> {
@Nullable
R get(ItemStack stack);
}
}

View File

@@ -22,8 +22,8 @@ package dev.architectury.transfer.fluid;
import dev.architectury.fluid.FluidStack;
import dev.architectury.injectables.annotations.ExpectPlatform;
import dev.architectury.transfer.TransferHandler;
import dev.architectury.transfer.access.BlockTransferAccess;
import dev.architectury.transfer.access.ItemTransferAccess;
import dev.architectury.transfer.access.BlockLookupAccess;
import dev.architectury.transfer.access.ItemLookupAccess;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
@@ -32,16 +32,25 @@ public class FluidTransfer {
private FluidTransfer() {
}
public static final BlockTransferAccess<TransferHandler<FluidStack>, Direction> BLOCK = instantiateBlockAccess();
public static final ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>> ITEM = instantiateItemAccess();
/**
* A lookup access for fluid transfer handlers, the direction context is
* only required on fabric.
* <p>
* This is the equivalent to getting the fluid transfer handler for a
* block entity on Forge, or the storage with the lookup api on Fabric.
* <p>
* There are performance implications for using the architectury lookups,
* please keep your implementations as simple as possible.
*/
public static final BlockLookupAccess<TransferHandler<FluidStack>, Direction> BLOCK = BlockLookupAccess.create();
// public static final ItemLookupAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>> ITEM = ItemLookupAccess.create();
@ExpectPlatform
private static BlockTransferAccess<TransferHandler<FluidStack>, Direction> instantiateBlockAccess() {
throw new AssertionError();
static {
init();
}
@ExpectPlatform
private static ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>> instantiateItemAccess() {
private static void init() {
throw new AssertionError();
}

View File

@@ -21,16 +21,20 @@ package dev.architectury.transfer.item;
import dev.architectury.injectables.annotations.ExpectPlatform;
import dev.architectury.transfer.TransferHandler;
import dev.architectury.transfer.access.BlockTransferAccess;
import dev.architectury.transfer.access.BlockLookupAccess;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
public class ItemTransfer {
public static final BlockTransferAccess<TransferHandler<ItemStack>, Direction> BLOCK = instantiateBlockAccess();
public static final BlockLookupAccess<TransferHandler<ItemStack>, Direction> BLOCK = BlockLookupAccess.create();
static {
init();
}
@ExpectPlatform
private static BlockTransferAccess<TransferHandler<ItemStack>, Direction> instantiateBlockAccess() {
private static void init() {
throw new AssertionError();
}

View File

@@ -0,0 +1,113 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.fluid.fabric;
import dev.architectury.fluid.FluidStack;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.material.Fluid;
import org.jetbrains.annotations.ApiStatus;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
@ApiStatus.Internal
public enum FluidStackImpl implements FluidStack.FluidStackAdapter {
INSTANCE;
public static Function<FluidStack, Object> toValue;
public static Function<Object, FluidStack> fromValue;
public static FluidStack.FluidStackAdapter adapt(Function<FluidStack, Object> toValue, Function<Object, dev.architectury.fluid.FluidStack> fromValue) {
FluidStackImpl.toValue = toValue;
FluidStackImpl.fromValue = fromValue;
return INSTANCE;
}
public static class Pair {
public FluidVariant variant;
public long amount;
public Pair(FluidVariant variant, long amount) {
this.variant = variant;
this.amount = amount;
}
}
@Override
public Object create(Supplier<Fluid> fluid, long amount, CompoundTag tag) {
return new Pair(FluidVariant.of(Objects.requireNonNull(fluid).get(), tag == null ? null : tag.copy()), amount);
}
@Override
public void check(Object object) {
if (!(object instanceof Pair)) {
throw new IllegalArgumentException("Expected FluidStackImpl.Pair, got " + object.getClass().getName());
}
}
@Override
public Supplier<Fluid> getRawFluidSupplier(Object object) {
return ((Pair) object).variant::getFluid;
}
@Override
public Fluid getFluid(Object object) {
return ((Pair) object).variant.getFluid();
}
@Override
public long getAmount(Object object) {
return ((Pair) object).amount;
}
@Override
public void setAmount(Object object, long amount) {
((Pair) object).amount = amount;
}
@Override
public CompoundTag getTag(Object value) {
return ((Pair) value).variant.getNbt();
}
@Override
public void setTag(Object value, CompoundTag tag) {
((Pair) value).variant = FluidVariant.of(((Pair) value).variant.getFluid(), tag);
}
@Override
public Object copy(Object value) {
return new Pair(FluidVariant.of(((Pair) value).variant.getFluid(), ((Pair) value).variant.getNbt()), ((Pair) value).amount);
}
@Override
public int hashCode(Object value) {
var pair = (Pair) value;
var code = 1;
code = 31 * code + pair.variant.getFluid().hashCode();
code = 31 * code + Long.hashCode(pair.amount);
var tag = pair.variant.getNbt();
if (tag != null)
code = 31 * code + tag.hashCode();
return code;
}
}

View File

@@ -20,6 +20,7 @@
package dev.architectury.hooks.fluid.fabric;
import dev.architectury.fluid.FluidStack;
import dev.architectury.fluid.fabric.FluidStackImpl;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.fabricmc.fabric.api.transfer.v1.storage.StorageView;
@@ -32,10 +33,10 @@ public final class FluidStackHooksFabric {
}
public static FluidStack fromFabric(FluidVariant variant, long amount) {
return FluidStack.create(variant.getFluid(), amount, variant.getNbt());
return FluidStackImpl.fromValue.apply(new FluidStackImpl.Pair(variant, amount));
}
public static FluidVariant toFabric(FluidStack stack) {
return FluidVariant.of(stack.getFluid(), stack.getTag());
return ((FluidStackImpl.Pair) FluidStackImpl.toValue.apply(stack)).variant;
}
}

View File

@@ -20,7 +20,7 @@
package dev.architectury.transfer.fabric;
import dev.architectury.transfer.TransferHandler;
import dev.architectury.transfer.access.BlockTransferAccess;
import dev.architectury.transfer.access.BlockLookup;
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
@@ -30,11 +30,11 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
public class FabricBlockTransferAccess<T, F, C> implements BlockTransferAccess<TransferHandler<T>, C> {
public class BlockApiLookupWrapper<T, F, C> implements BlockLookup<TransferHandler<T>, C> {
private final BlockApiLookup<F, C> lookup;
private final Function<F, TransferHandler<T>> wrapper;
private final Function<@Nullable F, @Nullable TransferHandler<T>> wrapper;
public FabricBlockTransferAccess(BlockApiLookup<F, C> lookup, Function<F, TransferHandler<T>> wrapper) {
public BlockApiLookupWrapper(BlockApiLookup<F, C> lookup, Function<@Nullable F, @Nullable TransferHandler<T>> wrapper) {
this.lookup = lookup;
this.wrapper = wrapper;
}
@@ -49,7 +49,7 @@ public class FabricBlockTransferAccess<T, F, C> implements BlockTransferAccess<T
@Nullable
public TransferHandler<T> get(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, C context) {
if (blockEntity != null) {
return wrapper.apply(lookup.find(level, pos, blockEntity.getBlockState(), blockEntity, context));
return wrapper.apply(lookup.find(level, pos, state, blockEntity, context));
} else {
return get(level, pos, context);
}

View File

@@ -0,0 +1,96 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer.fabric;
import dev.architectury.transfer.access.BlockLookupRegistration;
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
import java.util.function.Predicate;
public class FabricBlockLookupRegistration<T, A, Context> implements BlockLookupRegistration<T, Context> {
private final Function<T, A> unwrapper;
private final BlockApiLookup<A, Context> lookup;
public static <T, A, Context> FabricBlockLookupRegistration<T, A, Context> create(BlockApiLookup<A, Context> lookup, Function<T, A> unwrapper) {
return new FabricBlockLookupRegistration<>(unwrapper, lookup);
}
private FabricBlockLookupRegistration(Function<T, A> unwrapper, BlockApiLookup<A, Context> lookup) {
this.unwrapper = unwrapper;
this.lookup = lookup;
}
public BlockApiLookup.BlockApiProvider<A, Context> provider(BlockAccessProvider<Function<Context, T>, BlockEntity> provider) {
return (level, pos, state, blockEntity, context) -> {
Function<Context, T> function = provider.get(level, pos, state, blockEntity);
if (function != null) {
return unwrapper.apply(function.apply(context));
}
return null;
};
}
public <B extends BlockEntity> BlockApiLookup.BlockEntityApiProvider<A, Context> provider(Predicate<@Nullable BlockEntityType<?>> beTypePredicate, BlockAccessProvider<Function<Context, T>, B> provider) {
return (blockEntity, context) -> {
if (!beTypePredicate.test(blockEntity == null ? null : blockEntity.getType())) {
return null;
}
Function<Context, T> function = provider.get(blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), (B) blockEntity);
if (function != null) {
return unwrapper.apply(function.apply(context));
}
return null;
};
}
@Override
public boolean register(ResourceLocation id, BlockAccessProvider<Function<Context, T>, @Nullable BlockEntity> provider) {
lookup.registerFallback(provider(provider));
return true;
}
@Override
public boolean registerForBlocks(ResourceLocation id, BlockAccessProvider<Function<Context, T>, @Nullable BlockEntity> provider, Block... blocks) {
lookup.registerForBlocks(provider(provider), blocks);
return true;
}
@Override
public <B extends BlockEntity> boolean registerForBlockEntities(ResourceLocation id, BlockAccessProvider<Function<Context, T>, B> provider, BlockEntityType<B>... blockEntityTypes) {
lookup.registerForBlockEntities(provider(type -> true, provider), blockEntityTypes);
return true;
}
@Override
public <B extends BlockEntity> boolean registerForSelf(ResourceLocation id, BlockEntityType<B>... blockEntityTypes) {
lookup.registerSelf(blockEntityTypes);
return false;
}
}

View File

@@ -48,6 +48,10 @@ public class FabricStorageTransferHandler<F, S> implements TransferHandler<S> {
this.typeAdapter = typeAdapter;
}
public Storage<F> getStorage() {
return storage;
}
@Override
public Stream<ResourceView<S>> getContents() {
Transaction transaction = Transaction.openNested(this.transaction);
@@ -135,6 +139,16 @@ public class FabricStorageTransferHandler<F, S> implements TransferHandler<S> {
return typeAdapter.blank.get();
}
@Override
public Object saveState() {
throw new UnsupportedOperationException();
}
@Override
public void loadState(Object state) {
throw new UnsupportedOperationException();
}
private boolean isEmpty(S stack) {
return typeAdapter.isEmpty.test(stack);
}
@@ -179,15 +193,51 @@ public class FabricStorageTransferHandler<F, S> implements TransferHandler<S> {
public long getCapacity() {
return view.getCapacity();
}
@Override
public S copyWithAmount(S resource, long amount) {
return FabricStorageTransferHandler.this.copyWithAmount(resource, amount);
}
@Override
public S extract(S toExtract, TransferAction action) {
if (isEmpty(toExtract)) return blank();
long extracted;
try (Transaction nested = Transaction.openNested(FabricStorageTransferHandler.this.transaction)) {
extracted = this.view.extract(toFabric(toExtract), getAmount(toExtract), nested);
if (action == TransferAction.ACT) {
nested.commit();
}
}
return copyWithAmount(toExtract, extracted);
}
@Override
public S blank() {
return null;
}
@Override
public Object saveState() {
throw new UnsupportedOperationException();
}
@Override
public void loadState(Object state) {
throw new UnsupportedOperationException();
}
}
public static class TypeAdapter<F, S> {
private final Function<S, F> toFabric;
private final FunctionWithAmount<F, S> fromFabric;
private final FunctionWithAmount<S, S> copyWithAmount;
private final Supplier<S> blank;
private final Predicate<S> isEmpty;
private final ToLongFunction<S> toAmount;
final Function<S, F> toFabric;
final FunctionWithAmount<F, S> fromFabric;
final FunctionWithAmount<S, S> copyWithAmount;
final Supplier<S> blank;
final Predicate<S> isEmpty;
final ToLongFunction<S> toAmount;
public TypeAdapter(Function<S, F> toFabric, FunctionWithAmount<F, S> fromFabric, FunctionWithAmount<S, S> copyWithAmount, Supplier<S> blank, Predicate<S> isEmpty, ToLongFunction<S> toAmount) {
this.toFabric = toFabric;

View File

@@ -1,56 +0,0 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer.fabric;
import dev.architectury.transfer.TransferContext;
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class TransferContextImpl implements TransferContext {
@ApiStatus.Internal
public final Transaction transaction;
private TransferContextImpl(Transaction transaction) {
this.transaction = transaction;
}
public static TransferContext create() {
return new TransferContextImpl(Transaction.openOuter());
}
public static TransferContext create(@Nullable Object transaction) {
if (transaction != null && !(transaction instanceof Transaction)) {
throw new IllegalArgumentException("transaction must be a Transaction");
}
return new TransferContextImpl(transaction == null ? Transaction.openOuter() : (Transaction) transaction);
}
@Override
public int nestingDepth() {
return transaction.nestingDepth();
}
@Override
public void close() throws Exception {
this.transaction.close();
}
}

View File

@@ -0,0 +1,121 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer.fabric;
import dev.architectury.transfer.ResourceView;
import dev.architectury.transfer.TransferAction;
import dev.architectury.transfer.TransferHandler;
import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
import net.fabricmc.fabric.api.transfer.v1.storage.StorageView;
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;
import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant;
import java.util.Iterator;
import java.util.stream.Stream;
public class TransferHandlerStorage<F, S> extends SnapshotParticipant<Object> implements Storage<F> {
private final TransferHandler<S> handler;
private final FabricStorageTransferHandler.TypeAdapter<F, S> typeAdapter;
public TransferHandlerStorage(TransferHandler<S> handler, FabricStorageTransferHandler.TypeAdapter<F, S> typeAdapter) {
this.handler = handler;
this.typeAdapter = typeAdapter;
}
@Override
public Iterator<StorageView<F>> iterator(TransactionContext transaction) {
Stream<StorageView<F>> stream = this.handler.getContents()
.map(view -> new FabricStorageView<F, S>(view, typeAdapter));
transaction.addCloseCallback((t, result) -> {
stream.close();
});
return stream.iterator();
}
@Override
public long insert(F resource, long maxAmount, TransactionContext transaction) {
updateSnapshots(transaction);
return this.handler.insert(typeAdapter.fromFabric.apply(resource, maxAmount), TransferAction.ACT);
}
@Override
public long extract(F resource, long maxAmount, TransactionContext transaction) {
updateSnapshots(transaction);
S extracted = this.handler.extract(typeAdapter.fromFabric.apply(resource, maxAmount), TransferAction.ACT);
return typeAdapter.toAmount.applyAsLong(extracted);
}
@Override
protected Object createSnapshot() {
return this.handler.saveState();
}
@Override
protected void readSnapshot(Object snapshot) {
this.handler.loadState(snapshot);
}
private static class FabricStorageView<F, S> extends SnapshotParticipant<Object> implements StorageView<F> {
private final ResourceView<S> storage;
private final FabricStorageTransferHandler.TypeAdapter<F, S> typeAdapter;
private FabricStorageView(ResourceView<S> view, FabricStorageTransferHandler.TypeAdapter<F, S> typeAdapter) {
this.storage = view;
this.typeAdapter = typeAdapter;
}
@Override
public long extract(F resource, long maxAmount, TransactionContext transaction) {
updateSnapshots(transaction);
S extracted = this.storage.extract(typeAdapter.fromFabric.apply(resource, maxAmount), TransferAction.ACT);
return typeAdapter.toAmount.applyAsLong(extracted);
}
@Override
public boolean isResourceBlank() {
return getAmount() <= 0;
}
@Override
public F getResource() {
return typeAdapter.toFabric.apply(storage.getResource());
}
@Override
public long getAmount() {
return typeAdapter.toAmount.applyAsLong(storage.getResource());
}
@Override
public long getCapacity() {
return storage.getCapacity();
}
@Override
protected Object createSnapshot() {
return storage.saveState();
}
@Override
protected void readSnapshot(Object snapshot) {
storage.loadState(snapshot);
}
}
}

View File

@@ -22,10 +22,11 @@ package dev.architectury.transfer.fluid.fabric;
import dev.architectury.fluid.FluidStack;
import dev.architectury.hooks.fluid.fabric.FluidStackHooksFabric;
import dev.architectury.transfer.TransferHandler;
import dev.architectury.transfer.access.BlockTransferAccess;
import dev.architectury.transfer.access.ItemTransferAccess;
import dev.architectury.transfer.fabric.FabricBlockTransferAccess;
import dev.architectury.transfer.fabric.BlockApiLookupWrapper;
import dev.architectury.transfer.fabric.FabricBlockLookupRegistration;
import dev.architectury.transfer.fabric.FabricStorageTransferHandler;
import dev.architectury.transfer.fabric.TransferHandlerStorage;
import dev.architectury.transfer.fluid.FluidTransfer;
import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
@@ -34,7 +35,6 @@ import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage;
import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage;
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
@@ -64,18 +64,23 @@ public class FluidTransferImpl {
}
}
public static BlockTransferAccess<TransferHandler<FluidStack>, Direction> instantiateBlockAccess() {
return new FabricBlockTransferAccess<>(FluidStorage.SIDED, FluidTransferImpl::wrap);
@Nullable
public static Storage<FluidVariant> unwrap(@Nullable TransferHandler<FluidStack> handler) {
if (handler == null) return null;
if (handler instanceof FabricStorageTransferHandler) {
return ((FabricStorageTransferHandler) handler).getStorage();
} else {
return new TransferHandlerStorage<>(handler, TYPE_ADAPTER);
}
}
public static ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>> instantiateItemAccess() {
return new ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>>() {
@Override
@Nullable
public TransferHandler<FluidStack> get(ItemStack stack, TransferHandler<ItemStack> context) {
return wrap(FluidStorage.ITEM.find(stack, fromTransfer(stack, context)));
}
};
public static void init() {
FluidTransfer.BLOCK.addQueryHandler(new BlockApiLookupWrapper<>(FluidStorage.SIDED, FluidTransferImpl::wrap));
FluidTransfer.BLOCK.addRegistrationHandler(FabricBlockLookupRegistration.create(FluidStorage.SIDED, FluidTransferImpl::unwrap));
// FluidTransfer.ITEM.addQueryHandler((stack, context) -> {
// return wrap(FluidStorage.ITEM.find(stack, fromTransfer(stack, context)));
// });
}
public static ContainerItemContext fromTransfer(ItemStack stack, TransferHandler<ItemStack> transferHandler) {

View File

@@ -46,6 +46,10 @@ public class FabricContainerItemTransferHandler implements TransferHandler<ItemS
this.transaction = transaction;
}
public ContainerItemContext getContext() {
return context;
}
@Override
public Stream<ResourceView<ItemStack>> getContents() {
return Stream.concat(Stream.of(context.getMainSlot()), context.getAdditionalSlots().stream())
@@ -118,7 +122,17 @@ public class FabricContainerItemTransferHandler implements TransferHandler<ItemS
return ItemStack.EMPTY;
}
private static class FabricResourceView implements ResourceView<ItemStack> {
@Override
public Object saveState() {
throw new UnsupportedOperationException();
}
@Override
public void loadState(Object state) {
throw new UnsupportedOperationException();
}
private class FabricResourceView implements ResourceView<ItemStack> {
private final SingleSlotStorage<ItemVariant> storage;
private FabricResourceView(SingleSlotStorage<ItemVariant> storage) {
@@ -134,5 +148,41 @@ public class FabricContainerItemTransferHandler implements TransferHandler<ItemS
public long getCapacity() {
return storage.getCapacity();
}
@Override
public ItemStack copyWithAmount(ItemStack resource, long amount) {
return ItemStackHooks.copyWithCount(resource, (int) amount);
}
@Override
public ItemStack extract(ItemStack toExtract, TransferAction action) {
if (toExtract.isEmpty()) return blank();
long extracted;
try (Transaction nested = Transaction.openNested(FabricContainerItemTransferHandler.this.transaction)) {
extracted = this.storage.extract(ItemVariant.of(toExtract), toExtract.getCount(), nested);
if (action == TransferAction.ACT) {
nested.commit();
}
}
return copyWithAmount(toExtract, extracted);
}
@Override
public ItemStack blank() {
return ItemStack.EMPTY;
}
@Override
public Object saveState() {
throw new UnsupportedOperationException();
}
@Override
public void loadState(Object state) {
throw new UnsupportedOperationException();
}
}
}

View File

@@ -21,14 +21,15 @@ package dev.architectury.transfer.item.fabric;
import dev.architectury.hooks.item.ItemStackHooks;
import dev.architectury.transfer.TransferHandler;
import dev.architectury.transfer.access.BlockTransferAccess;
import dev.architectury.transfer.fabric.FabricBlockTransferAccess;
import dev.architectury.transfer.fabric.BlockApiLookupWrapper;
import dev.architectury.transfer.fabric.FabricBlockLookupRegistration;
import dev.architectury.transfer.fabric.FabricStorageTransferHandler;
import dev.architectury.transfer.fabric.TransferHandlerStorage;
import dev.architectury.transfer.item.ItemTransfer;
import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext;
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
@@ -59,7 +60,19 @@ public class ItemTransferImpl {
}
}
public static BlockTransferAccess<TransferHandler<ItemStack>, Direction> instantiateBlockAccess() {
return new FabricBlockTransferAccess<>(ItemStorage.SIDED, ItemTransferImpl::wrap);
@Nullable
public static Storage<ItemVariant> unwrap(@Nullable TransferHandler<ItemStack> handler) {
if (handler == null) return null;
if (handler instanceof FabricStorageTransferHandler) {
return ((FabricStorageTransferHandler) handler).getStorage();
} else {
return new TransferHandlerStorage<>(handler, TYPE_ADAPTER);
}
}
public static void init() {
ItemTransfer.BLOCK.addQueryHandler(new BlockApiLookupWrapper<>(ItemStorage.SIDED, ItemTransferImpl::wrap));
ItemTransfer.BLOCK.addRegistrationHandler(FabricBlockLookupRegistration.create(ItemStorage.SIDED, ItemTransferImpl::unwrap));
}
}

View File

@@ -0,0 +1,101 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.fluid.forge;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.fluids.FluidStack;
import org.jetbrains.annotations.ApiStatus;
import java.util.function.Function;
import java.util.function.Supplier;
@ApiStatus.Internal
public enum FluidStackImpl implements dev.architectury.fluid.FluidStack.FluidStackAdapter {
INSTANCE;
public static Function<dev.architectury.fluid.FluidStack, Object> toValue;
public static Function<Object, dev.architectury.fluid.FluidStack> fromValue;
public static dev.architectury.fluid.FluidStack.FluidStackAdapter adapt(Function<dev.architectury.fluid.FluidStack, Object> toValue, Function<Object, dev.architectury.fluid.FluidStack> fromValue) {
FluidStackImpl.toValue = toValue;
FluidStackImpl.fromValue = fromValue;
return INSTANCE;
}
@Override
public Object create(Supplier<Fluid> fluid, long amount, CompoundTag tag) {
return new FluidStack(fluid.get(), (int) amount, tag);
}
@Override
public void check(Object object) {
if (!(object instanceof FluidStack)) {
throw new IllegalArgumentException("Expected FluidStack, got " + object.getClass().getName());
}
}
@Override
public Supplier<Fluid> getRawFluidSupplier(Object object) {
return ((FluidStack) object).getRawFluid().delegate;
}
@Override
public Fluid getFluid(Object object) {
return ((FluidStack) object).getFluid();
}
@Override
public long getAmount(Object object) {
return ((FluidStack) object).getAmount();
}
@Override
public void setAmount(Object object, long amount) {
((FluidStack) object).setAmount((int) amount);
}
@Override
public CompoundTag getTag(Object value) {
return ((FluidStack) value).getTag();
}
@Override
public void setTag(Object value, CompoundTag tag) {
((FluidStack) value).setTag(tag);
}
@Override
public Object copy(Object value) {
return ((FluidStack) value).copy();
}
@Override
public int hashCode(Object value) {
var stack = (FluidStack) value;
var code = 1;
code = 31 * code + stack.getFluid().hashCode();
code = 31 * code + stack.getAmount();
var tag = stack.getTag();
if (tag != null)
code = 31 * code + tag.hashCode();
return code;
}
}

View File

@@ -20,16 +20,17 @@
package dev.architectury.hooks.fluid.forge;
import dev.architectury.fluid.FluidStack;
import dev.architectury.fluid.forge.FluidStackImpl;
public final class FluidStackHooksForge {
private FluidStackHooksForge() {
}
public static FluidStack fromForge(net.minecraftforge.fluids.FluidStack stack) {
return FluidStack.create(stack.getFluid().delegate, stack.getAmount(), stack.getTag());
return FluidStackImpl.fromValue.apply(stack);
}
public static net.minecraftforge.fluids.FluidStack toForge(FluidStack stack) {
return new net.minecraftforge.fluids.FluidStack(stack.getRawFluid(), (int) stack.getAmount(), stack.getTag());
return (net.minecraftforge.fluids.FluidStack) FluidStackImpl.toValue.apply(stack);
}
}

View File

@@ -24,9 +24,11 @@ import dev.architectury.hooks.fluid.forge.FluidStackHooksForge;
import dev.architectury.transfer.ResourceView;
import dev.architectury.transfer.TransferAction;
import dev.architectury.transfer.TransferHandler;
import dev.architectury.transfer.access.BlockTransferAccess;
import dev.architectury.transfer.access.ItemTransferAccess;
import dev.architectury.transfer.forge.ForgeBlockTransferAccess;
import dev.architectury.transfer.access.BlockLookup;
import dev.architectury.transfer.access.ItemLookup;
import dev.architectury.transfer.fluid.FluidTransfer;
import dev.architectury.transfer.forge.ForgeBlockLookupRegistration;
import dev.architectury.transfer.forge.ForgeItemLookupRegistration;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
@@ -35,10 +37,10 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.BucketPickup;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.fluids.IFluidBlock;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.fluids.capability.wrappers.BucketPickupHandlerWrapper;
import net.minecraftforge.fluids.capability.wrappers.FluidBlockWrapper;
import org.jetbrains.annotations.NotNull;
@@ -65,8 +67,17 @@ public class FluidTransferImpl {
}
}
public static BlockTransferAccess<TransferHandler<FluidStack>, Direction> instantiateBlockAccess() {
return new ForgeBlockTransferAccess<TransferHandler<FluidStack>, IFluidHandler>() {
public static void init() {
FluidTransfer.BLOCK.addQueryHandler(instantiateBlockLookup());
FluidTransfer.BLOCK.addRegistrationHandler(ForgeBlockLookupRegistration.create(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY,
(level, pos, state, blockEntity) -> (direction, handler) -> new ArchFluidHandler(handler)));
// FluidTransfer.ITEM.addQueryHandler(instantiateItemLookup());
// FluidTransfer.ITEM.addRegistrationHandler(ForgeItemLookupRegistration.create(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY,
// stack -> (direction, handler) -> new ArchFluidHandlerItem(handler, stack)));
}
public static BlockLookup<TransferHandler<FluidStack>, Direction> instantiateBlockLookup() {
return new BlockLookup<TransferHandler<FluidStack>, Direction>() {
@Override
@Nullable
public TransferHandler<FluidStack> get(Level level, BlockPos pos, Direction direction) {
@@ -92,21 +103,11 @@ public class FluidTransferImpl {
}
return wrap(handler);
}
@Override
public Capability<IFluidHandler> getCapability() {
return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY;
}
@Override
public IFluidHandler from(TransferHandler<FluidStack> handler) {
return new ArchFluidHandler(handler);
}
};
}
public static ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>> instantiateItemAccess() {
return new ItemTransferAccess<TransferHandler<FluidStack>, TransferHandler<ItemStack>>() {
public static ItemLookup<TransferHandler<FluidStack>, TransferHandler<ItemStack>> instantiateItemLookup() {
return new ItemLookup<TransferHandler<FluidStack>, TransferHandler<ItemStack>>() {
@Override
@Nullable
public TransferHandler<FluidStack> get(ItemStack stack, TransferHandler<ItemStack> context) {
@@ -163,6 +164,21 @@ public class FluidTransferImpl {
}
}
public static class ArchFluidHandlerItem extends ArchFluidHandler implements IFluidHandlerItem {
private final ItemStack stack;
public ArchFluidHandlerItem(TransferHandler<FluidStack> handler, ItemStack stack) {
super(handler);
this.stack = stack;
}
@NotNull
@Override
public ItemStack getContainer() {
return stack;
}
}
private static class ForgeTransferHandler implements TransferHandler<FluidStack> {
private IFluidHandler handler;
@@ -217,6 +233,16 @@ public class FluidTransferImpl {
return FluidStack.empty();
}
@Override
public Object saveState() {
throw new UnsupportedOperationException();
}
@Override
public void loadState(Object state) {
throw new UnsupportedOperationException();
}
private class Itr implements Iterator<ResourceView<FluidStack>> {
int cursor;
@@ -267,6 +293,32 @@ public class FluidTransferImpl {
public long getCapacity() {
return handler.getTankCapacity(index);
}
@Override
public FluidStack extract(FluidStack toExtract, TransferAction action) {
// TODO: implement
return null;
}
@Override
public FluidStack blank() {
return FluidStack.empty();
}
@Override
public FluidStack copyWithAmount(FluidStack resource, long amount) {
return resource.copyWithAmount(amount);
}
@Override
public Object saveState() {
throw new UnsupportedOperationException();
}
@Override
public void loadState(Object state) {
throw new UnsupportedOperationException();
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.transfer.forge;
import dev.architectury.transfer.access.BlockLookupRegistration;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.BiFunction;
import java.util.function.Function;
public interface ForgeBlockLookupRegistration<T, Cap> extends BlockLookupRegistration<T, Direction> {
static <T, Cap> ForgeBlockLookupRegistration<T, Cap> create(Capability<Cap> capability, BlockAccessProvider<BiFunction<Direction, T, Cap>, BlockEntity> transformer) {
return new ForgeBlockLookupRegistration<T, Cap>() {
@Override
public Capability<Cap> getCapability() {
return capability;
}
@Override
public Cap from(Level level, BlockPos blockPos, BlockState blockState, BlockEntity blockEntity, Direction direction, T handler) {
return transformer.get(level, blockPos, blockState, blockEntity).apply(direction, handler);
}
};
}
Capability<Cap> getCapability();
Cap from(Level level, BlockPos blockPos, BlockState blockState, BlockEntity blockEntity, Direction direction, T handler);
@Override
default boolean registerForBlocks(ResourceLocation id, BlockAccessProvider<Function<Direction, T>, @Nullable BlockEntity> provider, Block... blocks) {
ReferenceOpenHashSet<Block> set = new ReferenceOpenHashSet<>(blocks);
return register(id, (level, pos, state, blockEntity) -> {
if (set.contains(state.getBlock())) {
return provider.get(level, pos, state, blockEntity);
}
return null;
});
}
@Override
default <B extends BlockEntity> boolean registerForBlockEntities(ResourceLocation id, BlockAccessProvider<Function<Direction, T>, B> provider, BlockEntityType<B>... blockEntityTypes) {
ReferenceOpenHashSet<BlockEntityType<B>> set = new ReferenceOpenHashSet<>(blockEntityTypes);
return register(id, (level, pos, state, blockEntity) -> {
if (set.contains(blockEntity.getType())) {
return provider.get(level, pos, state, (B) blockEntity);
}
return null;
});
}
@Override
default <B extends BlockEntity> boolean registerForSelf(ResourceLocation id, BlockEntityType<B>... blockEntityTypes) {
ReferenceOpenHashSet<BlockEntityType<B>> set = new ReferenceOpenHashSet<>(blockEntityTypes);
return register(id, (level, pos, state, blockEntity) -> {
if (set.contains(blockEntity.getType())) {
return direction -> (T) blockEntity;
}
return null;
});
}
@Override
default boolean register(ResourceLocation id, BlockAccessProvider<Function<Direction, T>, @Nullable BlockEntity> provider) {
CapabilitiesAttachListeners.add(event -> {
if (event.getObject() instanceof BlockEntity) {
BlockEntity blockEntity = (BlockEntity) event.getObject();
Function<Direction, T> applicator = provider.get(blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity);
if (applicator != null) {
event.addCapability(id, new ICapabilityProvider() {
@NotNull
@Override
public <S> LazyOptional<S> getCapability(@NotNull Capability<S> capability, @Nullable Direction arg) {
if (capability == ForgeBlockLookupRegistration.this.getCapability()) {
T handler = applicator.apply(arg);
return handler == null ? LazyOptional.empty() : LazyOptional.of(() -> from(blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, arg, handler)).cast();
}
return LazyOptional.empty();
}
});
}
}
});
return true;
}
}

View File

@@ -19,38 +19,53 @@
package dev.architectury.transfer.forge;
import dev.architectury.transfer.access.BlockTransferAccess;
import dev.architectury.transfer.access.ItemLookupRegistration;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.BiFunction;
import java.util.function.Function;
public interface ForgeBlockTransferAccess<T, C> extends BlockTransferAccess<T, Direction> {
Capability<C> getCapability();
public interface ForgeItemLookupRegistration<T, Cap, Context> extends ItemLookupRegistration<T, Context> {
static <T, C, Context> ForgeItemLookupRegistration<T, C, Context> create(Capability<C> capability, ItemAccessProvider<BiFunction<Direction, T, C>> transformer) {
return new ForgeItemLookupRegistration<T, C, Context>() {
@Override
public Capability<C> getCapability() {
return capability;
}
@Override
public C from(ItemStack stack, @Nullable Direction arg, T handler) {
return transformer.get(stack).apply(arg, handler);
}
};
}
C from(T handler);
Capability<Cap> getCapability();
Cap from(ItemStack stack, @Nullable Direction arg, T handler);
@Override
default void register(ResourceLocation id, BlockAccessProvider<T, Direction> provider) {
default boolean register(ResourceLocation id, ItemAccessProvider<Function<Context, T>> provider) {
CapabilitiesAttachListeners.add(event -> {
if (event.getObject() instanceof BlockEntity) {
BlockEntity blockEntity = (BlockEntity) event.getObject();
Function<Direction, T> applicator = provider.get(blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity);
if (event.getObject() instanceof ItemStack) {
ItemStack stack = (ItemStack) event.getObject();
Function<Context, T> applicator = provider.get(stack);
if (applicator != null) {
event.addCapability(id, new ICapabilityProvider() {
@NotNull
@Override
public <S> LazyOptional<S> getCapability(@NotNull Capability<S> capability, @Nullable Direction arg) {
if (capability == ForgeBlockTransferAccess.this.getCapability()) {
T handler = applicator.apply(arg);
if (capability == ForgeItemLookupRegistration.this.getCapability()) {
T handler = applicator.apply(null);
return LazyOptional.of(() -> from(handler)).cast();
return handler == null ? LazyOptional.empty() : LazyOptional.of(() -> from(stack, arg, handler)).cast();
}
return LazyOptional.empty();
@@ -59,5 +74,7 @@ public interface ForgeBlockTransferAccess<T, C> extends BlockTransferAccess<T, D
}
}
});
return true;
}
}

View File

@@ -20,11 +20,13 @@
package dev.architectury.transfer.item.forge;
import dev.architectury.fluid.FluidStack;
import dev.architectury.hooks.item.ItemStackHooks;
import dev.architectury.transfer.ResourceView;
import dev.architectury.transfer.TransferAction;
import dev.architectury.transfer.TransferHandler;
import dev.architectury.transfer.access.BlockTransferAccess;
import dev.architectury.transfer.forge.ForgeBlockTransferAccess;
import dev.architectury.transfer.access.BlockLookup;
import dev.architectury.transfer.forge.ForgeBlockLookupRegistration;
import dev.architectury.transfer.item.ItemTransfer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
@@ -32,7 +34,6 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
@@ -56,8 +57,14 @@ public class ItemTransferImpl {
}
}
public static BlockTransferAccess<TransferHandler<ItemStack>, Direction> instantiateBlockAccess() {
return new ForgeBlockTransferAccess<TransferHandler<ItemStack>, IItemHandler>() {
public static void init() {
ItemTransfer.BLOCK.addQueryHandler(instantiateBlockLookup());
ItemTransfer.BLOCK.addRegistrationHandler(ForgeBlockLookupRegistration.create(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY,
(level, pos, state, blockEntity) -> (direction, handler) -> new ArchItemHandler(handler)));
}
public static BlockLookup<TransferHandler<ItemStack>, Direction> instantiateBlockLookup() {
return new BlockLookup<TransferHandler<ItemStack>, Direction>() {
@Override
@Nullable
public TransferHandler<ItemStack> get(Level level, BlockPos pos, Direction direction) {
@@ -79,16 +86,6 @@ public class ItemTransferImpl {
}
return wrap(handler);
}
@Override
public Capability<IItemHandler> getCapability() {
return CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
}
@Override
public IItemHandler from(TransferHandler<ItemStack> handler) {
return new ArchItemHandler(handler);
}
};
}
@@ -166,11 +163,13 @@ public class ItemTransferImpl {
@Override
public ItemStack extract(ItemStack toExtract, TransferAction action) {
// TODO: implement
return null;
}
@Override
public ItemStack extract(Predicate<ItemStack> toExtract, long maxAmount, TransferAction action) {
// TODO: implement
return null;
}
@@ -179,6 +178,16 @@ public class ItemTransferImpl {
return ItemStack.EMPTY;
}
@Override
public Object saveState() {
throw new UnsupportedOperationException();
}
@Override
public void loadState(Object state) {
throw new UnsupportedOperationException();
}
private class ForgeResourceView implements ResourceView<ItemStack> {
int index;
@@ -195,6 +204,31 @@ public class ItemTransferImpl {
public long getCapacity() {
return handler.getSlotLimit(index);
}
@Override
public ItemStack extract(ItemStack toExtract, TransferAction action) {
return handler.extractItem(index, toExtract.getCount(), action == TransferAction.SIMULATE);
}
@Override
public ItemStack blank() {
return ItemStack.EMPTY;
}
@Override
public ItemStack copyWithAmount(ItemStack resource, long amount) {
return ItemStackHooks.copyWithCount(resource, (int) amount);
}
@Override
public Object saveState() {
throw new UnsupportedOperationException();
}
@Override
public void loadState(Object state) {
throw new UnsupportedOperationException();
}
}
}
}