Add data driven item stack tag filtering when exposing them to VM via serialization. Closes #5.
This commit is contained in:
@@ -81,7 +81,6 @@ public final class Callbacks {
|
||||
public static boolean hasMethods(final Object object) {
|
||||
if (object instanceof Class<?>) {
|
||||
return !getMethods((Class<?>) object).isEmpty();
|
||||
|
||||
} else {
|
||||
return !getMethods(object.getClass()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -1,38 +1,33 @@
|
||||
package li.cil.oc2.common;
|
||||
|
||||
import li.cil.oc2.common.bus.device.data.FileSystems;
|
||||
import li.cil.oc2.common.bus.device.rpc.RPCItemStackTagFilters;
|
||||
import li.cil.oc2.common.bus.device.rpc.RPCMethodParameterTypeAdapters;
|
||||
import li.cil.oc2.common.capabilities.Capabilities;
|
||||
import li.cil.oc2.common.integration.IMC;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import li.cil.oc2.common.serialization.BlobStorage;
|
||||
import li.cil.oc2.common.serialization.serializers.DirectionJsonSerializer;
|
||||
import li.cil.oc2.common.serialization.serializers.ItemStackJsonSerializer;
|
||||
import li.cil.oc2.common.util.ServerScheduler;
|
||||
import li.cil.oc2.common.vm.Allocator;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent;
|
||||
import net.minecraftforge.fml.event.server.FMLServerStoppedEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
|
||||
public final class CommonSetup {
|
||||
@SubscribeEvent
|
||||
public static void handleSetupEvent(final FMLCommonSetupEvent event) {
|
||||
Capabilities.initialize();
|
||||
FileSystems.initialize();
|
||||
IMC.initialize();
|
||||
Network.initialize();
|
||||
RPCItemStackTagFilters.initialize();
|
||||
RPCMethodParameterTypeAdapters.initialize();
|
||||
ServerScheduler.initialize();
|
||||
|
||||
Network.setup();
|
||||
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(IMC::handleIMCMessages);
|
||||
MinecraftForge.EVENT_BUS.addListener(CommonSetup::handleServerAboutToStart);
|
||||
MinecraftForge.EVENT_BUS.addListener(CommonSetup::handleServerStopped);
|
||||
MinecraftForge.EVENT_BUS.addListener(FileSystems::handleAddReloadListenerEvent);
|
||||
ServerScheduler.register();
|
||||
|
||||
addBuiltinRPCMethodParameterTypeAdapters();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -46,9 +41,4 @@ public final class CommonSetup {
|
||||
Allocator.resetAndCheckLeaks();
|
||||
FileSystems.reset();
|
||||
}
|
||||
|
||||
private static void addBuiltinRPCMethodParameterTypeAdapters() {
|
||||
RPCMethodParameterTypeAdapters.addTypeAdapter(ItemStack.class, new ItemStackJsonSerializer());
|
||||
RPCMethodParameterTypeAdapters.addTypeAdapter(Direction.class, new DirectionJsonSerializer());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package li.cil.oc2.common.bus.device.data;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.common.vm.fs.LayeredFileSystem;
|
||||
import li.cil.oc2.common.vm.fs.ResourceFileSystem;
|
||||
import li.cil.sedna.fs.FileSystem;
|
||||
@@ -11,6 +10,7 @@ import net.minecraft.resources.IFutureReloadListener;
|
||||
import net.minecraft.resources.IResource;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.AddReloadListenerEvent;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -26,6 +26,10 @@ public final class FileSystems {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void initialize() {
|
||||
MinecraftForge.EVENT_BUS.addListener(FileSystems::handleAddReloadListenerEvent);
|
||||
}
|
||||
|
||||
public static FileSystem getLayeredFileSystem() {
|
||||
return LAYERED_FILE_SYSTEM;
|
||||
}
|
||||
@@ -34,12 +38,12 @@ public final class FileSystems {
|
||||
LAYERED_FILE_SYSTEM.clear();
|
||||
}
|
||||
|
||||
public static void handleAddReloadListenerEvent(final AddReloadListenerEvent event) {
|
||||
event.addListener(FileSystemsReloadListener.INSTANCE);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static void handleAddReloadListenerEvent(final AddReloadListenerEvent event) {
|
||||
event.addListener(ReloadListener.INSTANCE);
|
||||
}
|
||||
|
||||
private static void reload(final IResourceManager resourceManager) {
|
||||
reset();
|
||||
|
||||
@@ -83,13 +87,8 @@ public final class FileSystems {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final class FileSystemsReloadListener implements IFutureReloadListener {
|
||||
public static final FileSystemsReloadListener INSTANCE = new FileSystemsReloadListener();
|
||||
|
||||
@Override
|
||||
public String getSimpleName() {
|
||||
return API.MOD_ID + ":file_system";
|
||||
}
|
||||
private static final class ReloadListener implements IFutureReloadListener {
|
||||
public static final ReloadListener INSTANCE = new ReloadListener();
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> reload(final IFutureReloadListener.IStage stage, final IResourceManager resourceManager, final IProfiler preparationsProfiler, final IProfiler reloadProfiler, final Executor backgroundExecutor, final Executor gameExecutor) {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package li.cil.oc2.common.bus.device.rpc;
|
||||
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.StringUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class RPCItemStackTagFilter {
|
||||
public ResourceLocation item;
|
||||
public String[] tags;
|
||||
|
||||
private String[][] paths; // Cache of resolved paths specified in tags.
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Nullable
|
||||
public CompoundNBT apply(final ItemStack stack, final CompoundNBT tag) {
|
||||
if (stack.isEmpty() || tags == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (item != null && !Objects.equals(stack.getItem().getRegistryName(), item)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
validatePaths();
|
||||
|
||||
final CompoundNBT filtered = new CompoundNBT();
|
||||
for (final String[] path : paths) {
|
||||
final CompoundNBT filteredByPath = filterPath(path, tag);
|
||||
if (filteredByPath != null) {
|
||||
filtered.merge(filteredByPath);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Nullable
|
||||
private CompoundNBT filterPath(final String[] path, final CompoundNBT source) {
|
||||
if (path.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final CompoundNBT result = new CompoundNBT();
|
||||
|
||||
CompoundNBT currentSource = source;
|
||||
CompoundNBT currentTarget = result;
|
||||
for (int j = 0; j < path.length - 1; j++) {
|
||||
final String segment = path[j];
|
||||
if (currentSource.contains(segment, NBTTagIds.TAG_COMPOUND)) {
|
||||
currentSource = currentSource.getCompound(segment);
|
||||
currentTarget.put(segment, new CompoundNBT());
|
||||
currentTarget = currentTarget.getCompound(segment);
|
||||
} else {
|
||||
return null; // Path mismatch, inner element is not a compound tag.
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentSource.contains(path[path.length - 1])) {
|
||||
return null; // Cannot find tag at path.
|
||||
}
|
||||
|
||||
currentTarget.put(path[path.length - 1], currentSource.get(path[path.length - 1]));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void validatePaths() {
|
||||
paths = new String[tags.length][];
|
||||
for (int i = 0; i < tags.length; i++) {
|
||||
if (!StringUtils.isNullOrEmpty(tags[i])) {
|
||||
paths[i] = tags[i].split("\\.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package li.cil.oc2.common.bus.device.rpc;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.client.resources.JsonReloadListener;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.AddReloadListenerEvent;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
public final class RPCItemStackTagFilters {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final ArrayList<RPCItemStackTagFilter> FILTERS = new ArrayList<>();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void initialize() {
|
||||
MinecraftForge.EVENT_BUS.addListener(RPCItemStackTagFilters::handleAddReloadListenerEvent);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CompoundNBT getFilteredTag(final ItemStack stack, final CompoundNBT tag) {
|
||||
final CompoundNBT result = new CompoundNBT();
|
||||
for (final RPCItemStackTagFilter filter : FILTERS) {
|
||||
final CompoundNBT filtered = filter.apply(stack, tag);
|
||||
if (filtered != null) {
|
||||
result.merge(filtered);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static void handleAddReloadListenerEvent(final AddReloadListenerEvent event) {
|
||||
event.addListener(ReloadListener.INSTANCE);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final class ReloadListener extends JsonReloadListener {
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation.class, new ResourceLocation.Serializer())
|
||||
.create();
|
||||
|
||||
public static final ReloadListener INSTANCE = new ReloadListener();
|
||||
|
||||
public ReloadListener() {
|
||||
super(GSON, "item_tag_filters");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(final Map<ResourceLocation, JsonElement> objects, final IResourceManager resourceManager, final IProfiler profiler) {
|
||||
FILTERS.clear();
|
||||
|
||||
objects.forEach((location, element) -> {
|
||||
try {
|
||||
final RPCItemStackTagFilter filter = GSON.fromJson(element, RPCItemStackTagFilter.class);
|
||||
if (filter != null) {
|
||||
FILTERS.add(filter);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
LOGGER.error("Failed loading item tag filter [{}].", location, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,10 @@ package li.cil.oc2.common.bus.device.rpc;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import li.cil.oc2.api.imc.RPCMethodParameterTypeAdapter;
|
||||
import li.cil.oc2.common.serialization.serializers.DirectionJsonSerializer;
|
||||
import li.cil.oc2.common.serialization.serializers.ItemStackJsonSerializer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Direction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -10,6 +14,11 @@ public final class RPCMethodParameterTypeAdapters {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void initialize() {
|
||||
addTypeAdapter(ItemStack.class, new ItemStackJsonSerializer());
|
||||
addTypeAdapter(Direction.class, new DirectionJsonSerializer());
|
||||
}
|
||||
|
||||
public static void addTypeAdapter(final Class<?> type, final Object typeAdapter) {
|
||||
addTypeAdapter(new RPCMethodParameterTypeAdapter(type, typeAdapter));
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import li.cil.oc2.common.bus.device.rpc.RPCMethodParameterTypeAdapters;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraftforge.fml.InterModComms;
|
||||
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -24,7 +25,15 @@ public final class IMC {
|
||||
return map;
|
||||
});
|
||||
|
||||
public static void handleIMCMessages(final InterModProcessEvent event) {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void initialize() {
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(IMC::handleIMCMessages);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static void handleIMCMessages(final InterModProcessEvent event) {
|
||||
event.getIMCStream().forEach(message -> {
|
||||
final Consumer<InterModComms.IMCMessage> method = METHODS.get(message.getMethod());
|
||||
if (method != null) {
|
||||
|
||||
@@ -26,7 +26,7 @@ public final class Network {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void setup() {
|
||||
public static void initialize() {
|
||||
INSTANCE.messageBuilder(ComputerTerminalOutputMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(ComputerTerminalOutputMessage::toBytes)
|
||||
.decoder(ComputerTerminalOutputMessage::new)
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import li.cil.oc2.common.bus.device.rpc.RPCItemStackTagFilters;
|
||||
import li.cil.oc2.common.serialization.NBTToJsonConverter;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
@@ -16,10 +17,6 @@ public final class ItemStackJsonSerializer implements JsonSerializer<ItemStack>
|
||||
return JsonNull.INSTANCE;
|
||||
}
|
||||
|
||||
final JsonElement json = NBTToJsonConverter.convert(src.serializeNBT());
|
||||
|
||||
// TODO Postprocessing, filter out tags that should not be visible to the VM.
|
||||
|
||||
return json;
|
||||
return NBTToJsonConverter.convert(RPCItemStackTagFilters.getFilteredTag(src, src.serializeNBT()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public final class ServerScheduler {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void register() {
|
||||
public static void initialize() {
|
||||
MinecraftForge.EVENT_BUS.register(EventHandler.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"tags": [
|
||||
"id",
|
||||
"Count",
|
||||
"tag.display",
|
||||
"tag.Enchantments",
|
||||
"tag.RepairCost"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"item": "oc2:hard_drive",
|
||||
"tags": [
|
||||
"tag.oc2.size"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"item": "oc2:memory",
|
||||
"tags": [
|
||||
"tag.oc2.size"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user