ForgePatch/src/main/java/net/minecraftforge/fml/common/event/FMLInterModComms.java

415 lines
14 KiB
Java

/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library 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 version 2.1
* of the License.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fml.common.event;
import java.util.function.Function;
import java.util.Optional;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModThreadContext;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.common.Mod.Instance;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import javax.annotation.Nonnull;
/**
* Simple intermod communications to receive simple messages directed at you
* from other mods
*
* @author cpw
*
*/
public class FMLInterModComms {
private static final ImmutableList<IMCMessage> emptyIMCList = ImmutableList.of();
private static ArrayListMultimap<String, IMCMessage> modMessages = ArrayListMultimap.create();
/**
* Subscribe to this event to receive your messages (they are sent between
* {@link FMLInitializationEvent} and {@link FMLPostInitializationEvent})
*
* @see net.minecraftforge.fml.common.Mod.EventHandler for how to subscribe to this event
* @author cpw
*/
public static class IMCEvent extends ModLifecycleEvent
{
private ModContainer activeContainer;
public IMCEvent()
{
super(activeContainer);
}
@Override
public void applyModContainer(ModContainer activeContainer)
{
this.activeContainer = activeContainer;
this.currentList = null;
FMLLog.log.trace("Attempting to deliver {} IMC messages to mod {}", modMessages.get(activeContainer.getModId()).size(), activeContainer.getModId());
}
private ImmutableList<IMCMessage> currentList;
public ImmutableList<IMCMessage> getMessages()
{
if (currentList == null)
{
currentList = ImmutableList.copyOf(modMessages.removeAll(activeContainer.getModId()));
}
return currentList;
}
}
/**
* You will receive an instance of this for each message sent
*
* @author cpw
*
*/
public static final class IMCMessage {
private final boolean isFunction;
/**
* This is the modid of the mod that sent you the message
*/
private String sender;
/**
* This field, and {@link #value} are both at the mod's discretion
*/
@Nonnull
public final String key;
/**
* This field, and {@link #key} are both at the mod's discretion
*/
@Nonnull
private final Object value;
private IMCMessage(@Nonnull String key, @Nonnull Object value)
{
this.key = key;
this.value = value;
this.isFunction = false;
}
private IMCMessage(@Nonnull String key, @Nonnull String value, boolean isFunction) {
this.key = key;
this.value = value;
this.isFunction = isFunction;
}
@Override
public String toString()
{
return sender;
}
/**
* Get the sending modId of this message.
* @return The modId of the mod that originated the message
*/
public String getSender()
{
return this.sender;
}
void setSender(ModContainer activeModContainer)
{
this.sender = activeModContainer.getModId();
}
/**
* Get the string value from this message.
* @throws ClassCastException if this message doesn't contain a String value
* @return The string value
*/
public String getStringValue()
{
return (String) value;
}
/**
* Get the ResourceLocation value from this message.
* @throws ClassCastException if this message doesn't contain a ResourceLocation value
* @return The string value
*/
public ResourceLocation getResourceLocationValue()
{
return (ResourceLocation) value;
}
/**
* Get the {@link NBTTagCompound} value from this message
* @throws ClassCastException if this message doesn't contain an NBT value
* @return The NBT value
*/
public NBTTagCompound getNBTValue()
{
return (NBTTagCompound) value;
}
/**
* Get the {@link ItemStack} value from this message
* @throws ClassCastException if this message doesn't contain an Itemstack value
* @return The Itemstack value
*/
@Nonnull
public ItemStack getItemStackValue()
{
return (ItemStack) value;
}
/**
* Get the {@link Function} value from this message. This will attempt to classload the function
* supplied by the caller. The parameter classes are strictly to give a concrete generic function return value.
* @param functionFrom The type of the argument to the function
* @param functionTo The type of the result of the function
* @param <T> The argument type
* @param <V> The result type
* @return The function value or Optional.absent if it wasn't readable or isn't a function call
*/
@SuppressWarnings("unchecked")
public <T,V> Optional<Function<T,V>> getFunctionValue(Class<T> functionFrom, Class<V> functionTo) {
if (!isFunction) {
return Optional.empty();
}
try {
Function<T,V> f = Class.forName((String) value).asSubclass(Function.class).newInstance();
return Optional.of(f);
} catch (Exception e) {
FMLLog.log.info("An error occurred instantiating the IMC function. key: {} value: {}, caller: {}", key,value,sender);
return Optional.empty();
}
}
/**
* Get the actual message class type
* @return The type of the message
*/
public Class<?> getMessageType()
{
return value.getClass();
}
/**
* Is this a string type message
* @return if this is a string type message
*/
public boolean isStringMessage()
{
return String.class.isAssignableFrom(getMessageType());
}
/**
* Is this an {@link ItemStack} type message
* @return if this is an itemstack type message
*/
public boolean isItemStackMessage()
{
return ItemStack.class.isAssignableFrom(getMessageType());
}
/**
* Is this an {@link NBTTagCompound} type message
* @return if this is an NBT type message
*/
public boolean isNBTMessage()
{
return NBTTagCompound.class.isAssignableFrom(getMessageType());
}
/**
* Is this an {@link ResourceLocation} type message
* @return if this is an NBT type message
*/
public boolean isResourceLocationMessage()
{
return ResourceLocation.class.isAssignableFrom(getMessageType());
}
/**
* Is this a {@link Function} type message
* @return if this is a function type message
*/
public boolean isFunctionMessage() { return Function.class.isAssignableFrom(getMessageType()); }
}
/**
* Send a startup time message
* @param modId The modid to send it to
* @param key The mod specific key
* @param value An NBT type value
* @return if the message was enqueued successfully and will be processed during startup
*/
public static boolean sendMessage(String modId, String key, NBTTagCompound value)
{
return enqueueStartupMessage(modId, new IMCMessage(key, value));
}
/**
* Send a startup time message
* @param modId The modid to send it to
* @param key The mod specific key
* @param value An Itemstack value
* @return if the message was enqueued successfully and will be processed during startup
*/
public static boolean sendMessage(String modId, String key, ItemStack value)
{
return enqueueStartupMessage(modId, new IMCMessage(key, value));
}
/**
* Send a startup time message
* @param modId The modid to send it to
* @param key The mod specific key
* @param value A ResourceLocation value
* @return if the message was enqueued successfully and will be processed during startup
*/
public static boolean sendMessage(String modId, String key, ResourceLocation value)
{
return enqueueStartupMessage(modId, new IMCMessage(key, value));
}
/**
* Send a startup time message
* @param modId The modid to send it to
* @param key The mod specific key
* @param value A String value
* @return if the message was enqueued successfully and will be processed during startup
*/
public static boolean sendMessage(String modId, String key, String value)
{
return enqueueStartupMessage(modId, new IMCMessage(key, value));
}
/**
* Send a startup time function message
* @param modId The modid to send it to
* @param key The mod specific key
* @param functionClassName The class name of a function that will be instantiated when the
* message is read. It must implement {@link Function}
* @return if the message was enqueued successfully and will be processed during startup
*/
public static boolean sendFunctionMessage(String modId, String key, String functionClassName)
{
return enqueueStartupMessage(modId, new IMCMessage(key, functionClassName, true));
}
/**
* Send a post-startup message
* @param sourceMod The mod sending the message
* @param modId The modid to send it to
* @param key The mod specific key
* @param value An NBT type value
*/
public static void sendRuntimeMessage(Object sourceMod, String modId, String key, NBTTagCompound value)
{
enqueueMessage(sourceMod, modId, new IMCMessage(key, value));
}
/**
* Send a post-startup message
* @param sourceMod The mod sending the message
* @param modId The modid to send it to
* @param key The mod specific key
* @param value An Itemstack value
*/
public static void sendRuntimeMessage(Object sourceMod, String modId, String key, ItemStack value)
{
enqueueMessage(sourceMod, modId, new IMCMessage(key, value));
}
/**
* Send a post-startup message
* @param sourceMod The mod sending the message
* @param modId The modid to send it to
* @param key The mod specific key
* @param value A string value
*/
public static void sendRuntimeMessage(Object sourceMod, String modId, String key, String value)
{
enqueueMessage(sourceMod, modId, new IMCMessage(key, value));
}
/**
* Send a post-startup message
* @param sourceMod The mod sending the message
* @param modId The modid to send it to
* @param key The mod specific key
* @param value A string value
*/
public static void sendRuntimeMessage(Object sourceMod, String modId, String key, ResourceLocation value)
{
enqueueMessage(sourceMod, modId, new IMCMessage(key, value));
}
/**
* Send a post-startup function message.
*
* @param sourceMod The mod originating this message
* @param modId The modid to send it to
* @param key The mod specific key
* @param functionClassName The name of a class to be loaded when the caller processes this message.
* The named class must extend {@link Function}
*/
public static void sendRuntimeFunctionMessage(Object sourceMod, String modId, String key, String functionClassName)
{
enqueueMessage(sourceMod, modId, new IMCMessage(key, functionClassName, true));
}
private static boolean enqueueStartupMessage(String modTarget, IMCMessage message)
{
if (ModThreadContext.get().getActiveContainer() == null)
{
return false;
}
enqueueMessage(ModThreadContext.get().getActiveContainer(), modTarget, message);
return ModList.get().isLoaded(modTarget);
}
private static void enqueueMessage(ModContainer sourceMod, String modTarget, IMCMessage message)
{
if (ModList.get().isLoaded(modTarget))
{
message.setSender(sourceMod);
modMessages.put(modTarget, message);
}
}
/**
* Retrieve any pending runtime messages for the mod
* @param forMod The {@link Instance} of the Mod to fetch messages for
* @return any messages - the collection will never be null
*/
public static ImmutableList<IMCMessage> fetchRuntimeMessages(Object forMod)
{
ModContainer mc = FMLCommonHandler.instance().findContainerFor(forMod);
if (mc != null)
{
return ImmutableList.copyOf(modMessages.removeAll(mc.getModId()));
}
else
{
return emptyIMCList;
}
}
}