/*
* A Gradle plugin for the creation of Minecraft mods and MinecraftForge plugins.
* Copyright (C) 2013 Minecraft Forge
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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.gradle.common;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import org.gradle.api.Project;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Resources;
import net.minecraftforge.gradle.util.GradleConfigurationException;
import net.minecraftforge.gradle.util.delayed.ReplacementProvider;
public abstract class BaseExtension
{
protected static final transient Map<String, String> MCP_VERSION_MAP = ImmutableMap.of("1.8", "9.10");
public final String forgeGradleVersion;
protected transient Project project;
protected transient ReplacementProvider replacer;
protected String version;
protected String mcpVersion = "unknown";
// this should never be touched except by the base plugin in this package
Map<String, Map<String, int[]>> mcpJson;
protected boolean mappingsSet = false;
protected String mappingsChannel = null;
protected int mappingsVersion = -1;
// custom version for custom mappings
protected String mappingsCustom = null;
public BaseExtension(BasePlugin<? extends BaseExtension> plugin)
{
this.project = plugin.project;
this.replacer = plugin.replacer;
String version;
try
{
URL url = BaseExtension.class.getClassLoader().getResource("forgegradle.version.txt");
version = Resources.toString(url, Constants.CHARSET).trim();
if (version.equals("${version}"))
{
version = "2.0-SNAPSHOT";// fallback
}
}
catch (Exception e)
{
// again, the fallback
version = "2.0-SNAPSHOT";
}
forgeGradleVersion = version;
}
/**
* Get the Minecraft version
*
* @return The Minecraft version
*/
public String getVersion()
{
return version;
}
/**
* Set the Minecraft version
*
* @param version The Minecraft version
*/
public void setVersion(String version)
{
this.version = version;
replacer.putReplacement(Constants.REPLACE_MC_VERSION, version);
mcpVersion = MCP_VERSION_MAP.get(version);
// maybe they set the mappings first
checkMappings();
}
/**
* Get the MCP data version
*
* @return The MCP data version
*/
public String getMcpVersion()
{
return mcpVersion == null ? "unknown" : mcpVersion;
}
public void setMcpVersion(String mcpVersion)
{
this.mcpVersion = mcpVersion;
}
public void copyFrom(BaseExtension ext)
{
if ("null".equals(version))
{
setVersion(ext.getVersion());
}
if ("unknown".equals(mcpVersion))
{
setMcpVersion(ext.getMcpVersion());
}
}
/**
* Get the MCP mappings channel and version<br>
* Examples: {@code stable_17, snapshot_20151113}
*
* @return The channel and version in format: {@code channel_version}.
*
* @see <a href="http://export.mcpbot.bspk.rs/">http://export.mcpbot.bspk.rs/</a>
*/
public String getMappings()
{
return mappingsChannel + "_" + (mappingsCustom == null ? mappingsVersion : mappingsCustom);
}
/**
* Get the MCP mappings channel
*
* @return The channel
*
* @see <a href="http://export.mcpbot.bspk.rs/">http://export.mcpbot.bspk.rs/</a>
*/
public String getMappingsChannel()
{
return mappingsChannel;
}
/**
* Strips the _nodoc and _verbose channel subtypes from the channel name.
*
* @return The channel without subtype
*/
public String getMappingsChannelNoSubtype()
{
int underscore = mappingsChannel.indexOf('_');
if (underscore <= 0) // already has docs.
return mappingsChannel;
else
return mappingsChannel.substring(0, underscore);
}
/**
* Get the MCP mappings version
*
* @return The version
*
* @see <a href="http://export.mcpbot.bspk.rs/">http://export.mcpbot.bspk.rs/</a>
*/
public String getMappingsVersion()
{
return mappingsCustom == null ? "" + mappingsVersion : mappingsCustom;
}
/**
* Set the MCP mappings channel and version<br>
* The format is: {@code channel_version}.<br>
* Examples: {@code stable_17, snapshot_20151113}
*
* @param mappings The channel and version
*
* @see <a href="http://export.mcpbot.bspk.rs/">http://export.mcpbot.bspk.rs/</a>
*/
public void setMappings(String mappings)
{
if (Strings.isNullOrEmpty(mappings))
{
mappingsChannel = null;
mappingsVersion = -1;
replacer.putReplacement(Constants.REPLACE_MCP_CHANNEL, mappingsChannel);
replacer.putReplacement(Constants.REPLACE_MCP_VERSION, getMappingsVersion());
return;
}
mappings = mappings.toLowerCase();
if (!mappings.contains("_"))
{
throw new IllegalArgumentException("Mappings must be in format 'channel_version' or 'custom_something'. eg: snapshot_20140910 custom_AbrarIsCool");
}
int index = mappings.lastIndexOf('_');
mappingsChannel = mappings.substring(0, index);
mappingsCustom = mappings.substring(index + 1);
if (!mappingsCustom.equals("custom"))
{
try
{
mappingsVersion = Integer.parseInt(mappingsCustom);
mappingsCustom = null;
}
catch (NumberFormatException e)
{
throw new GradleConfigurationException("The mappings version must be a number! eg: channel_### or channel_custom (for custom mappings).");
}
}
mappingsSet = true;
replacer.putReplacement(Constants.REPLACE_MCP_CHANNEL, mappingsChannel);
replacer.putReplacement(Constants.REPLACE_MCP_VERSION, getMappingsVersion());
// check
checkMappings();
}
/**
* Checks that the set mappings are valid based on the channel, version, and MC version.
* If the mappings are invalid, this method will throw a runtime exception.
*/
protected void checkMappings()
{
// mappings or mc version are null
if (mappingsChannel == null || Strings.isNullOrEmpty(version) || mappingsCustom != null)
return;
// check if it exists
Map<String, int[]> versionMap = mcpJson.get(version);
if (versionMap == null)
throw new GradleConfigurationException("There are no mappings for MC " + version);
String channel = getMappingsChannelNoSubtype();
int[] channelList = versionMap.get(channel);
if (channelList == null)
throw new GradleConfigurationException("There is no such MCP mapping channel named " + channel);
// all is well with the world
if (searchArray(channelList, mappingsVersion))
return;
// if it gets here.. it wasnt found. Now we try to actually find it..
for (Entry<String, Map<String, int[]>> mcEntry : mcpJson.entrySet())
{
for (Entry<String, int[]> channelEntry : mcEntry.getValue().entrySet())
{
// found it!
if (searchArray(channelEntry.getValue(), mappingsVersion))
{
boolean rightMc = mcEntry.getKey().equals(version);
boolean rightChannel = channelEntry.getKey().equals(channel);
// right channel, but wrong mc
if (rightChannel && !rightMc)
{
throw new GradleConfigurationException("This mapping '" + getMappings() + "' exists only for MC " + mcEntry.getKey() + "!");
}
// right MC , but wrong channel
else if (rightMc && !rightChannel)
{
throw new GradleConfigurationException("This mapping '" + getMappings() + "' doesnt exist! perhaps you meant '" + channelEntry.getKey() + "_" + mappingsVersion + "'");
}
}
}
}
// wasnt found
throw new GradleConfigurationException("The specified mapping '" + getMappings() + "' does not exist!");
}
private static boolean searchArray(int[] array, int key)
{
Arrays.sort(array);
int foundIndex = Arrays.binarySearch(array, key);
return foundIndex >= 0 && array[foundIndex] == key;
}
}