base mod created
This commit is contained in:
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import net.minecraft.init.Biomes;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.biome.*;
|
||||
import static net.minecraftforge.common.BiomeDictionary.Type.*;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.registry.ForgeRegistries;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class BiomeDictionary
|
||||
{
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public static final class Type
|
||||
{
|
||||
|
||||
private static final Map<String, Type> byName = new HashMap<String, Type>();
|
||||
private static Collection<Type> allTypes = Collections.unmodifiableCollection(byName.values());
|
||||
|
||||
/*Temperature-based tags. Specifying neither implies a biome is temperate*/
|
||||
public static final Type HOT = new Type("HOT");
|
||||
public static final Type COLD = new Type("COLD");
|
||||
|
||||
/*Tags specifying the amount of vegetation a biome has. Specifying neither implies a biome to have moderate amounts*/
|
||||
public static final Type SPARSE = new Type("SPARSE");
|
||||
public static final Type DENSE = new Type("DENSE");
|
||||
|
||||
/*Tags specifying how moist a biome is. Specifying neither implies the biome as having moderate humidity*/
|
||||
public static final Type WET = new Type("WET");
|
||||
public static final Type DRY = new Type("DRY");
|
||||
|
||||
/*Tree-based tags, SAVANNA refers to dry, desert-like trees (Such as Acacia), CONIFEROUS refers to snowy trees (Such as Spruce) and JUNGLE refers to jungle trees.
|
||||
* Specifying no tag implies a biome has temperate trees (Such as Oak)*/
|
||||
public static final Type SAVANNA = new Type("SAVANNA");
|
||||
public static final Type CONIFEROUS = new Type("CONIFEROUS");
|
||||
public static final Type JUNGLE = new Type("JUNGLE");
|
||||
|
||||
/*Tags specifying the nature of a biome*/
|
||||
public static final Type SPOOKY = new Type("SPOOKY");
|
||||
public static final Type DEAD = new Type("DEAD");
|
||||
public static final Type LUSH = new Type("LUSH");
|
||||
public static final Type NETHER = new Type("NETHER");
|
||||
public static final Type END = new Type("END");
|
||||
public static final Type MUSHROOM = new Type("MUSHROOM");
|
||||
public static final Type MAGICAL = new Type("MAGICAL");
|
||||
public static final Type RARE = new Type("RARE");
|
||||
|
||||
public static final Type OCEAN = new Type("OCEAN");
|
||||
public static final Type RIVER = new Type("RIVER");
|
||||
/**
|
||||
* A general tag for all water-based biomes. Shown as present if OCEAN or RIVER are.
|
||||
**/
|
||||
public static final Type WATER = new Type("WATER", OCEAN, RIVER);
|
||||
|
||||
/*Generic types which a biome can be*/
|
||||
public static final Type MESA = new Type("MESA");
|
||||
public static final Type FOREST = new Type("FOREST");
|
||||
public static final Type PLAINS = new Type("PLAINS");
|
||||
public static final Type MOUNTAIN = new Type("MOUNTAIN");
|
||||
public static final Type HILLS = new Type("HILLS");
|
||||
public static final Type SWAMP = new Type("SWAMP");
|
||||
public static final Type SANDY = new Type("SANDY");
|
||||
public static final Type SNOWY = new Type("SNOWY");
|
||||
public static final Type WASTELAND = new Type("WASTELAND");
|
||||
public static final Type BEACH = new Type("BEACH");
|
||||
public static final Type VOID = new Type("VOID");
|
||||
|
||||
private final String name;
|
||||
private final List<Type> subTypes;
|
||||
private final Set<Biome> biomes = new HashSet<Biome>();
|
||||
private final Set<Biome> biomesUn = Collections.unmodifiableSet(biomes);
|
||||
|
||||
private Type(String name, Type... subTypes)
|
||||
{
|
||||
this.name = name;
|
||||
this.subTypes = ImmutableList.copyOf(subTypes);
|
||||
|
||||
byName.put(name, this);
|
||||
}
|
||||
|
||||
private boolean hasSubTypes()
|
||||
{
|
||||
return !subTypes.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name for this type.
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a Type instance by name,
|
||||
* if one does not exist already it creates one.
|
||||
* This can be used as intermediate measure for modders to
|
||||
* add their own Biome types.
|
||||
* <p>
|
||||
* There are <i>no</i> naming conventions besides:
|
||||
* <ul><li><b>Must</b> be all upper case (enforced by name.toUpper())</li>
|
||||
* <li><b>No</b> Special characters. {Unenforced, just don't be a pain, if it becomes a issue I WILL
|
||||
* make this RTE with no worry about backwards compatibility}</li></ul>
|
||||
* <p>
|
||||
* Note: For performance sake, the return value of this function SHOULD be cached.
|
||||
* Two calls with the same name SHOULD return the same value.
|
||||
*
|
||||
* @param name The name of this Type
|
||||
* @return An instance of Type for this name.
|
||||
*/
|
||||
public static Type getType(String name, Type... subTypes)
|
||||
{
|
||||
name = name.toUpperCase();
|
||||
Type t = byName.get(name);
|
||||
if (t == null)
|
||||
{
|
||||
t = new Type(name, subTypes);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An unmodifiable collection of all current biome types.
|
||||
*/
|
||||
public static Collection<Type> getAll()
|
||||
{
|
||||
return allTypes;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<ResourceLocation, BiomeInfo> biomeInfoMap = new HashMap<ResourceLocation, BiomeInfo>();
|
||||
|
||||
private static class BiomeInfo
|
||||
{
|
||||
|
||||
private final Set<Type> types = new HashSet<Type>();
|
||||
private final Set<Type> typesUn = Collections.unmodifiableSet(this.types);
|
||||
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
registerVanillaBiomes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given types to the biome.
|
||||
*
|
||||
*/
|
||||
public static void addTypes(Biome biome, Type... types)
|
||||
{
|
||||
Preconditions.checkArgument(ForgeRegistries.BIOMES.containsValue(biome), "Cannot add types to unregistered biome %s", biome);
|
||||
|
||||
Collection<Type> supertypes = listSupertypes(types);
|
||||
Collections.addAll(supertypes, types);
|
||||
|
||||
for (Type type : supertypes)
|
||||
{
|
||||
type.biomes.add(biome);
|
||||
}
|
||||
|
||||
BiomeInfo biomeInfo = getBiomeInfo(biome);
|
||||
Collections.addAll(biomeInfo.types, types);
|
||||
biomeInfo.types.addAll(supertypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the set of biomes that have the given type.
|
||||
*
|
||||
*/
|
||||
@Nonnull
|
||||
public static Set<Biome> getBiomes(Type type)
|
||||
{
|
||||
return type.biomesUn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the set of types that have been added to the given biome.
|
||||
*
|
||||
*/
|
||||
@Nonnull
|
||||
public static Set<Type> getTypes(Biome biome)
|
||||
{
|
||||
ensureHasTypes(biome);
|
||||
return getBiomeInfo(biome).typesUn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the two given biomes have types in common.
|
||||
*
|
||||
* @return returns true if a common type is found, false otherwise
|
||||
*/
|
||||
public static boolean areSimilar(Biome biomeA, Biome biomeB)
|
||||
{
|
||||
for (Type type : getTypes(biomeA))
|
||||
{
|
||||
if (getTypes(biomeB).contains(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given type has been added to the given biome.
|
||||
*
|
||||
*/
|
||||
public static boolean hasType(Biome biome, Type type)
|
||||
{
|
||||
return getTypes(biome).contains(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any type has been added to the given biome.
|
||||
*
|
||||
*/
|
||||
public static boolean hasAnyType(Biome biome)
|
||||
{
|
||||
return !getBiomeInfo(biome).types.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically adds appropriate types to a given biome based on certain heuristics.
|
||||
* If a biome's types are requested and no types have been added to the biome so far, the biome's types
|
||||
* will be determined and added using this method.
|
||||
*
|
||||
*/
|
||||
public static void makeBestGuess(Biome biome)
|
||||
{
|
||||
if (biome.decorator.treesPerChunk >= 3)
|
||||
{
|
||||
if (biome.isHighHumidity() && biome.getDefaultTemperature() >= 0.9F)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, JUNGLE);
|
||||
}
|
||||
else if (!biome.isHighHumidity())
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, FOREST);
|
||||
|
||||
if (biome.getDefaultTemperature() <= 0.2f)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, CONIFEROUS);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (biome.getHeightVariation() <= 0.3F && biome.getHeightVariation() >= 0.0F)
|
||||
{
|
||||
if (!biome.isHighHumidity() || biome.getBaseHeight() >= 0.0F)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, PLAINS);
|
||||
}
|
||||
}
|
||||
|
||||
if (biome.getRainfall() > 0.85f)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, WET);
|
||||
}
|
||||
|
||||
if (biome.getRainfall() < 0.15f)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, DRY);
|
||||
}
|
||||
|
||||
if (biome.getDefaultTemperature() > 0.85f)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, HOT);
|
||||
}
|
||||
|
||||
if (biome.getDefaultTemperature() < 0.15f)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, COLD);
|
||||
}
|
||||
|
||||
if (biome.decorator.treesPerChunk > 0 && biome.decorator.treesPerChunk < 3)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, SPARSE);
|
||||
}
|
||||
else if (biome.decorator.treesPerChunk >= 10)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, DENSE);
|
||||
}
|
||||
|
||||
if (biome.isHighHumidity() && biome.getBaseHeight() < 0.0F && (biome.getHeightVariation() <= 0.3F && biome.getHeightVariation() >= 0.0F))
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, SWAMP);
|
||||
}
|
||||
|
||||
if (biome.getBaseHeight() <= -0.5F)
|
||||
{
|
||||
if (biome.getHeightVariation() == 0.0F)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, RIVER);
|
||||
}
|
||||
else
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, OCEAN);
|
||||
}
|
||||
}
|
||||
|
||||
if (biome.getHeightVariation() >= 0.4F && biome.getHeightVariation() < 1.5F)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, HILLS);
|
||||
}
|
||||
|
||||
if (biome.getHeightVariation() >= 1.5F)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, MOUNTAIN);
|
||||
}
|
||||
|
||||
if (biome.getEnableSnow())
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, SNOWY);
|
||||
}
|
||||
|
||||
if (biome.topBlock.getBlock() != Blocks.SAND && biome.getDefaultTemperature() >= 1.0f && biome.getRainfall() < 0.2f)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, SAVANNA);
|
||||
}
|
||||
|
||||
if (biome.topBlock.getBlock() == Blocks.SAND)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, SANDY);
|
||||
}
|
||||
else if (biome.topBlock.getBlock() == Blocks.MYCELIUM)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, MUSHROOM);
|
||||
}
|
||||
if (biome.fillerBlock.getBlock() == Blocks.HARDENED_CLAY)
|
||||
{
|
||||
BiomeDictionary.addTypes(biome, MESA);
|
||||
}
|
||||
}
|
||||
|
||||
//Internal implementation
|
||||
private static BiomeInfo getBiomeInfo(Biome biome)
|
||||
{
|
||||
return biomeInfoMap.computeIfAbsent(biome.getRegistryName(), k -> new BiomeInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that at least one type has been added to the given biome.
|
||||
*/
|
||||
static void ensureHasTypes(Biome biome)
|
||||
{
|
||||
if (!hasAnyType(biome))
|
||||
{
|
||||
makeBestGuess(biome);
|
||||
FMLLog.log.warn("No types have been added to Biome {}, types have been assigned on a best-effort guess: {}", biome.getRegistryName(), getTypes(biome));
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<Type> listSupertypes(Type... types)
|
||||
{
|
||||
Set<Type> supertypes = new HashSet<Type>();
|
||||
Deque<Type> next = new ArrayDeque<Type>();
|
||||
Collections.addAll(next, types);
|
||||
|
||||
while (!next.isEmpty())
|
||||
{
|
||||
Type type = next.remove();
|
||||
|
||||
for (Type sType : Type.byName.values())
|
||||
{
|
||||
if (sType.subTypes.contains(type) && supertypes.add(sType))
|
||||
next.add(sType);
|
||||
}
|
||||
}
|
||||
|
||||
return supertypes;
|
||||
}
|
||||
|
||||
private static void registerVanillaBiomes()
|
||||
{
|
||||
addTypes(Biomes.OCEAN, OCEAN );
|
||||
addTypes(Biomes.PLAINS, PLAINS );
|
||||
addTypes(Biomes.DESERT, HOT, DRY, SANDY );
|
||||
addTypes(Biomes.EXTREME_HILLS, MOUNTAIN, HILLS );
|
||||
addTypes(Biomes.FOREST, FOREST );
|
||||
addTypes(Biomes.TAIGA, COLD, CONIFEROUS, FOREST );
|
||||
addTypes(Biomes.SWAMPLAND, WET, SWAMP );
|
||||
addTypes(Biomes.RIVER, RIVER );
|
||||
addTypes(Biomes.HELL, HOT, DRY, NETHER );
|
||||
addTypes(Biomes.SKY, COLD, DRY, END );
|
||||
addTypes(Biomes.FROZEN_OCEAN, COLD, OCEAN, SNOWY );
|
||||
addTypes(Biomes.FROZEN_RIVER, COLD, RIVER, SNOWY );
|
||||
addTypes(Biomes.ICE_PLAINS, COLD, SNOWY, WASTELAND );
|
||||
addTypes(Biomes.ICE_MOUNTAINS, COLD, SNOWY, MOUNTAIN );
|
||||
addTypes(Biomes.MUSHROOM_ISLAND, MUSHROOM, RARE );
|
||||
addTypes(Biomes.MUSHROOM_ISLAND_SHORE, MUSHROOM, BEACH, RARE );
|
||||
addTypes(Biomes.BEACH, BEACH );
|
||||
addTypes(Biomes.DESERT_HILLS, HOT, DRY, SANDY, HILLS );
|
||||
addTypes(Biomes.FOREST_HILLS, FOREST, HILLS );
|
||||
addTypes(Biomes.TAIGA_HILLS, COLD, CONIFEROUS, FOREST, HILLS );
|
||||
addTypes(Biomes.EXTREME_HILLS_EDGE, MOUNTAIN );
|
||||
addTypes(Biomes.JUNGLE, HOT, WET, DENSE, JUNGLE );
|
||||
addTypes(Biomes.JUNGLE_HILLS, HOT, WET, DENSE, JUNGLE, HILLS );
|
||||
addTypes(Biomes.JUNGLE_EDGE, HOT, WET, JUNGLE, FOREST, RARE );
|
||||
addTypes(Biomes.DEEP_OCEAN, OCEAN );
|
||||
addTypes(Biomes.STONE_BEACH, BEACH );
|
||||
addTypes(Biomes.COLD_BEACH, COLD, BEACH, SNOWY );
|
||||
addTypes(Biomes.BIRCH_FOREST, FOREST );
|
||||
addTypes(Biomes.BIRCH_FOREST_HILLS, FOREST, HILLS );
|
||||
addTypes(Biomes.ROOFED_FOREST, SPOOKY, DENSE, FOREST );
|
||||
addTypes(Biomes.COLD_TAIGA, COLD, CONIFEROUS, FOREST, SNOWY );
|
||||
addTypes(Biomes.COLD_TAIGA_HILLS, COLD, CONIFEROUS, FOREST, SNOWY, HILLS );
|
||||
addTypes(Biomes.REDWOOD_TAIGA, COLD, CONIFEROUS, FOREST );
|
||||
addTypes(Biomes.REDWOOD_TAIGA_HILLS, COLD, CONIFEROUS, FOREST, HILLS );
|
||||
addTypes(Biomes.EXTREME_HILLS_WITH_TREES, MOUNTAIN, FOREST, SPARSE );
|
||||
addTypes(Biomes.SAVANNA, HOT, SAVANNA, PLAINS, SPARSE );
|
||||
addTypes(Biomes.SAVANNA_PLATEAU, HOT, SAVANNA, PLAINS, SPARSE, RARE );
|
||||
addTypes(Biomes.MESA, MESA, SANDY );
|
||||
addTypes(Biomes.MESA_ROCK, MESA, SPARSE, SANDY );
|
||||
addTypes(Biomes.MESA_CLEAR_ROCK, MESA, SANDY );
|
||||
addTypes(Biomes.VOID, VOID );
|
||||
addTypes(Biomes.MUTATED_PLAINS, PLAINS, RARE );
|
||||
addTypes(Biomes.MUTATED_DESERT, HOT, DRY, SANDY, RARE );
|
||||
addTypes(Biomes.MUTATED_EXTREME_HILLS, MOUNTAIN, SPARSE, RARE );
|
||||
addTypes(Biomes.MUTATED_FOREST, FOREST, HILLS, RARE );
|
||||
addTypes(Biomes.MUTATED_TAIGA, COLD, CONIFEROUS, FOREST, MOUNTAIN, RARE );
|
||||
addTypes(Biomes.MUTATED_SWAMPLAND, WET, SWAMP, HILLS, RARE );
|
||||
addTypes(Biomes.MUTATED_ICE_FLATS, COLD, SNOWY, HILLS, RARE );
|
||||
addTypes(Biomes.MUTATED_JUNGLE, HOT, WET, DENSE, JUNGLE, MOUNTAIN, RARE);
|
||||
addTypes(Biomes.MUTATED_JUNGLE_EDGE, HOT, SPARSE, JUNGLE, HILLS, RARE );
|
||||
addTypes(Biomes.MUTATED_BIRCH_FOREST, FOREST, DENSE, HILLS, RARE );
|
||||
addTypes(Biomes.MUTATED_BIRCH_FOREST_HILLS, FOREST, DENSE, MOUNTAIN, RARE );
|
||||
addTypes(Biomes.MUTATED_ROOFED_FOREST, SPOOKY, DENSE, FOREST, MOUNTAIN, RARE );
|
||||
addTypes(Biomes.MUTATED_TAIGA_COLD, COLD, CONIFEROUS, FOREST, SNOWY, MOUNTAIN, RARE);
|
||||
addTypes(Biomes.MUTATED_REDWOOD_TAIGA, DENSE, FOREST, RARE );
|
||||
addTypes(Biomes.MUTATED_REDWOOD_TAIGA_HILLS, DENSE, FOREST, HILLS, RARE );
|
||||
addTypes(Biomes.MUTATED_EXTREME_HILLS_WITH_TREES, MOUNTAIN, SPARSE, RARE );
|
||||
addTypes(Biomes.MUTATED_SAVANNA, HOT, DRY, SPARSE, SAVANNA, MOUNTAIN, RARE);
|
||||
addTypes(Biomes.MUTATED_SAVANNA_ROCK, HOT, DRY, SPARSE, SAVANNA, HILLS, RARE);
|
||||
addTypes(Biomes.MUTATED_MESA, HOT, DRY, SPARSE, SAVANNA, MOUNTAIN, RARE);
|
||||
addTypes(Biomes.MUTATED_MESA_ROCK, HOT, DRY, SPARSE, HILLS, RARE );
|
||||
addTypes(Biomes.MUTATED_MESA_CLEAR_ROCK, HOT, DRY, SPARSE, SAVANNA, MOUNTAIN, RARE);
|
||||
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
FMLLog.log.debug("BiomeDictionary:");
|
||||
for (Type type : Type.byName.values())
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(" ").append(type.name).append(": ").append(type.biomes.stream().map(Biome::getBiomeName).collect(Collectors.joining(", ")));
|
||||
FMLLog.log.debug(buf.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import net.minecraft.init.Biomes;
|
||||
import net.minecraft.util.WeightedRandom;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeProvider;
|
||||
import net.minecraft.world.gen.structure.MapGenVillage;
|
||||
import net.minecraftforge.common.util.EnumHelper;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BiomeManager
|
||||
{
|
||||
private static TrackedList<BiomeEntry>[] biomes = setupBiomes();
|
||||
|
||||
public static List<Biome> oceanBiomes = new ArrayList<Biome>();
|
||||
|
||||
public static ArrayList<Biome> strongHoldBiomes = new ArrayList<Biome>();
|
||||
public static ArrayList<Biome> strongHoldBiomesBlackList = new ArrayList<Biome>();
|
||||
|
||||
static
|
||||
{
|
||||
oceanBiomes.add(Biomes.OCEAN);
|
||||
oceanBiomes.add(Biomes.DEEP_OCEAN);
|
||||
oceanBiomes.add(Biomes.FROZEN_OCEAN);
|
||||
}
|
||||
|
||||
private static TrackedList<BiomeEntry>[] setupBiomes()
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
TrackedList<BiomeEntry>[] currentBiomes = new TrackedList[BiomeType.values().length];
|
||||
List<BiomeEntry> list = new ArrayList<BiomeEntry>();
|
||||
|
||||
list.add(new BiomeEntry(Biomes.FOREST, 10));
|
||||
list.add(new BiomeEntry(Biomes.ROOFED_FOREST, 10));
|
||||
list.add(new BiomeEntry(Biomes.EXTREME_HILLS, 10));
|
||||
list.add(new BiomeEntry(Biomes.PLAINS, 10));
|
||||
list.add(new BiomeEntry(Biomes.BIRCH_FOREST, 10));
|
||||
list.add(new BiomeEntry(Biomes.SWAMPLAND, 10));
|
||||
|
||||
currentBiomes[BiomeType.WARM.ordinal()] = new TrackedList<BiomeEntry>(list);
|
||||
list.clear();
|
||||
|
||||
list.add(new BiomeEntry(Biomes.FOREST, 10));
|
||||
list.add(new BiomeEntry(Biomes.EXTREME_HILLS, 10));
|
||||
list.add(new BiomeEntry(Biomes.TAIGA, 10));
|
||||
list.add(new BiomeEntry(Biomes.PLAINS, 10));
|
||||
|
||||
currentBiomes[BiomeType.COOL.ordinal()] = new TrackedList<BiomeEntry>(list);
|
||||
list.clear();
|
||||
|
||||
list.add(new BiomeEntry(Biomes.ICE_PLAINS, 30));
|
||||
list.add(new BiomeEntry(Biomes.COLD_TAIGA, 10));
|
||||
|
||||
currentBiomes[BiomeType.ICY.ordinal()] = new TrackedList<BiomeEntry>(list);
|
||||
list.clear();
|
||||
|
||||
currentBiomes[BiomeType.DESERT.ordinal()] = new TrackedList<BiomeEntry>(list);
|
||||
|
||||
return currentBiomes;
|
||||
}
|
||||
|
||||
public static void addVillageBiome(Biome biome, boolean canSpawn)
|
||||
{
|
||||
if (!MapGenVillage.VILLAGE_SPAWN_BIOMES.contains(biome))
|
||||
{
|
||||
ArrayList<Biome> biomes = new ArrayList<Biome>(MapGenVillage.VILLAGE_SPAWN_BIOMES);
|
||||
biomes.add(biome);
|
||||
MapGenVillage.VILLAGE_SPAWN_BIOMES = biomes;
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeVillageBiome(Biome biome)
|
||||
{
|
||||
if (MapGenVillage.VILLAGE_SPAWN_BIOMES.contains(biome))
|
||||
{
|
||||
ArrayList<Biome> biomes = new ArrayList<Biome>(MapGenVillage.VILLAGE_SPAWN_BIOMES);
|
||||
biomes.remove(biome);
|
||||
MapGenVillage.VILLAGE_SPAWN_BIOMES = biomes;
|
||||
}
|
||||
}
|
||||
|
||||
public static void addStrongholdBiome(Biome biome)
|
||||
{
|
||||
if (!strongHoldBiomes.contains(biome))
|
||||
{
|
||||
strongHoldBiomes.add(biome);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeStrongholdBiome(Biome biome)
|
||||
{
|
||||
if (!strongHoldBiomesBlackList.contains(biome))
|
||||
{
|
||||
strongHoldBiomesBlackList.add(biome);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addSpawnBiome(Biome biome)
|
||||
{
|
||||
if (!BiomeProvider.allowedBiomes.contains(biome))
|
||||
{
|
||||
BiomeProvider.allowedBiomes.add(biome);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeSpawnBiome(Biome biome)
|
||||
{
|
||||
if (BiomeProvider.allowedBiomes.contains(biome))
|
||||
{
|
||||
BiomeProvider.allowedBiomes.remove(biome);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addBiome(BiomeType type, BiomeEntry entry)
|
||||
{
|
||||
int idx = type.ordinal();
|
||||
List<BiomeEntry> list = idx > biomes.length ? null : biomes[idx];
|
||||
if (list != null) list.add(entry);
|
||||
}
|
||||
|
||||
public static void removeBiome(BiomeType type, BiomeEntry entry)
|
||||
{
|
||||
int idx = type.ordinal();
|
||||
List<BiomeEntry> list = idx > biomes.length ? null : biomes[idx];
|
||||
|
||||
if (list != null && list.contains(entry))
|
||||
{
|
||||
list.remove(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ImmutableList<BiomeEntry> getBiomes(BiomeType type)
|
||||
{
|
||||
int idx = type.ordinal();
|
||||
List<BiomeEntry> list = idx >= biomes.length ? null : biomes[idx];
|
||||
|
||||
return list != null ? ImmutableList.copyOf(list) : null;
|
||||
}
|
||||
|
||||
public static boolean isTypeListModded(BiomeType type)
|
||||
{
|
||||
int idx = type.ordinal();
|
||||
TrackedList<BiomeEntry> list = idx > biomes.length ? null : biomes[idx];
|
||||
|
||||
if (list != null) return list.isModded();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static enum BiomeType
|
||||
{
|
||||
DESERT, WARM, COOL, ICY;
|
||||
|
||||
public static BiomeType getType(String name)
|
||||
{
|
||||
name = name.toUpperCase();
|
||||
|
||||
for (BiomeType t : values())
|
||||
{
|
||||
if (t.name().equals(name)) return t;
|
||||
}
|
||||
|
||||
BiomeType ret = EnumHelper.addEnum(BiomeType.class, name, new Class[0], new Object[0]);
|
||||
|
||||
if (ret.ordinal() >= biomes.length)
|
||||
{
|
||||
biomes = Arrays.copyOf(biomes, ret.ordinal() + 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BiomeEntry extends WeightedRandom.Item
|
||||
{
|
||||
public final Biome biome;
|
||||
|
||||
public BiomeEntry(Biome biome, int weight)
|
||||
{
|
||||
super(weight);
|
||||
|
||||
this.biome = biome;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TrackedList<E> extends ArrayList<E>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private boolean isModded = false;
|
||||
|
||||
public TrackedList(Collection<? extends E> c)
|
||||
{
|
||||
super(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E set(int index, E element)
|
||||
{
|
||||
isModded = true;
|
||||
return super.set(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e)
|
||||
{
|
||||
isModded = true;
|
||||
return super.add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, E element)
|
||||
{
|
||||
isModded = true;
|
||||
super.add(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove(int index)
|
||||
{
|
||||
isModded = true;
|
||||
return super.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o)
|
||||
{
|
||||
isModded = true;
|
||||
return super.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
isModded = true;
|
||||
super.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> c)
|
||||
{
|
||||
isModded = true;
|
||||
return super.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends E> c)
|
||||
{
|
||||
isModded = true;
|
||||
return super.addAll(index, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c)
|
||||
{
|
||||
isModded = true;
|
||||
return super.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c)
|
||||
{
|
||||
isModded = true;
|
||||
return super.retainAll(c);
|
||||
}
|
||||
|
||||
public boolean isModded()
|
||||
{
|
||||
return isModded;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.IntIterator;
|
||||
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSets;
|
||||
import it.unimi.dsi.fastutil.ints.IntSortedSet;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.collect.Multiset;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.MinecraftException;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.ServerWorldEventHandler;
|
||||
import net.minecraft.world.WorldProvider;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraft.world.WorldServerMulti;
|
||||
import net.minecraft.world.storage.ISaveHandler;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class DimensionManager
|
||||
{
|
||||
private static class Dimension
|
||||
{
|
||||
private final DimensionType type;
|
||||
private int ticksWaited;
|
||||
private Dimension(DimensionType type)
|
||||
{
|
||||
this.type = type;
|
||||
this.ticksWaited = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasInit = false;
|
||||
|
||||
private static final Int2ObjectMap<WorldServer> worlds = Int2ObjectMaps.synchronize(new Int2ObjectLinkedOpenHashMap<>());
|
||||
private static final Int2ObjectMap<Dimension> dimensions = Int2ObjectMaps.synchronize(new Int2ObjectLinkedOpenHashMap<>());
|
||||
private static final IntSet keepLoaded = IntSets.synchronize(new IntOpenHashSet());
|
||||
private static final IntSet unloadQueue = IntSets.synchronize(new IntLinkedOpenHashSet());
|
||||
private static final BitSet dimensionMap = new BitSet(Long.SIZE << 4);
|
||||
private static final ConcurrentMap<World, World> weakWorldMap = new MapMaker().weakKeys().weakValues().makeMap();
|
||||
private static final Multiset<Integer> leakedWorlds = HashMultiset.create();
|
||||
|
||||
/**
|
||||
* Returns a list of dimensions associated with this DimensionType.
|
||||
*/
|
||||
public static int[] getDimensions(DimensionType type)
|
||||
{
|
||||
int[] ret = new int[dimensions.size()];
|
||||
int x = 0;
|
||||
for (Int2ObjectMap.Entry<Dimension> ent : dimensions.int2ObjectEntrySet())
|
||||
{
|
||||
if (ent.getValue().type == type)
|
||||
{
|
||||
ret[x++] = ent.getIntKey();
|
||||
}
|
||||
}
|
||||
|
||||
return Arrays.copyOf(ret, x);
|
||||
}
|
||||
|
||||
public static Map<DimensionType, IntSortedSet> getRegisteredDimensions()
|
||||
{
|
||||
Map<DimensionType, IntSortedSet> map = new IdentityHashMap<>();
|
||||
for (Int2ObjectMap.Entry<Dimension> entry : dimensions.int2ObjectEntrySet())
|
||||
{
|
||||
map.computeIfAbsent(entry.getValue().type, k -> new IntRBTreeSet()).add(entry.getIntKey());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void init()
|
||||
{
|
||||
if (hasInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hasInit = true;
|
||||
|
||||
registerDimension( 0, DimensionType.OVERWORLD);
|
||||
registerDimension(-1, DimensionType.NETHER);
|
||||
registerDimension( 1, DimensionType.THE_END);
|
||||
}
|
||||
|
||||
public static void registerDimension(int id, DimensionType type)
|
||||
{
|
||||
DimensionType.getById(type.getId()); //Check if type is invalid {will throw an error} No clue how it would be invalid tho...
|
||||
if (dimensions.containsKey(id))
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Failed to register dimension for id %d, One is already registered", id));
|
||||
}
|
||||
dimensions.put(id, new Dimension(type));
|
||||
if (id >= 0)
|
||||
{
|
||||
dimensionMap.set(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For unregistering a dimension when the save is changed (disconnected from a server or loaded a new save
|
||||
*/
|
||||
public static void unregisterDimension(int id)
|
||||
{
|
||||
if (!dimensions.containsKey(id))
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Failed to unregister dimension for id %d; No provider registered", id));
|
||||
}
|
||||
dimensions.remove(id);
|
||||
}
|
||||
|
||||
public static boolean isDimensionRegistered(int dim)
|
||||
{
|
||||
return dimensions.containsKey(dim);
|
||||
}
|
||||
|
||||
public static DimensionType getProviderType(int dim)
|
||||
{
|
||||
if (!dimensions.containsKey(dim))
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Could not get provider type for dimension %d, does not exist", dim));
|
||||
}
|
||||
return dimensions.get(dim).type;
|
||||
}
|
||||
|
||||
public static WorldProvider getProvider(int dim)
|
||||
{
|
||||
return getWorld(dim).provider;
|
||||
}
|
||||
|
||||
public static Integer[] getIDs(boolean check)
|
||||
{
|
||||
if (check)
|
||||
{
|
||||
List<World> allWorlds = Lists.newArrayList(weakWorldMap.keySet());
|
||||
allWorlds.removeAll(worlds.values());
|
||||
for (ListIterator<World> li = allWorlds.listIterator(); li.hasNext(); )
|
||||
{
|
||||
World w = li.next();
|
||||
leakedWorlds.add(System.identityHashCode(w));
|
||||
}
|
||||
for (World w : allWorlds)
|
||||
{
|
||||
int leakCount = leakedWorlds.count(System.identityHashCode(w));
|
||||
if (leakCount == 5)
|
||||
{
|
||||
FMLLog.log.debug("The world {} ({}) may have leaked: first encounter (5 occurrences).\n", Integer.toHexString(System.identityHashCode(w)), w.getWorldInfo().getWorldName());
|
||||
}
|
||||
else if (leakCount % 5 == 0)
|
||||
{
|
||||
FMLLog.log.debug("The world {} ({}) may have leaked: seen {} times.\n", Integer.toHexString(System.identityHashCode(w)), w.getWorldInfo().getWorldName(), leakCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getIDs();
|
||||
}
|
||||
|
||||
public static Integer[] getIDs()
|
||||
{
|
||||
return worlds.keySet().toArray(new Integer[0]); // Only loaded dims, since usually used to cycle through loaded worlds
|
||||
}
|
||||
|
||||
public static void setWorld(int id, @Nullable WorldServer world, MinecraftServer server)
|
||||
{
|
||||
if (world != null)
|
||||
{
|
||||
worlds.put(id, world);
|
||||
weakWorldMap.put(world, world);
|
||||
server.worldTickTimes.put(id, new long[100]);
|
||||
FMLLog.log.info("Loading dimension {} ({}) ({})", id, world.getWorldInfo().getWorldName(), world.getMinecraftServer());
|
||||
}
|
||||
else
|
||||
{
|
||||
worlds.remove(id);
|
||||
server.worldTickTimes.remove(id);
|
||||
FMLLog.log.info("Unloading dimension {}", id);
|
||||
}
|
||||
|
||||
ArrayList<WorldServer> tmp = new ArrayList<WorldServer>();
|
||||
if (worlds.get( 0) != null)
|
||||
tmp.add(worlds.get( 0));
|
||||
if (worlds.get(-1) != null)
|
||||
tmp.add(worlds.get(-1));
|
||||
if (worlds.get( 1) != null)
|
||||
tmp.add(worlds.get( 1));
|
||||
|
||||
for (Int2ObjectMap.Entry<WorldServer> entry : worlds.int2ObjectEntrySet())
|
||||
{
|
||||
int dim = entry.getIntKey();
|
||||
if (dim >= -1 && dim <= 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tmp.add(entry.getValue());
|
||||
}
|
||||
|
||||
server.worlds = tmp.toArray(new WorldServer[0]);
|
||||
}
|
||||
|
||||
public static void initDimension(int dim)
|
||||
{
|
||||
WorldServer overworld = getWorld(0);
|
||||
if (overworld == null)
|
||||
{
|
||||
throw new RuntimeException("Cannot Hotload Dim: Overworld is not Loaded!");
|
||||
}
|
||||
try
|
||||
{
|
||||
DimensionManager.getProviderType(dim);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log.error("Cannot Hotload Dim: {}", dim, e);
|
||||
return; // If a provider hasn't been registered then we can't hotload the dim
|
||||
}
|
||||
MinecraftServer mcServer = overworld.getMinecraftServer();
|
||||
ISaveHandler savehandler = overworld.getSaveHandler();
|
||||
//WorldSettings worldSettings = new WorldSettings(overworld.getWorldInfo());
|
||||
|
||||
WorldServer world = (dim == 0 ? overworld : (WorldServer)(new WorldServerMulti(mcServer, savehandler, dim, overworld, mcServer.profiler).init()));
|
||||
world.addEventListener(new ServerWorldEventHandler(mcServer, world));
|
||||
MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world));
|
||||
if (!mcServer.isSinglePlayer())
|
||||
{
|
||||
world.getWorldInfo().setGameType(mcServer.getGameType());
|
||||
}
|
||||
|
||||
mcServer.setDifficultyForAllWorlds(mcServer.getDifficulty());
|
||||
}
|
||||
|
||||
public static WorldServer getWorld(int id)
|
||||
{
|
||||
return getWorld(id, false);
|
||||
}
|
||||
|
||||
public static WorldServer getWorld(int id, boolean resetUnloadDelay)
|
||||
{
|
||||
if (resetUnloadDelay && unloadQueue.contains(id))
|
||||
{
|
||||
dimensions.get(id).ticksWaited = 0;
|
||||
}
|
||||
return worlds.get(id);
|
||||
}
|
||||
|
||||
public static WorldServer[] getWorlds()
|
||||
{
|
||||
return worlds.values().toArray(new WorldServer[0]);
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Not public API: used internally to get dimensions that should load at
|
||||
* server startup
|
||||
*/
|
||||
public static Integer[] getStaticDimensionIDs()
|
||||
{
|
||||
return dimensions.keySet().toArray(new Integer[0]);
|
||||
}
|
||||
|
||||
public static WorldProvider createProviderFor(int dim)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (dimensions.containsKey(dim))
|
||||
{
|
||||
WorldProvider ret = getProviderType(dim).createDimension();
|
||||
ret.setDimension(dim);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException(String.format("No WorldProvider bound for dimension %d", dim)); //It's going to crash anyway at this point. Might as well be informative
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log.error("An error occurred trying to create an instance of WorldProvider {} ({})",
|
||||
dim, getProviderType(dim), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if a dimension should stay loaded.
|
||||
* @param dim the dimension ID
|
||||
* @param keep whether or not the dimension should be kept loaded
|
||||
* @return true iff the dimension's status changed
|
||||
*/
|
||||
public static boolean keepDimensionLoaded(int dim, boolean keep)
|
||||
{
|
||||
return keep ? keepLoaded.add(dim) : keepLoaded.remove(dim);
|
||||
}
|
||||
|
||||
private static boolean canUnloadWorld(WorldServer world)
|
||||
{
|
||||
return ForgeChunkManager.getPersistentChunksFor(world).isEmpty()
|
||||
&& world.playerEntities.isEmpty()
|
||||
&& !world.provider.getDimensionType().shouldLoadSpawn()
|
||||
&& !keepLoaded.contains(world.provider.getDimension());
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a dimension to unload, if it can be unloaded.
|
||||
* @param id The id of the dimension
|
||||
*/
|
||||
public static void unloadWorld(int id)
|
||||
{
|
||||
WorldServer world = worlds.get(id);
|
||||
if (world == null || !canUnloadWorld(world)) return;
|
||||
|
||||
if (unloadQueue.add(id))
|
||||
{
|
||||
FMLLog.log.debug("Queueing dimension {} to unload", id);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isWorldQueuedToUnload(int id)
|
||||
{
|
||||
return unloadQueue.contains(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* To be called by the server at the appropriate time, do not call from mod code.
|
||||
*/
|
||||
public static void unloadWorlds(Hashtable<Integer, long[]> worldTickTimes)
|
||||
{
|
||||
IntIterator queueIterator = unloadQueue.iterator();
|
||||
while (queueIterator.hasNext())
|
||||
{
|
||||
int id = queueIterator.nextInt();
|
||||
Dimension dimension = dimensions.get(id);
|
||||
if (dimension.ticksWaited < ForgeModContainer.dimensionUnloadQueueDelay)
|
||||
{
|
||||
dimension.ticksWaited++;
|
||||
continue;
|
||||
}
|
||||
WorldServer w = worlds.get(id);
|
||||
queueIterator.remove();
|
||||
dimension.ticksWaited = 0;
|
||||
// Don't unload the world if the status changed
|
||||
if (w == null || !canUnloadWorld(w))
|
||||
{
|
||||
FMLLog.log.debug("Aborting unload for dimension {} as status changed", id);
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
w.saveAllChunks(true, null);
|
||||
}
|
||||
catch (MinecraftException e)
|
||||
{
|
||||
FMLLog.log.error("Caught an exception while saving all chunks:", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(w));
|
||||
w.flush();
|
||||
setWorld(id, null, w.getMinecraftServer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next free dimension ID. Note: you are not guaranteed a contiguous
|
||||
* block of free ids. Always call for each individual ID you wish to get.
|
||||
* @return the next free dimension ID
|
||||
*/
|
||||
public static int getNextFreeDimId() {
|
||||
int next = 0;
|
||||
while (true)
|
||||
{
|
||||
next = dimensionMap.nextClearBit(next);
|
||||
if (dimensions.containsKey(next))
|
||||
{
|
||||
dimensionMap.set(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static NBTTagCompound saveDimensionDataMap()
|
||||
{
|
||||
int[] data = new int[(dimensionMap.length() + Integer.SIZE - 1 )/ Integer.SIZE];
|
||||
NBTTagCompound dimMap = new NBTTagCompound();
|
||||
for (int i = 0; i < data.length; i++)
|
||||
{
|
||||
int val = 0;
|
||||
for (int j = 0; j < Integer.SIZE; j++)
|
||||
{
|
||||
val |= dimensionMap.get(i * Integer.SIZE + j) ? (1 << j) : 0;
|
||||
}
|
||||
data[i] = val;
|
||||
}
|
||||
dimMap.setIntArray("DimensionArray", data);
|
||||
return dimMap;
|
||||
}
|
||||
|
||||
public static void loadDimensionDataMap(@Nullable NBTTagCompound compoundTag)
|
||||
{
|
||||
dimensionMap.clear();
|
||||
if (compoundTag == null)
|
||||
{
|
||||
IntIterator iterator = dimensions.keySet().iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
int id = iterator.nextInt();
|
||||
if (id >= 0)
|
||||
{
|
||||
dimensionMap.set(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int[] intArray = compoundTag.getIntArray("DimensionArray");
|
||||
for (int i = 0; i < intArray.length; i++)
|
||||
{
|
||||
for (int j = 0; j < Integer.SIZE; j++)
|
||||
{
|
||||
dimensionMap.set(i * Integer.SIZE + j, (intArray[i] & (1 << j)) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current root directory for the world save. Accesses getSaveHandler from the overworld
|
||||
* @return the root directory of the save
|
||||
*/
|
||||
@Nullable
|
||||
public static File getCurrentSaveRootDirectory()
|
||||
{
|
||||
if (DimensionManager.getWorld(0) != null)
|
||||
{
|
||||
return DimensionManager.getWorld(0).getSaveHandler().getWorldDirectory();
|
||||
}/*
|
||||
else if (MinecraftServer.getServer() != null)
|
||||
{
|
||||
MinecraftServer srv = MinecraftServer.getServer();
|
||||
SaveHandler saveHandler = (SaveHandler) srv.getActiveAnvilConverter().getSaveLoader(srv.getFolderName(), false);
|
||||
return saveHandler.getWorldDirectory();
|
||||
}*/
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.WeightedRandom;
|
||||
|
||||
public class DungeonHooks
|
||||
{
|
||||
private static ArrayList<DungeonMob> dungeonMobs = new ArrayList<DungeonMob>();
|
||||
|
||||
/**
|
||||
* Adds a mob to the possible list of creatures the spawner will create.
|
||||
* If the mob is already in the spawn list, the rarity will be added to the existing one,
|
||||
* causing the mob to be more common.
|
||||
*
|
||||
* @param name The name of the monster, use the same name used when registering the entity.
|
||||
* @param rarity The rarity of selecting this mob over others. Must be greater then 0.
|
||||
* Vanilla Minecraft has the following mobs:
|
||||
* Spider 100
|
||||
* Skeleton 100
|
||||
* Zombie 200
|
||||
* Meaning, Zombies are twice as common as spiders or skeletons.
|
||||
* @return The new rarity of the monster,
|
||||
*/
|
||||
public static float addDungeonMob(ResourceLocation name, int rarity)
|
||||
{
|
||||
if (rarity <= 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Rarity must be greater then zero");
|
||||
}
|
||||
|
||||
for (DungeonMob mob : dungeonMobs)
|
||||
{
|
||||
if (name.equals(mob.type))
|
||||
{
|
||||
return mob.itemWeight += rarity;
|
||||
}
|
||||
}
|
||||
|
||||
dungeonMobs.add(new DungeonMob(rarity, name));
|
||||
return rarity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will completely remove a Mob from the dungeon spawn list.
|
||||
*
|
||||
* @param name The name of the mob to remove
|
||||
* @return The rarity of the removed mob, prior to being removed.
|
||||
*/
|
||||
public static int removeDungeonMob(ResourceLocation name)
|
||||
{
|
||||
for (DungeonMob mob : dungeonMobs)
|
||||
{
|
||||
if (name.equals(mob.type))
|
||||
{
|
||||
dungeonMobs.remove(mob);
|
||||
return mob.itemWeight;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random mob name from the list.
|
||||
* @param rand World generation random number generator
|
||||
* @return The mob name
|
||||
*/
|
||||
public static ResourceLocation getRandomDungeonMob(Random rand)
|
||||
{
|
||||
DungeonMob mob = WeightedRandom.getRandomItem(rand, dungeonMobs);
|
||||
return mob.type;
|
||||
}
|
||||
|
||||
|
||||
public static class DungeonMob extends WeightedRandom.Item
|
||||
{
|
||||
public ResourceLocation type;
|
||||
public DungeonMob(int weight, ResourceLocation type)
|
||||
{
|
||||
super(weight);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object target)
|
||||
{
|
||||
return target instanceof DungeonMob && type.equals(((DungeonMob)target).type);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
addDungeonMob(new ResourceLocation("skeleton"), 100);
|
||||
addDungeonMob(new ResourceLocation("zombie"), 200);
|
||||
addDungeonMob(new ResourceLocation("spider"), 100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraftforge.common.util.EnumHelper;
|
||||
|
||||
public enum EnumPlantType
|
||||
{
|
||||
Plains,
|
||||
Desert,
|
||||
Beach,
|
||||
Cave,
|
||||
Water,
|
||||
Nether,
|
||||
Crop;
|
||||
|
||||
/**
|
||||
* Getting a custom {@link EnumPlantType}, or an existing one if it has the same name as that one. Your plant should implement {@link IPlantable}
|
||||
* and return this custom type in {@link IPlantable#getPlantType(IBlockAccess, BlockPos)}.
|
||||
*
|
||||
* <p>If your new plant grows on blocks like any one of them above, never create a new {@link EnumPlantType}.
|
||||
* This enumeration is only functioning in
|
||||
* {@link net.minecraft.block.Block#canSustainPlant(IBlockState, IBlockAccess, BlockPos, EnumFacing, IPlantable)},
|
||||
* which you are supposed to override this function in your new block and create a new plant type to grow on that block.
|
||||
*
|
||||
* <p>You can create an instance of your plant type in your API and let your/others mods access it. It will be faster than calling this method.
|
||||
* @param name the name of the type of plant, you had better follow the style above
|
||||
* @return the acquired {@link EnumPlantType}, a new one if not found.
|
||||
*/
|
||||
public static EnumPlantType getPlantType(String name)
|
||||
{
|
||||
for (EnumPlantType t : values())
|
||||
{
|
||||
if (t.name().equalsIgnoreCase(name)) return t;
|
||||
}
|
||||
return EnumHelper.addEnum(EnumPlantType.class, name, new Class[0], new Object[0]);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraftforge.common.util.FakePlayerFactory;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.fml.client.FMLClientHandler;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent.ServerTickEvent;
|
||||
|
||||
public class ForgeInternalHandler
|
||||
{
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public void onEntityJoinWorld(EntityJoinWorldEvent event)
|
||||
{
|
||||
if (!event.getWorld().isRemote)
|
||||
{
|
||||
ForgeChunkManager.loadEntity(event.getEntity());
|
||||
}
|
||||
|
||||
Entity entity = event.getEntity();
|
||||
if (entity.getClass().equals(EntityItem.class))
|
||||
{
|
||||
ItemStack stack = ((EntityItem)entity).getItem();
|
||||
Item item = stack.getItem();
|
||||
if (item.hasCustomEntity(stack))
|
||||
{
|
||||
Entity newEntity = item.createEntity(event.getWorld(), entity, stack);
|
||||
if (newEntity != null)
|
||||
{
|
||||
entity.setDead();
|
||||
event.setCanceled(true);
|
||||
event.getWorld().spawnEntity(newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public void onDimensionLoad(WorldEvent.Load event)
|
||||
{
|
||||
ForgeChunkManager.loadWorld(event.getWorld());
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public void onDimensionSave(WorldEvent.Save event)
|
||||
{
|
||||
ForgeChunkManager.saveWorld(event.getWorld());
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public void onDimensionUnload(WorldEvent.Unload event)
|
||||
{
|
||||
ForgeChunkManager.unloadWorld(event.getWorld());
|
||||
if (event.getWorld() instanceof WorldServer)
|
||||
FakePlayerFactory.unloadWorld((WorldServer) event.getWorld());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onServerTick(ServerTickEvent event)
|
||||
{
|
||||
WorldWorkerManager.tick(event.phase == TickEvent.Phase.START);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void checkSettings(ClientTickEvent event)
|
||||
{
|
||||
if (event.phase == Phase.END)
|
||||
FMLClientHandler.instance().updateCloudSettings();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,646 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import static net.minecraftforge.common.config.Configuration.CATEGORY_CLIENT;
|
||||
import static net.minecraftforge.common.config.Configuration.CATEGORY_GENERAL;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.minecraft.crash.ICrashReportDetail;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.stats.StatList;
|
||||
import net.minecraft.world.storage.SaveHandler;
|
||||
import net.minecraft.world.storage.WorldInfo;
|
||||
import net.minecraftforge.classloading.FMLForgePlugin;
|
||||
import net.minecraftforge.client.ForgeClientHandler;
|
||||
import net.minecraftforge.common.config.Config;
|
||||
import net.minecraftforge.common.config.ConfigCategory;
|
||||
import net.minecraftforge.common.config.ConfigManager;
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
import net.minecraftforge.common.config.Property;
|
||||
import net.minecraftforge.common.model.animation.CapabilityAnimation;
|
||||
import net.minecraftforge.common.network.ForgeNetworkHandler;
|
||||
import net.minecraftforge.energy.CapabilityEnergy;
|
||||
import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.event.RegistryEvent.MissingMappings;
|
||||
import net.minecraftforge.event.terraingen.DeferredBiomeDecorator;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fml.common.registry.ForgeRegistries;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
import net.minecraftforge.fluids.UniversalBucket;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
import net.minecraftforge.oredict.RecipeSorter;
|
||||
import net.minecraftforge.server.command.ForgeCommand;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import net.minecraftforge.fml.client.FMLFileResourcePack;
|
||||
import net.minecraftforge.fml.client.FMLFolderResourcePack;
|
||||
import net.minecraftforge.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent;
|
||||
import net.minecraftforge.fml.common.DummyModContainer;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.ICrashCallable;
|
||||
import net.minecraftforge.fml.common.LoadController;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||
import net.minecraftforge.fml.common.ModMetadata;
|
||||
import net.minecraftforge.fml.common.WorldAccessContainer;
|
||||
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;
|
||||
import net.minecraftforge.fml.common.discovery.json.JsonAnnotationLoader;
|
||||
import net.minecraftforge.fml.common.event.FMLConstructionEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLLoadCompleteEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLModIdMappingEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.PlayerEvent;
|
||||
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@EventBusSubscriber(modid = "forge")
|
||||
public class ForgeModContainer extends DummyModContainer implements WorldAccessContainer
|
||||
{
|
||||
public static final String VERSION_CHECK_CAT = "version_checking";
|
||||
public static int clumpingThreshold = 64;
|
||||
public static boolean removeErroringEntities = false;
|
||||
public static boolean removeErroringTileEntities = false;
|
||||
public static boolean fullBoundingBoxLadders = false;
|
||||
public static double zombieSummonBaseChance = 0.1;
|
||||
public static int[] blendRanges = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34 };
|
||||
public static float zombieBabyChance = 0.05f;
|
||||
public static boolean shouldSortRecipies = true;
|
||||
public static boolean disableVersionCheck = false;
|
||||
public static boolean forgeLightPipelineEnabled = true;
|
||||
public static boolean selectiveResourceReloadEnabled = false;
|
||||
@Deprecated // TODO remove in 1.13
|
||||
public static boolean replaceVanillaBucketModel = true;
|
||||
public static boolean zoomInMissingModelTextInGui = false;
|
||||
public static boolean forgeCloudsEnabled = true;
|
||||
public static boolean disableStairSlabCulling = false; // Also known as the "DontCullStairsBecauseIUseACrappyTexturePackThatBreaksBasicBlockShapesSoICantTrustBasicBlockCulling" flag
|
||||
public static boolean alwaysSetupTerrainOffThread = false; // In RenderGlobal.setupTerrain, always force the chunk render updates to be queued to the thread
|
||||
public static int dimensionUnloadQueueDelay = 0;
|
||||
public static boolean logCascadingWorldGeneration = true; // see Chunk#logCascadingWorldGeneration()
|
||||
public static boolean fixVanillaCascading = false; // There are various places in vanilla that cause cascading worldgen. Enabling this WILL change where blocks are placed to prevent this.
|
||||
// DO NOT contact Forge about worldgen not 'matching' vanilla if this flag is set.
|
||||
|
||||
static final Logger log = LogManager.getLogger(ForgeVersion.MOD_ID);
|
||||
|
||||
private static Configuration config;
|
||||
private static ForgeModContainer INSTANCE;
|
||||
public static ForgeModContainer getInstance()
|
||||
{
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private URL updateJSONUrl = null;
|
||||
public UniversalBucket universalBucket;
|
||||
|
||||
public ForgeModContainer()
|
||||
{
|
||||
super(new ModMetadata());
|
||||
ModMetadata meta = getMetadata();
|
||||
meta.modId = ForgeVersion.MOD_ID;
|
||||
meta.name = "Minecraft Forge";
|
||||
meta.version = ForgeVersion.getVersion();
|
||||
meta.credits = "Made possible with help from many people";
|
||||
meta.authorList = Arrays.asList("LexManos", "cpw", "fry");
|
||||
meta.description = "Minecraft Forge is a common open source API allowing a broad range of mods " +
|
||||
"to work cooperatively together. It allows many mods to be created without " +
|
||||
"them editing the main Minecraft code.";
|
||||
meta.url = "http://minecraftforge.net";
|
||||
meta.screenshots = new String[0];
|
||||
meta.logoFile = "/forge_logo.png";
|
||||
try {
|
||||
updateJSONUrl = new URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json");
|
||||
} catch (MalformedURLException e) {}
|
||||
|
||||
config = null;
|
||||
File cfgFile = new File(Loader.instance().getConfigDir(), "forge.cfg");
|
||||
config = new Configuration(cfgFile);
|
||||
|
||||
syncConfig(true);
|
||||
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGuiClassName()
|
||||
{
|
||||
return "net.minecraftforge.client.gui.ForgeGuiFactory";
|
||||
}
|
||||
|
||||
public static Configuration getConfig()
|
||||
{
|
||||
return config;
|
||||
}
|
||||
|
||||
private static void remapGeneralPropertyToClient(String key)
|
||||
{
|
||||
ConfigCategory GENERAL = config.getCategory(CATEGORY_GENERAL);
|
||||
if (GENERAL.containsKey(key))
|
||||
{
|
||||
FMLLog.log.debug("Remapping property {} from category general to client", key);
|
||||
Property property = GENERAL.get(key);
|
||||
GENERAL.remove(key);
|
||||
config.getCategory(CATEGORY_CLIENT).put(key, property);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes the local fields with the values in the Configuration object.
|
||||
*/
|
||||
private static void syncConfig(boolean load)
|
||||
{
|
||||
// By adding a property order list we are defining the order that the properties will appear both in the config file and on the GUIs.
|
||||
// Property order lists are defined per-ConfigCategory.
|
||||
List<String> propOrder = new ArrayList<String>();
|
||||
|
||||
if (!config.isChild)
|
||||
{
|
||||
if (load)
|
||||
{
|
||||
config.load();
|
||||
}
|
||||
Property enableGlobalCfg = config.get(Configuration.CATEGORY_GENERAL, "enableGlobalConfig", false).setShowInGui(false);
|
||||
if (enableGlobalCfg.getBoolean(false))
|
||||
{
|
||||
Configuration.enableGlobalConfig();
|
||||
}
|
||||
}
|
||||
|
||||
Property prop;
|
||||
|
||||
// clean up old properties that are not used anymore
|
||||
if (config.getCategory(CATEGORY_GENERAL).containsKey("defaultSpawnFuzz")) config.getCategory(CATEGORY_GENERAL).remove("defaultSpawnFuzz");
|
||||
if (config.getCategory(CATEGORY_GENERAL).containsKey("spawnHasFuzz")) config.getCategory(CATEGORY_GENERAL).remove("spawnHasFuzz");
|
||||
if (config.getCategory(CATEGORY_GENERAL).containsKey("disableStitchedFileSaving")) config.getCategory(CATEGORY_GENERAL).remove("disableStitchedFileSaving");
|
||||
if (config.getCategory(CATEGORY_CLIENT).containsKey("java8Reminder")) config.getCategory(CATEGORY_CLIENT).remove("java8Reminder");
|
||||
if (config.getCategory(CATEGORY_CLIENT).containsKey("replaceVanillaBucketModel")) config.getCategory(CATEGORY_CLIENT).remove("replaceVanillaBucketModel");
|
||||
|
||||
// remap properties wrongly listed as general properties to client properties
|
||||
remapGeneralPropertyToClient("biomeSkyBlendRange");
|
||||
remapGeneralPropertyToClient("forgeLightPipelineEnabled");
|
||||
|
||||
prop = config.get(CATEGORY_GENERAL, "disableVersionCheck", false);
|
||||
prop.setComment("Set to true to disable Forge's version check mechanics. Forge queries a small json file on our server for version information. For more details see the ForgeVersion class in our github.");
|
||||
// Language keys are a good idea to implement if you are using config GUIs. This allows you to use a .lang file that will hold the
|
||||
// "pretty" version of the property name as well as allow others to provide their own localizations.
|
||||
// This language key is also used to get the tooltip for a property. The tooltip language key is langKey + ".tooltip".
|
||||
// If no tooltip language key is defined in your .lang file, the tooltip will default to the property comment field.
|
||||
prop.setLanguageKey("forge.configgui.disableVersionCheck");
|
||||
disableVersionCheck = prop.getBoolean(disableVersionCheck);
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "clumpingThreshold", 64,
|
||||
"Controls the number threshold at which Packet51 is preferred over Packet52, default and minimum 64, maximum 1024", 64, 1024);
|
||||
prop.setLanguageKey("forge.configgui.clumpingThreshold").setRequiresWorldRestart(true);
|
||||
clumpingThreshold = prop.getInt(64);
|
||||
if (clumpingThreshold > 1024 || clumpingThreshold < 64)
|
||||
{
|
||||
clumpingThreshold = 64;
|
||||
prop.set(64);
|
||||
}
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(CATEGORY_GENERAL, "sortRecipies", true);
|
||||
prop.setComment("Set to true to enable the post initialization sorting of crafting recipes using Forge's sorter. May cause desyncing on conflicting recipes. MUST RESTART MINECRAFT IF CHANGED FROM THE CONFIG GUI.");
|
||||
prop.setLanguageKey("forge.configgui.sortRecipies").setRequiresMcRestart(true);
|
||||
shouldSortRecipies = prop.getBoolean(true);
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "removeErroringEntities", false);
|
||||
prop.setComment("Set this to true to remove any Entity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.");
|
||||
prop.setLanguageKey("forge.configgui.removeErroringEntities").setRequiresWorldRestart(true);
|
||||
removeErroringEntities = prop.getBoolean(false);
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
if (removeErroringEntities)
|
||||
{
|
||||
FMLLog.log.warn("Enabling removal of erroring Entities - USE AT YOUR OWN RISK");
|
||||
}
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "removeErroringTileEntities", false);
|
||||
prop.setComment("Set this to true to remove any TileEntity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.");
|
||||
prop.setLanguageKey("forge.configgui.removeErroringTileEntities").setRequiresWorldRestart(true);
|
||||
removeErroringTileEntities = prop.getBoolean(false);
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
if (removeErroringTileEntities)
|
||||
{
|
||||
FMLLog.log.warn("Enabling removal of erroring Tile Entities - USE AT YOUR OWN RISK");
|
||||
}
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "fullBoundingBoxLadders", false);
|
||||
prop.setComment("Set this to true to check the entire entity's collision bounding box for ladders instead of just the block they are in. Causes noticeable differences in mechanics so default is vanilla behavior. Default: false");
|
||||
prop.setLanguageKey("forge.configgui.fullBoundingBoxLadders").setRequiresWorldRestart(true);
|
||||
fullBoundingBoxLadders = prop.getBoolean(false);
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "zombieBaseSummonChance", 0.1,
|
||||
"Base zombie summoning spawn chance. Allows changing the bonus zombie summoning mechanic.", 0.0D, 1.0D);
|
||||
prop.setLanguageKey("forge.configgui.zombieBaseSummonChance").setRequiresWorldRestart(true);
|
||||
zombieSummonBaseChance = prop.getDouble(0.1);
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "zombieBabyChance", 0.05,
|
||||
"Chance that a zombie (or subclass) is a baby. Allows changing the zombie spawning mechanic.", 0.0D, 1.0D);
|
||||
prop.setLanguageKey("forge.configgui.zombieBabyChance").setRequiresWorldRestart(true);
|
||||
zombieBabyChance = (float) prop.getDouble(0.05);
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "logCascadingWorldGeneration", true,
|
||||
"Log cascading chunk generation issues during terrain population.");
|
||||
logCascadingWorldGeneration = prop.getBoolean();
|
||||
prop.setLanguageKey("forge.configgui.logCascadingWorldGeneration");
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "fixVanillaCascading", false,
|
||||
"Fix vanilla issues that cause worldgen cascading. This DOES change vanilla worldgen so DO NOT report bugs related to world differences if this flag is on.");
|
||||
fixVanillaCascading = prop.getBoolean();
|
||||
prop.setLanguageKey("forge.configgui.fixVanillaCascading");
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "dimensionUnloadQueueDelay", 0,
|
||||
"The time in ticks the server will wait when a dimension was queued to unload. " +
|
||||
"This can be useful when rapidly loading and unloading dimensions, like e.g. throwing items through a nether portal a few time per second.");
|
||||
dimensionUnloadQueueDelay = prop.getInt(0);
|
||||
prop.setLanguageKey("forge.configgui.dimensionUnloadQueueDelay");
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
config.setCategoryPropertyOrder(CATEGORY_GENERAL, propOrder);
|
||||
|
||||
propOrder = new ArrayList<String>();
|
||||
prop = config.get(VERSION_CHECK_CAT, "Global", true, "Enable the entire mod update check system. This only applies to mods using the Forge system.");
|
||||
propOrder.add("Global");
|
||||
|
||||
config.setCategoryPropertyOrder(VERSION_CHECK_CAT, propOrder);
|
||||
|
||||
// Client-Side only properties
|
||||
propOrder = new ArrayList<String>();
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_CLIENT, "zoomInMissingModelTextInGui", false,
|
||||
"Toggle off to make missing model text in the gui fit inside the slot.");
|
||||
zoomInMissingModelTextInGui = prop.getBoolean(false);
|
||||
prop.setLanguageKey("forge.configgui.zoomInMissingModelTextInGui");
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_CLIENT, "forgeCloudsEnabled", true,
|
||||
"Enable uploading cloud geometry to the GPU for faster rendering.");
|
||||
prop.setLanguageKey("forge.configgui.forgeCloudsEnabled");
|
||||
forgeCloudsEnabled = prop.getBoolean();
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_CLIENT, "disableStairSlabCulling", false,
|
||||
"Disable culling of hidden faces next to stairs and slabs. Causes extra rendering, but may fix some resource packs that exploit this vanilla mechanic.");
|
||||
disableStairSlabCulling = prop.getBoolean(false);
|
||||
prop.setLanguageKey("forge.configgui.disableStairSlabCulling");
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_CLIENT, "alwaysSetupTerrainOffThread", false,
|
||||
"Enable forge to queue all chunk updates to the Chunk Update thread. May increase FPS significantly, but may also cause weird rendering lag. Not recommended for computers " +
|
||||
"without a significant number of cores available.");
|
||||
alwaysSetupTerrainOffThread = prop.getBoolean(false);
|
||||
prop.setLanguageKey("forge.configgui.alwaysSetupTerrainOffThread");
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_CLIENT, "biomeSkyBlendRange", new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34 });
|
||||
prop.setComment("Control the range of sky blending for colored skies in biomes.");
|
||||
prop.setLanguageKey("forge.configgui.biomeSkyBlendRange");
|
||||
blendRanges = prop.getIntList();
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_CLIENT, "forgeLightPipelineEnabled", true,
|
||||
"Enable the forge block rendering pipeline - fixes the lighting of custom models.");
|
||||
forgeLightPipelineEnabled = prop.getBoolean(true);
|
||||
prop.setLanguageKey("forge.configgui.forgeLightPipelineEnabled");
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_CLIENT, "selectiveResourceReloadEnabled", false,
|
||||
"When enabled, makes specific reload tasks such as language changing quicker to run.");
|
||||
selectiveResourceReloadEnabled = prop.getBoolean(false);
|
||||
prop.setLanguageKey("forge.configgui.selectiveResourceReloadEnabled");
|
||||
propOrder.add(prop.getName());
|
||||
|
||||
config.setCategoryPropertyOrder(CATEGORY_CLIENT, propOrder);
|
||||
|
||||
if (config.hasChanged())
|
||||
{
|
||||
config.save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By subscribing to the OnConfigChangedEvent we are able to execute code when our config screens are closed.
|
||||
* This implementation uses the optional configID string to handle multiple Configurations using one event handler.
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public void onConfigChanged(OnConfigChangedEvent event)
|
||||
{
|
||||
if (getMetadata().modId.equals(event.getModID()))
|
||||
{
|
||||
if ("chunkLoader".equals(event.getConfigID()))
|
||||
{
|
||||
ForgeChunkManager.syncConfigDefaults();
|
||||
ForgeChunkManager.loadConfiguration();
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean tmpStairs = disableStairSlabCulling;
|
||||
|
||||
syncConfig(false);
|
||||
|
||||
if (event.isWorldRunning() && tmpStairs != disableStairSlabCulling)
|
||||
{
|
||||
FMLCommonHandler.instance().reloadRenderers();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void missingMapping(RegistryEvent.MissingMappings<Item> event)
|
||||
{
|
||||
for (MissingMappings.Mapping<Item> entry : event.getAllMappings())
|
||||
{
|
||||
if (entry.key.toString().equals("minecraft:totem")) //This item changed from 1.11 -> 1.11.2
|
||||
{
|
||||
ResourceLocation newTotem = new ResourceLocation("minecraft:totem_of_undying");
|
||||
entry.remap(ForgeRegistries.ITEMS.getValue(newTotem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void playerLogin(PlayerEvent.PlayerLoggedInEvent event)
|
||||
{
|
||||
UsernameCache.setUsername(event.player.getPersistentID(), event.player.getGameProfile().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerBus(EventBus bus, LoadController controller)
|
||||
{
|
||||
bus.register(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void modConstruction(FMLConstructionEvent evt)
|
||||
{
|
||||
InputStream is = ForgeModContainer.class.getResourceAsStream("/META-INF/vanilla_annotations.json");
|
||||
if (is != null)
|
||||
JsonAnnotationLoader.loadJson(is, null, evt.getASMHarvestedData());
|
||||
log.debug("Loading Vanilla annotations: " + is);
|
||||
|
||||
List<String> all = Lists.newArrayList();
|
||||
for (ASMData asm : evt.getASMHarvestedData().getAll(ICrashReportDetail.class.getName().replace('.', '/')))
|
||||
all.add(asm.getClassName());
|
||||
for (ASMData asm : evt.getASMHarvestedData().getAll(ICrashCallable.class.getName().replace('.', '/')))
|
||||
all.add(asm.getClassName());
|
||||
// Add table classes for mod list tabulation
|
||||
all.add("net/minecraftforge/common/util/TextTable");
|
||||
all.add("net/minecraftforge/common/util/TextTable$Column");
|
||||
all.add("net/minecraftforge/common/util/TextTable$Row");
|
||||
all.add("net/minecraftforge/common/util/TextTable$Alignment");
|
||||
|
||||
all.removeIf(cls -> !cls.startsWith("net/minecraft/") && !cls.startsWith("net/minecraftforge/"));
|
||||
|
||||
log.debug("Preloading CrashReport Classes");
|
||||
Collections.sort(all); //Sort it because I like pretty output ;)
|
||||
for (String name : all)
|
||||
{
|
||||
log.debug("\t{}", name);
|
||||
try
|
||||
{
|
||||
Class.forName(name.replace('/', '.'), false, MinecraftForge.class.getClassLoader());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("Could not find class for name '{}'.", name, e);
|
||||
}
|
||||
}
|
||||
|
||||
NetworkRegistry.INSTANCE.register(this, this.getClass(), "*", evt.getASMHarvestedData());
|
||||
ForgeNetworkHandler.registerChannel(this, evt.getSide());
|
||||
ConfigManager.sync(this.getModId(), Config.Type.INSTANCE);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void preInit(FMLPreInitializationEvent evt)
|
||||
{
|
||||
CapabilityItemHandler.register();
|
||||
CapabilityFluidHandler.register();
|
||||
CapabilityAnimation.register();
|
||||
CapabilityEnergy.register();
|
||||
MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER);
|
||||
if (FMLCommonHandler.instance().getSide() == Side.CLIENT)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.register(ForgeClientHandler.class);
|
||||
}
|
||||
ForgeChunkManager.captureConfig(evt.getModConfigurationDirectory());
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
||||
if (!ForgeModContainer.disableVersionCheck)
|
||||
{
|
||||
ForgeVersion.startVersionCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void registrItems(RegistryEvent.Register<Item> event)
|
||||
{
|
||||
// Add and register the forge universal bucket, if it's enabled
|
||||
if(FluidRegistry.isUniversalBucketEnabled())
|
||||
{
|
||||
universalBucket = new UniversalBucket();
|
||||
universalBucket.setUnlocalizedName("forge.bucketFilled");
|
||||
event.getRegistry().register(universalBucket.setRegistryName(ForgeVersion.MOD_ID, "bucketFilled"));
|
||||
MinecraftForge.EVENT_BUS.register(universalBucket);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void postInit(FMLPostInitializationEvent evt)
|
||||
{
|
||||
registerAllBiomesAndGenerateEvents();
|
||||
ForgeChunkManager.loadConfiguration();
|
||||
}
|
||||
|
||||
private static void registerAllBiomesAndGenerateEvents()
|
||||
{
|
||||
for (Biome biome : ForgeRegistries.BIOMES.getValuesCollection())
|
||||
{
|
||||
if (biome.decorator instanceof DeferredBiomeDecorator)
|
||||
{
|
||||
DeferredBiomeDecorator decorator = (DeferredBiomeDecorator)biome.decorator;
|
||||
decorator.fireCreateEventAndReplace(biome);
|
||||
}
|
||||
|
||||
BiomeDictionary.ensureHasTypes(biome);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onAvailable(FMLLoadCompleteEvent evt)
|
||||
{
|
||||
if (shouldSortRecipies)
|
||||
{
|
||||
RecipeSorter.sortCraftManager();
|
||||
}
|
||||
FluidRegistry.validateFluidRegistry();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void serverStarting(FMLServerStartingEvent evt)
|
||||
{
|
||||
evt.registerServerCommand(new ForgeCommand());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void serverStopping(FMLServerStoppingEvent evt)
|
||||
{
|
||||
WorldWorkerManager.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound getDataForWriting(SaveHandler handler, WorldInfo info)
|
||||
{
|
||||
NBTTagCompound forgeData = new NBTTagCompound();
|
||||
NBTTagCompound dimData = DimensionManager.saveDimensionDataMap();
|
||||
forgeData.setTag("DimensionData", dimData);
|
||||
FluidRegistry.writeDefaultFluidList(forgeData);
|
||||
return forgeData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readData(SaveHandler handler, WorldInfo info, Map<String, NBTBase> propertyMap, NBTTagCompound tag)
|
||||
{
|
||||
DimensionManager.loadDimensionDataMap(tag.hasKey("DimensionData") ? tag.getCompoundTag("DimensionData") : null);
|
||||
FluidRegistry.loadFluidDefaults(tag);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void mappingChanged(FMLModIdMappingEvent evt)
|
||||
{
|
||||
OreDictionary.rebakeMap();
|
||||
StatList.reinit();
|
||||
Ingredient.invalidateAll();
|
||||
FMLCommonHandler.instance().resetClientRecipeBook();
|
||||
FMLCommonHandler.instance().reloadSearchTrees();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public File getSource()
|
||||
{
|
||||
return FMLForgePlugin.forgeLocation;
|
||||
}
|
||||
@Override
|
||||
public Class<?> getCustomResourcePackClass()
|
||||
{
|
||||
if (getSource().isDirectory())
|
||||
{
|
||||
return FMLFolderResourcePack.class;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FMLFileResourcePack.class;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOwnedPackages()
|
||||
{
|
||||
// All the packages which are part of forge. Only needs updating if new logic is added
|
||||
// that requires event handlers
|
||||
return ImmutableList.of(
|
||||
"net.minecraftforge.classloading",
|
||||
"net.minecraftforge.client",
|
||||
"net.minecraftforge.client.event",
|
||||
"net.minecraftforge.client.event.sound",
|
||||
"net.minecraftforge.client.model",
|
||||
"net.minecraftforge.client.model.obj",
|
||||
"net.minecraftforge.client.model.techne",
|
||||
"net.minecraftforge.common",
|
||||
"net.minecraftforge.common.config",
|
||||
"net.minecraftforge.common.network",
|
||||
"net.minecraftforge.common.util",
|
||||
"net.minecraftforge.event",
|
||||
"net.minecraftforge.event.brewing",
|
||||
"net.minecraftforge.event.entity",
|
||||
"net.minecraftforge.event.entity.item",
|
||||
"net.minecraftforge.event.entity.living",
|
||||
"net.minecraftforge.event.entity.minecart",
|
||||
"net.minecraftforge.event.entity.player",
|
||||
"net.minecraftforge.event.terraingen",
|
||||
"net.minecraftforge.event.world",
|
||||
"net.minecraftforge.fluids",
|
||||
"net.minecraftforge.oredict",
|
||||
"net.minecraftforge.server",
|
||||
"net.minecraftforge.server.command",
|
||||
"net.minecraftforge.transformers"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Certificate getSigningCertificate()
|
||||
{
|
||||
Certificate[] certificates = getClass().getProtectionDomain().getCodeSource().getCertificates();
|
||||
return certificates != null ? certificates[0] : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUpdateUrl()
|
||||
{
|
||||
return updateJSONUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import static net.minecraftforge.common.ForgeVersion.Status.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import net.minecraftforge.fml.common.InjectedModContainer;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.ModContainer;
|
||||
import net.minecraftforge.fml.common.versioning.ComparableVersion;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ForgeVersion
|
||||
{
|
||||
// This is Forge's Mod Id, used for the ForgeModContainer and resource locations
|
||||
public static final String MOD_ID = "forge";
|
||||
//This number is incremented every time we remove deprecated code/major API changes, never reset
|
||||
public static final int majorVersion = 14;
|
||||
//This number is incremented every minecraft release, never reset
|
||||
public static final int minorVersion = 23;
|
||||
//This number is incremented every time a interface changes or new major feature is added, and reset every Minecraft version
|
||||
public static final int revisionVersion = 4;
|
||||
//This number is incremented every time Jenkins builds Forge, and never reset. Should always be 0 in the repo code.
|
||||
public static final int buildVersion = 2744;
|
||||
// This is the minecraft version we're building for - used in various places in Forge/FML code
|
||||
public static final String mcVersion = "1.12.2";
|
||||
// This is the MCP data version we're using
|
||||
public static final String mcpVersion = "9.42";
|
||||
@SuppressWarnings("unused")
|
||||
private static Status status = PENDING;
|
||||
@SuppressWarnings("unused")
|
||||
private static String target = null;
|
||||
|
||||
private static final Logger log = LogManager.getLogger(MOD_ID + ".VersionCheck");
|
||||
|
||||
private static final int MAX_HTTP_REDIRECTS = Integer.getInteger("http.maxRedirects", 20);
|
||||
|
||||
public static int getMajorVersion()
|
||||
{
|
||||
return majorVersion;
|
||||
}
|
||||
|
||||
public static int getMinorVersion()
|
||||
{
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
public static int getRevisionVersion()
|
||||
{
|
||||
return revisionVersion;
|
||||
}
|
||||
|
||||
public static int getBuildVersion()
|
||||
{
|
||||
return buildVersion;
|
||||
}
|
||||
|
||||
public static Status getStatus()
|
||||
{
|
||||
return getResult(ForgeModContainer.getInstance()).status;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getTarget()
|
||||
{
|
||||
CheckResult res = getResult(ForgeModContainer.getInstance());
|
||||
return res.target != null ? res.target.toString() : null;
|
||||
}
|
||||
|
||||
public static String getVersion()
|
||||
{
|
||||
return String.format("%d.%d.%d.%d", majorVersion, minorVersion, revisionVersion, buildVersion);
|
||||
}
|
||||
|
||||
public static enum Status
|
||||
{
|
||||
PENDING(),
|
||||
FAILED(),
|
||||
UP_TO_DATE(),
|
||||
OUTDATED(3, true),
|
||||
AHEAD(),
|
||||
BETA(),
|
||||
BETA_OUTDATED(6, true);
|
||||
|
||||
final int sheetOffset;
|
||||
final boolean draw, animated;
|
||||
|
||||
Status()
|
||||
{
|
||||
this(0, false, false);
|
||||
}
|
||||
|
||||
Status(int sheetOffset)
|
||||
{
|
||||
this(sheetOffset, true, false);
|
||||
}
|
||||
|
||||
Status(int sheetOffset, boolean animated)
|
||||
{
|
||||
this(sheetOffset, true, animated);
|
||||
}
|
||||
|
||||
Status(int sheetOffset, boolean draw, boolean animated)
|
||||
{
|
||||
this.sheetOffset = sheetOffset;
|
||||
this.draw = draw;
|
||||
this.animated = animated;
|
||||
}
|
||||
|
||||
public int getSheetOffset()
|
||||
{
|
||||
return sheetOffset;
|
||||
}
|
||||
|
||||
public boolean shouldDraw()
|
||||
{
|
||||
return draw;
|
||||
}
|
||||
|
||||
public boolean isAnimated()
|
||||
{
|
||||
return animated;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CheckResult
|
||||
{
|
||||
public final Status status;
|
||||
@Nullable
|
||||
public final ComparableVersion target;
|
||||
public final Map<ComparableVersion, String> changes;
|
||||
@Nullable
|
||||
public final String url;
|
||||
|
||||
private CheckResult(Status status, @Nullable ComparableVersion target, @Nullable Map<ComparableVersion, String> changes, @Nullable String url)
|
||||
{
|
||||
this.status = status;
|
||||
this.target = target;
|
||||
this.changes = changes == null ? Collections.<ComparableVersion, String>emptyMap() : Collections.unmodifiableMap(changes);
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
public static void startVersionCheck()
|
||||
{
|
||||
new Thread("Forge Version Check")
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!ForgeModContainer.getConfig().get(ForgeModContainer.VERSION_CHECK_CAT, "Global", true).getBoolean())
|
||||
{
|
||||
log.info("Global Forge version check system disabled, no further processing.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (Entry<ModContainer, URL> entry : gatherMods().entrySet())
|
||||
{
|
||||
ModContainer mod = entry.getKey();
|
||||
if (ForgeModContainer.getConfig().get(ForgeModContainer.VERSION_CHECK_CAT, mod.getModId(), true).getBoolean())
|
||||
{
|
||||
process(mod, entry.getValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
log.info("[{}] Skipped version check", mod.getModId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens stream for given URL while following redirects
|
||||
*/
|
||||
private InputStream openUrlStream(URL url) throws IOException
|
||||
{
|
||||
URL currentUrl = url;
|
||||
for (int redirects = 0; redirects < MAX_HTTP_REDIRECTS; redirects++)
|
||||
{
|
||||
URLConnection c = currentUrl.openConnection();
|
||||
if (c instanceof HttpURLConnection)
|
||||
{
|
||||
HttpURLConnection huc = (HttpURLConnection) c;
|
||||
huc.setInstanceFollowRedirects(false);
|
||||
int responseCode = huc.getResponseCode();
|
||||
if (responseCode >= 300 && responseCode <= 399)
|
||||
{
|
||||
try
|
||||
{
|
||||
String loc = huc.getHeaderField("Location");
|
||||
currentUrl = new URL(currentUrl, loc);
|
||||
continue;
|
||||
}
|
||||
finally
|
||||
{
|
||||
huc.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c.getInputStream();
|
||||
}
|
||||
throw new IOException("Too many redirects while trying to fetch " + url);
|
||||
}
|
||||
|
||||
private void process(ModContainer mod, URL url)
|
||||
{
|
||||
try
|
||||
{
|
||||
log.info("[{}] Starting version check at {}", mod.getModId(), url.toString());
|
||||
Status status = PENDING;
|
||||
ComparableVersion target = null;
|
||||
|
||||
InputStream con = openUrlStream(url);
|
||||
String data = new String(ByteStreams.toByteArray(con), "UTF-8");
|
||||
con.close();
|
||||
|
||||
log.debug("[{}] Received version check data:\n{}", mod.getModId(), data);
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> json = new Gson().fromJson(data, Map.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> promos = (Map<String, String>)json.get("promos");
|
||||
String display_url = (String)json.get("homepage");
|
||||
|
||||
String rec = promos.get(MinecraftForge.MC_VERSION + "-recommended");
|
||||
String lat = promos.get(MinecraftForge.MC_VERSION + "-latest");
|
||||
ComparableVersion current = new ComparableVersion(mod.getVersion());
|
||||
|
||||
if (rec != null)
|
||||
{
|
||||
ComparableVersion recommended = new ComparableVersion(rec);
|
||||
int diff = recommended.compareTo(current);
|
||||
|
||||
if (diff == 0)
|
||||
status = UP_TO_DATE;
|
||||
else if (diff < 0)
|
||||
{
|
||||
status = AHEAD;
|
||||
if (lat != null)
|
||||
{
|
||||
ComparableVersion latest = new ComparableVersion(lat);
|
||||
if (current.compareTo(latest) < 0)
|
||||
{
|
||||
status = OUTDATED;
|
||||
target = latest;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status = OUTDATED;
|
||||
target = recommended;
|
||||
}
|
||||
}
|
||||
else if (lat != null)
|
||||
{
|
||||
ComparableVersion latest = new ComparableVersion(lat);
|
||||
if (current.compareTo(latest) < 0)
|
||||
{
|
||||
status = BETA_OUTDATED;
|
||||
target = latest;
|
||||
}
|
||||
else
|
||||
status = BETA;
|
||||
}
|
||||
else
|
||||
status = BETA;
|
||||
|
||||
log.info("[{}] Found status: {} Target: {}", mod.getModId(), status, target);
|
||||
|
||||
Map<ComparableVersion, String> changes = new LinkedHashMap<ComparableVersion, String>();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> tmp = (Map<String, String>)json.get(MinecraftForge.MC_VERSION);
|
||||
if (tmp != null)
|
||||
{
|
||||
List<ComparableVersion> ordered = new ArrayList<ComparableVersion>();
|
||||
for (String key : tmp.keySet())
|
||||
{
|
||||
ComparableVersion ver = new ComparableVersion(key);
|
||||
if (ver.compareTo(current) > 0 && (target == null || ver.compareTo(target) < 1))
|
||||
{
|
||||
ordered.add(ver);
|
||||
}
|
||||
}
|
||||
Collections.sort(ordered);
|
||||
|
||||
for (ComparableVersion ver : ordered)
|
||||
{
|
||||
changes.put(ver, tmp.get(ver.toString()));
|
||||
}
|
||||
}
|
||||
if (mod instanceof InjectedModContainer)
|
||||
mod = ((InjectedModContainer)mod).wrappedContainer;
|
||||
results.put(mod, new CheckResult(status, target, changes, display_url));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.debug("Failed to process update information", e);
|
||||
status = FAILED;
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
// Gather a list of mods that have opted in to this update system by providing a URL.
|
||||
public static Map<ModContainer, URL> gatherMods()
|
||||
{
|
||||
Map<ModContainer, URL> ret = new HashMap<ModContainer, URL>();
|
||||
for (ModContainer mod : Loader.instance().getActiveModList())
|
||||
{
|
||||
URL url = mod.getUpdateUrl();
|
||||
if (url != null)
|
||||
ret.put(mod, url);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static Map<ModContainer, CheckResult> results = new ConcurrentHashMap<ModContainer, CheckResult>();
|
||||
private static final CheckResult PENDING_CHECK = new CheckResult(PENDING, null, null, null);
|
||||
|
||||
public static CheckResult getResult(ModContainer mod)
|
||||
{
|
||||
if (mod == null) return PENDING_CHECK;
|
||||
if (mod instanceof InjectedModContainer)
|
||||
mod = ((InjectedModContainer)mod).wrappedContainer;
|
||||
CheckResult ret = results.get(mod);
|
||||
return ret == null ? PENDING_CHECK : ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.item.EntityMinecart;
|
||||
|
||||
/**
|
||||
* This class defines a replacement for the default minecart collision code.
|
||||
* Only one handler can be registered at a time. It it registered with EntityMinecart.registerCollisionHandler().
|
||||
* If you use this, make it a configuration option.
|
||||
* @author CovertJaguar
|
||||
*/
|
||||
public interface IMinecartCollisionHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* This basically replaces the function of the same name in EntityMinecart.
|
||||
* Code in IMinecartHooks.applyEntityCollisionHook is still run.
|
||||
* @param cart The cart that called the collision.
|
||||
* @param other The object it collided with.
|
||||
*/
|
||||
void onEntityCollision(EntityMinecart cart, Entity other);
|
||||
|
||||
/**
|
||||
* This function replaced the function of the same name in EntityMinecart.
|
||||
* It is used to define whether minecarts collide with specific entities,
|
||||
* for example items.
|
||||
* @param cart The cart for which the collision box was requested.
|
||||
* @param other The entity requesting the collision box.
|
||||
* @return The collision box or null.
|
||||
*/
|
||||
AxisAlignedBB getCollisionBox(EntityMinecart cart, Entity other);
|
||||
|
||||
/**
|
||||
* This function is used to define the box used for detecting minecart collisions.
|
||||
* It is generally bigger that the normal collision box.
|
||||
* @param cart The cart for which the collision box was requested.
|
||||
* @return The collision box, cannot be null.
|
||||
*/
|
||||
AxisAlignedBB getMinecartCollisionBox(EntityMinecart cart);
|
||||
|
||||
/**
|
||||
* This function replaces the function of the same name in EntityMinecart.
|
||||
* It defines whether minecarts are solid to the player.
|
||||
* @param cart The cart for which the bounding box was requested.
|
||||
* @return The bounding box or null.
|
||||
*/
|
||||
AxisAlignedBB getBoundingBox(EntityMinecart cart);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
public interface IPlantable
|
||||
{
|
||||
EnumPlantType getPlantType(IBlockAccess world, BlockPos pos);
|
||||
IBlockState getPlant(IBlockAccess world, BlockPos pos);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
*
|
||||
* This allows for mods to create there own Shear-like items
|
||||
* and have them interact with Blocks/Entities without extra work.
|
||||
* Also, if your block/entity supports the Shears, this allows you
|
||||
* to support mod-shears as well.
|
||||
*
|
||||
*/
|
||||
//TODO Change to World, not IBlockAccess and make Implementor responsible for removing itself from the world.
|
||||
//Better mimics vanilla behavior and allows more control for the user.
|
||||
public interface IShearable
|
||||
{
|
||||
/**
|
||||
* Checks if the object is currently shearable
|
||||
* Example: Sheep return false when they have no wool
|
||||
*
|
||||
* @param item The ItemStack that is being used, may be empty.
|
||||
* @param world The current world.
|
||||
* @param pos Block's position in world.
|
||||
* @return If this is shearable, and onSheared should be called.
|
||||
*/
|
||||
boolean isShearable(@Nonnull ItemStack item, IBlockAccess world, BlockPos pos);
|
||||
|
||||
/**
|
||||
* Performs the shear function on this object.
|
||||
* This is called for both client, and server.
|
||||
* The object should perform all actions related to being sheared,
|
||||
* except for dropping of the items, and removal of the block.
|
||||
* As those are handled by ItemShears itself.
|
||||
*
|
||||
* Returns a list of items that resulted from the shearing process.
|
||||
*
|
||||
* For entities, they should trust there internal location information
|
||||
* over the values passed into this function.
|
||||
*
|
||||
* @param item The ItemStack that is being used, may be empty.
|
||||
* @param world The current world.
|
||||
* @param pos If this is a block, the block's position in world.
|
||||
* @param fortune The fortune level of the shears being used.
|
||||
* @return A List containing all items from this shearing. May be empty.
|
||||
*/
|
||||
@Nonnull
|
||||
List<ItemStack> onSheared(@Nonnull ItemStack item, IBlockAccess world, BlockPos pos, int fortune);
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.minecraft.util.CombatRules;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.ItemArmor;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* This interface is to be implemented by ItemArmor classes. It will allow to
|
||||
* modify computation of damage and health loss. Computation will be called
|
||||
* before the actual armor computation, which can then be cancelled.
|
||||
*
|
||||
* @see ItemArmor
|
||||
*/
|
||||
public interface ISpecialArmor
|
||||
{
|
||||
//TODO: Change 'int slot' to EnumArmorType
|
||||
/**
|
||||
* Retrieves the modifiers to be used when calculating armor damage.
|
||||
*
|
||||
* Armor will higher priority will have damage applied to them before
|
||||
* lower priority ones. If there are multiple pieces of armor with the
|
||||
* same priority, damage will be distributed between them based on there
|
||||
* absorption ratio.
|
||||
*
|
||||
* @param player The entity wearing the armor.
|
||||
* @param armor The ItemStack of the armor item itself.
|
||||
* @param source The source of the damage, which can be used to alter armor
|
||||
* properties based on the type or source of damage.
|
||||
* @param damage The total damage being applied to the entity
|
||||
* @param slot The armor slot the item is in.
|
||||
* @return A ArmorProperties instance holding information about how the armor effects damage.
|
||||
*/
|
||||
ArmorProperties getProperties(EntityLivingBase player, @Nonnull ItemStack armor, DamageSource source, double damage, int slot);
|
||||
|
||||
/**
|
||||
* Get the displayed effective armor.
|
||||
*
|
||||
* @param player The player wearing the armor.
|
||||
* @param armor The ItemStack of the armor item itself.
|
||||
* @param slot The armor slot the item is in.
|
||||
* @return The number of armor points for display, 2 per shield.
|
||||
*/
|
||||
int getArmorDisplay(EntityPlayer player, @Nonnull ItemStack armor, int slot);
|
||||
|
||||
/**
|
||||
* Applies damage to the ItemStack. The mod is responsible for reducing the
|
||||
* item durability and stack size. If the stack is depleted it will be cleaned
|
||||
* up automatically.
|
||||
*
|
||||
* @param entity The entity wearing the armor
|
||||
* @param stack The ItemStack of the armor item itself.
|
||||
* @param source The source of the damage, which can be used to alter armor
|
||||
* properties based on the type or source of damage.
|
||||
* @param damage The amount of damage being applied to the armor
|
||||
* @param slot The armor slot the item is in.
|
||||
*/
|
||||
void damageArmor(EntityLivingBase entity, @Nonnull ItemStack stack, DamageSource source, int damage, int slot);
|
||||
|
||||
/**
|
||||
* Simple check to see if the armor should interact with "Unblockable" damage
|
||||
* sources. A fair number of vanilla damage sources have this tag, such as
|
||||
* Anvils, Falling, Fire, and Magic.
|
||||
*
|
||||
* Returning true here means that the armor is able to meaningfully respond
|
||||
* to this damage source. Otherwise, no interaction is allowed.
|
||||
*/
|
||||
default boolean handleUnblockableDamage(EntityLivingBase entity, @Nonnull ItemStack armor, DamageSource source, double damage, int slot)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class ArmorProperties implements Comparable<ArmorProperties>
|
||||
{
|
||||
public int Priority = 0;
|
||||
public int AbsorbMax = Integer.MAX_VALUE;
|
||||
public double AbsorbRatio = 0;
|
||||
public double Armor = 0; //Additional armor, separate from the armor added by vanilla attributes.
|
||||
public double Toughness = 0; //Additional toughness, separate from the armor added by vanilla attributes.
|
||||
public int Slot = 0;
|
||||
private static final boolean DEBUG = false; //Only enable this if you wish to be spammed with debugging information.
|
||||
//Left it in because I figured it'd be useful for modders developing custom armor.
|
||||
|
||||
|
||||
public ArmorProperties(int priority, double ratio, int max)
|
||||
{
|
||||
Priority = priority;
|
||||
AbsorbRatio = ratio;
|
||||
Armor = 0;
|
||||
Toughness = 0;
|
||||
AbsorbMax = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers and applies armor reduction to damage being dealt to a entity.
|
||||
*
|
||||
* @param entity The Entity being damage
|
||||
* @param inventory An array of armor items
|
||||
* @param source The damage source type
|
||||
* @param damage The total damage being done
|
||||
* @return The left over damage that has not been absorbed by the armor
|
||||
*/
|
||||
public static float applyArmor(EntityLivingBase entity, NonNullList<ItemStack> inventory, DamageSource source, double damage)
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("Start: " + damage);
|
||||
}
|
||||
|
||||
double totalArmor = entity.getTotalArmorValue();
|
||||
double totalToughness = entity.getEntityAttribute(SharedMonsterAttributes.ARMOR_TOUGHNESS).getAttributeValue();
|
||||
|
||||
if (source.isUnblockable())
|
||||
{
|
||||
totalArmor = 0;
|
||||
totalToughness = 0;
|
||||
}
|
||||
|
||||
ArrayList<ArmorProperties> dmgVals = new ArrayList<ArmorProperties>();
|
||||
for (int slot = 0; slot < inventory.size(); slot++)
|
||||
{
|
||||
ItemStack stack = inventory.get(slot);
|
||||
|
||||
if (stack.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ArmorProperties prop = null;
|
||||
if (stack.getItem() instanceof ISpecialArmor)
|
||||
{
|
||||
if (!source.isUnblockable() || ((ISpecialArmor) stack.getItem()).handleUnblockableDamage(entity, stack, source, damage, slot)) {
|
||||
ISpecialArmor armor = (ISpecialArmor)stack.getItem();
|
||||
prop = armor.getProperties(entity, stack, source, damage, slot).copy();
|
||||
totalArmor += prop.Armor;
|
||||
totalToughness += prop.Toughness;
|
||||
}
|
||||
}
|
||||
else if (stack.getItem() instanceof ItemArmor && !source.isUnblockable())
|
||||
{
|
||||
ItemArmor armor = (ItemArmor)stack.getItem();
|
||||
prop = new ArmorProperties(0, 0, Integer.MAX_VALUE);
|
||||
prop.Armor = armor.damageReduceAmount;
|
||||
prop.Toughness = armor.toughness;
|
||||
}
|
||||
if (prop != null)
|
||||
{
|
||||
prop.Slot = slot;
|
||||
dmgVals.add(prop);
|
||||
}
|
||||
}
|
||||
if (dmgVals.size() > 0)
|
||||
{
|
||||
ArmorProperties[] props = dmgVals.toArray(new ArmorProperties[dmgVals.size()]);
|
||||
StandardizeList(props, damage);
|
||||
int level = props[0].Priority;
|
||||
double ratio = 0;
|
||||
for (ArmorProperties prop : props)
|
||||
{
|
||||
if (level != prop.Priority)
|
||||
{
|
||||
damage -= (damage * ratio);
|
||||
ratio = 0;
|
||||
level = prop.Priority;
|
||||
}
|
||||
ratio += prop.AbsorbRatio;
|
||||
|
||||
double absorb = damage * prop.AbsorbRatio;
|
||||
if (absorb > 0)
|
||||
{
|
||||
ItemStack stack = inventory.get(prop.Slot);
|
||||
int itemDamage = (int)Math.max(1, absorb);
|
||||
if (stack.getItem() instanceof ISpecialArmor)
|
||||
{
|
||||
((ISpecialArmor)stack.getItem()).damageArmor(entity, stack, source, itemDamage, prop.Slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("Item: " + stack.toString() + " Absorbed: " + absorb + " Damaged: " + itemDamage);
|
||||
}
|
||||
stack.damageItem(itemDamage, entity);
|
||||
}
|
||||
if (stack.isEmpty())
|
||||
{
|
||||
/*if (entity instanceof EntityPlayer)
|
||||
{
|
||||
stack.onItemDestroyedByUse((EntityPlayer)entity);
|
||||
}*/
|
||||
inventory.set(prop.Slot, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
damage -= (damage * ratio);
|
||||
}
|
||||
if (damage > 0 && (totalArmor > 0 || totalToughness > 0))
|
||||
{
|
||||
double armorDamage = Math.max(1.0F, damage / 4.0F);
|
||||
|
||||
for (int i = 0; i < inventory.size(); i++)
|
||||
{
|
||||
if (inventory.get(i).getItem() instanceof ItemArmor)
|
||||
{
|
||||
inventory.get(i).damageItem((int)armorDamage, entity);
|
||||
|
||||
if (inventory.get(i).getCount() == 0)
|
||||
{
|
||||
inventory.set(i, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
damage = CombatRules.getDamageAfterAbsorb((float)damage, (float)totalArmor, (float)totalToughness);
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("Return: " + (int)(damage) + " " + damage);
|
||||
}
|
||||
return (float)(damage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and standardizes the distribution of damage over armor.
|
||||
*
|
||||
* @param armor The armor information
|
||||
* @param damage The total damage being received
|
||||
*/
|
||||
private static void StandardizeList(ArmorProperties[] armor, double damage)
|
||||
{
|
||||
Arrays.sort(armor);
|
||||
|
||||
int start = 0;
|
||||
double total = 0;
|
||||
int priority = armor[0].Priority;
|
||||
int pStart = 0;
|
||||
boolean pChange = false;
|
||||
boolean pFinished = false;
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
for (ArmorProperties prop : armor)
|
||||
{
|
||||
System.out.println(prop);
|
||||
}
|
||||
System.out.println("========================");
|
||||
}
|
||||
|
||||
for (int x = 0; x < armor.length; x++)
|
||||
{
|
||||
total += armor[x].AbsorbRatio;
|
||||
if (x == armor.length - 1 || armor[x].Priority != priority)
|
||||
{
|
||||
if (armor[x].Priority != priority)
|
||||
{
|
||||
total -= armor[x].AbsorbRatio;
|
||||
x--;
|
||||
pChange = true;
|
||||
}
|
||||
if (total > 1)
|
||||
{
|
||||
for (int y = start; y <= x; y++)
|
||||
{
|
||||
double newRatio = armor[y].AbsorbRatio / total;
|
||||
if (newRatio * damage > armor[y].AbsorbMax)
|
||||
{
|
||||
armor[y].AbsorbRatio = (double)armor[y].AbsorbMax / damage;
|
||||
total = 0;
|
||||
for (int z = pStart; z <= y; z++)
|
||||
{
|
||||
total += armor[z].AbsorbRatio;
|
||||
}
|
||||
start = y + 1;
|
||||
x = y;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
armor[y].AbsorbRatio = newRatio;
|
||||
pFinished = true;
|
||||
}
|
||||
}
|
||||
if (pChange && pFinished)
|
||||
{
|
||||
damage -= (damage * total);
|
||||
total = 0;
|
||||
start = x + 1;
|
||||
priority = armor[start].Priority;
|
||||
pStart = start;
|
||||
pChange = false;
|
||||
pFinished = false;
|
||||
if (damage <= 0)
|
||||
{
|
||||
for (int y = x + 1; y < armor.length; y++)
|
||||
{
|
||||
armor[y].AbsorbRatio = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int y = start; y <= x; y++)
|
||||
{
|
||||
total -= armor[y].AbsorbRatio;
|
||||
if (damage * armor[y].AbsorbRatio > armor[y].AbsorbMax)
|
||||
{
|
||||
armor[y].AbsorbRatio = (double)armor[y].AbsorbMax / damage;
|
||||
}
|
||||
total += armor[y].AbsorbRatio;
|
||||
}
|
||||
damage -= (damage * total);
|
||||
total = 0;
|
||||
if (x != armor.length - 1)
|
||||
{
|
||||
start = x + 1;
|
||||
priority = armor[start].Priority;
|
||||
pStart = start;
|
||||
pChange = false;
|
||||
if (damage <= 0)
|
||||
{
|
||||
for (int y = x + 1; y < armor.length; y++)
|
||||
{
|
||||
armor[y].AbsorbRatio = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
for (ArmorProperties prop : armor)
|
||||
{
|
||||
System.out.println(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ArmorProperties o)
|
||||
{
|
||||
if (o.Priority != Priority)
|
||||
{
|
||||
return o.Priority - Priority;
|
||||
}
|
||||
double left = ( AbsorbRatio == 0 ? 0 : AbsorbMax * 100.0D / AbsorbRatio);
|
||||
double right = (o.AbsorbRatio == 0 ? 0 : o.AbsorbMax * 100.0D / o.AbsorbRatio);
|
||||
return (int)(left - right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%d, %d, %f, %d", Priority, AbsorbMax, AbsorbRatio, (AbsorbRatio == 0 ? 0 : (int)(AbsorbMax * 100.0D / AbsorbRatio)));
|
||||
}
|
||||
|
||||
public ArmorProperties copy()
|
||||
{
|
||||
ArmorProperties copy = new ArmorProperties(Priority, AbsorbRatio, AbsorbMax);
|
||||
copy.Armor = Armor;
|
||||
copy.Toughness = Toughness;
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.ICrashCallable;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.discovery.ASMDataTable;
|
||||
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventBus;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.crash.ICrashReportDetail;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.ForgeHooks.SeedEntry;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class MinecraftForge
|
||||
{
|
||||
/**
|
||||
* The core Forge EventBusses, all events for Forge will be fired on these,
|
||||
* you should use this to register all your listeners.
|
||||
* This replaces every register*Handler() function in the old version of Forge.
|
||||
* TERRAIN_GEN_BUS for terrain gen events
|
||||
* ORE_GEN_BUS for ore gen events
|
||||
* EVENT_BUS for everything else
|
||||
*/
|
||||
public static final EventBus EVENT_BUS = new EventBus();
|
||||
public static final EventBus TERRAIN_GEN_BUS = new EventBus();
|
||||
public static final EventBus ORE_GEN_BUS = new EventBus();
|
||||
public static final String MC_VERSION = Loader.MC_VERSION;
|
||||
|
||||
static final ForgeInternalHandler INTERNAL_HANDLER = new ForgeInternalHandler();
|
||||
|
||||
/**
|
||||
* Register a new seed to be dropped when breaking tall grass.
|
||||
*
|
||||
* @param seed The item to drop as a seed.
|
||||
* @param weight The relative probability of the seeds,
|
||||
* where wheat seeds are 10.
|
||||
*
|
||||
* Note: These functions may be going away soon, we're looking into loot tables....
|
||||
*/
|
||||
public static void addGrassSeed(@Nonnull ItemStack seed, int weight)
|
||||
{
|
||||
addGrassSeed(new SeedEntry(seed, weight));
|
||||
}
|
||||
public static void addGrassSeed(SeedEntry seed)
|
||||
{
|
||||
ForgeHooks.seedList.add(seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method invoked by FML before any other mods are loaded.
|
||||
*/
|
||||
public static void initialize()
|
||||
{
|
||||
FMLLog.log.info("MinecraftForge v{} Initialized", ForgeVersion.getVersion());
|
||||
|
||||
OreDictionary.getOreName(0);
|
||||
|
||||
UsernameCache.load();
|
||||
// Load before all the mods, so MC owns the MC fluids
|
||||
FluidRegistry.validateFluidRegistry();
|
||||
ForgeHooks.initTools();
|
||||
|
||||
//For all the normal CrashReport classes to be defined. We're in MC's classloader so this should all be fine
|
||||
new CrashReport("ThisIsFake", new Exception("Not real"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void preloadCrashClasses(ASMDataTable table, String modID, Set<String> classes)
|
||||
{
|
||||
//Find all ICrashReportDetail's handlers and preload them.
|
||||
List<String> all = Lists.newArrayList();
|
||||
for (ASMData asm : table.getAll(ICrashReportDetail.class.getName().replace('.', '/')))
|
||||
all.add(asm.getClassName());
|
||||
for (ASMData asm : table.getAll(ICrashCallable.class.getName().replace('.', '/')))
|
||||
all.add(asm.getClassName());
|
||||
|
||||
all.retainAll(classes);
|
||||
|
||||
if (all.size() == 0)
|
||||
return;
|
||||
|
||||
ForgeModContainer.log.debug("Preloading CrashReport Classes");
|
||||
Collections.sort(all); //Sort it because I like pretty output ;)
|
||||
for (String name : all)
|
||||
{
|
||||
ForgeModContainer.log.debug("\t{}", name);
|
||||
try
|
||||
{
|
||||
Class.forName(name.replace('/', '.'), false, MinecraftForge.class.getClassLoader());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log.error("Could not find class for name '{}'.", name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.FMLInjectionData;
|
||||
|
||||
/**
|
||||
* Caches player's last known usernames
|
||||
* <p>
|
||||
* Modders should use {@link #getLastKnownUsername(UUID)} to determine a players
|
||||
* last known username.<br>
|
||||
* For convenience, {@link #getMap()} is provided to get an immutable copy of
|
||||
* the caches underlying map.
|
||||
*/
|
||||
public final class UsernameCache {
|
||||
|
||||
private static Map<UUID, String> map = Maps.newHashMap();
|
||||
|
||||
private static final Charset charset = StandardCharsets.UTF_8;
|
||||
|
||||
private static final File saveFile = new File( /* The minecraft dir */(File) FMLInjectionData.data()[6], "usernamecache.json");
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
private static final Logger log = LogManager.getLogger(ForgeVersion.MOD_ID + ".UsernameCache");
|
||||
|
||||
private UsernameCache() {}
|
||||
|
||||
/**
|
||||
* Set a player's current username
|
||||
*
|
||||
* @param uuid
|
||||
* the player's {@link java.util.UUID UUID}
|
||||
* @param username
|
||||
* the player's username
|
||||
*/
|
||||
protected static void setUsername(UUID uuid, String username)
|
||||
{
|
||||
checkNotNull(uuid);
|
||||
checkNotNull(username);
|
||||
|
||||
if (username.equals(map.get(uuid))) return;
|
||||
|
||||
map.put(uuid, username);
|
||||
save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a player's username from the cache
|
||||
*
|
||||
* @param uuid
|
||||
* the player's {@link java.util.UUID UUID}
|
||||
* @return if the cache contained the user
|
||||
*/
|
||||
protected static boolean removeUsername(UUID uuid)
|
||||
{
|
||||
checkNotNull(uuid);
|
||||
|
||||
if (map.remove(uuid) != null)
|
||||
{
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the player's last known username
|
||||
* <p>
|
||||
* <b>May be <code>null</code></b>
|
||||
*
|
||||
* @param uuid
|
||||
* the player's {@link java.util.UUID UUID}
|
||||
* @return the player's last known username, or <code>null</code> if the
|
||||
* cache doesn't have a record of the last username
|
||||
*/
|
||||
@Nullable
|
||||
public static String getLastKnownUsername(UUID uuid)
|
||||
{
|
||||
checkNotNull(uuid);
|
||||
return map.get(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cache contains the given player's username
|
||||
*
|
||||
* @param uuid
|
||||
* the player's {@link java.util.UUID UUID}
|
||||
* @return if the cache contains a username for the given player
|
||||
*/
|
||||
public static boolean containsUUID(UUID uuid)
|
||||
{
|
||||
checkNotNull(uuid);
|
||||
return map.containsKey(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an immutable copy of the cache's underlying map
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
public static Map<UUID, String> getMap()
|
||||
{
|
||||
return ImmutableMap.copyOf(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the cache to file
|
||||
*/
|
||||
protected static void save()
|
||||
{
|
||||
new SaveThread(gson.toJson(map)).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the cache from file
|
||||
*/
|
||||
protected static void load()
|
||||
{
|
||||
if (!saveFile.exists()) return;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
String json = Files.toString(saveFile, charset);
|
||||
Type type = new TypeToken<Map<UUID, String>>() { private static final long serialVersionUID = 1L; }.getType();
|
||||
|
||||
map = gson.fromJson(json, type);
|
||||
}
|
||||
catch (JsonSyntaxException e)
|
||||
{
|
||||
log.error("Could not parse username cache file as valid json, deleting file", e);
|
||||
saveFile.delete();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.error("Failed to read username cache file from disk, deleting file", e);
|
||||
saveFile.delete();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Can sometimes occur when the json file is malformed
|
||||
if (map == null)
|
||||
{
|
||||
map = Maps.newHashMap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for saving the {@link com.google.gson.Gson#toJson(Object) Gson}
|
||||
* representation of the cache to disk
|
||||
*/
|
||||
private static class SaveThread extends Thread {
|
||||
|
||||
/** The data that will be saved to disk */
|
||||
private final String data;
|
||||
|
||||
public SaveThread(String data)
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Make sure we don't save when another thread is still saving
|
||||
synchronized (saveFile)
|
||||
{
|
||||
Files.write(data, saveFile, charset);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.error("Failed to save username cache to file!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.minecraft.world.gen.structure.template.TemplateManager;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import net.minecraft.world.chunk.storage.IChunkLoader;
|
||||
import net.minecraft.world.storage.IPlayerFileData;
|
||||
import net.minecraft.world.storage.ISaveHandler;
|
||||
import net.minecraft.world.MinecraftException;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.world.storage.WorldInfo;
|
||||
import net.minecraft.world.WorldProvider;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
|
||||
//Class used internally to provide the world specific data directories.
|
||||
|
||||
public class WorldSpecificSaveHandler implements ISaveHandler
|
||||
{
|
||||
private WorldServer world;
|
||||
private ISaveHandler parent;
|
||||
private File dataDir;
|
||||
|
||||
public WorldSpecificSaveHandler(WorldServer world, ISaveHandler parent)
|
||||
{
|
||||
this.world = world;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override public WorldInfo loadWorldInfo() { return parent.loadWorldInfo(); }
|
||||
@Override public void checkSessionLock() throws MinecraftException { parent.checkSessionLock(); }
|
||||
@Override public IChunkLoader getChunkLoader(WorldProvider var1) { return parent.getChunkLoader(var1); }
|
||||
@Override public void saveWorldInfoWithPlayer(WorldInfo var1, NBTTagCompound var2) { parent.saveWorldInfoWithPlayer(var1, var2); }
|
||||
@Override public void saveWorldInfo(WorldInfo var1){ parent.saveWorldInfo(var1); }
|
||||
@Override public IPlayerFileData getPlayerNBTManager() { return parent.getPlayerNBTManager(); }
|
||||
@Override public void flush() { parent.flush(); }
|
||||
@Override public File getWorldDirectory() { return parent.getWorldDirectory(); }
|
||||
|
||||
/**
|
||||
* Gets the file location of the given map
|
||||
*/
|
||||
@Override
|
||||
public File getMapFileFromName(String name)
|
||||
{
|
||||
if (dataDir == null) //Delayed down here do that world has time to be initialized first.
|
||||
{
|
||||
dataDir = new File(world.getChunkSaveLocation(), "data");
|
||||
dataDir.mkdirs();
|
||||
}
|
||||
File file = new File(dataDir, name + ".dat");
|
||||
if (!file.exists())
|
||||
{
|
||||
switch (world.provider.getDimension())
|
||||
{
|
||||
case -1:
|
||||
if (name.equalsIgnoreCase("FORTRESS")) copyFile(name, file);
|
||||
break;
|
||||
case 1:
|
||||
if (name.equalsIgnoreCase("VILLAGES_END")) copyFile(name, file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private void copyFile(String name, File to)
|
||||
{
|
||||
File parentFile = parent.getMapFileFromName(name);
|
||||
if (parentFile.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.copy(parentFile, to);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
FMLLog.log.error("A critical error occurred copying {} to world specific dat folder - new file will be created.", parentFile.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemplateManager getStructureTemplateManager()
|
||||
{
|
||||
return parent.getStructureTemplateManager();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WorldWorkerManager
|
||||
{
|
||||
private static List<IWorker> workers = new ArrayList<IWorker>();
|
||||
private static long startTime = -1;
|
||||
private static int index = 0;
|
||||
|
||||
public static void tick(boolean start)
|
||||
{
|
||||
if (start)
|
||||
{
|
||||
startTime = System.currentTimeMillis();
|
||||
return;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
IWorker task = getNext();
|
||||
if (task == null)
|
||||
return;
|
||||
|
||||
long time = 50 - (System.currentTimeMillis() - startTime);
|
||||
if (time < 10)
|
||||
time = 10; //If ticks are lagging, give us at least 10ms to do something.
|
||||
time += System.currentTimeMillis();
|
||||
|
||||
while (System.currentTimeMillis() < time && task != null)
|
||||
{
|
||||
boolean again = task.doWork();
|
||||
|
||||
if (!task.hasWork())
|
||||
{
|
||||
remove(task);
|
||||
task = getNext();
|
||||
}
|
||||
else if (!again)
|
||||
{
|
||||
task = getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void addWorker(IWorker worker)
|
||||
{
|
||||
workers.add(worker);
|
||||
}
|
||||
|
||||
private static synchronized IWorker getNext()
|
||||
{
|
||||
return workers.size() > index ? workers.get(index++) : null;
|
||||
}
|
||||
|
||||
private static synchronized void remove(IWorker worker)
|
||||
{
|
||||
workers.remove(worker);
|
||||
index--;
|
||||
}
|
||||
|
||||
//Internal only, used to clear everything when the server shuts down.
|
||||
public static synchronized void clear()
|
||||
{
|
||||
workers.clear();
|
||||
}
|
||||
|
||||
public static interface IWorker
|
||||
{
|
||||
boolean hasWork();
|
||||
|
||||
default void work() {}; //TODO: Remove in 1.13.
|
||||
|
||||
/**
|
||||
* Perform a task, returning true from this will have the manager call this function again this tick if there is time left.
|
||||
* Returning false will skip calling this worker until next tick.
|
||||
*/
|
||||
default boolean doWork()
|
||||
{
|
||||
work();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.common.animation;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
|
||||
/**
|
||||
* Event stored in the clip
|
||||
*/
|
||||
public final class Event implements Comparable<Event>
|
||||
{
|
||||
private final String event;
|
||||
private final float offset;
|
||||
|
||||
public Event(String event, float offset)
|
||||
{
|
||||
this.event = event;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the event.
|
||||
*/
|
||||
public String event()
|
||||
{
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return how long ago the event happened, relative to the next event / first query time
|
||||
*/
|
||||
public float offset()
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Event event)
|
||||
{
|
||||
return new Float(offset).compareTo(event.offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return MoreObjects.toStringHelper(getClass()).add("event", event).add("offset", offset).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.common.animation;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handler for animation events;
|
||||
*/
|
||||
public interface IEventHandler<T>
|
||||
{
|
||||
void handleEvents(T instance, float time, Iterable<Event> pastEvents);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.common.animation;
|
||||
|
||||
/**
|
||||
* Time-varying value associated with the animation.
|
||||
* Return value should be constant with the respect to the input and reasonable context (current render frame).
|
||||
* Simplest example is the input time itself.
|
||||
* Unity calls them Parameters, Unreal calls them Variables.
|
||||
*/
|
||||
public interface ITimeValue
|
||||
{
|
||||
float apply(float input);
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* 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.common.animation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.minecraft.util.IStringSerializable;
|
||||
|
||||
import java.util.function.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Various implementations of ITimeValue.
|
||||
*/
|
||||
public final class TimeValues
|
||||
{
|
||||
public static enum IdentityValue implements ITimeValue, IStringSerializable
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public float apply(float input)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "identity";
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ConstValue implements ITimeValue
|
||||
{
|
||||
private final float output;
|
||||
|
||||
public ConstValue(float output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float apply(float input)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ConstValue other = (ConstValue) obj;
|
||||
return output == other.output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple value holder.
|
||||
*/
|
||||
public static final class VariableValue implements ITimeValue
|
||||
{
|
||||
private float output;
|
||||
|
||||
public VariableValue(float initialValue)
|
||||
{
|
||||
this.output = initialValue;
|
||||
}
|
||||
|
||||
public void setValue(float newValue)
|
||||
{
|
||||
this.output = newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float apply(float input)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
VariableValue other = (VariableValue) obj;
|
||||
return output == other.output;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SimpleExprValue implements ITimeValue
|
||||
{
|
||||
private static final Pattern opsPattern = Pattern.compile("[+\\-*/mMrRfF]+");
|
||||
|
||||
private final String operators;
|
||||
private final ImmutableList<ITimeValue> args;
|
||||
|
||||
public SimpleExprValue(String operators, ImmutableList<ITimeValue> args)
|
||||
{
|
||||
this.operators = operators;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float apply(float input)
|
||||
{
|
||||
float ret = input;
|
||||
for(int i = 0; i < operators.length(); i++)
|
||||
{
|
||||
float arg = args.get(i).apply(input);
|
||||
switch(operators.charAt(i))
|
||||
{
|
||||
case '+': ret += arg; break;
|
||||
case '-': ret -= arg; break;
|
||||
case '*': ret *= arg; break;
|
||||
case '/': ret /= arg; break;
|
||||
case 'm': ret = Math.min(ret, arg); break;
|
||||
case 'M': ret = Math.max(ret, arg); break;
|
||||
case 'r': ret = (float)Math.floor(ret / arg) * arg; break;
|
||||
case 'R': ret = (float)Math.ceil(ret / arg) * arg; break;
|
||||
case 'f': ret -= Math.floor(ret / arg) * arg; break;
|
||||
case 'F': ret = (float)Math.ceil(ret / arg) * arg - ret; break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(operators, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
SimpleExprValue other = (SimpleExprValue) obj;
|
||||
return Objects.equal(operators, other.operators) && Objects.equal(args, other.args);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CompositionValue implements ITimeValue
|
||||
{
|
||||
private final ITimeValue g;
|
||||
private final ITimeValue f;
|
||||
|
||||
public CompositionValue(ITimeValue g, ITimeValue f)
|
||||
{
|
||||
super();
|
||||
this.g = g;
|
||||
this.f = f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float apply(float input)
|
||||
{
|
||||
return g.apply(f.apply(input));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(g, f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
CompositionValue other = (CompositionValue) obj;
|
||||
return Objects.equal(g, other.g) && Objects.equal(f, other.f);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ParameterValue implements ITimeValue, IStringSerializable
|
||||
{
|
||||
private final String parameterName;
|
||||
private final Function<String, ITimeValue> valueResolver;
|
||||
private ITimeValue parameter;
|
||||
|
||||
public ParameterValue(String parameterName, Function<String, ITimeValue> valueResolver)
|
||||
{
|
||||
this.parameterName = parameterName;
|
||||
this.valueResolver = valueResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return parameterName;
|
||||
}
|
||||
|
||||
private void resolve()
|
||||
{
|
||||
if(parameter == null)
|
||||
{
|
||||
if(valueResolver != null)
|
||||
{
|
||||
parameter = valueResolver.apply(parameterName);
|
||||
}
|
||||
if(parameter == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Couldn't resolve parameter value " + parameterName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float apply(float input)
|
||||
{
|
||||
resolve();
|
||||
return parameter.apply(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
resolve();
|
||||
return parameter.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ParameterValue other = (ParameterValue) obj;
|
||||
resolve();
|
||||
other.resolve();
|
||||
return Objects.equal(parameter, other.parameter);
|
||||
}
|
||||
}
|
||||
|
||||
public static enum CommonTimeValueTypeAdapterFactory implements TypeAdapterFactory
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
private final ThreadLocal<Function<String, ITimeValue>> valueResolver = new ThreadLocal<Function<String, ITimeValue>>();
|
||||
|
||||
public void setValueResolver(@Nullable Function<String, ITimeValue> valueResolver)
|
||||
{
|
||||
this.valueResolver.set(valueResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type)
|
||||
{
|
||||
if(type.getRawType() != ITimeValue.class)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (TypeAdapter<T>)new TypeAdapter<ITimeValue>()
|
||||
{
|
||||
@Override
|
||||
public void write(JsonWriter out, ITimeValue parameter) throws IOException
|
||||
{
|
||||
if(parameter instanceof ConstValue)
|
||||
{
|
||||
out.value(((ConstValue)parameter).output);
|
||||
}
|
||||
else if(parameter instanceof SimpleExprValue)
|
||||
{
|
||||
SimpleExprValue p = (SimpleExprValue)parameter;
|
||||
out.beginArray();
|
||||
out.value(p.operators);
|
||||
for(ITimeValue v : p.args)
|
||||
{
|
||||
write(out, v);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
else if(parameter instanceof CompositionValue)
|
||||
{
|
||||
CompositionValue p = (CompositionValue)parameter;
|
||||
out.beginArray();
|
||||
out.value("compose");
|
||||
write(out, p.g);
|
||||
write(out, p.f);
|
||||
out.endArray();
|
||||
}
|
||||
else if(parameter instanceof IStringSerializable)
|
||||
{
|
||||
out.value("#" + ((IStringSerializable)parameter).getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITimeValue read(JsonReader in) throws IOException
|
||||
{
|
||||
switch(in.peek())
|
||||
{
|
||||
case NUMBER:
|
||||
return new ConstValue((float)in.nextDouble());
|
||||
case BEGIN_ARRAY:
|
||||
in.beginArray();
|
||||
String type = in.nextString();
|
||||
ITimeValue p;
|
||||
if(SimpleExprValue.opsPattern.matcher(type).matches())
|
||||
{
|
||||
ImmutableList.Builder<ITimeValue> builder = ImmutableList.builder();
|
||||
while(in.hasNext())
|
||||
{
|
||||
builder.add(read(in));
|
||||
}
|
||||
p = new SimpleExprValue(type, builder.build());
|
||||
}
|
||||
else if("compose".equals(type))
|
||||
{
|
||||
p = new CompositionValue(read(in), read(in));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException("Unknown TimeValue type \"" + type + "\"");
|
||||
}
|
||||
in.endArray();
|
||||
return p;
|
||||
case STRING:
|
||||
String string = in.nextString();
|
||||
if(string.equals("#identity"))
|
||||
{
|
||||
return IdentityValue.INSTANCE;
|
||||
}
|
||||
if(!string.startsWith("#"))
|
||||
{
|
||||
throw new IOException("Expected TimeValue reference, got \"" + string + "\"");
|
||||
}
|
||||
// User Parameter TimeValue
|
||||
return new ParameterValue(string.substring(1), valueResolver.get());
|
||||
default:
|
||||
throw new IOException("Expected TimeValue, got " + in.peek());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.common.brewing;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class AbstractBrewingRecipe<T> implements IBrewingRecipe
|
||||
{
|
||||
@Nonnull
|
||||
private final ItemStack input;
|
||||
private final T ingredient;
|
||||
private final ItemStack output;
|
||||
|
||||
protected AbstractBrewingRecipe(@Nonnull ItemStack input, @Nonnull T ingredient, @Nonnull ItemStack output)
|
||||
{
|
||||
this.input = input;
|
||||
this.ingredient = ingredient;
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInput(@Nonnull ItemStack stack)
|
||||
{
|
||||
return OreDictionary.itemMatches(this.getInput(), stack, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public ItemStack getOutput(@Nonnull ItemStack input, @Nonnull ItemStack ingredient)
|
||||
{
|
||||
return isInput(input) && isIngredient(ingredient) ? getOutput().copy() : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ItemStack getInput()
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public T getIngredient()
|
||||
{
|
||||
return ingredient;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ItemStack getOutput()
|
||||
{
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.common.brewing;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BrewingOreRecipe extends AbstractBrewingRecipe<List<ItemStack>> {
|
||||
|
||||
public BrewingOreRecipe(@Nonnull ItemStack input, @Nonnull String ingredient, @Nonnull ItemStack output)
|
||||
{
|
||||
super(input, OreDictionary.getOres(ingredient), output);
|
||||
}
|
||||
|
||||
public BrewingOreRecipe(@Nonnull ItemStack input, @Nonnull List<ItemStack> ingredient, @Nonnull ItemStack output)
|
||||
{
|
||||
super(input, ingredient, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIngredient(@Nonnull ItemStack stack)
|
||||
{
|
||||
for (ItemStack target : this.getIngredient())
|
||||
{
|
||||
if (OreDictionary.itemMatches(target, stack, false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.common.brewing;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BrewingRecipe extends AbstractBrewingRecipe<ItemStack> {
|
||||
|
||||
public BrewingRecipe(@Nonnull ItemStack input, @Nonnull ItemStack ingredient, @Nonnull ItemStack output)
|
||||
{
|
||||
super(input, ingredient, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIngredient(@Nonnull ItemStack stack)
|
||||
{
|
||||
return OreDictionary.itemMatches(this.getIngredient(), stack, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.common.brewing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BrewingRecipeRegistry {
|
||||
|
||||
private static List<IBrewingRecipe> recipes = new ArrayList<IBrewingRecipe>();
|
||||
|
||||
static
|
||||
{
|
||||
addRecipe(new VanillaBrewingRecipe());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipe to the registry. Due to the nature of the brewing stand
|
||||
* inputs that stack (a.k.a max stack size > 1) are not allowed.
|
||||
*
|
||||
* @param input
|
||||
* The ItemStack that goes in same slots as the water bottles
|
||||
* would.
|
||||
* @param ingredient
|
||||
* The ItemStack that goes in the same slot as nether wart would.
|
||||
* @param output
|
||||
* The ItemStack that will replace the input once the brewing is
|
||||
* done.
|
||||
* @return true if the recipe was added.
|
||||
*/
|
||||
public static boolean addRecipe(@Nonnull ItemStack input, @Nonnull ItemStack ingredient, @Nonnull ItemStack output)
|
||||
{
|
||||
return addRecipe(new BrewingRecipe(input, ingredient, output));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipe to the registry. Due to the nature of the brewing stand
|
||||
* inputs that stack (a.k.a max stack size > 1) are not allowed.
|
||||
*
|
||||
* @param input
|
||||
* The ItemStack that goes in same slots as the water bottles
|
||||
* would.
|
||||
* @param ingredient
|
||||
* The ItemStack that goes in the same slot as nether wart would.
|
||||
* @param output
|
||||
* The ItemStack that will replace the input once the brewing is
|
||||
* done.
|
||||
* @return true if the recipe was added.
|
||||
*/
|
||||
public static boolean addRecipe(@Nonnull ItemStack input, @Nonnull String ingredient, @Nonnull ItemStack output)
|
||||
{
|
||||
return addRecipe(new BrewingOreRecipe(input, ingredient, output));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipe to the registry. Due to the nature of the brewing stand
|
||||
* inputs that stack (a.k.a max stack size > 1) are not allowed.
|
||||
*/
|
||||
public static boolean addRecipe(IBrewingRecipe recipe)
|
||||
{
|
||||
return recipes.add(recipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output ItemStack obtained by brewing the passed input and
|
||||
* ingredient.
|
||||
*/
|
||||
@Nonnull
|
||||
public static ItemStack getOutput(@Nonnull ItemStack input, @Nonnull ItemStack ingredient)
|
||||
{
|
||||
if (input.isEmpty() || input.getCount() != 1) return ItemStack.EMPTY;
|
||||
if (ingredient.isEmpty()) return ItemStack.EMPTY;
|
||||
|
||||
for (IBrewingRecipe recipe : recipes)
|
||||
{
|
||||
ItemStack output = recipe.getOutput(input, ingredient);
|
||||
if (!output.isEmpty())
|
||||
{
|
||||
return output;
|
||||
}
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the passed input and ingredient have an output
|
||||
*/
|
||||
public static boolean hasOutput(@Nonnull ItemStack input, @Nonnull ItemStack ingredient)
|
||||
{
|
||||
return !getOutput(input, ingredient).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the brewing stand to determine if its contents can be brewed.
|
||||
* Extra parameters exist to allow modders to create bigger brewing stands
|
||||
* without much hassle
|
||||
*/
|
||||
public static boolean canBrew(NonNullList<ItemStack> inputs, @Nonnull ItemStack ingredient, int[] inputIndexes)
|
||||
{
|
||||
if (ingredient.isEmpty()) return false;
|
||||
|
||||
for (int i : inputIndexes)
|
||||
{
|
||||
if (hasOutput(inputs.get(i), ingredient))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the brewing stand to brew its inventory Extra parameters exist to
|
||||
* allow modders to create bigger brewing stands without much hassle
|
||||
*/
|
||||
public static void brewPotions(NonNullList<ItemStack> inputs, @Nonnull ItemStack ingredient, int[] inputIndexes)
|
||||
{
|
||||
for (int i : inputIndexes)
|
||||
{
|
||||
ItemStack output = getOutput(inputs.get(i), ingredient);
|
||||
if (!output.isEmpty())
|
||||
{
|
||||
inputs.set(i, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the passed ItemStack is a valid ingredient for any of the
|
||||
* recipes in the registry.
|
||||
*/
|
||||
public static boolean isValidIngredient(@Nonnull ItemStack stack)
|
||||
{
|
||||
if (stack.isEmpty()) return false;
|
||||
|
||||
for (IBrewingRecipe recipe : recipes)
|
||||
{
|
||||
if (recipe.isIngredient(stack))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the passed ItemStack is a valid input for any of the
|
||||
* recipes in the registry.
|
||||
*/
|
||||
public static boolean isValidInput(@Nonnull ItemStack stack)
|
||||
{
|
||||
if (stack.getCount() != 1) return false;
|
||||
|
||||
for (IBrewingRecipe recipe : recipes)
|
||||
{
|
||||
if (recipe.isInput(stack))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable list containing all the recipes in the registry
|
||||
*/
|
||||
public static List<IBrewingRecipe> getRecipes()
|
||||
{
|
||||
return Collections.unmodifiableList(recipes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.common.brewing;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public interface IBrewingRecipe {
|
||||
|
||||
/**
|
||||
* Returns true is the passed ItemStack is an input for this recipe. "Input"
|
||||
* being the item that goes in one of the three bottom slots of the brewing
|
||||
* stand (e.g: water bottle)
|
||||
*/
|
||||
boolean isInput(@Nonnull ItemStack input);
|
||||
|
||||
/**
|
||||
* Returns true if the passed ItemStack is an ingredient for this recipe.
|
||||
* "Ingredient" being the item that goes in the top slot of the brewing
|
||||
* stand (e.g: nether wart)
|
||||
*/
|
||||
boolean isIngredient(@Nonnull ItemStack ingredient);
|
||||
|
||||
/**
|
||||
* Returns the output when the passed input is brewed with the passed
|
||||
* ingredient. Empty if invalid input or ingredient.
|
||||
*/
|
||||
@Nonnull
|
||||
ItemStack getOutput(@Nonnull ItemStack input, @Nonnull ItemStack ingredient);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.common.brewing;
|
||||
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.potion.PotionHelper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Used in BrewingRecipeRegistry to maintain the vanilla behaviour.
|
||||
*
|
||||
* Most of the code was simply adapted from net.minecraft.tileentity.TileEntityBrewingStand
|
||||
*/
|
||||
public class VanillaBrewingRecipe implements IBrewingRecipe {
|
||||
|
||||
/**
|
||||
* Code adapted from TileEntityBrewingStand.isItemValidForSlot(int index, ItemStack stack)
|
||||
*/
|
||||
@Override
|
||||
public boolean isInput(@Nonnull ItemStack stack)
|
||||
{
|
||||
Item item = stack.getItem();
|
||||
return item == Items.POTIONITEM || item == Items.SPLASH_POTION || item == Items.LINGERING_POTION || item == Items.GLASS_BOTTLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Code adapted from TileEntityBrewingStand.isItemValidForSlot(int index, ItemStack stack)
|
||||
*/
|
||||
@Override
|
||||
public boolean isIngredient(@Nonnull ItemStack stack)
|
||||
{
|
||||
return PotionHelper.isReagent(stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Code copied from TileEntityBrewingStand.brewPotions()
|
||||
* It brews the potion by doing the bit-shifting magic and then checking if the new PotionEffect list is different to the old one,
|
||||
* or if the new potion is a splash potion when the old one wasn't.
|
||||
*/
|
||||
@Override
|
||||
@Nonnull
|
||||
public ItemStack getOutput(@Nonnull ItemStack input, @Nonnull ItemStack ingredient)
|
||||
{
|
||||
if (!input.isEmpty() && !ingredient.isEmpty() && isIngredient(ingredient))
|
||||
{
|
||||
ItemStack result = PotionHelper.doReaction(ingredient, input);
|
||||
if (result != input)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.common.capabilities;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This is the core holder object Capabilities.
|
||||
* Each capability will have ONE instance of this class,
|
||||
* and it will the the one passed into the ICapabilityProvider functions.
|
||||
*
|
||||
* The CapabilityManager is in charge of creating this class.
|
||||
*/
|
||||
public class Capability<T>
|
||||
{
|
||||
public static interface IStorage<T>
|
||||
{
|
||||
/**
|
||||
* Serialize the capability instance to a NBTTag.
|
||||
* This allows for a central implementation of saving the data.
|
||||
*
|
||||
* It is important to note that it is up to the API defining
|
||||
* the capability what requirements the 'instance' value must have.
|
||||
*
|
||||
* Due to the possibility of manipulating internal data, some
|
||||
* implementations MAY require that the 'instance' be an instance
|
||||
* of the 'default' implementation.
|
||||
*
|
||||
* Review the API docs for more info.
|
||||
*
|
||||
* @param capability The Capability being stored.
|
||||
* @param instance An instance of that capabilities interface.
|
||||
* @param side The side of the object the instance is associated with.
|
||||
* @return a NBT holding the data. Null if no data needs to be stored.
|
||||
*/
|
||||
@Nullable
|
||||
NBTBase writeNBT(Capability<T> capability, T instance, EnumFacing side);
|
||||
|
||||
/**
|
||||
* Read the capability instance from a NBT tag.
|
||||
*
|
||||
* This allows for a central implementation of saving the data.
|
||||
*
|
||||
* It is important to note that it is up to the API defining
|
||||
* the capability what requirements the 'instance' value must have.
|
||||
*
|
||||
* Due to the possibility of manipulating internal data, some
|
||||
* implementations MAY require that the 'instance' be an instance
|
||||
* of the 'default' implementation.
|
||||
*
|
||||
* Review the API docs for more info. *
|
||||
*
|
||||
* @param capability The Capability being stored.
|
||||
* @param instance An instance of that capabilities interface.
|
||||
* @param side The side of the object the instance is associated with.
|
||||
* @param nbt A NBT holding the data. Must not be null, as doesn't make sense to call this function with nothing to read...
|
||||
*/
|
||||
void readNBT(Capability<T> capability, T instance, EnumFacing side, NBTBase nbt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The unique name of this capability, typically this is
|
||||
* the fully qualified class name for the target interface.
|
||||
*/
|
||||
public String getName() { return name; }
|
||||
|
||||
/**
|
||||
* @return An instance of the default storage handler. You can safely use this store your default implementation in NBT.
|
||||
*/
|
||||
public IStorage<T> getStorage() { return storage; }
|
||||
|
||||
/**
|
||||
* Quick access to the IStorage's readNBT.
|
||||
* See {@link IStorage#readNBT(Capability, Object, EnumFacing, NBTBase)} for documentation.
|
||||
*/
|
||||
public void readNBT(T instance, EnumFacing side, NBTBase nbt)
|
||||
{
|
||||
storage.readNBT(this, instance, side, nbt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick access to the IStorage's writeNBT.
|
||||
* See {@link IStorage#writeNBT(Capability, Object, EnumFacing)} for documentation.
|
||||
*/
|
||||
@Nullable
|
||||
public NBTBase writeNBT(T instance, EnumFacing side)
|
||||
{
|
||||
return storage.writeNBT(this, instance, side);
|
||||
}
|
||||
|
||||
/**
|
||||
* A NEW instance of the default implementation.
|
||||
*
|
||||
* If it important to note that if you want to use the default storage
|
||||
* you may be required to use this exact implementation.
|
||||
* Refer to the owning API of the Capability in question.
|
||||
*
|
||||
* @return A NEW instance of the default implementation.
|
||||
*/
|
||||
@Nullable
|
||||
public T getDefaultInstance()
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.factory.call();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Throwables.throwIfUnchecked(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this inside ICapabilityProvider.getCapability to avoid unchecked cast warnings.
|
||||
* Example: return SOME_CAPABILITY.cast(instance);
|
||||
* Use with caution;
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <R> R cast(T instance)
|
||||
{
|
||||
return (R)instance;
|
||||
}
|
||||
|
||||
// INTERNAL
|
||||
private final String name;
|
||||
private final IStorage<T> storage;
|
||||
private final Callable<? extends T> factory;
|
||||
|
||||
Capability(String name, IStorage<T> storage, Callable<? extends T> factory)
|
||||
{
|
||||
this.name = name;
|
||||
this.storage = storage;
|
||||
this.factory = factory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.common.capabilities;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
|
||||
/**
|
||||
* A high-speed implementation of a capability delegator.
|
||||
* This is used to wrap the results of the AttachCapabilitiesEvent.
|
||||
* It is HIGHLY recommended that you DO NOT use this approach unless
|
||||
* you MUST delegate to multiple providers instead just implement y
|
||||
* our handlers using normal if statements.
|
||||
*
|
||||
* Internally the handlers are baked into arrays for fast iteration.
|
||||
* The ResourceLocations will be used for the NBT Key when serializing.
|
||||
*/
|
||||
public final class CapabilityDispatcher implements INBTSerializable<NBTTagCompound>, ICapabilityProvider
|
||||
{
|
||||
private ICapabilityProvider[] caps;
|
||||
private INBTSerializable<NBTBase>[] writers;
|
||||
private String[] names;
|
||||
|
||||
public CapabilityDispatcher(Map<ResourceLocation, ICapabilityProvider> list)
|
||||
{
|
||||
this(list, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public CapabilityDispatcher(Map<ResourceLocation, ICapabilityProvider> list, @Nullable ICapabilityProvider parent)
|
||||
{
|
||||
List<ICapabilityProvider> lstCaps = Lists.newArrayList();
|
||||
List<INBTSerializable<NBTBase>> lstWriters = Lists.newArrayList();
|
||||
List<String> lstNames = Lists.newArrayList();
|
||||
|
||||
if (parent != null) // Parents go first!
|
||||
{
|
||||
lstCaps.add(parent);
|
||||
if (parent instanceof INBTSerializable)
|
||||
{
|
||||
lstWriters.add((INBTSerializable<NBTBase>)parent);
|
||||
lstNames.add("Parent");
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<ResourceLocation, ICapabilityProvider> entry : list.entrySet())
|
||||
{
|
||||
ICapabilityProvider prov = entry.getValue();
|
||||
lstCaps.add(prov);
|
||||
if (prov instanceof INBTSerializable)
|
||||
{
|
||||
lstWriters.add((INBTSerializable<NBTBase>)prov);
|
||||
lstNames.add(entry.getKey().toString());
|
||||
}
|
||||
}
|
||||
|
||||
caps = lstCaps.toArray(new ICapabilityProvider[lstCaps.size()]);
|
||||
writers = lstWriters.toArray(new INBTSerializable[lstWriters.size()]);
|
||||
names = lstNames.toArray(new String[lstNames.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
for (ICapabilityProvider cap : caps)
|
||||
{
|
||||
if (cap.hasCapability(capability, facing))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
for (ICapabilityProvider cap : caps)
|
||||
{
|
||||
T ret = cap.getCapability(capability, facing);
|
||||
if (ret != null)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeNBT()
|
||||
{
|
||||
NBTTagCompound nbt = new NBTTagCompound();
|
||||
for (int x = 0; x < writers.length; x++)
|
||||
{
|
||||
nbt.setTag(names[x], writers[x].serializeNBT());
|
||||
}
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeNBT(NBTTagCompound nbt)
|
||||
{
|
||||
for (int x = 0; x < writers.length; x++)
|
||||
{
|
||||
if (nbt.hasKey(names[x]))
|
||||
{
|
||||
writers[x].deserializeNBT(nbt.getTag(names[x]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean areCompatible(CapabilityDispatcher other) //Called from ItemStack to compare equality.
|
||||
{ // Only compares serializeable caps.
|
||||
if (other == null) return this.writers.length == 0; // Done this way so we can do some pre-checks before doing the costly NBT serialization and compare
|
||||
if (this.writers.length == 0) return other.writers.length == 0;
|
||||
return this.serializeNBT().equals(other.serializeNBT());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.common.capabilities;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* When placed on a FIELD, the field will be set to an
|
||||
* instance of Capability once that capability is registered.
|
||||
* That field must be static and be able to hold a instance
|
||||
* of 'Capability'
|
||||
*
|
||||
* Example:
|
||||
* @CapabilityInject(IExampleCapability.class)
|
||||
* private static final Capability<IExampleCapability> TEST_CAP = null;
|
||||
*
|
||||
* When placed on a METHOD, the method will be invoked once the
|
||||
* capability is registered. This allows you to have a 'enable features'
|
||||
* callback. It MUST have one parameter of type 'Capability;
|
||||
*
|
||||
* Example:
|
||||
* @CapabilityInject(IExampleCapability.class)
|
||||
* private static void capRegistered(Capability<IExampleCapability> cap) {}
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface CapabilityInject
|
||||
{
|
||||
/**
|
||||
* The capability interface to listen for registration.
|
||||
* Note:
|
||||
* When reading annotations, DO NOT call this function as it will cause a hard dependency on the class.
|
||||
*/
|
||||
Class<?> value();
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.common.capabilities;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.util.function.Function;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.minecraftforge.common.util.EnumHelper;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.discovery.ASMDataTable;
|
||||
|
||||
public enum CapabilityManager
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
/**
|
||||
* Registers a capability to be consumed by others.
|
||||
* APIs who define the capability should call this.
|
||||
* To retrieve the Capability instance, use the @CapabilityInject annotation.
|
||||
*
|
||||
* @param type The Interface to be registered
|
||||
* @param storage A default implementation of the storage handler.
|
||||
* @param implementation A default implementation of the interface.
|
||||
* @deprecated Use the overload that takes a factory instead of a class.
|
||||
* You can easily do this by passing a constructor reference
|
||||
* (MyImpl::new instead of MyImpl.class). TODO remove in 1.13.
|
||||
*/
|
||||
@Deprecated
|
||||
public <T> void register(Class<T> type, Capability.IStorage<T> storage, final Class<? extends T> implementation)
|
||||
{
|
||||
Preconditions.checkArgument(implementation != null, "Attempted to register a capability with no default implementation");
|
||||
register(type, storage, () ->
|
||||
{
|
||||
try {
|
||||
return implementation.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a capability to be consumed by others.
|
||||
* APIs who define the capability should call this.
|
||||
* To retrieve the Capability instance, use the @CapabilityInject annotation.
|
||||
*
|
||||
* @param type The Interface to be registered
|
||||
* @param storage A default implementation of the storage handler.
|
||||
* @param factory A Factory that will produce new instances of the default implementation.
|
||||
*/
|
||||
public <T> void register(Class<T> type, Capability.IStorage<T> storage, Callable<? extends T> factory)
|
||||
{
|
||||
Preconditions.checkArgument(type != null, "Attempted to register a capability with invalid type");
|
||||
Preconditions.checkArgument(storage != null, "Attempted to register a capability with no storage implementation");
|
||||
Preconditions.checkArgument(factory != null, "Attempted to register a capability with no default implementation factory");
|
||||
String realName = type.getName().intern();
|
||||
Preconditions.checkState(!providers.containsKey(realName), "Can not register a capability implementation multiple times: %s", realName);
|
||||
|
||||
Capability<T> cap = new Capability<T>(realName, storage, factory);
|
||||
providers.put(realName, cap);
|
||||
|
||||
List<Function<Capability<?>, Object>> list = callbacks.get(realName);
|
||||
if (list != null)
|
||||
{
|
||||
for (Function<Capability<?>, Object> func : list)
|
||||
{
|
||||
func.apply(cap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL
|
||||
private IdentityHashMap<String, Capability<?>> providers = Maps.newIdentityHashMap();
|
||||
private IdentityHashMap<String, List<Function<Capability<?>, Object>>> callbacks = Maps.newIdentityHashMap();
|
||||
public void injectCapabilities(ASMDataTable data)
|
||||
{
|
||||
for (ASMDataTable.ASMData entry : data.getAll(CapabilityInject.class.getName()))
|
||||
{
|
||||
final String targetClass = entry.getClassName();
|
||||
final String targetName = entry.getObjectName();
|
||||
Type type = (Type)entry.getAnnotationInfo().get("value");
|
||||
if (type == null)
|
||||
{
|
||||
FMLLog.log.warn("Unable to inject capability at {}.{} (Invalid Annotation)", targetClass, targetName);
|
||||
continue;
|
||||
}
|
||||
final String capabilityName = type.getInternalName().replace('/', '.').intern();
|
||||
|
||||
List<Function<Capability<?>, Object>> list = callbacks.computeIfAbsent(capabilityName, k -> Lists.newArrayList());
|
||||
|
||||
if (entry.getObjectName().indexOf('(') > 0)
|
||||
{
|
||||
list.add(new Function<Capability<?>, Object>()
|
||||
{
|
||||
@Override
|
||||
public Object apply(Capability<?> input)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (Method mtd : Class.forName(targetClass).getDeclaredMethods())
|
||||
{
|
||||
if (targetName.equals(mtd.getName() + Type.getMethodDescriptor(mtd)))
|
||||
{
|
||||
if ((mtd.getModifiers() & Modifier.STATIC) != Modifier.STATIC)
|
||||
{
|
||||
FMLLog.log.warn("Unable to inject capability {} at {}.{} (Non-Static)", capabilityName, targetClass, targetName);
|
||||
return null;
|
||||
}
|
||||
|
||||
mtd.setAccessible(true);
|
||||
mtd.invoke(null, input);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
FMLLog.log.warn("Unable to inject capability {} at {}.{} (Method Not Found)", capabilityName, targetClass, targetName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log.warn("Unable to inject capability {} at {}.{}", capabilityName, targetClass, targetName, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
list.add(new Function<Capability<?>, Object>()
|
||||
{
|
||||
@Override
|
||||
public Object apply(Capability<?> input)
|
||||
{
|
||||
try
|
||||
{
|
||||
Field field = Class.forName(targetClass).getDeclaredField(targetName);
|
||||
if ((field.getModifiers() & Modifier.STATIC) != Modifier.STATIC)
|
||||
{
|
||||
FMLLog.log.warn("Unable to inject capability {} at {}.{} (Non-Static)", capabilityName, targetClass, targetName);
|
||||
return null;
|
||||
}
|
||||
EnumHelper.setFailsafeFieldValue(field, null, input);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log.warn("Unable to inject capability {} at {}.{}", capabilityName, targetClass, targetName, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.common.capabilities;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
public interface ICapabilityProvider
|
||||
{
|
||||
/**
|
||||
* Determines if this object has support for the capability in question on the specific side.
|
||||
* The return value of this MIGHT change during runtime if this object gains or loses support
|
||||
* for a capability. It is not required to call this function before calling
|
||||
* {@link #getCapability(Capability, EnumFacing)}.
|
||||
* <p>
|
||||
* Basically, this method functions analogously to {@link Map#containsKey(Object)}.
|
||||
* <p>
|
||||
* <em>Example:</em>
|
||||
* A Pipe getting a cover placed on one side causing it lose the Inventory attachment function for that side.
|
||||
* </p><p>
|
||||
* This is a light weight version of getCapability, intended for metadata uses.
|
||||
* </p>
|
||||
* @param capability The capability to check
|
||||
* @param facing The Side to check from:
|
||||
* CAN BE NULL. Null is defined to represent 'internal' or 'self'
|
||||
* @return True if this object supports the capability. If true, then {@link #getCapability(Capability, EnumFacing)}
|
||||
* must not return null.
|
||||
*/
|
||||
boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing);
|
||||
|
||||
/**
|
||||
* Retrieves the handler for the capability requested on the specific side.
|
||||
* <ul>
|
||||
* <li>The return value <strong>CAN</strong> be null if the object does not support the capability.</il>
|
||||
* <li>The return value <strong>CAN</strong> be the same for multiple faces.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Basically, this method functions analogously to {@link Map#get(Object)}.
|
||||
*
|
||||
* @param capability The capability to check
|
||||
* @param facing The Side to check from,
|
||||
* <strong>CAN BE NULL</strong>. Null is defined to represent 'internal' or 'self'
|
||||
* @return The requested capability. Must <strong>NOT</strong> be null when {@link #hasCapability(Capability, EnumFacing)}
|
||||
* would return true.
|
||||
*/
|
||||
@Nullable
|
||||
<T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.common.capabilities;
|
||||
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
|
||||
//Just a mix of the two, useful in patches to lower the size.
|
||||
public interface ICapabilitySerializable<T extends NBTBase> extends ICapabilityProvider, INBTSerializable<T>{}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.common.chunkio;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.storage.AnvilChunkLoader;
|
||||
import net.minecraft.world.gen.ChunkProviderServer;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
|
||||
public class ChunkIOExecutor
|
||||
{
|
||||
private static final int BASE_THREADS = 1;
|
||||
private static final int PLAYERS_PER_THREAD = 50;
|
||||
|
||||
private static final Map<QueuedChunk, ChunkIOProvider> tasks = Maps.newConcurrentMap();
|
||||
private static final ThreadPoolExecutor pool = new ChunkIOThreadPoolExecutor(BASE_THREADS, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(),
|
||||
new ThreadFactory()
|
||||
{
|
||||
private AtomicInteger count = new AtomicInteger(1);
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
Thread thread = new Thread(r, "Chunk I/O Executor Thread-" + count.getAndIncrement());
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//Load the chunk completely in this thread. Dequeue as needed...
|
||||
public static Chunk syncChunkLoad(World world, AnvilChunkLoader loader, ChunkProviderServer provider, int x, int z)
|
||||
{
|
||||
QueuedChunk key = new QueuedChunk(x, z, world);
|
||||
ChunkIOProvider task = tasks.remove(key); // Remove task because we will call the sync callbacks directly
|
||||
if (task != null)
|
||||
{
|
||||
if (!pool.remove(task)) // If it wasn't in the pool, and run hasn't finished, then wait for the async thread.
|
||||
{
|
||||
synchronized(task)
|
||||
{
|
||||
while (!task.runFinished())
|
||||
{
|
||||
try
|
||||
{
|
||||
task.wait();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace(); // Something happened? Log it?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the task was not run yet we still need to load the chunk
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
task = new ChunkIOProvider(key, loader, provider);
|
||||
task.run();
|
||||
}
|
||||
task.syncCallback();
|
||||
return task.getChunk();
|
||||
}
|
||||
|
||||
//Queue the chunk to be loaded, and call the runnable when finished
|
||||
public static void queueChunkLoad(World world, AnvilChunkLoader loader, ChunkProviderServer provider, int x, int z, Runnable runnable)
|
||||
{
|
||||
QueuedChunk key = new QueuedChunk(x, z, world);
|
||||
ChunkIOProvider task = tasks.get(key);
|
||||
if (task == null)
|
||||
{
|
||||
task = new ChunkIOProvider(key, loader, provider);
|
||||
task.addCallback(runnable); // Add before calling execute for thread safety
|
||||
tasks.put(key, task);
|
||||
pool.execute(task);
|
||||
}
|
||||
else
|
||||
{
|
||||
task.addCallback(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the chunk from the queue if it's in the list.
|
||||
public static void dropQueuedChunkLoad(World world, int x, int z, Runnable runnable)
|
||||
{
|
||||
QueuedChunk key = new QueuedChunk(x, z, world);
|
||||
ChunkIOProvider task = tasks.get(key);
|
||||
if (task == null)
|
||||
{
|
||||
FMLLog.log.warn("Attempted to dequeue chunk that wasn't queued? {} @ ({}, {})", world.provider.getDimension(), x, z);
|
||||
return;
|
||||
}
|
||||
|
||||
task.removeCallback(runnable);
|
||||
|
||||
if (!task.hasCallback())
|
||||
{
|
||||
tasks.remove(key);
|
||||
pool.remove(task);
|
||||
}
|
||||
}
|
||||
|
||||
public static void adjustPoolSize(int players)
|
||||
{
|
||||
pool.setCorePoolSize(Math.max(BASE_THREADS, players / PLAYERS_PER_THREAD));
|
||||
}
|
||||
|
||||
public static void tick()
|
||||
{
|
||||
Iterator<ChunkIOProvider> itr = tasks.values().iterator();
|
||||
while (itr.hasNext())
|
||||
{
|
||||
ChunkIOProvider task = itr.next();
|
||||
if (task.runFinished())
|
||||
{
|
||||
if (task.hasCallback())
|
||||
task.syncCallback();
|
||||
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.common.chunkio;
|
||||
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.storage.AnvilChunkLoader;
|
||||
import net.minecraft.world.gen.ChunkProviderServer;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.world.ChunkDataEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
class ChunkIOProvider implements Runnable
|
||||
{
|
||||
private final QueuedChunk chunkInfo;
|
||||
private final AnvilChunkLoader loader;
|
||||
private final ChunkProviderServer provider;
|
||||
|
||||
private Chunk chunk;
|
||||
private NBTTagCompound nbt;
|
||||
private final ConcurrentLinkedQueue<Runnable> callbacks = new ConcurrentLinkedQueue<Runnable>();
|
||||
private boolean ran = false;
|
||||
|
||||
ChunkIOProvider(QueuedChunk chunk, AnvilChunkLoader loader, ChunkProviderServer provider)
|
||||
{
|
||||
this.chunkInfo = chunk;
|
||||
this.loader = loader;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public void addCallback(Runnable callback)
|
||||
{
|
||||
this.callbacks.add(callback);
|
||||
}
|
||||
public void removeCallback(Runnable callback)
|
||||
{
|
||||
this.callbacks.remove(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() // async stuff
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object[] data = null;
|
||||
try
|
||||
{
|
||||
data = this.loader.loadChunk__Async(chunkInfo.world, chunkInfo.x, chunkInfo.z);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e); // Allow exception to bubble up to afterExecute
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
this.nbt = (NBTTagCompound)data[1];
|
||||
this.chunk = (Chunk)data[0];
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.ran = true;
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sync stuff
|
||||
public void syncCallback()
|
||||
{
|
||||
if (chunk == null)
|
||||
{
|
||||
this.runCallbacks();
|
||||
return;
|
||||
}
|
||||
|
||||
// Load Entities
|
||||
this.loader.loadEntities(this.chunkInfo.world, this.nbt.getCompoundTag("Level"), this.chunk);
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(this.chunk, this.nbt)); // Don't call ChunkDataEvent.Load async
|
||||
|
||||
this.chunk.setLastSaveTime(provider.world.getTotalWorldTime());
|
||||
this.provider.chunkGenerator.recreateStructures(this.chunk, this.chunkInfo.x, this.chunkInfo.z);
|
||||
|
||||
provider.id2ChunkMap.put(ChunkPos.asLong(this.chunkInfo.x, this.chunkInfo.z), this.chunk);
|
||||
this.chunk.onLoad();
|
||||
this.chunk.populate(provider, provider.chunkGenerator);
|
||||
|
||||
this.runCallbacks();
|
||||
}
|
||||
|
||||
public Chunk getChunk()
|
||||
{
|
||||
return this.chunk;
|
||||
}
|
||||
|
||||
public boolean runFinished()
|
||||
{
|
||||
return this.ran;
|
||||
}
|
||||
|
||||
public boolean hasCallback()
|
||||
{
|
||||
return this.callbacks.size() > 0;
|
||||
}
|
||||
|
||||
public void runCallbacks()
|
||||
{
|
||||
for (Runnable r : this.callbacks)
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
|
||||
this.callbacks.clear();
|
||||
}
|
||||
|
||||
public QueuedChunk getChunkInfo()
|
||||
{
|
||||
return chunkInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.common.chunkio;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.minecraft.crash.CrashReportCategory;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
|
||||
class ChunkIOThreadPoolExecutor extends ThreadPoolExecutor {
|
||||
|
||||
public ChunkIOThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
|
||||
{
|
||||
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable r, Throwable t)
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
FMLLog.log.error("Unhandled exception loading chunk:", t);
|
||||
QueuedChunk queuedChunk = ((ChunkIOProvider) r).getChunkInfo();
|
||||
FMLLog.log.error(queuedChunk);
|
||||
FMLLog.log.error(CrashReportCategory.getCoordinateInfo(queuedChunk.x << 4, 64, queuedChunk.z << 4));
|
||||
}
|
||||
catch (Throwable t2)
|
||||
{
|
||||
FMLLog.log.error(t2);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Make absolutely sure that we do not leave any deadlock case
|
||||
r.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.common.chunkio;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
|
||||
class QueuedChunk {
|
||||
final int x;
|
||||
final int z;
|
||||
final World world;
|
||||
|
||||
public QueuedChunk(int x, int z, World world) {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (x * 31 + z * 29) ^ world.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object instanceof QueuedChunk) {
|
||||
QueuedChunk other = (QueuedChunk) object;
|
||||
return x == other.x && z == other.z && world == other.world;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
String NEW_LINE = System.getProperty("line.separator");
|
||||
|
||||
result.append(this.getClass().getName() + " {" + NEW_LINE);
|
||||
result.append(" x: " + x + NEW_LINE);
|
||||
result.append(" z: " + z + NEW_LINE);
|
||||
result.append(" world: " + world.getWorldInfo().getWorldName() + NEW_LINE);
|
||||
result.append(" dimension: " + world.provider.getDimension() + NEW_LINE);
|
||||
result.append(" provider: " + world.provider.getClass().getName() + NEW_LINE);
|
||||
result.append("}");
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.common.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
/**
|
||||
* Handler for custom types of selectors registered with {@link SelectorHandlerManager}
|
||||
*/
|
||||
public interface SelectorHandler
|
||||
{
|
||||
/**
|
||||
* Returns a {@link List} of {@link Entity Entities} of class {@code targetClass} ({@code T}) represented by {@code token}<br>
|
||||
* <b>Note:</b> If {@code token} does not match the overall syntax defined by {@link #isSelector}, this method should return an empty list.
|
||||
* For any other error, an exception should be thrown
|
||||
*
|
||||
* @param sender The {@link ICommandSender} that initiated the query
|
||||
*/
|
||||
<T extends Entity> List<T> matchEntities(ICommandSender sender, String token, Class<? extends T> targetClass) throws CommandException;
|
||||
|
||||
/**
|
||||
* Returns whether the selector string potentially matches multiple entities
|
||||
*/
|
||||
boolean matchesMultiplePlayers(String selectorStr) throws CommandException;
|
||||
|
||||
/**
|
||||
* Returns whether the string matches the overall syntax of the selector<br>
|
||||
* <b>Note:</b> If this returns {@code false}, {@link #matchEntities} should return an empty list
|
||||
*/
|
||||
boolean isSelector(String selectorStr);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.common.command;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.command.EntitySelector;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
|
||||
/**
|
||||
* Allows registration of custom selector types by assigning a {@link SelectorHandler} to a prefix
|
||||
* This class handles calls to the {@link EntitySelector} methods {@link EntitySelector#matchEntities matchEntities},
|
||||
* {@link EntitySelector#matchesMultiplePlayers matchesMultiplePlayers} and {@link EntitySelector#isSelector isSelector}.<br>
|
||||
* The calls are delegated to the handler with the longest matching prefix.<br>
|
||||
* <br>
|
||||
* <b>Note:</b> If you register a {@link SelectorHandler} to a broader domain (not just a single selector), you should take care of possible shadowing conflicts yourself.
|
||||
* For this you can use the information provided by {@link #selectorHandlers} and {@link #registeringMods}.
|
||||
*/
|
||||
public class SelectorHandlerManager
|
||||
{
|
||||
private SelectorHandlerManager()
|
||||
{
|
||||
}
|
||||
|
||||
//the ordering is reversed such that longer prefixes appear before their shorter substrings
|
||||
public static final NavigableMap<String, SelectorHandler> selectorHandlers = new TreeMap<String, SelectorHandler>(Collections.<String> reverseOrder());
|
||||
public static final NavigableMap<String, String> registeringMods = new TreeMap<String, String>(Collections.<String> reverseOrder());
|
||||
|
||||
private static final SelectorHandler vanillaHandler = new SelectorHandler()
|
||||
{
|
||||
@Override
|
||||
public <T extends Entity> List<T> matchEntities(final ICommandSender sender, final String token, final Class<? extends T> targetClass) throws CommandException
|
||||
{
|
||||
return EntitySelector.matchEntitiesDefault(sender, token, targetClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesMultiplePlayers(final String selectorStr) throws CommandException
|
||||
{
|
||||
return EntitySelector.matchesMultiplePlayersDefault(selectorStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelector(final String selectorStr)
|
||||
{
|
||||
return EntitySelector.isSelectorDefault(selectorStr);
|
||||
}
|
||||
};
|
||||
|
||||
static
|
||||
{
|
||||
for (final String prefix : ArrayUtils.toArray("@p", "@a", "@r", "@e", "@s"))
|
||||
{
|
||||
selectorHandlers.put(prefix, vanillaHandler);
|
||||
registeringMods.put(prefix, "minecraft");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new {@link SelectorHandler} for {@code prefix}.<br>
|
||||
*
|
||||
* @param prefix The domain the specified {@code handler} is registered for.
|
||||
* If you want to register just a single selector, {@code prefix} has the form '@{selectorName}'
|
||||
*/
|
||||
public static void register(final String prefix, final SelectorHandler handler)
|
||||
{
|
||||
if (prefix.isEmpty())
|
||||
{
|
||||
throw new IllegalArgumentException("Prefix must not be empty");
|
||||
}
|
||||
|
||||
final String modId = Loader.instance().activeModContainer().getModId();
|
||||
|
||||
selectorHandlers.put(prefix, handler);
|
||||
registeringMods.put(prefix, modId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best matching handler for the given string. Defaults to the vanilla handler if no prefix applies
|
||||
*/
|
||||
public static SelectorHandler getHandler(final String selectorStr)
|
||||
{
|
||||
if (!selectorStr.isEmpty())
|
||||
{
|
||||
for (final Entry<String, SelectorHandler> handler : selectorHandlers.subMap(selectorStr, true, selectorStr.substring(0, 1), true).entrySet())
|
||||
{
|
||||
if (selectorStr.startsWith(handler.getKey()))
|
||||
{
|
||||
return handler.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vanillaHandler;
|
||||
}
|
||||
|
||||
//These methods are called by the vanilla methods
|
||||
|
||||
public static <T extends Entity> List<T> matchEntities(final ICommandSender sender, final String token, final Class<? extends T> targetClass) throws CommandException
|
||||
{
|
||||
return getHandler(token).matchEntities(sender, token, targetClass);
|
||||
}
|
||||
|
||||
public static boolean matchesMultiplePlayers(final String selectorStr) throws CommandException
|
||||
{
|
||||
return getHandler(selectorStr).matchesMultiplePlayers(selectorStr);
|
||||
}
|
||||
|
||||
public static boolean isSelector(final String selectorStr)
|
||||
{
|
||||
return getHandler(selectorStr).isSelector(selectorStr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Config
|
||||
{
|
||||
/**
|
||||
* The mod id that this configuration is associated with.
|
||||
*/
|
||||
String modid();
|
||||
/**
|
||||
* A user friendly name for the config file,
|
||||
* the default will be modid
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* The type this is, right now the only value is INSTANCE.
|
||||
* This is intended to be expanded upon later for more Forge controlled
|
||||
* configs.
|
||||
*/
|
||||
Type type() default Type.INSTANCE;
|
||||
|
||||
/**
|
||||
* Root element category, defaults to "general", if this is an empty string then the root category is disabled.
|
||||
* Any primitive fields will cause an error, and you must specify sub-category objects
|
||||
*/
|
||||
String category() default "general";
|
||||
|
||||
public static enum Type
|
||||
{
|
||||
/**
|
||||
* Loaded once, directly after mod construction. Before pre-init.
|
||||
* This class must have static fields.
|
||||
*/
|
||||
INSTANCE(true);
|
||||
|
||||
|
||||
private boolean isStatic = true;
|
||||
private Type(boolean isStatic) { this.isStatic = isStatic; }
|
||||
public boolean isStatic(){ return this.isStatic; }
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||
@interface LangKey
|
||||
{
|
||||
String value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@interface Comment
|
||||
{
|
||||
String[] value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@interface Ignore
|
||||
{}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@interface RangeInt
|
||||
{
|
||||
int min() default Integer.MIN_VALUE;
|
||||
int max() default Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@interface RangeDouble
|
||||
{
|
||||
double min() default Double.MIN_VALUE;
|
||||
double max() default Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@interface Name
|
||||
{
|
||||
String value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||
@interface RequiresMcRestart
|
||||
{}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||
@interface RequiresWorldRestart
|
||||
{}
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
import static net.minecraftforge.common.config.Configuration.COMMENT_SEPARATOR;
|
||||
import static net.minecraftforge.common.config.Configuration.NEW_LINE;
|
||||
import static net.minecraftforge.common.config.Configuration.allowedProperties;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries.IConfigEntry;
|
||||
|
||||
public class ConfigCategory implements Map<String, Property>
|
||||
{
|
||||
private String name;
|
||||
private String comment;
|
||||
private String languagekey;
|
||||
private ArrayList<ConfigCategory> children = new ArrayList<ConfigCategory>();
|
||||
private Map<String, Property> properties = new TreeMap<String, Property>();
|
||||
@SuppressWarnings("unused")
|
||||
private int propNumber = 0;
|
||||
public final ConfigCategory parent;
|
||||
private boolean changed = false;
|
||||
private boolean requiresWorldRestart = false;
|
||||
private boolean showInGui = true;
|
||||
private boolean requiresMcRestart = false;
|
||||
private Class<? extends IConfigEntry> customEntryClass = null;
|
||||
private List<String> propertyOrder = null;
|
||||
|
||||
public ConfigCategory(String name)
|
||||
{
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
public ConfigCategory(String name, ConfigCategory parent)
|
||||
{
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
if (parent != null)
|
||||
{
|
||||
parent.children.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj instanceof ConfigCategory)
|
||||
{
|
||||
ConfigCategory cat = (ConfigCategory)obj;
|
||||
return name.equals(cat.name) && children.equals(cat.children);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getQualifiedName()
|
||||
{
|
||||
return getQualifiedName(name, parent);
|
||||
}
|
||||
|
||||
public static String getQualifiedName(String name, ConfigCategory parent)
|
||||
{
|
||||
return (parent == null ? name : parent.getQualifiedName() + Configuration.CATEGORY_SPLITTER + name);
|
||||
}
|
||||
|
||||
public ConfigCategory getFirstParent()
|
||||
{
|
||||
return (parent == null ? this : parent.getFirstParent());
|
||||
}
|
||||
|
||||
public boolean isChild()
|
||||
{
|
||||
return parent != null;
|
||||
}
|
||||
|
||||
public Map<String, Property> getValues()
|
||||
{
|
||||
return ImmutableMap.copyOf(properties);
|
||||
}
|
||||
|
||||
public List<Property> getOrderedValues()
|
||||
{
|
||||
if (this.propertyOrder != null)
|
||||
{
|
||||
ArrayList<Property> set = new ArrayList<Property>();
|
||||
for (String key : this.propertyOrder)
|
||||
if (properties.containsKey(key))
|
||||
set.add(properties.get(key));
|
||||
|
||||
return ImmutableList.copyOf(set);
|
||||
}
|
||||
else
|
||||
return ImmutableList.copyOf(properties.values());
|
||||
}
|
||||
|
||||
public ConfigCategory setConfigEntryClass(Class<? extends IConfigEntry> clazz)
|
||||
{
|
||||
this.customEntryClass = clazz;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Class<? extends IConfigEntry> getConfigEntryClass()
|
||||
{
|
||||
return this.customEntryClass;
|
||||
}
|
||||
|
||||
public ConfigCategory setLanguageKey(String languagekey)
|
||||
{
|
||||
this.languagekey = languagekey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getLanguagekey()
|
||||
{
|
||||
if (this.languagekey != null)
|
||||
return this.languagekey;
|
||||
else
|
||||
return getQualifiedName();
|
||||
}
|
||||
|
||||
public void setComment(String comment)
|
||||
{
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public String getComment()
|
||||
{
|
||||
return this.comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag for whether or not this category can be edited while a world is running. Care should be taken to ensure
|
||||
* that only properties that are truly dynamic can be changed from the in-game options menu. Only set this flag to
|
||||
* true if all child properties/categories are unable to be modified while a world is running.
|
||||
*/
|
||||
public ConfigCategory setRequiresWorldRestart(boolean requiresWorldRestart)
|
||||
{
|
||||
this.requiresWorldRestart = requiresWorldRestart;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this category is able to be edited while a world is running using the in-game Mod Options screen
|
||||
* as well as the Mods list screen, or only from the Mods list screen.
|
||||
*/
|
||||
public boolean requiresWorldRestart()
|
||||
{
|
||||
return this.requiresWorldRestart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not this ConfigCategory should be allowed to show on config GUIs.
|
||||
* Defaults to true.
|
||||
*/
|
||||
public ConfigCategory setShowInGui(boolean showInGui)
|
||||
{
|
||||
this.showInGui = showInGui;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not this ConfigCategory should be allowed to show on config GUIs.
|
||||
* Defaults to true unless set to false.
|
||||
*/
|
||||
public boolean showInGui()
|
||||
{
|
||||
return showInGui;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not this ConfigCategory requires Minecraft to be restarted when changed.
|
||||
* Defaults to false. Only set this flag to true if ALL child properties/categories require
|
||||
* Minecraft to be restarted when changed. Setting this flag will also prevent modification
|
||||
* of the child properties/categories while a world is running.
|
||||
*/
|
||||
public ConfigCategory setRequiresMcRestart(boolean requiresMcRestart)
|
||||
{
|
||||
this.requiresMcRestart = this.requiresWorldRestart = requiresMcRestart;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not this ConfigCategory requires Minecraft to be restarted when changed.
|
||||
* Defaults to false unless set to true.
|
||||
*/
|
||||
public boolean requiresMcRestart()
|
||||
{
|
||||
return this.requiresMcRestart;
|
||||
}
|
||||
|
||||
public ConfigCategory setPropertyOrder(List<String> propertyOrder)
|
||||
{
|
||||
this.propertyOrder = propertyOrder;
|
||||
for (String s : properties.keySet())
|
||||
if (!propertyOrder.contains(s))
|
||||
propertyOrder.add(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getPropertyOrder()
|
||||
{
|
||||
if (this.propertyOrder != null)
|
||||
return ImmutableList.copyOf(this.propertyOrder);
|
||||
else
|
||||
return ImmutableList.copyOf(properties.keySet());
|
||||
}
|
||||
|
||||
public boolean containsKey(String key)
|
||||
{
|
||||
return properties.containsKey(key);
|
||||
}
|
||||
|
||||
public Property get(String key)
|
||||
{
|
||||
return properties.get(key);
|
||||
}
|
||||
|
||||
private void write(BufferedWriter out, String... data) throws IOException
|
||||
{
|
||||
write(out, true, data);
|
||||
}
|
||||
|
||||
private void write(BufferedWriter out, boolean new_line, String... data) throws IOException
|
||||
{
|
||||
for (int x = 0; x < data.length; x++)
|
||||
{
|
||||
out.write(data[x]);
|
||||
}
|
||||
if (new_line) out.write(NEW_LINE);
|
||||
}
|
||||
|
||||
public void write(BufferedWriter out, int indent) throws IOException
|
||||
{
|
||||
String pad0 = getIndent(indent);
|
||||
String pad1 = getIndent(indent + 1);
|
||||
String pad2 = getIndent(indent + 2);
|
||||
|
||||
if (comment != null && !comment.isEmpty())
|
||||
{
|
||||
write(out, pad0, COMMENT_SEPARATOR);
|
||||
write(out, pad0, "# ", name);
|
||||
write(out, pad0, "#--------------------------------------------------------------------------------------------------------#");
|
||||
Splitter splitter = Splitter.onPattern("\r?\n");
|
||||
|
||||
for (String line : splitter.split(comment))
|
||||
{
|
||||
write(out, pad0, "# ", line);
|
||||
}
|
||||
|
||||
write(out, pad0, COMMENT_SEPARATOR, NEW_LINE);
|
||||
}
|
||||
|
||||
String displayName = name;
|
||||
|
||||
if (!allowedProperties.matchesAllOf(name))
|
||||
{
|
||||
displayName = '"' + name + '"';
|
||||
}
|
||||
|
||||
write(out, pad0, displayName, " {");
|
||||
|
||||
Property[] props = getOrderedValues().toArray(new Property[] {});
|
||||
|
||||
for (int x = 0; x < props.length; x++)
|
||||
{
|
||||
Property prop = props[x];
|
||||
|
||||
if (prop.getComment() != null && !prop.getComment().isEmpty())
|
||||
{
|
||||
if (x != 0)
|
||||
{
|
||||
out.newLine();
|
||||
}
|
||||
|
||||
Splitter splitter = Splitter.onPattern("\r?\n");
|
||||
for (String commentLine : splitter.split(prop.getComment()))
|
||||
{
|
||||
write(out, pad1, "# ", commentLine);
|
||||
}
|
||||
}
|
||||
|
||||
String propName = prop.getName();
|
||||
|
||||
if (!allowedProperties.matchesAllOf(propName))
|
||||
{
|
||||
propName = '"' + propName + '"';
|
||||
}
|
||||
|
||||
if (prop.isList())
|
||||
{
|
||||
char type = prop.getType().getID();
|
||||
|
||||
write(out, pad1, String.valueOf(type), ":", propName, " <");
|
||||
|
||||
for (String line : prop.getStringList())
|
||||
{
|
||||
write(out, pad2, line);
|
||||
}
|
||||
|
||||
write(out, pad1, " >");
|
||||
}
|
||||
else if (prop.getType() == null)
|
||||
{
|
||||
write(out, pad1, propName, "=", prop.getString());
|
||||
}
|
||||
else
|
||||
{
|
||||
char type = prop.getType().getID();
|
||||
write(out, pad1, String.valueOf(type), ":", propName, "=", prop.getString());
|
||||
}
|
||||
|
||||
prop.resetChangedState();
|
||||
}
|
||||
|
||||
if (children.size() > 0)
|
||||
out.newLine();
|
||||
|
||||
for (ConfigCategory child : children)
|
||||
{
|
||||
child.write(out, indent + 1);
|
||||
}
|
||||
|
||||
write(out, pad0, "}", NEW_LINE);
|
||||
}
|
||||
|
||||
private String getIndent(int indent)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder("");
|
||||
for (int x = 0; x < indent; x++)
|
||||
{
|
||||
buf.append(" ");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public boolean hasChanged()
|
||||
{
|
||||
if (changed) return true;
|
||||
for (Property prop : properties.values())
|
||||
{
|
||||
if (prop.hasChanged()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void resetChangedState()
|
||||
{
|
||||
changed = false;
|
||||
for (Property prop : properties.values())
|
||||
{
|
||||
prop.resetChangedState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Map bouncer functions for compatibility with older mods, to be removed once all mods stop using it.
|
||||
@Override public int size(){ return properties.size(); }
|
||||
@Override public boolean isEmpty() { return properties.isEmpty(); }
|
||||
@Override public boolean containsKey(Object key) { return properties.containsKey(key); }
|
||||
@Override public boolean containsValue(Object value){ return properties.containsValue(value); }
|
||||
@Override public Property get(Object key) { return properties.get(key); }
|
||||
@Override public Property put(String key, Property value)
|
||||
{
|
||||
changed = true;
|
||||
if (this.propertyOrder != null && !this.propertyOrder.contains(key))
|
||||
this.propertyOrder.add(key);
|
||||
return properties.put(key, value);
|
||||
}
|
||||
@Override public Property remove(Object key)
|
||||
{
|
||||
changed = true;
|
||||
return properties.remove(key);
|
||||
}
|
||||
@Override public void putAll(Map<? extends String, ? extends Property> m)
|
||||
{
|
||||
changed = true;
|
||||
if (this.propertyOrder != null)
|
||||
for (String key : m.keySet())
|
||||
if (!this.propertyOrder.contains(key))
|
||||
this.propertyOrder.add(key);
|
||||
properties.putAll(m);
|
||||
}
|
||||
@Override public void clear()
|
||||
{
|
||||
changed = true;
|
||||
properties.clear();
|
||||
}
|
||||
@Override public Set<String> keySet() { return properties.keySet(); }
|
||||
@Override public Collection<Property> values() { return properties.values(); }
|
||||
|
||||
@Override //Immutable copy, changes will NOT be reflected in this category
|
||||
public Set<java.util.Map.Entry<String, Property>> entrySet()
|
||||
{
|
||||
return ImmutableSet.copyOf(properties.entrySet());
|
||||
}
|
||||
|
||||
public Set<ConfigCategory> getChildren(){ return ImmutableSet.copyOf(children); }
|
||||
|
||||
public void removeChild(ConfigCategory child)
|
||||
{
|
||||
if (children.contains(child))
|
||||
{
|
||||
children.remove(child);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.minecraftforge.fml.client.config.ConfigGuiType;
|
||||
import net.minecraftforge.fml.client.config.DummyConfigElement.DummyCategoryElement;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries.IConfigEntry;
|
||||
import net.minecraftforge.fml.client.config.GuiEditArrayEntries.IArrayEntry;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
/**
|
||||
* This class bridges the gap between the FML config GUI classes and the Forge Configuration classes.
|
||||
*/
|
||||
public class ConfigElement implements IConfigElement
|
||||
{
|
||||
private Property prop;
|
||||
private Property.Type type;
|
||||
private boolean isProperty;
|
||||
private ConfigCategory category;
|
||||
private boolean categoriesFirst = true;
|
||||
|
||||
public ConfigElement(ConfigCategory category)
|
||||
{
|
||||
this.category = category;
|
||||
isProperty = false;
|
||||
}
|
||||
|
||||
public ConfigElement(Property prop)
|
||||
{
|
||||
this.prop = prop;
|
||||
this.type = prop.getType();
|
||||
this.isProperty = true;
|
||||
}
|
||||
|
||||
public ConfigElement listCategoriesFirst(boolean categoriesFirst)
|
||||
{
|
||||
this.categoriesFirst = categoriesFirst;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IConfigElement> getChildElements()
|
||||
{
|
||||
if (!isProperty)
|
||||
{
|
||||
List<IConfigElement> elements = new ArrayList<IConfigElement>();
|
||||
Iterator<ConfigCategory> ccI = category.getChildren().iterator();
|
||||
Iterator<Property> pI = category.getOrderedValues().iterator();
|
||||
@SuppressWarnings("unused")
|
||||
int index = 0;
|
||||
|
||||
if (categoriesFirst)
|
||||
while (ccI.hasNext())
|
||||
{
|
||||
ConfigElement temp = new ConfigElement(ccI.next());
|
||||
if (temp.showInGui()) // don't bother adding elements that shouldn't show
|
||||
elements.add(temp);
|
||||
}
|
||||
|
||||
while (pI.hasNext())
|
||||
{
|
||||
ConfigElement temp = new ConfigElement(pI.next());
|
||||
if (temp.showInGui())
|
||||
elements.add(temp);
|
||||
}
|
||||
|
||||
if (!categoriesFirst)
|
||||
while (ccI.hasNext())
|
||||
{
|
||||
ConfigElement temp = new ConfigElement(ccI.next());
|
||||
if (temp.showInGui())
|
||||
elements.add(temp);
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return isProperty ? prop.getName() : category.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProperty()
|
||||
{
|
||||
return isProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IConfigEntry> getConfigEntryClass()
|
||||
{
|
||||
return isProperty ? prop.getConfigEntryClass() : category.getConfigEntryClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IArrayEntry> getArrayEntryClass()
|
||||
{
|
||||
return isProperty ? prop.getArrayEntryClass() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQualifiedName()
|
||||
{
|
||||
return isProperty ? prop.getName() : category.getQualifiedName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigGuiType getType()
|
||||
{
|
||||
return isProperty ? getType(this.prop) : ConfigGuiType.CONFIG_CATEGORY;
|
||||
}
|
||||
|
||||
public static ConfigGuiType getType(Property prop)
|
||||
{
|
||||
return prop.getType() == Property.Type.BOOLEAN ? ConfigGuiType.BOOLEAN : prop.getType() == Property.Type.DOUBLE ? ConfigGuiType.DOUBLE :
|
||||
prop.getType() == Property.Type.INTEGER ? ConfigGuiType.INTEGER : prop.getType() == Property.Type.COLOR ? ConfigGuiType.COLOR :
|
||||
prop.getType() == Property.Type.MOD_ID ? ConfigGuiType.MOD_ID : ConfigGuiType.STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList()
|
||||
{
|
||||
return isProperty && prop.isList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isListLengthFixed()
|
||||
{
|
||||
return isProperty && prop.isListLengthFixed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxListLength()
|
||||
{
|
||||
return isProperty ? prop.getMaxListLength() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment()
|
||||
{
|
||||
return isProperty ? prop.getComment() : category.getComment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault()
|
||||
{
|
||||
return !isProperty || prop.isDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToDefault()
|
||||
{
|
||||
if (isProperty)
|
||||
prop.setToDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresWorldRestart()
|
||||
{
|
||||
return isProperty ? prop.requiresWorldRestart() : category.requiresWorldRestart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showInGui()
|
||||
{
|
||||
return isProperty ? prop.showInGui() : category.showInGui();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresMcRestart()
|
||||
{
|
||||
return isProperty ? prop.requiresMcRestart() : category.requiresMcRestart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getValidValues()
|
||||
{
|
||||
return isProperty ? prop.getValidValues() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageKey()
|
||||
{
|
||||
return isProperty ? prop.getLanguageKey() : category.getLanguagekey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefault()
|
||||
{
|
||||
return isProperty ? prop.getDefault() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getDefaults()
|
||||
{
|
||||
if (isProperty)
|
||||
{
|
||||
String[] aVal = prop.getDefaults();
|
||||
if (type == Property.Type.BOOLEAN)
|
||||
{
|
||||
Boolean[] ba = new Boolean[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
ba[i] = Boolean.valueOf(aVal[i]);
|
||||
return ba;
|
||||
}
|
||||
else if (type == Property.Type.DOUBLE)
|
||||
{
|
||||
Double[] da = new Double[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
da[i] = Double.valueOf(aVal[i].toString());
|
||||
return da;
|
||||
}
|
||||
else if (type == Property.Type.INTEGER)
|
||||
{
|
||||
Integer[] ia = new Integer[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
ia[i] = Integer.valueOf(aVal[i].toString());
|
||||
return ia;
|
||||
}
|
||||
else
|
||||
return aVal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pattern getValidationPattern()
|
||||
{
|
||||
return isProperty ? prop.getValidationPattern() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get()
|
||||
{
|
||||
return isProperty ? prop.getString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getList()
|
||||
{
|
||||
if (isProperty)
|
||||
{
|
||||
String[] aVal = prop.getStringList();
|
||||
if (type == Property.Type.BOOLEAN)
|
||||
{
|
||||
Boolean[] ba = new Boolean[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
ba[i] = Boolean.valueOf(aVal[i]);
|
||||
return ba;
|
||||
}
|
||||
else if (type == Property.Type.DOUBLE)
|
||||
{
|
||||
Double[] da = new Double[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
da[i] = Double.valueOf(aVal[i].toString());
|
||||
return da;
|
||||
}
|
||||
else if (type == Property.Type.INTEGER)
|
||||
{
|
||||
Integer[] ia = new Integer[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
ia[i] = Integer.valueOf(aVal[i].toString());
|
||||
return ia;
|
||||
}
|
||||
else
|
||||
return aVal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object value)
|
||||
{
|
||||
if (isProperty)
|
||||
{
|
||||
if (type == Property.Type.BOOLEAN)
|
||||
prop.set(Boolean.parseBoolean(value.toString()));
|
||||
else if (type == Property.Type.DOUBLE)
|
||||
prop.set(Double.parseDouble(value.toString()));
|
||||
else if (type == Property.Type.INTEGER)
|
||||
prop.set(Integer.parseInt(value.toString()));
|
||||
else
|
||||
prop.set(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object[] aVal)
|
||||
{
|
||||
if (isProperty)
|
||||
{
|
||||
if (type == Property.Type.BOOLEAN)
|
||||
{
|
||||
boolean[] ba = new boolean[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
ba[i] = Boolean.valueOf(aVal[i].toString());
|
||||
prop.set(ba);
|
||||
}
|
||||
else if (type == Property.Type.DOUBLE)
|
||||
{
|
||||
double[] da = new double[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
da[i] = Double.valueOf(aVal[i].toString());
|
||||
prop.set(da);
|
||||
}
|
||||
else if (type == Property.Type.INTEGER)
|
||||
{
|
||||
int[] ia = new int[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
ia[i] = Integer.valueOf(aVal[i].toString());
|
||||
prop.set(ia);
|
||||
}
|
||||
else
|
||||
{
|
||||
String[] is = new String[aVal.length];
|
||||
for(int i = 0; i < aVal.length; i++)
|
||||
is[i] = aVal[i].toString();
|
||||
prop.set(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getMinValue()
|
||||
{
|
||||
return isProperty ? prop.getMinValue() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getMaxValue()
|
||||
{
|
||||
return isProperty ? prop.getMaxValue() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a ConfigElement derived from the annotation-based config system
|
||||
* @param configClass the class which contains the configuration
|
||||
* @return A ConfigElement based on the described category.
|
||||
*/
|
||||
public static IConfigElement from(Class<?> configClass)
|
||||
{
|
||||
Config annotation = configClass.getAnnotation(Config.class);
|
||||
if (annotation == null)
|
||||
throw new RuntimeException(String.format("The class '%s' has no @Config annotation!", configClass.getName()));
|
||||
|
||||
Configuration config = ConfigManager.getConfiguration(annotation.modid(), annotation.name());
|
||||
if (config == null)
|
||||
{
|
||||
String error = String.format("The configuration '%s' of mod '%s' isn't loaded with the ConfigManager!", annotation.name(), annotation.modid());
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
|
||||
String name = Strings.isNullOrEmpty(annotation.name()) ? annotation.modid() : annotation.name();
|
||||
String langKey = name;
|
||||
Config.LangKey langKeyAnnotation = configClass.getAnnotation(Config.LangKey.class);
|
||||
if (langKeyAnnotation != null)
|
||||
{
|
||||
langKey = langKeyAnnotation.value();
|
||||
}
|
||||
|
||||
if (annotation.category().isEmpty())
|
||||
{
|
||||
List<IConfigElement> elements = Lists.newArrayList();
|
||||
Set<String> catNames = config.getCategoryNames();
|
||||
for (String catName : catNames)
|
||||
{
|
||||
if (catName.isEmpty())
|
||||
continue;
|
||||
ConfigCategory category = config.getCategory(catName);
|
||||
if (category.isChild())
|
||||
continue;
|
||||
DummyCategoryElement element = new DummyCategoryElement(category.getName(), category.getLanguagekey(), new ConfigElement(category).getChildElements());
|
||||
element.setRequiresMcRestart(category.requiresMcRestart());
|
||||
element.setRequiresWorldRestart(category.requiresWorldRestart());
|
||||
elements.add(element);
|
||||
}
|
||||
|
||||
return new DummyCategoryElement(name, langKey, elements);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConfigCategory category = config.getCategory(annotation.category());
|
||||
DummyCategoryElement element = new DummyCategoryElement(name, langKey, new ConfigElement(category).getChildElements());
|
||||
element.setRequiresMcRestart(category.requiresMcRestart());
|
||||
element.setRequiresWorldRestart(category.requiresWorldRestart());
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import net.minecraftforge.common.config.Config.Comment;
|
||||
import net.minecraftforge.common.config.Config.LangKey;
|
||||
import net.minecraftforge.common.config.Config.Name;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.LoaderException;
|
||||
import net.minecraftforge.fml.common.discovery.ASMDataTable;
|
||||
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;
|
||||
import net.minecraftforge.fml.common.discovery.asm.ModAnnotation.EnumHolder;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class ConfigManager
|
||||
{
|
||||
private static Map<String, Multimap<Config.Type, ASMData>> asm_data = Maps.newHashMap();
|
||||
static Map<Class<?>, ITypeAdapter> ADAPTERS = Maps.newHashMap();
|
||||
static Map<Class<?>, Class<?>> ARRAY_REMAP = Maps.newHashMap();
|
||||
private static Map<String, Configuration> CONFIGS = Maps.newHashMap();
|
||||
private static Map<String, Set<Class<?>>> MOD_CONFIG_CLASSES = Maps.newHashMap();
|
||||
|
||||
static
|
||||
{
|
||||
register(boolean.class, TypeAdapters.bool);
|
||||
register(boolean[].class, TypeAdapters.boolA);
|
||||
register(Boolean.class, TypeAdapters.Bool);
|
||||
register(Boolean[].class, TypeAdapters.BoolA);
|
||||
register(float.class, TypeAdapters.flt);
|
||||
register(float[].class, TypeAdapters.fltA);
|
||||
register(Float.class, TypeAdapters.Flt);
|
||||
register(Float[].class, TypeAdapters.FltA);
|
||||
register(double.class, TypeAdapters.dbl);
|
||||
register(double[].class, TypeAdapters.dblA);
|
||||
register(Double.class, TypeAdapters.Dbl);
|
||||
register(Double[].class, TypeAdapters.DblA);
|
||||
register(byte.class, TypeAdapters.byt);
|
||||
register(byte[].class, TypeAdapters.bytA);
|
||||
register(Byte.class, TypeAdapters.Byt);
|
||||
register(Byte[].class, TypeAdapters.BytA);
|
||||
register(char.class, TypeAdapters.chr);
|
||||
register(char[].class, TypeAdapters.chrA);
|
||||
register(Character.class, TypeAdapters.Chr);
|
||||
register(Character[].class, TypeAdapters.ChrA);
|
||||
register(short.class, TypeAdapters.shrt);
|
||||
register(short[].class, TypeAdapters.shrtA);
|
||||
register(Short.class, TypeAdapters.Shrt);
|
||||
register(Short[].class, TypeAdapters.ShrtA);
|
||||
register(int.class, TypeAdapters.int_);
|
||||
register(int[].class, TypeAdapters.intA);
|
||||
register(Integer.class, TypeAdapters.Int);
|
||||
register(Integer[].class, TypeAdapters.IntA);
|
||||
register(String.class, TypeAdapters.Str);
|
||||
register(String[].class, TypeAdapters.StrA);
|
||||
|
||||
|
||||
ARRAY_REMAP.put(Boolean.class, Boolean[].class );
|
||||
ARRAY_REMAP.put(Float.class, Float[].class );
|
||||
ARRAY_REMAP.put(Double.class, Double[].class );
|
||||
ARRAY_REMAP.put(Byte.class, Byte[].class );
|
||||
ARRAY_REMAP.put(Character.class, Character[].class);
|
||||
ARRAY_REMAP.put(Short.class, Short[].class );
|
||||
ARRAY_REMAP.put(Integer.class, Integer[].class );
|
||||
ARRAY_REMAP.put(String.class, String[].class );
|
||||
}
|
||||
private static void register(Class<?> cls, ITypeAdapter adpt)
|
||||
{
|
||||
ADAPTERS.put(cls, adpt);
|
||||
}
|
||||
|
||||
public static void loadData(ASMDataTable data)
|
||||
{
|
||||
FMLLog.log.debug("Loading @Config anotation data");
|
||||
for (ASMData target : data.getAll(Config.class.getName()))
|
||||
{
|
||||
String modid = (String)target.getAnnotationInfo().get("modid");
|
||||
Multimap<Config.Type, ASMData> map = asm_data.computeIfAbsent(modid, k -> ArrayListMultimap.create());
|
||||
|
||||
EnumHolder tholder = (EnumHolder)target.getAnnotationInfo().get("type");
|
||||
Config.Type type = tholder == null ? Config.Type.INSTANCE : Config.Type.valueOf(tholder.getValue());
|
||||
|
||||
map.put(type, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bounces to sync().
|
||||
* TODO: remove
|
||||
*/
|
||||
public static void load(String modid, Config.Type type)
|
||||
{
|
||||
sync(modid, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes configuration data between the file on disk, the {@code Configuration} object and the annotated
|
||||
* mod classes containing the configuration variables.
|
||||
*
|
||||
* When first called, this method will try to load the configuration from disk. If this fails, because the file
|
||||
* does not exist, it will be created with default values derived from the mods config classes variable default values
|
||||
* and comments and ranges, as well as configuration names based on the appropriate annotations found in {@code @Config}.
|
||||
*
|
||||
* Note, that this method is being called by the {@link FMLModContaier}, so the mod needn't call it in init().
|
||||
*
|
||||
* If this method is called after the initial load, it will check whether the values in the Configuration object differ
|
||||
* from the values in the corresponding variables. If they differ, it will either overwrite the variables if the Configuration
|
||||
* object is marked as changed (e.g. if it was changed with the ConfigGui) or otherwise overwrite the Configuration object's values.
|
||||
* It then proceeds to saving the changes to disk.
|
||||
* @param modid the mod's ID for which the configuration shall be loaded
|
||||
* @param type the configuration type, currently always {@code Config.Type.INSTANCE}
|
||||
*/
|
||||
public static void sync(String modid, Config.Type type)
|
||||
{
|
||||
FMLLog.log.debug("Attempting to inject @Config classes into {} for type {}", modid, type);
|
||||
ClassLoader mcl = Loader.instance().getModClassLoader();
|
||||
File configDir = Loader.instance().getConfigDir();
|
||||
Multimap<Config.Type, ASMData> map = asm_data.get(modid);
|
||||
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
for (ASMData targ : map.get(type))
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> cls = Class.forName(targ.getClassName(), true, mcl);
|
||||
|
||||
Set<Class<?>> modConfigClasses = MOD_CONFIG_CLASSES.computeIfAbsent(modid, k -> Sets.<Class<?>>newHashSet());
|
||||
modConfigClasses.add(cls);
|
||||
|
||||
String name = (String)targ.getAnnotationInfo().get("name");
|
||||
if (name == null)
|
||||
name = modid;
|
||||
String category = (String)targ.getAnnotationInfo().get("category");
|
||||
if (category == null)
|
||||
category = "general";
|
||||
|
||||
File file = new File(configDir, name + ".cfg");
|
||||
|
||||
boolean loading = false;
|
||||
Configuration cfg = CONFIGS.get(file.getAbsolutePath());
|
||||
if (cfg == null)
|
||||
{
|
||||
cfg = new Configuration(file);
|
||||
cfg.load();
|
||||
CONFIGS.put(file.getAbsolutePath(), cfg);
|
||||
loading = true;
|
||||
}
|
||||
|
||||
sync(cfg, cls, modid, category, loading, null);
|
||||
|
||||
cfg.save();
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log.error("An error occurred trying to load a config for {} into {}", targ.getClassName(), e);
|
||||
throw new LoaderException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Class<?>[] getModConfigClasses(String modid)
|
||||
{
|
||||
return (MOD_CONFIG_CLASSES.containsKey(modid) ? MOD_CONFIG_CLASSES.get(modid).toArray(new Class<?>[0]) : new Class<?>[0]);
|
||||
}
|
||||
|
||||
public static boolean hasConfigForMod(String modid)
|
||||
{
|
||||
return asm_data.containsKey(modid);
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// INTERNAL
|
||||
// =======================================================
|
||||
static Configuration getConfiguration(String modid, String name) {
|
||||
if (Strings.isNullOrEmpty(name))
|
||||
name = modid;
|
||||
File configDir = Loader.instance().getConfigDir();
|
||||
File configFile = new File(configDir, name + ".cfg");
|
||||
return CONFIGS.get(configFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
private static void sync(Configuration cfg, Class<?> cls, String modid, String category, boolean loading, Object instance)
|
||||
{
|
||||
for (Field f : cls.getDeclaredFields())
|
||||
{
|
||||
if (!Modifier.isPublic(f.getModifiers()))
|
||||
continue;
|
||||
|
||||
//Only the root class may have static fields. Otherwise category tree nodes of the same type would share the
|
||||
//contained value messing up the sync
|
||||
if (Modifier.isStatic(f.getModifiers()) != (instance == null))
|
||||
continue;
|
||||
|
||||
if (f.isAnnotationPresent(Config.Ignore.class))
|
||||
continue;
|
||||
|
||||
String comment = null;
|
||||
Comment ca = f.getAnnotation(Comment.class);
|
||||
if (ca != null)
|
||||
comment = NEW_LINE.join(ca.value());
|
||||
|
||||
String langKey = modid + "." + (category.isEmpty() ? "" : category + Configuration.CATEGORY_SPLITTER) + f.getName().toLowerCase(Locale.ENGLISH);
|
||||
LangKey la = f.getAnnotation(LangKey.class);
|
||||
if (la != null)
|
||||
langKey = la.value();
|
||||
|
||||
boolean requiresMcRestart = f.isAnnotationPresent(Config.RequiresMcRestart.class);
|
||||
boolean requiresWorldRestart = f.isAnnotationPresent(Config.RequiresWorldRestart.class);
|
||||
|
||||
if (FieldWrapper.hasWrapperFor(f)) //Wrappers exist for primitives, enums, maps and arrays
|
||||
{
|
||||
if (Strings.isNullOrEmpty(category))
|
||||
throw new RuntimeException("An empty category may not contain anything but objects representing categories!");
|
||||
try
|
||||
{
|
||||
IFieldWrapper wrapper = FieldWrapper.get(instance, f, category);
|
||||
ITypeAdapter adapt = wrapper.getTypeAdapter();
|
||||
Property.Type propType = adapt.getType();
|
||||
|
||||
for (String key : wrapper.getKeys()) //Iterate the fully qualified property names the field provides
|
||||
{
|
||||
String suffix = StringUtils.replaceOnce(key, wrapper.getCategory() + Configuration.CATEGORY_SPLITTER, "");
|
||||
|
||||
boolean existed = exists(cfg, wrapper.getCategory(), suffix);
|
||||
if (!existed || loading) //Creates keys in category specified by the wrapper if new ones are programaticaly added
|
||||
{
|
||||
Property property = property(cfg, wrapper.getCategory(), suffix, propType, adapt.isArrayAdapter());
|
||||
|
||||
adapt.setDefaultValue(property, wrapper.getValue(key));
|
||||
if (!existed)
|
||||
adapt.setValue(property, wrapper.getValue(key));
|
||||
else
|
||||
wrapper.setValue(key, adapt.getValue(property));
|
||||
}
|
||||
else //If the key is not new, sync according to shouldReadFromVar()
|
||||
{
|
||||
Property property = property(cfg, wrapper.getCategory(), suffix, propType, adapt.isArrayAdapter());
|
||||
Object propVal = adapt.getValue(property);
|
||||
Object mapVal = wrapper.getValue(key);
|
||||
if (shouldReadFromVar(property, propVal, mapVal))
|
||||
adapt.setValue(property, mapVal);
|
||||
else
|
||||
wrapper.setValue(key, propVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ConfigCategory confCat = cfg.getCategory(wrapper.getCategory());
|
||||
|
||||
for (Property property : confCat.getOrderedValues()) //Iterate the properties to check for new data from the config side
|
||||
{
|
||||
String key = confCat.getQualifiedName() + Configuration.CATEGORY_SPLITTER + property.getName();
|
||||
if (!wrapper.handlesKey(key))
|
||||
continue;
|
||||
|
||||
if (loading || !wrapper.hasKey(key))
|
||||
{
|
||||
Object value = wrapper.getTypeAdapter().getValue(property);
|
||||
wrapper.setValue(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (loading)
|
||||
wrapper.setupConfiguration(cfg, comment, langKey, requiresMcRestart, requiresWorldRestart);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
String format = "Error syncing field '%s' of class '%s'!";
|
||||
String error = String.format(format, f.getName(), cls.getName());
|
||||
throw new RuntimeException(error, e);
|
||||
}
|
||||
}
|
||||
else if (f.getType().getSuperclass() != null && f.getType().getSuperclass().equals(Object.class))
|
||||
{ //If the field extends Object directly, descend the object tree and access the objects members
|
||||
Object newInstance = null;
|
||||
try
|
||||
{
|
||||
newInstance = f.get(instance);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
//Setup the sub category with its respective name, comment, language key, etc.
|
||||
String sub = (category.isEmpty() ? "" : category + Configuration.CATEGORY_SPLITTER) + getName(f).toLowerCase(Locale.ENGLISH);
|
||||
ConfigCategory confCat = cfg.getCategory(sub);
|
||||
confCat.setComment(comment);
|
||||
confCat.setLanguageKey(langKey);
|
||||
confCat.setRequiresMcRestart(requiresMcRestart);
|
||||
confCat.setRequiresWorldRestart(requiresWorldRestart);
|
||||
|
||||
sync(cfg, f.getType(), modid, sub, loading, newInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
String format = "Can't handle field '%s' of class '%s': Unknown type.";
|
||||
String error = String.format(format, f.getName(), cls.getCanonicalName());
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final Joiner NEW_LINE = Joiner.on('\n');
|
||||
static final Joiner PIPE = Joiner.on('|');
|
||||
|
||||
private static Property property(Configuration cfg, String category, String property, Property.Type type, boolean isList)
|
||||
{
|
||||
Property prop = cfg.getCategory(category).get(property);
|
||||
if (prop == null)
|
||||
{
|
||||
if (isList)
|
||||
prop = new Property(property, new String[0], type);
|
||||
else
|
||||
prop = new Property(property, (String)null, type);
|
||||
cfg.getCategory(category).put(property, prop);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
private static boolean exists(Configuration cfg, String category, String property)
|
||||
{
|
||||
return cfg.hasCategory(category) && cfg.getCategory(category).containsKey(property);
|
||||
}
|
||||
|
||||
private static boolean shouldReadFromVar(Property property, Object propValue, Object fieldValue)
|
||||
{
|
||||
if (!propValue.equals(fieldValue))
|
||||
{
|
||||
if (property.hasChanged())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String getName(Field f)
|
||||
{
|
||||
if (f.isAnnotationPresent(Name.class))
|
||||
return f.getAnnotation(Name.class).value();
|
||||
return f.getName();
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.minecraftforge.common.config.Config.RangeDouble;
|
||||
import net.minecraftforge.common.config.Config.RangeInt;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import static net.minecraftforge.common.config.ConfigManager.*;
|
||||
|
||||
public abstract class FieldWrapper implements IFieldWrapper
|
||||
{
|
||||
protected String category, name;
|
||||
|
||||
protected Field field;
|
||||
protected Object instance;
|
||||
|
||||
public FieldWrapper(String category, Field field, Object instance)
|
||||
{
|
||||
this.instance = instance;
|
||||
this.field = field;
|
||||
this.category = category;
|
||||
this.name = field.getName();
|
||||
|
||||
if (field.isAnnotationPresent(Config.Name.class))
|
||||
this.name = field.getAnnotation(Config.Name.class).value();
|
||||
|
||||
this.field.setAccessible(true); // Just in case
|
||||
}
|
||||
|
||||
public static IFieldWrapper get(Object instance, Field field, String category)
|
||||
{
|
||||
if (ADAPTERS.get(field.getType()) != null)
|
||||
return new PrimitiveWrapper(category, field, instance);
|
||||
else if (Enum.class.isAssignableFrom(field.getType()))
|
||||
return new EnumWrapper(category, field, instance);
|
||||
else if (Map.class.isAssignableFrom(field.getType()))
|
||||
return new MapWrapper(category, field, instance);
|
||||
else if (field.getType().getSuperclass().equals(Object.class))
|
||||
throw new RuntimeException("Objects should not be handled by field wrappers");
|
||||
else
|
||||
throw new IllegalArgumentException(String.format("Fields of type '%s' are not supported!", field.getType().getCanonicalName()));
|
||||
}
|
||||
|
||||
public static boolean hasWrapperFor(Field field)
|
||||
{
|
||||
if (ADAPTERS.get(field.getType()) != null)
|
||||
return true;
|
||||
else if (Enum.class.isAssignableFrom(field.getType()))
|
||||
return true;
|
||||
else if (Map.class.isAssignableFrom(field.getType()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class MapWrapper extends FieldWrapper
|
||||
{
|
||||
private Map<String, Object> theMap = null;
|
||||
private Type mType;
|
||||
private final String baseName;
|
||||
ITypeAdapter adapter;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private MapWrapper(String category, Field field, Object instance)
|
||||
{
|
||||
super(category, field, instance);
|
||||
|
||||
this.baseName = (this.category == null) ? "" : (this.category + ".") + this.name.toLowerCase(Locale.ENGLISH) + ".";
|
||||
|
||||
try
|
||||
{
|
||||
theMap = (Map<String, Object>) field.get(instance);
|
||||
}
|
||||
catch (ClassCastException cce)
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("The map '%s' of class '%s' must have the key type String!", field.getName(),
|
||||
field.getDeclaringClass().getCanonicalName()), cce);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
ParameterizedType type = (ParameterizedType) field.getGenericType();
|
||||
mType = type.getActualTypeArguments()[1];
|
||||
|
||||
this.adapter = ADAPTERS.get(mType);
|
||||
if (this.adapter == null && mType instanceof GenericArrayType)
|
||||
{
|
||||
this.adapter = ADAPTERS.get(ARRAY_REMAP.get(((GenericArrayType)mType).getGenericComponentType())); //J6 seems to have issues, Need to find a better way to translate this. We don't have access to array depth.
|
||||
}
|
||||
|
||||
if (mType instanceof Class && Enum.class.isAssignableFrom((Class<?>)mType))
|
||||
{
|
||||
this.adapter = TypeAdapters.Str;
|
||||
}
|
||||
|
||||
if (this.adapter == null)
|
||||
throw new IllegalArgumentException(String.format("The map '%s' of class '%s' has target values which are neither primitive nor an enum!",
|
||||
field.getName(), field.getDeclaringClass().getCanonicalName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITypeAdapter getTypeAdapter()
|
||||
{
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getKeys()
|
||||
{
|
||||
Set<String> keys = theMap.keySet();
|
||||
String[] keyArray = new String[keys.size()];
|
||||
|
||||
Iterator<String> it = keys.iterator();
|
||||
for (int i = 0; i < keyArray.length; i++)
|
||||
{
|
||||
keyArray[i] = this.baseName + it.next();
|
||||
}
|
||||
|
||||
return keyArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(String key)
|
||||
{
|
||||
return theMap.get(getSuffix(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String key, Object value)
|
||||
{
|
||||
theMap.put(getSuffix(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasKey(String key)
|
||||
{
|
||||
return theMap.containsKey(getSuffix(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlesKey(String key)
|
||||
{
|
||||
if (key == null)
|
||||
return false;
|
||||
return key.startsWith(this.baseName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupConfiguration(Configuration cfg, String desc, String langKey, boolean reqMCRestart, boolean reqWorldRestart)
|
||||
{
|
||||
ConfigCategory confCat = cfg.getCategory(getCategory());
|
||||
confCat.setComment(desc);
|
||||
confCat.setLanguageKey(langKey);
|
||||
confCat.setRequiresMcRestart(reqMCRestart);
|
||||
confCat.setRequiresWorldRestart(reqWorldRestart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCategory()
|
||||
{
|
||||
return (this.category == null) ? "" : (this.category + ".") + this.name.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the {@code this.baseName} prefix from the key
|
||||
* @param key the key to be edited
|
||||
* @return the keys suffix
|
||||
*/
|
||||
private String getSuffix(String key)
|
||||
{
|
||||
return StringUtils.replaceOnce(key, this.baseName, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class EnumWrapper extends SingleValueFieldWrapper
|
||||
{
|
||||
|
||||
private EnumWrapper(String category, Field field, Object instance)
|
||||
{
|
||||
super(category, field, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITypeAdapter getTypeAdapter()
|
||||
{
|
||||
return TypeAdapters.Str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(String key)
|
||||
{
|
||||
if (!hasKey(key))
|
||||
throw new IllegalArgumentException("Unsupported Key!");
|
||||
|
||||
try
|
||||
{
|
||||
@SuppressWarnings("rawtypes")
|
||||
Enum enu = (Enum) field.get(instance);
|
||||
return enu.name();
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String key, Object value)
|
||||
{
|
||||
if (!hasKey(key))
|
||||
throw new IllegalArgumentException("Unsupported Key!");
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
Enum enu = Enum.valueOf((Class<? extends Enum>) field.getType(), (String) value);
|
||||
try
|
||||
{
|
||||
field.set(instance, enu);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public void setupConfiguration(Configuration cfg, String desc, String langKey, boolean reqMCRestart, boolean reqWorldRestart)
|
||||
{
|
||||
super.setupConfiguration(cfg, desc, langKey, reqMCRestart, reqWorldRestart);
|
||||
|
||||
Property prop = cfg.getCategory(this.category).get(this.name); // Will be setup in general by ConfigManager
|
||||
|
||||
List<String> lst = Lists.newArrayList();
|
||||
for (Enum e : ((Class<? extends Enum>) field.getType()).getEnumConstants())
|
||||
lst.add(e.name());
|
||||
|
||||
prop.setValidationPattern(Pattern.compile(PIPE.join(lst)));
|
||||
prop.setValidValues(lst.toArray(new String[0]));
|
||||
|
||||
String validValues = NEW_LINE.join(lst);
|
||||
|
||||
if (desc != null)
|
||||
prop.setComment(NEW_LINE.join(new String[] { desc, "Valid values:" }) + "\n" + validValues);
|
||||
else
|
||||
prop.setComment("Valid values:" + "\n" + validValues);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PrimitiveWrapper extends SingleValueFieldWrapper
|
||||
{
|
||||
|
||||
private PrimitiveWrapper(String category, Field field, Object instance)
|
||||
{
|
||||
super(category, field, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITypeAdapter getTypeAdapter()
|
||||
{
|
||||
return ADAPTERS.get(field.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(String key)
|
||||
{
|
||||
if (!hasKey(key))
|
||||
throw new IllegalArgumentException("Unknown key!");
|
||||
try
|
||||
{
|
||||
return field.get(instance);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String key, Object value)
|
||||
{
|
||||
if (!hasKey(key))
|
||||
throw new IllegalArgumentException("Unknown key: " + key);
|
||||
try
|
||||
{
|
||||
field.set(instance, value);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupConfiguration(Configuration cfg, String desc, String langKey, boolean reqMCRestart, boolean reqWorldRestart)
|
||||
{
|
||||
super.setupConfiguration(cfg, desc, langKey, reqMCRestart, reqWorldRestart);
|
||||
|
||||
Property prop = cfg.getCategory(this.category).get(this.name);
|
||||
|
||||
RangeInt ia = field.getAnnotation(RangeInt.class);
|
||||
if (ia != null)
|
||||
{
|
||||
prop.setMinValue(ia.min());
|
||||
prop.setMaxValue(ia.max());
|
||||
if (desc != null)
|
||||
prop.setComment(NEW_LINE.join(new String[] { desc, "Min: " + ia.min(), "Max: " + ia.max() }));
|
||||
else
|
||||
prop.setComment(NEW_LINE.join(new String[] { "Min: " + ia.min(), "Max: " + ia.max() }));
|
||||
}
|
||||
|
||||
RangeDouble da = field.getAnnotation(RangeDouble.class);
|
||||
if (da != null)
|
||||
{
|
||||
prop.setMinValue(da.min());
|
||||
prop.setMaxValue(da.max());
|
||||
if (desc != null)
|
||||
prop.setComment(NEW_LINE.join(new String[] { desc, "Min: " + da.min(), "Max: " + da.max() }));
|
||||
else
|
||||
prop.setComment(NEW_LINE.join(new String[] { "Min: " + da.min(), "Max: " + da.max() }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class SingleValueFieldWrapper extends FieldWrapper
|
||||
{
|
||||
private SingleValueFieldWrapper(String category, Field field, Object instance)
|
||||
{
|
||||
super(category, field, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getKeys()
|
||||
{
|
||||
return asArray(this.category + "." + this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasKey(String key)
|
||||
{
|
||||
return (this.category + "." + this.name).equals(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlesKey(String key)
|
||||
{
|
||||
return hasKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupConfiguration(Configuration cfg, String desc, String langKey, boolean reqMCRestart, boolean reqWorldRestart)
|
||||
{
|
||||
Property prop = cfg.getCategory(this.category).get(this.name); // Will be setup in general by ConfigManager
|
||||
|
||||
prop.setComment(desc);
|
||||
prop.setLanguageKey(langKey);
|
||||
prop.setRequiresMcRestart(reqMCRestart);
|
||||
prop.setRequiresWorldRestart(reqWorldRestart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCategory()
|
||||
{
|
||||
return this.category;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static <T> T[] asArray(T... in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
public static class BeanEntry<K, V> implements Entry<K, V>
|
||||
{
|
||||
private K key;
|
||||
private V value;
|
||||
|
||||
public BeanEntry(K key, V value)
|
||||
{
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value)
|
||||
{
|
||||
throw new UnsupportedOperationException("This is a static bean.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
/**
|
||||
* The objects are expected to get their wrapped field, the owning class, instance and category string on initialization.
|
||||
* In general: The key is the fully qualified property name, where each subcategory is appended with a dot.
|
||||
* i.e: general.map.isPresent
|
||||
*/
|
||||
public interface IFieldWrapper
|
||||
{
|
||||
|
||||
/**
|
||||
* @return The type adapter to serialize the values returned by getValue. Null if non-primitive.
|
||||
*/
|
||||
ITypeAdapter getTypeAdapter();
|
||||
|
||||
/**
|
||||
* @return a list of fully qualified property keys handled by this field
|
||||
*/
|
||||
String[] getKeys();
|
||||
|
||||
/**
|
||||
* @param key the fully qualified property key
|
||||
* @return the value the wrapped field associates with the given key
|
||||
*/
|
||||
Object getValue(String key);
|
||||
|
||||
/**
|
||||
* @param key the fully qualified property key
|
||||
* @param value the target value of the property associated with the key
|
||||
*/
|
||||
void setValue(String key, Object value);
|
||||
|
||||
/**
|
||||
* @param key a fully qualified property key
|
||||
* @return true if the wrapped field contains a property associated with the given key
|
||||
*/
|
||||
boolean hasKey(String key);
|
||||
|
||||
/**
|
||||
* @param key a fully qualified property key
|
||||
* @return true if the wrapped field can save information associated with the given key, false otherwise
|
||||
*/
|
||||
boolean handlesKey(String key);
|
||||
|
||||
/**
|
||||
* @param cfg The configuration object holding the properties
|
||||
* @param desc The properties description
|
||||
* @param langKey The languageKey of the property, used in GUI
|
||||
* @param reqMCRestart True, if a change in this property requires a restart of Minecraft
|
||||
* @param reqWorldRestart True, if the world needs to be reloaded after changes to this property
|
||||
*/
|
||||
void setupConfiguration(Configuration cfg, String desc, String langKey, boolean reqMCRestart, boolean reqWorldRestart);
|
||||
|
||||
/**
|
||||
* i.e. general.map in the example above
|
||||
* @return the category name in which the entries should be saved. This includes the parent categories
|
||||
*/
|
||||
String getCategory();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
import net.minecraftforge.common.config.Property.Type;
|
||||
|
||||
/**
|
||||
* Abstracts the types of properties away. Higher level logic must prevent invalid data types.
|
||||
*/
|
||||
interface ITypeAdapter
|
||||
{
|
||||
/**
|
||||
* Assigns the default value to the property
|
||||
* @param property the property whose default value will be assigned
|
||||
* @param value the default value
|
||||
*/
|
||||
void setDefaultValue(Property property, Object value);
|
||||
|
||||
/**
|
||||
* Sets the properties value.
|
||||
* @param property the property whose value will be set
|
||||
* @param value the set value
|
||||
*/
|
||||
void setValue(Property property, Object value);
|
||||
|
||||
/**
|
||||
* Retrieves the properties value
|
||||
* @param prop the property whose value will be retrieved
|
||||
* @return the properties value
|
||||
*/
|
||||
Object getValue(Property prop);
|
||||
|
||||
Type getType();
|
||||
|
||||
boolean isArrayAdapter();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,869 @@
|
||||
/*
|
||||
* 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.common.config;
|
||||
|
||||
//=========================================================
|
||||
// Run away thar' be dragons!
|
||||
//=========================================================
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.google.common.primitives.Booleans;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.Doubles;
|
||||
import com.google.common.primitives.Floats;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Shorts;
|
||||
|
||||
import net.minecraftforge.common.config.Property.Type;
|
||||
|
||||
class TypeAdapters
|
||||
{
|
||||
/*
|
||||
* boolean, boolean[], Boolean, Boolean[]
|
||||
* float, float[], Float, Float[]
|
||||
* double, double[], Double, Double[]
|
||||
* byte, byte[], Byte, Byte[]
|
||||
* char, char[], Character, Character[]
|
||||
* short, short[], Short, Short[]
|
||||
* int, int[], Integer, Integer[]
|
||||
* String, String[]
|
||||
*/
|
||||
static ITypeAdapter bool = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return prop.getBoolean();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Boolean)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Boolean)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.BOOLEAN;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter boolA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return prop.getBooleanList();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues((boolean[])value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues((boolean[])value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.BOOLEAN;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter Bool = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Boolean.valueOf(prop.getBoolean());
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Boolean)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Boolean)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.BOOLEAN;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter BoolA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Booleans.asList(prop.getBooleanList()).toArray(new Boolean[prop.getBooleanList().length]);
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(Booleans.toArray(Arrays.asList((Boolean[]) value)));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(Booleans.toArray(Arrays.asList((Boolean[]) value)));
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.BOOLEAN;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter flt = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return (float)prop.getDouble();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Float)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Float)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter fltA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Floats.toArray(Doubles.asList(prop.getDoubleList()));
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(Doubles.toArray(Floats.asList((float[])value)));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(Doubles.toArray(Floats.asList((float[])value)));
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter Flt = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Float.valueOf((float)prop.getDouble());
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Float)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Float)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter FltA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Floats.asList(Floats.toArray(Doubles.asList(prop.getDoubleList()))).toArray(new Float[prop.getDoubleList().length]);
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(Doubles.toArray(Arrays.asList((Float[])value)));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(Doubles.toArray(Arrays.asList((Float[])value)));
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter dbl = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop)
|
||||
{
|
||||
return prop.getDouble();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Double)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Double)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter dblA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return prop.getDoubleList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues((double[])value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues((double[])value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter Dbl = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Double.valueOf(prop.getDouble());
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Double)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Double) value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter DblA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Doubles.asList(prop.getDoubleList()).toArray(new Double[prop.getDoubleList().length]);
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(Doubles.toArray(Arrays.asList((Double[])value)));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(Doubles.toArray(Arrays.asList((Double[])value)));
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter byt = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop)
|
||||
{
|
||||
return (byte)prop.getInt();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Byte)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Byte)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter bytA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Bytes.toArray(Ints.asList(prop.getIntList()));
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(Ints.toArray(Bytes.asList((byte[])value)));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(Ints.toArray(Bytes.asList((byte[])value)));
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter Byt = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Byte.valueOf((byte)prop.getInt());
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Byte)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Byte)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter BytA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Bytes.asList(Bytes.toArray(Ints.asList(prop.getIntList()))).toArray(new Byte[prop.getIntList().length]);
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(Ints.toArray(Arrays.asList((Byte[]) value)));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(Ints.toArray(Arrays.asList((Byte[]) value)));
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter chr = new ITypeAdapter() {
|
||||
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return (char)prop.getInt();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Character)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Character)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter chrA = new ITypeAdapter() {
|
||||
private int[] toPrim(char[] v) {
|
||||
if (v == null) return new int[0];
|
||||
int[] ret = new int[v.length];
|
||||
for (int x = 0; x < v.length; x++)
|
||||
ret[x] = v[x];
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
int[] v = prop.getIntList();
|
||||
char[] ret = new char[v.length];
|
||||
for (int x = 0; x < v.length; x++)
|
||||
ret[x] = (char)v[x];
|
||||
return ret;
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(toPrim((char[])value));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(toPrim((char[])value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter Chr = new ITypeAdapter() {
|
||||
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Character.valueOf((char)prop.getInt());
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Character)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Character)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter ChrA = new ITypeAdapter() {
|
||||
private int[] toPrim(Character[] v) {
|
||||
if (v == null) return new int[0];
|
||||
int[] ret = new int[v.length];
|
||||
for (int x = 0; x < v.length; x++)
|
||||
ret[x] = v[x] == null ? 0 : v[x];
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
int[] v = prop.getIntList();
|
||||
Character[] ret = new Character[v.length];
|
||||
for (int x = 0; x < v.length; x++)
|
||||
ret[x] = Character.valueOf((char)v[x]);
|
||||
return ret;
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(toPrim((Character[])value));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(toPrim((Character[]) value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
static ITypeAdapter shrt = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return (short)prop.getInt();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Short)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Short)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter shrtA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Shorts.toArray(Ints.asList(prop.getIntList()));
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(Ints.toArray(Shorts.asList((short[])value)));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(Ints.toArray(Shorts.asList((short[])value)));
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
static ITypeAdapter Shrt = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Short.valueOf((short)prop.getInt());
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Short)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Short)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter ShrtA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
int[] v = prop.getIntList();
|
||||
Short[] ret = new Short[v.length];
|
||||
for (int x = 0; x < ret.length; x++)
|
||||
ret[x] = Short.valueOf((short)v[x]);
|
||||
return ret;
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(Ints.toArray(Arrays.asList((Short[])value)));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(Ints.toArray(Arrays.asList((Short[])value)));
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter int_ = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return prop.getInt();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Integer)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Integer)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter intA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return prop.getIntList();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues((int[])value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues((int[])value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter Int = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return (Integer)prop.getInt();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((Integer)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((Integer)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter IntA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return Ints.asList(prop.getIntList()).toArray(new Integer[prop.getIntList().length]);
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues(Ints.toArray(Arrays.asList((Integer[])value)));
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues(Ints.toArray(Arrays.asList((Integer[])value)));
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.INTEGER;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter Str = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return prop.getString();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValue((String)value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValue((String)value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.STRING;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
static ITypeAdapter StrA = new ITypeAdapter() {
|
||||
@Override
|
||||
public Object getValue(Property prop) {
|
||||
return prop.getStringList();
|
||||
}
|
||||
@Override
|
||||
public void setDefaultValue(Property property, Object value)
|
||||
{
|
||||
property.setDefaultValues((String[])value);
|
||||
}
|
||||
@Override
|
||||
public void setValue(Property property, Object value)
|
||||
{
|
||||
property.setValues((String[])value);
|
||||
}
|
||||
@Override
|
||||
public Type getType()
|
||||
{
|
||||
return Type.STRING;
|
||||
}
|
||||
@Override
|
||||
public boolean isArrayAdapter()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.common.crafting;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntComparators;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
|
||||
public class CompoundIngredient extends Ingredient
|
||||
{
|
||||
private Collection<Ingredient> children;
|
||||
private ItemStack[] stacks;
|
||||
private IntList itemIds;
|
||||
private final boolean isSimple;
|
||||
|
||||
protected CompoundIngredient(Collection<Ingredient> children)
|
||||
{
|
||||
super(0);
|
||||
this.children = children;
|
||||
|
||||
boolean simple = true;
|
||||
for (Ingredient child : children)
|
||||
simple &= child.isSimple();
|
||||
this.isSimple = simple;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public ItemStack[] getMatchingStacks()
|
||||
{
|
||||
if (stacks == null)
|
||||
{
|
||||
List<ItemStack> tmp = Lists.newArrayList();
|
||||
for (Ingredient child : children)
|
||||
Collections.addAll(tmp, child.getMatchingStacks());
|
||||
stacks = tmp.toArray(new ItemStack[tmp.size()]);
|
||||
|
||||
}
|
||||
return stacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public IntList getValidItemStacksPacked()
|
||||
{
|
||||
//TODO: Add a child.isInvalid()?
|
||||
if (this.itemIds == null)
|
||||
{
|
||||
this.itemIds = new IntArrayList();
|
||||
for (Ingredient child : children)
|
||||
this.itemIds.addAll(child.getValidItemStacksPacked());
|
||||
this.itemIds.sort(IntComparators.NATURAL_COMPARATOR);
|
||||
}
|
||||
|
||||
return this.itemIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@Nullable ItemStack target)
|
||||
{
|
||||
if (target == null)
|
||||
return false;
|
||||
|
||||
for (Ingredient child : children)
|
||||
if (child.apply(target))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidate()
|
||||
{
|
||||
this.itemIds = null;
|
||||
this.stacks = null;
|
||||
//Shouldn't need to invalidate children as this is only called form invalidateAll..
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimple()
|
||||
{
|
||||
return isSimple;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,842 @@
|
||||
/*
|
||||
* 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.common.crafting;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.CraftingManager;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.item.crafting.ShapedRecipes;
|
||||
import net.minecraft.item.crafting.ShapelessRecipes;
|
||||
import net.minecraft.nbt.JsonToNBT;
|
||||
import net.minecraft.nbt.NBTException;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.ModContainer;
|
||||
import net.minecraftforge.fml.common.registry.ForgeRegistries;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
import net.minecraftforge.oredict.OreIngredient;
|
||||
import net.minecraftforge.oredict.ShapedOreRecipe;
|
||||
import net.minecraftforge.oredict.ShapelessOreRecipe;
|
||||
import net.minecraftforge.registries.ForgeRegistry;
|
||||
import net.minecraftforge.registries.GameData;
|
||||
import net.minecraftforge.registries.RegistryManager;
|
||||
|
||||
public class CraftingHelper {
|
||||
|
||||
private static final boolean DEBUG_LOAD_MINECRAFT = false;
|
||||
private static Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
private static Map<ResourceLocation, IConditionFactory> conditions = Maps.newHashMap();
|
||||
private static Map<ResourceLocation, IIngredientFactory> ingredients = Maps.newHashMap();
|
||||
private static Map<ResourceLocation, IRecipeFactory> recipes = Maps.newHashMap();
|
||||
|
||||
static {
|
||||
init();
|
||||
}
|
||||
|
||||
public static void register(ResourceLocation key, IConditionFactory factory)
|
||||
{
|
||||
if (conditions.containsKey(key))
|
||||
throw new IllegalStateException("Duplicate recipe condition factory: " + key);
|
||||
conditions.put(key, factory);
|
||||
}
|
||||
public static void register(ResourceLocation key, IRecipeFactory factory)
|
||||
{
|
||||
if (recipes.containsKey(key))
|
||||
throw new IllegalStateException("Duplicate recipe factory: " + key);
|
||||
recipes.put(key, factory);
|
||||
}
|
||||
public static void register(ResourceLocation key, IIngredientFactory factory)
|
||||
{
|
||||
if (ingredients.containsKey(key))
|
||||
throw new IllegalStateException("Duplicate recipe ingredient factory: " + key);
|
||||
ingredients.put(key, factory);
|
||||
}
|
||||
|
||||
|
||||
public static Ingredient getIngredient(Object obj)
|
||||
{
|
||||
if (obj instanceof Ingredient)
|
||||
return (Ingredient)obj;
|
||||
else if (obj instanceof ItemStack)
|
||||
return Ingredient.fromStacks(((ItemStack)obj).copy());
|
||||
else if (obj instanceof Item)
|
||||
return Ingredient.fromItem((Item)obj);
|
||||
else if (obj instanceof Block)
|
||||
return Ingredient.fromStacks(new ItemStack((Block)obj, 1, OreDictionary.WILDCARD_VALUE));
|
||||
else if (obj instanceof String)
|
||||
return new OreIngredient((String)obj);
|
||||
else if (obj instanceof JsonElement)
|
||||
throw new IllegalArgumentException("JsonObjects must use getIngredient(JsonObject, JsonContext)");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Ingredient getIngredient(JsonElement json, JsonContext context)
|
||||
{
|
||||
if (json == null || json.isJsonNull())
|
||||
throw new JsonSyntaxException("Json cannot be null");
|
||||
if (context == null)
|
||||
throw new IllegalArgumentException("getIngredient Context cannot be null");
|
||||
|
||||
if (json.isJsonArray())
|
||||
{
|
||||
List<Ingredient> ingredients = Lists.newArrayList();
|
||||
List<Ingredient> vanilla = Lists.newArrayList();
|
||||
json.getAsJsonArray().forEach((ele) ->
|
||||
{
|
||||
Ingredient ing = CraftingHelper.getIngredient(ele, context);
|
||||
|
||||
if (ing.getClass() == Ingredient.class)
|
||||
{
|
||||
//Vanilla, Due to how we read it splits each itemstack, so we pull out to re-merge later
|
||||
vanilla.add(ing);
|
||||
}
|
||||
else
|
||||
{
|
||||
ingredients.add(ing);
|
||||
}
|
||||
});
|
||||
|
||||
if (!vanilla.isEmpty())
|
||||
{
|
||||
ingredients.add(Ingredient.merge(vanilla));
|
||||
}
|
||||
|
||||
if (ingredients.size() == 0)
|
||||
throw new JsonSyntaxException("Item array cannot be empty, at least one item must be defined");
|
||||
|
||||
if (ingredients.size() == 1)
|
||||
return ingredients.get(0);
|
||||
|
||||
return new CompoundIngredient(ingredients);
|
||||
}
|
||||
|
||||
if (!json.isJsonObject())
|
||||
throw new JsonSyntaxException("Expcted ingredient to be a object or array of objects");
|
||||
|
||||
JsonObject obj = (JsonObject)json;
|
||||
|
||||
String type = context.appendModId(JsonUtils.getString(obj, "type", "minecraft:item"));
|
||||
if (type.isEmpty())
|
||||
throw new JsonSyntaxException("Ingredient type can not be an empty string");
|
||||
|
||||
if (type.equals("minecraft:item"))
|
||||
{
|
||||
String item = JsonUtils.getString(obj, "item");
|
||||
if (item.startsWith("#"))
|
||||
{
|
||||
Ingredient constant = context.getConstant(item.substring(1));
|
||||
if (constant == null)
|
||||
throw new JsonSyntaxException("Ingredient referenced invalid constant: " + item);
|
||||
return constant;
|
||||
}
|
||||
}
|
||||
|
||||
IIngredientFactory factory = ingredients.get(new ResourceLocation(type));
|
||||
if (factory == null)
|
||||
throw new JsonSyntaxException("Unknown ingredient type: " + type);
|
||||
|
||||
return factory.parse(context, obj);
|
||||
}
|
||||
|
||||
public static ItemStack getItemStack(JsonObject json, JsonContext context)
|
||||
{
|
||||
String itemName = context.appendModId(JsonUtils.getString(json, "item"));
|
||||
|
||||
Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(itemName));
|
||||
|
||||
if (item == null)
|
||||
throw new JsonSyntaxException("Unknown item '" + itemName + "'");
|
||||
|
||||
if (item.getHasSubtypes() && !json.has("data"))
|
||||
throw new JsonParseException("Missing data for item '" + itemName + "'");
|
||||
|
||||
if (json.has("nbt"))
|
||||
{
|
||||
// Lets hope this works? Needs test
|
||||
try
|
||||
{
|
||||
JsonElement element = json.get("nbt");
|
||||
NBTTagCompound nbt;
|
||||
if(element.isJsonObject())
|
||||
nbt = JsonToNBT.getTagFromJson(GSON.toJson(element));
|
||||
else
|
||||
nbt = JsonToNBT.getTagFromJson(element.getAsString());
|
||||
|
||||
NBTTagCompound tmp = new NBTTagCompound();
|
||||
if (nbt.hasKey("ForgeCaps"))
|
||||
{
|
||||
tmp.setTag("ForgeCaps", nbt.getTag("ForgeCaps"));
|
||||
nbt.removeTag("ForgeCaps");
|
||||
}
|
||||
|
||||
tmp.setTag("tag", nbt);
|
||||
tmp.setString("id", itemName);
|
||||
tmp.setInteger("Count", JsonUtils.getInt(json, "count", 1));
|
||||
tmp.setInteger("Damage", JsonUtils.getInt(json, "data", 0));
|
||||
|
||||
return new ItemStack(tmp);
|
||||
}
|
||||
catch (NBTException e)
|
||||
{
|
||||
throw new JsonSyntaxException("Invalid NBT Entry: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return new ItemStack(item, JsonUtils.getInt(json, "count", 1), JsonUtils.getInt(json, "data", 0));
|
||||
}
|
||||
|
||||
|
||||
public static ItemStack getItemStackBasic(JsonObject json, JsonContext context)
|
||||
{
|
||||
String itemName = context.appendModId(JsonUtils.getString(json, "item"));
|
||||
|
||||
Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(itemName));
|
||||
|
||||
if (item == null)
|
||||
throw new JsonSyntaxException("Unknown item '" + itemName + "'");
|
||||
|
||||
if (item.getHasSubtypes() && !json.has("data"))
|
||||
throw new JsonParseException("Missing data for item '" + itemName + "'");
|
||||
|
||||
return new ItemStack(item, 1, JsonUtils.getInt(json, "data", 0));
|
||||
}
|
||||
|
||||
public static class ShapedPrimer {
|
||||
public int height, width;
|
||||
public boolean mirrored = true;
|
||||
public NonNullList<Ingredient> input;
|
||||
}
|
||||
|
||||
public static ShapedPrimer parseShaped(Object... recipe)
|
||||
{
|
||||
ShapedPrimer ret = new ShapedPrimer();
|
||||
String shape = "";
|
||||
int idx = 0;
|
||||
|
||||
if (recipe[idx] instanceof Boolean)
|
||||
{
|
||||
ret.mirrored = (Boolean)recipe[idx];
|
||||
if (recipe[idx+1] instanceof Object[])
|
||||
recipe = (Object[])recipe[idx+1];
|
||||
else
|
||||
idx = 1;
|
||||
}
|
||||
|
||||
if (recipe[idx] instanceof String[])
|
||||
{
|
||||
String[] parts = ((String[])recipe[idx++]);
|
||||
|
||||
for (String s : parts)
|
||||
{
|
||||
ret.width = s.length();
|
||||
shape += s;
|
||||
}
|
||||
|
||||
ret.height = parts.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (recipe[idx] instanceof String)
|
||||
{
|
||||
String s = (String)recipe[idx++];
|
||||
shape += s;
|
||||
ret.width = s.length();
|
||||
ret.height++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.width * ret.height != shape.length() || shape.length() == 0)
|
||||
{
|
||||
String err = "Invalid shaped recipe: ";
|
||||
for (Object tmp : recipe)
|
||||
{
|
||||
err += tmp + ", ";
|
||||
}
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
|
||||
HashMap<Character, Ingredient> itemMap = Maps.newHashMap();
|
||||
itemMap.put(' ', Ingredient.EMPTY);
|
||||
|
||||
for (; idx < recipe.length; idx += 2)
|
||||
{
|
||||
Character chr = (Character)recipe[idx];
|
||||
Object in = recipe[idx + 1];
|
||||
Ingredient ing = CraftingHelper.getIngredient(in);
|
||||
|
||||
if (' ' == chr.charValue())
|
||||
throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol.");
|
||||
|
||||
if (ing != null)
|
||||
{
|
||||
itemMap.put(chr, ing);
|
||||
}
|
||||
else
|
||||
{
|
||||
String err = "Invalid shaped ore recipe: ";
|
||||
for (Object tmp : recipe)
|
||||
{
|
||||
err += tmp + ", ";
|
||||
}
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
}
|
||||
|
||||
ret.input = NonNullList.withSize(ret.width * ret.height, Ingredient.EMPTY);
|
||||
|
||||
Set<Character> keys = Sets.newHashSet(itemMap.keySet());
|
||||
keys.remove(' ');
|
||||
|
||||
int x = 0;
|
||||
for (char chr : shape.toCharArray())
|
||||
{
|
||||
Ingredient ing = itemMap.get(chr);
|
||||
if (ing == null)
|
||||
throw new IllegalArgumentException("Pattern references symbol '" + chr + "' but it's not defined in the key");
|
||||
ret.input.set(x++, ing);
|
||||
keys.remove(chr);
|
||||
}
|
||||
|
||||
if (!keys.isEmpty())
|
||||
throw new IllegalArgumentException("Key defines symbols that aren't used in pattern: " + keys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static boolean processConditions(JsonArray conditions, JsonContext context)
|
||||
{
|
||||
for (int x = 0; x < conditions.size(); x++)
|
||||
{
|
||||
if (!conditions.get(x).isJsonObject())
|
||||
throw new JsonSyntaxException("Conditions must be an array of JsonObjects");
|
||||
|
||||
JsonObject json = conditions.get(x).getAsJsonObject();
|
||||
BooleanSupplier cond = CraftingHelper.getCondition(json, context);
|
||||
if (!cond.getAsBoolean())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static BooleanSupplier getCondition(JsonObject json, JsonContext context)
|
||||
{
|
||||
ResourceLocation type = new ResourceLocation(context.appendModId(JsonUtils.getString(json, "type")));
|
||||
IConditionFactory factory = conditions.get(type);
|
||||
if (factory == null)
|
||||
throw new JsonSyntaxException("Unknown condition type: " + type.toString());
|
||||
return factory.parse(context, json);
|
||||
}
|
||||
|
||||
public static IRecipe getRecipe(JsonObject json, JsonContext context)
|
||||
{
|
||||
if (json == null || json.isJsonNull())
|
||||
throw new JsonSyntaxException("Json cannot be null");
|
||||
if (context == null)
|
||||
throw new IllegalArgumentException("getRecipe Context cannot be null");
|
||||
|
||||
String type = context.appendModId(JsonUtils.getString(json, "type"));
|
||||
if (type.isEmpty())
|
||||
throw new JsonSyntaxException("Recipe type can not be an empty string");
|
||||
|
||||
IRecipeFactory factory = recipes.get(new ResourceLocation(type));
|
||||
if (factory == null)
|
||||
throw new JsonSyntaxException("Unknown recipe type: " + type);
|
||||
|
||||
return factory.parse(context, json);
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// INTERNAL
|
||||
//=======================================================
|
||||
|
||||
private static void init()
|
||||
{
|
||||
conditions.clear();
|
||||
ingredients.clear();
|
||||
recipes.clear();
|
||||
|
||||
registerC("forge:mod_loaded", (context, json) -> {
|
||||
String modid = JsonUtils.getString(json, "modid");
|
||||
return () -> Loader.isModLoaded(modid);
|
||||
});
|
||||
registerC("minecraft:item_exists", (context, json) -> {
|
||||
String itemName = context.appendModId(JsonUtils.getString(json, "item"));
|
||||
return () -> ForgeRegistries.ITEMS.containsKey(new ResourceLocation(itemName));
|
||||
});
|
||||
registerC("forge:not", (context, json) -> {
|
||||
BooleanSupplier child = CraftingHelper.getCondition(JsonUtils.getJsonObject(json, "value"), context);
|
||||
return () -> !child.getAsBoolean();
|
||||
});
|
||||
registerC("forge:or", (context, json) -> {
|
||||
JsonArray values = JsonUtils.getJsonArray(json, "values");
|
||||
List<BooleanSupplier> children = Lists.newArrayList();
|
||||
for (JsonElement j : values)
|
||||
{
|
||||
if (!j.isJsonObject())
|
||||
throw new JsonSyntaxException("Or condition values must be an array of JsonObjects");
|
||||
children.add(CraftingHelper.getCondition(j.getAsJsonObject(), context));
|
||||
}
|
||||
return () -> children.stream().anyMatch(BooleanSupplier::getAsBoolean);
|
||||
});
|
||||
registerC("forge:and", (context, json) -> {
|
||||
JsonArray values = JsonUtils.getJsonArray(json, "values");
|
||||
List<BooleanSupplier> children = Lists.newArrayList();
|
||||
for (JsonElement j : values)
|
||||
{
|
||||
if (!j.isJsonObject())
|
||||
throw new JsonSyntaxException("And condition values must be an array of JsonObjects");
|
||||
children.add(CraftingHelper.getCondition(j.getAsJsonObject(), context));
|
||||
}
|
||||
return () -> children.stream().allMatch(c -> c.getAsBoolean());
|
||||
});
|
||||
registerC("forge:false", (context, json) -> {
|
||||
return () -> false;
|
||||
});
|
||||
|
||||
registerR("minecraft:crafting_shaped", (context, json) -> {
|
||||
String group = JsonUtils.getString(json, "group", "");
|
||||
//if (!group.isEmpty() && group.indexOf(':') == -1)
|
||||
// group = context.getModId() + ":" + group;
|
||||
|
||||
Map<Character, Ingredient> ingMap = Maps.newHashMap();
|
||||
for (Entry<String, JsonElement> entry : JsonUtils.getJsonObject(json, "key").entrySet())
|
||||
{
|
||||
if (entry.getKey().length() != 1)
|
||||
throw new JsonSyntaxException("Invalid key entry: '" + entry.getKey() + "' is an invalid symbol (must be 1 character only).");
|
||||
if (" ".equals(entry.getKey()))
|
||||
throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol.");
|
||||
|
||||
ingMap.put(entry.getKey().toCharArray()[0], CraftingHelper.getIngredient(entry.getValue(), context));
|
||||
}
|
||||
ingMap.put(' ', Ingredient.EMPTY);
|
||||
|
||||
JsonArray patternJ = JsonUtils.getJsonArray(json, "pattern");
|
||||
|
||||
if (patternJ.size() == 0)
|
||||
throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed");
|
||||
if (patternJ.size() > 3)
|
||||
throw new JsonSyntaxException("Invalid pattern: too many rows, 3 is maximum");
|
||||
|
||||
String[] pattern = new String[patternJ.size()];
|
||||
for (int x = 0; x < pattern.length; ++x)
|
||||
{
|
||||
String line = JsonUtils.getString(patternJ.get(x), "pattern[" + x + "]");
|
||||
if (line.length() > 3)
|
||||
throw new JsonSyntaxException("Invalid pattern: too many columns, 3 is maximum");
|
||||
if (x > 0 && pattern[0].length() != line.length())
|
||||
throw new JsonSyntaxException("Invalid pattern: each row must be the same width");
|
||||
pattern[x] = line;
|
||||
}
|
||||
|
||||
NonNullList<Ingredient> input = NonNullList.withSize(pattern[0].length() * pattern.length, Ingredient.EMPTY);
|
||||
Set<Character> keys = Sets.newHashSet(ingMap.keySet());
|
||||
keys.remove(' ');
|
||||
|
||||
int x = 0;
|
||||
for (String line : pattern)
|
||||
{
|
||||
for (char chr : line.toCharArray())
|
||||
{
|
||||
Ingredient ing = ingMap.get(chr);
|
||||
if (ing == null)
|
||||
throw new JsonSyntaxException("Pattern references symbol '" + chr + "' but it's not defined in the key");
|
||||
input.set(x++, ing);
|
||||
keys.remove(chr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!keys.isEmpty())
|
||||
throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + keys);
|
||||
|
||||
ItemStack result = CraftingHelper.getItemStack(JsonUtils.getJsonObject(json, "result"), context);
|
||||
return new ShapedRecipes(group, pattern[0].length(), pattern.length, input, result);
|
||||
});
|
||||
registerR("minecraft:crafting_shapeless", (context, json) -> {
|
||||
String group = JsonUtils.getString(json, "group", "");
|
||||
|
||||
NonNullList<Ingredient> ings = NonNullList.create();
|
||||
for (JsonElement ele : JsonUtils.getJsonArray(json, "ingredients"))
|
||||
ings.add(CraftingHelper.getIngredient(ele, context));
|
||||
|
||||
if (ings.isEmpty())
|
||||
throw new JsonParseException("No ingredients for shapeless recipe");
|
||||
if (ings.size() > 9)
|
||||
throw new JsonParseException("Too many ingredients for shapeless recipe");
|
||||
|
||||
ItemStack itemstack = CraftingHelper.getItemStack(JsonUtils.getJsonObject(json, "result"), context);
|
||||
return new ShapelessRecipes(group, itemstack, ings);
|
||||
});
|
||||
registerR("forge:ore_shaped", ShapedOreRecipe::factory);
|
||||
registerR("forge:ore_shapeless", ShapelessOreRecipe::factory);
|
||||
|
||||
registerI("minecraft:item", (context, json) -> Ingredient.fromStacks(CraftingHelper.getItemStackBasic(json, context)));
|
||||
registerI("minecraft:empty", (context, json) -> Ingredient.EMPTY);
|
||||
registerI("minecraft:item_nbt", (context, json) -> new IngredientNBT(CraftingHelper.getItemStack(json, context)));
|
||||
registerI("forge:ore_dict", (context, json) -> new OreIngredient(JsonUtils.getString(json, "ore")));
|
||||
}
|
||||
|
||||
private static void registerC(String name, IConditionFactory fac) {
|
||||
register(new ResourceLocation(name), fac);
|
||||
}
|
||||
private static void registerR(String name, IRecipeFactory fac) {
|
||||
register(new ResourceLocation(name), fac);
|
||||
}
|
||||
private static void registerI(String name, IIngredientFactory fac) {
|
||||
register(new ResourceLocation(name), fac);
|
||||
}
|
||||
|
||||
static void loadFactories(JsonObject json, JsonContext context)
|
||||
{
|
||||
if (json.has("ingredients"))
|
||||
{
|
||||
for (Entry<String, JsonElement> entry : JsonUtils.getJsonObject(json, "ingredients").entrySet())
|
||||
{
|
||||
ResourceLocation key = new ResourceLocation(context.getModId(), entry.getKey());
|
||||
String clsName = JsonUtils.getString(entry.getValue(), "ingredients[" + entry.getValue() + "]");
|
||||
register(key, getClassInstance(clsName, IIngredientFactory.class));
|
||||
}
|
||||
}
|
||||
|
||||
if (json.has("recipes"))
|
||||
{
|
||||
for (Entry<String, JsonElement> entry : JsonUtils.getJsonObject(json, "recipes").entrySet())
|
||||
{
|
||||
ResourceLocation key = new ResourceLocation(context.getModId(), entry.getKey());
|
||||
String clsName = JsonUtils.getString(entry.getValue(), "recipes[" + entry.getValue() + "]");
|
||||
register(key, getClassInstance(clsName, IRecipeFactory.class));
|
||||
}
|
||||
}
|
||||
|
||||
if (json.has("conditions"))
|
||||
{
|
||||
for (Entry<String, JsonElement> entry : JsonUtils.getJsonObject(json, "conditions").entrySet())
|
||||
{
|
||||
ResourceLocation key = new ResourceLocation(context.getModId(), entry.getKey());
|
||||
String clsName = JsonUtils.getString(entry.getValue(), "conditions[" + entry.getValue() + "]");
|
||||
register(key, getClassInstance(clsName, IConditionFactory.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T getClassInstance(String clsName, Class<T> expected)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> cls = Class.forName(clsName);
|
||||
if (!expected.isAssignableFrom(cls))
|
||||
throw new JsonSyntaxException("Class '" + clsName + "' is not an " + expected.getSimpleName());
|
||||
return (T)cls.newInstance();
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
throw new JsonSyntaxException("Could not find " + expected.getSimpleName() + ": " + clsName, e);
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e)
|
||||
{
|
||||
throw new JsonSyntaxException("Could not instantiate " + expected.getSimpleName() + ": " + clsName, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadRecipes(boolean revertFrozen)
|
||||
{
|
||||
//TODO: If this errors in ServerInit it freezes the client at loading world, find a way to pop that up?
|
||||
//TODO: Figure out how to remove recipes, and override them. This relies on cpw to help.
|
||||
//For now this is only done one after mod init, I want to move this to ServerInit and re-do it many times.
|
||||
init();
|
||||
ForgeRegistry<IRecipe> reg = (ForgeRegistry<IRecipe>)ForgeRegistries.RECIPES;
|
||||
//reg.unfreeze();
|
||||
if (DEBUG_LOAD_MINECRAFT)
|
||||
reg.clear();
|
||||
else if (revertFrozen)
|
||||
GameData.revert(RegistryManager.FROZEN, GameData.RECIPES, false);
|
||||
//ModContainer old = Loader.instance().activeModContainer();
|
||||
Loader.instance().setActiveModContainer(null);
|
||||
Loader.instance().getActiveModList().forEach(CraftingHelper::loadFactories);
|
||||
Loader.instance().getActiveModList().forEach(CraftingHelper::loadRecipes);
|
||||
Loader.instance().setActiveModContainer(null);
|
||||
|
||||
GameData.fireRegistryEvents(rl -> rl.equals(GameData.RECIPES));
|
||||
|
||||
//reg.freeze();
|
||||
FMLCommonHandler.instance().resetClientRecipeBook();
|
||||
}
|
||||
|
||||
private static void loadFactories(ModContainer mod)
|
||||
{
|
||||
FileSystem fs = null;
|
||||
BufferedReader reader = null;
|
||||
try
|
||||
{
|
||||
JsonContext ctx = new JsonContext(mod.getModId());
|
||||
Path fPath = null;
|
||||
if (mod.getSource().isFile())
|
||||
{
|
||||
fs = FileSystems.newFileSystem(mod.getSource().toPath(), null);
|
||||
fPath = fs.getPath("/assets/" + ctx.getModId() + "/recipes/_factories.json");
|
||||
}
|
||||
else if (mod.getSource().isDirectory())
|
||||
{
|
||||
fPath = mod.getSource().toPath().resolve("assets/" + ctx.getModId() + "/recipes/_factories.json");
|
||||
}
|
||||
if (fPath != null && Files.exists(fPath))
|
||||
{
|
||||
reader = Files.newBufferedReader(fPath);
|
||||
JsonObject json = JsonUtils.fromJson(GSON, reader, JsonObject.class);
|
||||
loadFactories(json, ctx);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(fs);
|
||||
IOUtils.closeQuietly(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean loadRecipes(ModContainer mod)
|
||||
{
|
||||
JsonContext ctx = new JsonContext(mod.getModId());
|
||||
|
||||
return findFiles(mod, "assets/" + mod.getModId() + "/recipes",
|
||||
root ->
|
||||
{
|
||||
Path fPath = root.resolve("_constants.json");
|
||||
if (fPath != null && Files.exists(fPath))
|
||||
{
|
||||
BufferedReader reader = null;
|
||||
try
|
||||
{
|
||||
reader = Files.newBufferedReader(fPath);
|
||||
JsonObject[] json = JsonUtils.fromJson(GSON, reader, JsonObject[].class);
|
||||
ctx.loadConstants(json);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
FMLLog.log.error("Error loading _constants.json: ", e);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(reader);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
(root, file) ->
|
||||
{
|
||||
Loader.instance().setActiveModContainer(mod);
|
||||
|
||||
String relative = root.relativize(file).toString();
|
||||
if (!"json".equals(FilenameUtils.getExtension(file.toString())) || relative.startsWith("_"))
|
||||
return true;
|
||||
|
||||
String name = FilenameUtils.removeExtension(relative).replaceAll("\\\\", "/");
|
||||
ResourceLocation key = new ResourceLocation(ctx.getModId(), name);
|
||||
|
||||
BufferedReader reader = null;
|
||||
try
|
||||
{
|
||||
reader = Files.newBufferedReader(file);
|
||||
JsonObject json = JsonUtils.fromJson(GSON, reader, JsonObject.class);
|
||||
if (json.has("conditions") && !CraftingHelper.processConditions(JsonUtils.getJsonArray(json, "conditions"), ctx))
|
||||
return true;
|
||||
IRecipe recipe = CraftingHelper.getRecipe(json, ctx);
|
||||
ForgeRegistries.RECIPES.register(recipe.setRegistryName(key));
|
||||
}
|
||||
catch (JsonParseException e)
|
||||
{
|
||||
FMLLog.log.error("Parsing error loading recipe {}", key, e);
|
||||
return false;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
FMLLog.log.error("Couldn't read recipe {} from {}", key, file, e);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(reader);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
true, true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link CraftingHelper#findFiles(ModContainer, String, Function, BiFunction, boolean, boolean)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean findFiles(ModContainer mod, String base, Function<Path, Boolean> preprocessor, BiFunction<Path, Path, Boolean> processor)
|
||||
{
|
||||
return findFiles(mod, base, preprocessor, processor, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link CraftingHelper#findFiles(ModContainer, String, Function, BiFunction, boolean, boolean)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean findFiles(ModContainer mod, String base, Function<Path, Boolean> preprocessor, BiFunction<Path, Path, Boolean> processor, boolean defaultUnfoundRoot)
|
||||
{
|
||||
return findFiles(mod, base, preprocessor, processor, defaultUnfoundRoot, false);
|
||||
}
|
||||
|
||||
public static boolean findFiles(ModContainer mod, String base, Function<Path, Boolean> preprocessor, BiFunction<Path, Path, Boolean> processor, boolean defaultUnfoundRoot, boolean visitAllFiles)
|
||||
{
|
||||
FileSystem fs = null;
|
||||
try
|
||||
{
|
||||
File source = mod.getSource();
|
||||
|
||||
if ("minecraft".equals(mod.getModId()))
|
||||
{
|
||||
if (!DEBUG_LOAD_MINECRAFT)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
URI tmp = CraftingManager.class.getResource("/assets/.mcassetsroot").toURI();
|
||||
source = new File(tmp.resolve("..").getPath());
|
||||
}
|
||||
catch (URISyntaxException e)
|
||||
{
|
||||
FMLLog.log.error("Error finding Minecraft jar: ", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Path root = null;
|
||||
if (source.isFile())
|
||||
{
|
||||
try
|
||||
{
|
||||
fs = FileSystems.newFileSystem(source.toPath(), null);
|
||||
root = fs.getPath("/" + base);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
FMLLog.log.error("Error loading FileSystem from jar: ", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (source.isDirectory())
|
||||
{
|
||||
root = source.toPath().resolve(base);
|
||||
}
|
||||
|
||||
if (root == null || !Files.exists(root))
|
||||
return defaultUnfoundRoot;
|
||||
|
||||
if (preprocessor != null)
|
||||
{
|
||||
Boolean cont = preprocessor.apply(root);
|
||||
if (cont == null || !cont.booleanValue())
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
|
||||
if (processor != null)
|
||||
{
|
||||
Iterator<Path> itr = null;
|
||||
try
|
||||
{
|
||||
itr = Files.walk(root).iterator();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
FMLLog.log.error("Error iterating filesystem for: {}", mod.getModId(), e);
|
||||
return false;
|
||||
}
|
||||
|
||||
while (itr != null && itr.hasNext())
|
||||
{
|
||||
Boolean cont = processor.apply(root, itr.next());
|
||||
|
||||
if (visitAllFiles)
|
||||
{
|
||||
success &= cont != null && cont;
|
||||
}
|
||||
else if (cont == null || !cont)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.common.crafting;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public interface IConditionFactory {
|
||||
BooleanSupplier parse(JsonContext context, JsonObject json);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.common.crafting;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
|
||||
public interface IIngredientFactory
|
||||
{
|
||||
@Nonnull //If you would return null throw JsonSyntaxException to explain why
|
||||
Ingredient parse(JsonContext context, JsonObject json);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.common.crafting;
|
||||
|
||||
import net.minecraft.inventory.ContainerPlayer;
|
||||
import net.minecraft.inventory.ContainerWorkbench;
|
||||
import net.minecraft.inventory.InventoryCraftResult;
|
||||
import net.minecraft.inventory.InventoryCrafting;
|
||||
|
||||
/**
|
||||
* This interface is to be implemented on Container objects.
|
||||
* For GUIs with recipe books, this allows their containers to have
|
||||
* recipe completion and ghost recipes in their craft matrices.
|
||||
*/
|
||||
public interface IRecipeContainer
|
||||
{
|
||||
/**
|
||||
* The crafting result slot of your container, where you take out the crafted item.
|
||||
* The equivalent for {@link ContainerWorkbench} is {@link ContainerWorkbench#craftResult}.
|
||||
* The equivalent for {@link ContainerPlayer} is {@link ContainerPlayer#craftResult}.
|
||||
*/
|
||||
InventoryCraftResult getCraftResult();
|
||||
|
||||
/**
|
||||
* The crafting matrix of your container, where ingredients go for crafting.
|
||||
* The equivalent for {@link ContainerWorkbench} is {@link ContainerWorkbench#craftMatrix}.
|
||||
* The equivalent for {@link ContainerPlayer} is {@link ContainerPlayer#craftMatrix}.
|
||||
*/
|
||||
InventoryCrafting getCraftMatrix();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.common.crafting;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
|
||||
public interface IRecipeFactory {
|
||||
IRecipe parse(JsonContext context, JsonObject json);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.common.crafting;
|
||||
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
|
||||
/**
|
||||
* Used to mark a recipe that shape matters so that the recipe
|
||||
* book and auto crafting picks the correct shape.
|
||||
* Note: These methods can't be named 'getHeight' or 'getWidth' due to obfusication issues.
|
||||
*/
|
||||
public interface IShapedRecipe extends IRecipe
|
||||
{
|
||||
int getRecipeWidth();
|
||||
int getRecipeHeight();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.common.crafting;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
|
||||
public class IngredientNBT extends Ingredient
|
||||
{
|
||||
private final ItemStack stack;
|
||||
protected IngredientNBT(ItemStack stack)
|
||||
{
|
||||
super(stack);
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@Nullable ItemStack input)
|
||||
{
|
||||
if (input == null)
|
||||
return false;
|
||||
//Can't use areItemStacksEqualUsingNBTShareTag because it compares stack size as well
|
||||
return this.stack.getItem() == input.getItem() && this.stack.getItemDamage() == input.getItemDamage() && ItemStack.areItemStackShareTagsEqual(this.stack, input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimple()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.common.crafting;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
|
||||
public class JsonContext
|
||||
{
|
||||
private String modId;
|
||||
private Map<String, Ingredient> constants = Maps.newHashMap();
|
||||
|
||||
public JsonContext(String modId)
|
||||
{
|
||||
this.modId = modId;
|
||||
}
|
||||
|
||||
public String getModId()
|
||||
{
|
||||
return this.modId;
|
||||
}
|
||||
|
||||
public String appendModId(String data)
|
||||
{
|
||||
if (data.indexOf(':') == -1)
|
||||
return modId + ":" + data;
|
||||
return data;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Ingredient getConstant(String name)
|
||||
{
|
||||
return constants.get(name);
|
||||
}
|
||||
|
||||
void loadConstants(JsonObject[] jsons)
|
||||
{
|
||||
for (JsonObject json : jsons)
|
||||
{
|
||||
if (json.has("conditions") && !CraftingHelper.processConditions(json.getAsJsonArray("conditions"), this))
|
||||
continue;
|
||||
if (!json.has("ingredient"))
|
||||
throw new JsonSyntaxException("Constant entry must contain 'ingredient' value");
|
||||
constants.put(JsonUtils.getString(json, "name"), CraftingHelper.getIngredient(json.get("ingredient"), this));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.common.model;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
final class HiddenModelPart implements IModelPart
|
||||
{
|
||||
private final ImmutableList<String> path;
|
||||
|
||||
HiddenModelPart(ImmutableList<String> path)
|
||||
{
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
ImmutableList<String> getPath()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.common.model;
|
||||
|
||||
/*
|
||||
* Represents the part of the model you can refer to, for example: mesh,
|
||||
* skeleton, bone or some separately animated submodel.
|
||||
* Primary function of this interface is the argument type of IModelState.apply.
|
||||
*/
|
||||
public interface IModelPart {}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.common.model;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/*
|
||||
* Represents the dynamic information associated with the model.
|
||||
* Common use case is (possibly interpolated) animation frame.
|
||||
*/
|
||||
public interface IModelState
|
||||
{
|
||||
/*
|
||||
* Returns the transformation that needs to be applied to the specific part of the model.
|
||||
* Coordinate system is determined by the part type.
|
||||
* if no part is provided, global model transformation is returned.
|
||||
*/
|
||||
Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.common.model;
|
||||
|
||||
import javax.vecmath.Matrix4f;
|
||||
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
/*
|
||||
* Replacement interface for ModelRotation to allow custom transformations of vanilla models.
|
||||
* You should probably use TRSRTransformation directly.
|
||||
*/
|
||||
public interface ITransformation
|
||||
{
|
||||
Matrix4f getMatrix();
|
||||
|
||||
EnumFacing rotate(EnumFacing facing);
|
||||
|
||||
int rotate(EnumFacing facing, int vertexIndex);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.common.model;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.collect.UnmodifiableIterator;
|
||||
|
||||
public enum Models
|
||||
{
|
||||
;
|
||||
|
||||
public static IModelPart getHiddenModelPart(ImmutableList<String> path)
|
||||
{
|
||||
return new HiddenModelPart(path);
|
||||
}
|
||||
|
||||
public static UnmodifiableIterator<String> getParts(IModelPart part)
|
||||
{
|
||||
if(part instanceof HiddenModelPart)
|
||||
{
|
||||
return ((HiddenModelPart) part).getPath().iterator();
|
||||
}
|
||||
ImmutableSet<String> ret = ImmutableSet.of();
|
||||
return ret.iterator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,865 @@
|
||||
/*
|
||||
* 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.common.model;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.AxisAngle4f;
|
||||
import javax.vecmath.Matrix3f;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Quat4f;
|
||||
import javax.vecmath.SingularMatrixException;
|
||||
import javax.vecmath.Tuple3f;
|
||||
import javax.vecmath.Tuple4f;
|
||||
import javax.vecmath.Vector3f;
|
||||
import javax.vecmath.Vector4f;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.ItemTransformVec3f;
|
||||
import net.minecraft.client.renderer.block.model.ModelRotation;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.Vec3i;
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/*
|
||||
* Interpolation-friendly affine transformation.
|
||||
* If created with matrix, should successfully decompose it to a composition
|
||||
* of easily interpolatable transformations (translation, first rotation, scale
|
||||
* (with generally speaking different factors for each axis) and second rotation.
|
||||
* If the input matrix is a composition of translation, rotation and scale (in
|
||||
* any order), then the interpolation of the derived primitive transformations
|
||||
* should result in the same transformation as the interpolation of the originals.
|
||||
* Decomposition happens lazily (and is hopefully fast enough), so performance
|
||||
* should be comparable to using Matrix4f directly.
|
||||
* Immutable.
|
||||
*/
|
||||
public final class TRSRTransformation implements IModelState, ITransformation
|
||||
{
|
||||
private final Matrix4f matrix;
|
||||
|
||||
private boolean full;
|
||||
private Vector3f translation;
|
||||
private Quat4f leftRot;
|
||||
private Vector3f scale;
|
||||
private Quat4f rightRot;
|
||||
|
||||
public TRSRTransformation(@Nullable Matrix4f matrix)
|
||||
{
|
||||
if(matrix == null)
|
||||
{
|
||||
this.matrix = identity.matrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.matrix = matrix;
|
||||
}
|
||||
}
|
||||
|
||||
public TRSRTransformation(@Nullable Vector3f translation, @Nullable Quat4f leftRot, @Nullable Vector3f scale, @Nullable Quat4f rightRot)
|
||||
{
|
||||
this.matrix = mul(translation, leftRot, scale, rightRot);
|
||||
this.translation = translation != null ? translation : new Vector3f();
|
||||
this.leftRot = leftRot != null ? leftRot : new Quat4f(0, 0, 0, 1);
|
||||
this.scale = scale != null ? scale : new Vector3f(1, 1, 1);
|
||||
this.rightRot = rightRot!= null ? rightRot : new Quat4f(0, 0, 0, 1);
|
||||
full = true;
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #from(ItemTransformVec3f)} */
|
||||
@Deprecated // TODO: remove / make private
|
||||
@SideOnly(Side.CLIENT)
|
||||
public TRSRTransformation(ItemTransformVec3f transform)
|
||||
{
|
||||
this(toVecmath(transform.translation), quatFromXYZDegrees(toVecmath(transform.rotation)), toVecmath(transform.scale), null);
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #from(ModelRotation)} */
|
||||
@Deprecated // TODO: remove
|
||||
@SideOnly(Side.CLIENT)
|
||||
public TRSRTransformation(ModelRotation rotation)
|
||||
{
|
||||
this(rotation.getMatrix());
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #from(EnumFacing)} */
|
||||
@Deprecated // TODO: remove
|
||||
@SideOnly(Side.CLIENT)
|
||||
public TRSRTransformation(EnumFacing facing)
|
||||
{
|
||||
this(getMatrix(facing));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static TRSRTransformation from(ItemTransformVec3f transform)
|
||||
{
|
||||
return transform.equals(ItemTransformVec3f.DEFAULT) ? identity : new TRSRTransformation(transform);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static TRSRTransformation from(ModelRotation rotation)
|
||||
{
|
||||
return Cache.get(rotation);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static TRSRTransformation from(EnumFacing facing)
|
||||
{
|
||||
return Cache.get(getRotation(facing));
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static Matrix4f getMatrix(EnumFacing facing)
|
||||
{
|
||||
return getRotation(facing).getMatrix();
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static ModelRotation getRotation(EnumFacing facing)
|
||||
{
|
||||
switch (facing)
|
||||
{
|
||||
case DOWN: return ModelRotation.X90_Y0;
|
||||
case UP: return ModelRotation.X270_Y0;
|
||||
case NORTH: return ModelRotation.X0_Y0;
|
||||
case SOUTH: return ModelRotation.X0_Y180;
|
||||
case WEST: return ModelRotation.X0_Y270;
|
||||
case EAST: return ModelRotation.X0_Y90;
|
||||
}
|
||||
throw new IllegalArgumentException(String.valueOf(facing));
|
||||
}
|
||||
|
||||
private static final TRSRTransformation identity;
|
||||
|
||||
static
|
||||
{
|
||||
Matrix4f m = new Matrix4f();
|
||||
m.setIdentity();
|
||||
identity = new TRSRTransformation(m);
|
||||
identity.getLeftRot();
|
||||
}
|
||||
|
||||
public static TRSRTransformation identity()
|
||||
{
|
||||
return identity;
|
||||
}
|
||||
|
||||
public TRSRTransformation compose(TRSRTransformation b)
|
||||
{
|
||||
if (this.isIdentity()) return b;
|
||||
if (b.isIdentity()) return this;
|
||||
Matrix4f m = getMatrix();
|
||||
m.mul(b.getMatrix());
|
||||
return new TRSRTransformation(m);
|
||||
}
|
||||
|
||||
public TRSRTransformation inverse()
|
||||
{
|
||||
if (this.isIdentity()) return this;
|
||||
Matrix4f m = getMatrix();
|
||||
m.invert();
|
||||
return new TRSRTransformation(m);
|
||||
}
|
||||
|
||||
private void genCheck()
|
||||
{
|
||||
if(!full)
|
||||
{
|
||||
Pair<Matrix3f, Vector3f> pair = toAffine(matrix);
|
||||
Triple<Quat4f, Vector3f, Quat4f> triple = svdDecompose(pair.getLeft());
|
||||
this.translation = pair.getRight();
|
||||
this.leftRot = triple.getLeft();
|
||||
this.scale = triple.getMiddle();
|
||||
this.rightRot = triple.getRight();
|
||||
full = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static Quat4f quatFromYXZ(float y, float x, float z)
|
||||
{
|
||||
Quat4f ret = new Quat4f(0, 0, 0, 1), t = new Quat4f();
|
||||
t.set(0, (float)Math.sin(y/2), 0, (float)Math.cos(y/2));
|
||||
ret.mul(t);
|
||||
t.set((float)Math.sin(x/2), 0, 0, (float)Math.cos(x/2));
|
||||
ret.mul(t);
|
||||
t.set(0, 0, (float)Math.sin(z/2), (float)Math.cos(z/2));
|
||||
ret.mul(t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static Quat4f quatFromXYZDegrees(Vector3f xyz)
|
||||
{
|
||||
return quatFromXYZ((float)Math.toRadians(xyz.x), (float)Math.toRadians(xyz.y), (float)Math.toRadians(xyz.z));
|
||||
}
|
||||
|
||||
public static Quat4f quatFromXYZ(Vector3f xyz)
|
||||
{
|
||||
return quatFromXYZ(xyz.x, xyz.y, xyz.z);
|
||||
}
|
||||
|
||||
public static Quat4f quatFromXYZ(float x, float y, float z)
|
||||
{
|
||||
Quat4f ret = new Quat4f(0, 0, 0, 1), t = new Quat4f();
|
||||
t.set((float)Math.sin(x/2), 0, 0, (float)Math.cos(x/2));
|
||||
ret.mul(t);
|
||||
t.set(0, (float)Math.sin(y/2), 0, (float)Math.cos(y/2));
|
||||
ret.mul(t);
|
||||
t.set(0, 0, (float)Math.sin(z/2), (float)Math.cos(z/2));
|
||||
ret.mul(t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static Vector3f toYXZDegrees(Quat4f q)
|
||||
{
|
||||
Vector3f yxz = toYXZ(q);
|
||||
return new Vector3f((float)Math.toDegrees(yxz.x), (float)Math.toDegrees(yxz.y), (float)Math.toDegrees(yxz.z));
|
||||
}
|
||||
|
||||
public static Vector3f toYXZ(Quat4f q)
|
||||
{
|
||||
float w2 = q.w * q.w;
|
||||
float x2 = q.x * q.x;
|
||||
float y2 = q.y * q.y;
|
||||
float z2 = q.z * q.z;
|
||||
float l = w2 + x2 + y2 + z2;
|
||||
float sx = 2 * q.w * q.x - 2 * q.y * q.z;
|
||||
float x = (float)Math.asin(sx / l);
|
||||
if(Math.abs(sx) > .999f * l)
|
||||
{
|
||||
return new Vector3f(
|
||||
x,
|
||||
2 * (float)Math.atan2(q.y, q.w),
|
||||
0
|
||||
);
|
||||
}
|
||||
return new Vector3f(
|
||||
x,
|
||||
(float)Math.atan2(2 * q.x * q.z + 2 * q.y * q.w, w2 - x2 - y2 + z2),
|
||||
(float)Math.atan2(2 * q.x * q.y + 2 * q.w * q.z, w2 - x2 + y2 - z2)
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector3f toXYZDegrees(Quat4f q)
|
||||
{
|
||||
Vector3f xyz = toXYZ(q);
|
||||
return new Vector3f((float)Math.toDegrees(xyz.x), (float)Math.toDegrees(xyz.y), (float)Math.toDegrees(xyz.z));
|
||||
}
|
||||
|
||||
public static Vector3f toXYZ(Quat4f q)
|
||||
{
|
||||
float w2 = q.w * q.w;
|
||||
float x2 = q.x * q.x;
|
||||
float y2 = q.y * q.y;
|
||||
float z2 = q.z * q.z;
|
||||
float l = w2 + x2 + y2 + z2;
|
||||
float sy = 2 * q.w * q.x - 2 * q.y * q.z;
|
||||
float y = (float)Math.asin(sy / l);
|
||||
if(Math.abs(sy) > .999f * l)
|
||||
{
|
||||
return new Vector3f(
|
||||
2 * (float)Math.atan2(q.x, q.w),
|
||||
y,
|
||||
0
|
||||
);
|
||||
}
|
||||
return new Vector3f(
|
||||
(float)Math.atan2(2 * q.y * q.z + 2 * q.x * q.w, w2 - x2 - y2 + z2),
|
||||
y,
|
||||
(float)Math.atan2(2 * q.x * q.y + 2 * q.w * q.z, w2 + x2 - y2 - z2)
|
||||
);
|
||||
}
|
||||
|
||||
public static Matrix4f mul(@Nullable Vector3f translation, @Nullable Quat4f leftRot, @Nullable Vector3f scale, @Nullable Quat4f rightRot)
|
||||
{
|
||||
Matrix4f res = new Matrix4f(), t = new Matrix4f();
|
||||
res.setIdentity();
|
||||
if(leftRot != null)
|
||||
{
|
||||
t.set(leftRot);
|
||||
res.mul(t);
|
||||
}
|
||||
if(scale != null)
|
||||
{
|
||||
t.setIdentity();
|
||||
t.m00 = scale.x;
|
||||
t.m11 = scale.y;
|
||||
t.m22 = scale.z;
|
||||
res.mul(t);
|
||||
}
|
||||
if(rightRot != null)
|
||||
{
|
||||
t.set(rightRot);
|
||||
res.mul(t);
|
||||
}
|
||||
if(translation != null) res.setTranslation(translation);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs SVD decomposition of m, accumulating reflection in the scale (U and V are pure rotations).
|
||||
*/
|
||||
public static Triple<Quat4f, Vector3f, Quat4f> svdDecompose(Matrix3f m)
|
||||
{
|
||||
// determine V by doing 5 steps of Jacobi iteration on MT * M
|
||||
Quat4f u = new Quat4f(0, 0, 0, 1), v = new Quat4f(0, 0, 0, 1), qt = new Quat4f();
|
||||
Matrix3f b = new Matrix3f(m), t = new Matrix3f();
|
||||
t.transpose(m);
|
||||
b.mul(t, b);
|
||||
|
||||
for(int i = 0; i < 5; i++) v.mul(stepJacobi(b));
|
||||
|
||||
v.normalize();
|
||||
t.set(v);
|
||||
b.set(m);
|
||||
b.mul(t);
|
||||
|
||||
// FIXME: this doesn't work correctly for some reason; not crucial, so disabling for now; investigate in the future.
|
||||
//sortSingularValues(b, v);
|
||||
|
||||
Pair<Float, Float> p;
|
||||
|
||||
float ul = 1f;
|
||||
|
||||
p = qrGivensQuat(b.m00, b.m10);
|
||||
qt.set(0, 0, p.getLeft(), p.getRight());
|
||||
u.mul(qt);
|
||||
t.setIdentity();
|
||||
t.m00 = qt.w * qt.w - qt.z * qt.z;
|
||||
t.m11 = t.m00;
|
||||
t.m10 = -2 * qt.z * qt.w;
|
||||
t.m01 = -t.m10;
|
||||
t.m22 = qt.w * qt.w + qt.z * qt.z;
|
||||
ul *= t.m22;
|
||||
b.mul(t, b);
|
||||
|
||||
p = qrGivensQuat(b.m00, b.m20);
|
||||
qt.set(0, -p.getLeft(), 0, p.getRight());
|
||||
u.mul(qt);
|
||||
t.setIdentity();
|
||||
t.m00 = qt.w * qt.w - qt.y * qt.y;
|
||||
t.m22 = t.m00;
|
||||
t.m20 = 2 * qt.y * qt.w;
|
||||
t.m02 = -t.m20;
|
||||
t.m11 = qt.w * qt.w + qt.y * qt.y;
|
||||
ul *= t.m11;
|
||||
b.mul(t, b);
|
||||
|
||||
p = qrGivensQuat(b.m11, b.m21);
|
||||
qt.set(p.getLeft(), 0, 0, p.getRight());
|
||||
u.mul(qt);
|
||||
t.setIdentity();
|
||||
t.m11 = qt.w * qt.w - qt.x * qt.x;
|
||||
t.m22 = t.m11;
|
||||
t.m21 = -2 * qt.x * qt.w;
|
||||
t.m12 = -t.m21;
|
||||
t.m00 = qt.w * qt.w + qt.x * qt.x;
|
||||
ul *= t.m00;
|
||||
b.mul(t, b);
|
||||
|
||||
ul = 1f / ul;
|
||||
u.scale((float)Math.sqrt(ul));
|
||||
|
||||
Vector3f s = new Vector3f(b.m00 * ul, b.m11 * ul, b.m22 * ul);
|
||||
|
||||
return Triple.of(u, s, v);
|
||||
}
|
||||
|
||||
private static float rsqrt(float f)
|
||||
{
|
||||
float f2 = .5f * f;
|
||||
int i = Float.floatToIntBits(f);
|
||||
i = 0x5f3759df - (i >> 1);
|
||||
f = Float.intBitsToFloat(i);
|
||||
f *= 1.5f - f2 * f * f;
|
||||
return f;
|
||||
}
|
||||
|
||||
private static final float eps = 1e-6f;
|
||||
private static final float g = 3f + 2f * (float)Math.sqrt(2);
|
||||
private static final float cs = (float)Math.cos(Math.PI / 8);
|
||||
private static final float ss = (float)Math.sin(Math.PI / 8);
|
||||
private static final float sq2 = 1f / (float)Math.sqrt(2);
|
||||
|
||||
private static Pair<Float, Float> approxGivensQuat(float a11, float a12, float a22)
|
||||
{
|
||||
float ch = 2f * (a11 - a22);
|
||||
float sh = a12;
|
||||
boolean b = g * sh * sh < ch * ch;
|
||||
float w = rsqrt(sh * sh + ch * ch);
|
||||
ch = b ? w * ch : cs;
|
||||
sh = b ? w * sh : ss;
|
||||
return Pair.of(sh, ch);
|
||||
}
|
||||
|
||||
private static final void swapNeg(Matrix3f m, int i, int j)
|
||||
{
|
||||
float[] t = new float[3];
|
||||
m.getColumn(j, t);
|
||||
for(int k = 0; k < 3; k++)
|
||||
{
|
||||
m.setElement(k, j, -m.getElement(k, i));
|
||||
}
|
||||
m.setColumn(i, t);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void sortSingularValues(Matrix3f b, Quat4f v)
|
||||
{
|
||||
float p0 = b.m00 * b.m00 + b.m10 * b.m10 + b.m20 * b.m20;
|
||||
float p1 = b.m01 * b.m01 + b.m11 * b.m11 + b.m21 * b.m21;
|
||||
float p2 = b.m02 * b.m02 + b.m12 * b.m12 + b.m22 * b.m22;
|
||||
Quat4f t = new Quat4f();
|
||||
if(p0 < p1)
|
||||
{
|
||||
swapNeg(b, 0, 1);
|
||||
t.set(0, 0, sq2, sq2);
|
||||
v.mul(t);
|
||||
float f = p0;
|
||||
p0 = p1;
|
||||
p1 = f;
|
||||
}
|
||||
if(p0 < p2)
|
||||
{
|
||||
swapNeg(b, 0, 2);
|
||||
t.set(0, sq2, 0, sq2);
|
||||
v.mul(t);
|
||||
float f = p0;
|
||||
p0 = p2;
|
||||
p2 = f;
|
||||
}
|
||||
if(p1 < p2)
|
||||
{
|
||||
swapNeg(b, 1, 2);
|
||||
t.set(sq2, 0, 0, sq2);
|
||||
v.mul(t);
|
||||
}
|
||||
}
|
||||
|
||||
private static Pair<Float, Float> qrGivensQuat(float a1, float a2)
|
||||
{
|
||||
float p = (float)Math.sqrt(a1 * a1 + a2 * a2);
|
||||
float sh = p > eps ? a2 : 0;
|
||||
float ch = Math.abs(a1) + Math.max(p, eps);
|
||||
if(a1 < 0)
|
||||
{
|
||||
float f = sh;
|
||||
sh = ch;
|
||||
ch = f;
|
||||
}
|
||||
//float w = 1.f / (float)Math.sqrt(ch * ch + sh * sh);
|
||||
float w = rsqrt(ch * ch + sh * sh);
|
||||
ch *= w;
|
||||
sh *= w;
|
||||
return Pair.of(sh, ch);
|
||||
}
|
||||
|
||||
private static Quat4f stepJacobi(Matrix3f m)
|
||||
{
|
||||
Matrix3f t = new Matrix3f();
|
||||
Quat4f qt = new Quat4f(), ret = new Quat4f(0, 0, 0, 1);
|
||||
Pair<Float, Float> p;
|
||||
// 01
|
||||
if(m.m01 * m.m01 + m.m10 * m.m10 > eps)
|
||||
{
|
||||
p = approxGivensQuat(m.m00, .5f * (m.m01 + m.m10), m.m11);
|
||||
qt.set(0, 0, p.getLeft(), p.getRight());
|
||||
//qt.normalize();
|
||||
ret.mul(qt);
|
||||
//t.set(qt);
|
||||
t.setIdentity();
|
||||
t.m00 = qt.w * qt.w - qt.z * qt.z;
|
||||
t.m11 = t.m00;
|
||||
t.m10 = 2 * qt.z * qt.w;
|
||||
t.m01 = -t.m10;
|
||||
t.m22 = qt.w * qt.w + qt.z * qt.z;
|
||||
m.mul(m, t);
|
||||
t.transpose();
|
||||
m.mul(t, m);
|
||||
}
|
||||
// 02
|
||||
if(m.m02 * m.m02 + m.m20 * m.m20 > eps)
|
||||
{
|
||||
p = approxGivensQuat(m.m00, .5f * (m.m02 + m.m20), m.m22);
|
||||
qt.set(0, -p.getLeft(), 0, p.getRight());
|
||||
//qt.normalize();
|
||||
ret.mul(qt);
|
||||
//t.set(qt);
|
||||
t.setIdentity();
|
||||
t.m00 = qt.w * qt.w - qt.y * qt.y;
|
||||
t.m22 = t.m00;
|
||||
t.m20 = -2 * qt.y * qt.w;
|
||||
t.m02 = -t.m20;
|
||||
t.m11 = qt.w * qt.w + qt.y * qt.y;
|
||||
m.mul(m, t);
|
||||
t.transpose();
|
||||
m.mul(t, m);
|
||||
}
|
||||
// 12
|
||||
if(m.m12 * m.m12 + m.m21 * m.m21 > eps)
|
||||
{
|
||||
p = approxGivensQuat(m.m11, .5f * (m.m12 + m.m21), m.m22);
|
||||
qt.set(p.getLeft(), 0, 0, p.getRight());
|
||||
//qt.normalize();
|
||||
ret.mul(qt);
|
||||
//t.set(qt);
|
||||
t.setIdentity();
|
||||
t.m11 = qt.w * qt.w - qt.x * qt.x;
|
||||
t.m22 = t.m11;
|
||||
t.m21 = 2 * qt.x * qt.w;
|
||||
t.m12 = -t.m21;
|
||||
t.m00 = qt.w * qt.w + qt.x * qt.x;
|
||||
m.mul(m, t);
|
||||
t.transpose();
|
||||
m.mul(t, m);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Divides m by m33, sets last row to (0, 0, 0, 1), extracts linear and translation parts
|
||||
*/
|
||||
public static Pair<Matrix3f, Vector3f> toAffine(Matrix4f m)
|
||||
{
|
||||
m.mul(1.f / m.m33);
|
||||
Vector3f trans = new Vector3f(m.m03, m.m13, m.m23);
|
||||
Matrix3f linear = new Matrix3f(m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, m.m21, m.m22);
|
||||
return Pair.of(linear, trans);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't use this if you don't need to, conversion is lossy (second rotation component is lost).
|
||||
*/
|
||||
@Deprecated
|
||||
@SideOnly(Side.CLIENT)
|
||||
public ItemTransformVec3f toItemTransform()
|
||||
{
|
||||
return new ItemTransformVec3f(toLwjgl(toXYZDegrees(getLeftRot())), toLwjgl(getTranslation()), toLwjgl(getScale()));
|
||||
}
|
||||
|
||||
public boolean isIdentity()
|
||||
{
|
||||
return this.equals(identity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matrix4f getMatrix()
|
||||
{
|
||||
return (Matrix4f)matrix.clone();
|
||||
}
|
||||
|
||||
public Vector3f getTranslation()
|
||||
{
|
||||
genCheck();
|
||||
return (Vector3f)translation.clone();
|
||||
}
|
||||
|
||||
public Quat4f getLeftRot()
|
||||
{
|
||||
genCheck();
|
||||
return (Quat4f)leftRot.clone();
|
||||
}
|
||||
|
||||
public Vector3f getScale()
|
||||
{
|
||||
genCheck();
|
||||
return (Vector3f)scale.clone();
|
||||
}
|
||||
|
||||
public Quat4f getRightRot()
|
||||
{
|
||||
genCheck();
|
||||
return (Quat4f)rightRot.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part)
|
||||
{
|
||||
if(part.isPresent())
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumFacing rotate(EnumFacing facing)
|
||||
{
|
||||
return rotate(matrix, facing);
|
||||
}
|
||||
|
||||
public static EnumFacing rotate(Matrix4f matrix, EnumFacing facing)
|
||||
{
|
||||
Vec3i dir = facing.getDirectionVec();
|
||||
Vector4f vec = new Vector4f(dir.getX(), dir.getY(), dir.getZ(), 0);
|
||||
matrix.transform(vec);
|
||||
return EnumFacing.getFacingFromVector(vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
public static boolean isInteger(Matrix4f matrix)
|
||||
{
|
||||
Matrix4f m = new Matrix4f();
|
||||
m.setIdentity();
|
||||
m.m30 = m.m31 = m.m32 = 1;
|
||||
m.m33 = 0;
|
||||
m.mul(matrix, m);
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
for(int j = 0; j < 3; j++)
|
||||
{
|
||||
float v = m.getElement(i, j) / m.getElement(3, j);
|
||||
if(Math.abs(v - Math.round(v)) > 1e-5) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rotate(EnumFacing facing, int vertexIndex)
|
||||
{
|
||||
// FIXME check if this is good enough
|
||||
return vertexIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
genCheck();
|
||||
return MoreObjects.toStringHelper(this.getClass())
|
||||
.add("matrix", matrix)
|
||||
.add("translation", translation)
|
||||
.add("leftRot", leftRot)
|
||||
.add("scale", scale)
|
||||
.add("rightRot", rightRot)
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* convert transformation from assuming center-block system to corner-block system
|
||||
*/
|
||||
public static TRSRTransformation blockCenterToCorner(TRSRTransformation transform)
|
||||
{
|
||||
if (transform.isIdentity()) return transform;
|
||||
|
||||
Matrix4f ret = new Matrix4f(transform.getMatrix()), tmp = new Matrix4f();
|
||||
tmp.setIdentity();
|
||||
tmp.m03 = tmp.m13 = tmp.m23 = .5f;
|
||||
ret.mul(tmp, ret);
|
||||
tmp.m03 = tmp.m13 = tmp.m23 = -.5f;
|
||||
ret.mul(tmp);
|
||||
return new TRSRTransformation(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* convert transformation from assuming corner-block system to center-block system
|
||||
*/
|
||||
public static TRSRTransformation blockCornerToCenter(TRSRTransformation transform)
|
||||
{
|
||||
if (transform.isIdentity()) return transform;
|
||||
|
||||
Matrix4f ret = new Matrix4f(transform.getMatrix()), tmp = new Matrix4f();
|
||||
tmp.setIdentity();
|
||||
tmp.m03 = tmp.m13 = tmp.m23 = -.5f;
|
||||
ret.mul(tmp, ret);
|
||||
tmp.m03 = tmp.m13 = tmp.m23 = .5f;
|
||||
ret.mul(tmp);
|
||||
return new TRSRTransformation(ret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Objects.hashCode(matrix);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
TRSRTransformation other = (TRSRTransformation) obj;
|
||||
return Objects.equals(matrix, other.matrix);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static Vector3f toVecmath(org.lwjgl.util.vector.Vector3f vec)
|
||||
{
|
||||
return new Vector3f(vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static Vector4f toVecmath(org.lwjgl.util.vector.Vector4f vec)
|
||||
{
|
||||
return new Vector4f(vec.x, vec.y, vec.z, vec.w);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static Matrix4f toVecmath(org.lwjgl.util.vector.Matrix4f m)
|
||||
{
|
||||
return new Matrix4f(
|
||||
m.m00, m.m10, m.m20, m.m30,
|
||||
m.m01, m.m11, m.m21, m.m31,
|
||||
m.m02, m.m12, m.m22, m.m32,
|
||||
m.m03, m.m13, m.m23, m.m33);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static org.lwjgl.util.vector.Vector3f toLwjgl(Vector3f vec)
|
||||
{
|
||||
return new org.lwjgl.util.vector.Vector3f(vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static org.lwjgl.util.vector.Vector4f toLwjgl(Vector4f vec)
|
||||
{
|
||||
return new org.lwjgl.util.vector.Vector4f(vec.x, vec.y, vec.z, vec.w);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static org.lwjgl.util.vector.Matrix4f toLwjgl(Matrix4f m)
|
||||
{
|
||||
org.lwjgl.util.vector.Matrix4f r = new org.lwjgl.util.vector.Matrix4f();
|
||||
r.m00 = m.m00;
|
||||
r.m01 = m.m10;
|
||||
r.m02 = m.m20;
|
||||
r.m03 = m.m30;
|
||||
r.m10 = m.m01;
|
||||
r.m11 = m.m11;
|
||||
r.m12 = m.m21;
|
||||
r.m13 = m.m31;
|
||||
r.m20 = m.m02;
|
||||
r.m21 = m.m12;
|
||||
r.m22 = m.m22;
|
||||
r.m23 = m.m32;
|
||||
r.m30 = m.m03;
|
||||
r.m31 = m.m13;
|
||||
r.m32 = m.m23;
|
||||
r.m33 = m.m33;
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Vector3f lerp(Tuple3f from, Tuple3f to, float progress)
|
||||
{
|
||||
Vector3f res = new Vector3f(from);
|
||||
res.interpolate(from, to, progress);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static Vector4f lerp(Tuple4f from, Tuple4f to, float progress)
|
||||
{
|
||||
Vector4f res = new Vector4f(from);
|
||||
res.interpolate(from, to, progress);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static Quat4f slerp(Quat4f from, Quat4f to, float progress)
|
||||
{
|
||||
Quat4f res = new Quat4f();
|
||||
res.interpolate(from, to, progress);
|
||||
return res;
|
||||
}
|
||||
|
||||
public TRSRTransformation slerp(TRSRTransformation that, float progress)
|
||||
{
|
||||
return new TRSRTransformation(
|
||||
lerp(this.getTranslation(), that.getTranslation(), progress),
|
||||
slerp(this.getLeftRot(), that.getLeftRot(), progress),
|
||||
lerp(this.getScale(), that.getScale(), progress),
|
||||
slerp(this.getRightRot(), that.getRightRot(), progress)
|
||||
);
|
||||
}
|
||||
|
||||
private static final EnumMap<EnumFacing, TRSRTransformation> vanillaUvTransformLocalToGlobal = Maps.newEnumMap(EnumFacing.class);
|
||||
private static final EnumMap<EnumFacing, TRSRTransformation> vanillaUvTransformGlobalToLocal = Maps.newEnumMap(EnumFacing.class);
|
||||
|
||||
static
|
||||
{
|
||||
vanillaUvTransformLocalToGlobal.put(EnumFacing.SOUTH, identity);
|
||||
Quat4f tmp = new Quat4f();
|
||||
tmp.set(new AxisAngle4f(0, 1, 0, (float)Math.toRadians(90)));
|
||||
vanillaUvTransformLocalToGlobal.put(EnumFacing.EAST, new TRSRTransformation(null, new Quat4f(tmp), null, null));
|
||||
tmp.set(new AxisAngle4f(0, 1, 0, (float)Math.toRadians(-90)));
|
||||
vanillaUvTransformLocalToGlobal.put(EnumFacing.WEST, new TRSRTransformation(null, new Quat4f(tmp), null, null));
|
||||
tmp.set(new AxisAngle4f(0, 1, 0, (float)Math.toRadians(180)));
|
||||
vanillaUvTransformLocalToGlobal.put(EnumFacing.NORTH, new TRSRTransformation(null, new Quat4f(tmp), null, null));
|
||||
tmp.set(new AxisAngle4f(1, 0, 0, (float)Math.toRadians(-90)));
|
||||
vanillaUvTransformLocalToGlobal.put(EnumFacing.UP, new TRSRTransformation(null, new Quat4f(tmp), null, null));
|
||||
tmp.set(new AxisAngle4f(1, 0, 0, (float)Math.toRadians(90)));
|
||||
vanillaUvTransformLocalToGlobal.put(EnumFacing.DOWN, new TRSRTransformation(null, new Quat4f(tmp), null, null));
|
||||
|
||||
for(EnumFacing side : EnumFacing.values())
|
||||
{
|
||||
vanillaUvTransformGlobalToLocal.put(side, vanillaUvTransformLocalToGlobal.get(side).inverse());
|
||||
}
|
||||
}
|
||||
|
||||
public static TRSRTransformation getVanillaUvTransformLocalToGlobal(EnumFacing side)
|
||||
{
|
||||
return vanillaUvTransformLocalToGlobal.get(side);
|
||||
}
|
||||
|
||||
public static TRSRTransformation getVanillaUvTransformGlobalToLocal(EnumFacing side)
|
||||
{
|
||||
return vanillaUvTransformGlobalToLocal.get(side);
|
||||
}
|
||||
|
||||
public TRSRTransformation getUVLockTransform(EnumFacing originalSide)
|
||||
{
|
||||
EnumFacing newSide = rotate(originalSide);
|
||||
try
|
||||
{
|
||||
return blockCenterToCorner(vanillaUvTransformGlobalToLocal.get(originalSide).compose(blockCornerToCenter(this.inverse())).compose(vanillaUvTransformLocalToGlobal.get(newSide)));
|
||||
}
|
||||
catch(SingularMatrixException e)
|
||||
{
|
||||
return new TRSRTransformation(null, null, new Vector3f(0, 0, 0), null);
|
||||
}
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
private static final class Cache
|
||||
{
|
||||
private static final Map<ModelRotation, TRSRTransformation> rotations = new EnumMap<>(ModelRotation.class);
|
||||
|
||||
static
|
||||
{
|
||||
rotations.put(ModelRotation.X0_Y0, identity());
|
||||
}
|
||||
|
||||
static TRSRTransformation get(ModelRotation rotation)
|
||||
{
|
||||
return rotations.computeIfAbsent(rotation, r -> new TRSRTransformation(ForgeHooksClient.getMatrix(r)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* 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.common.model.animation;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.animation.Event;
|
||||
import net.minecraftforge.common.animation.ITimeValue;
|
||||
import net.minecraftforge.common.animation.TimeValues;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
import net.minecraftforge.common.util.JsonUtils;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class AnimationStateMachine implements IAnimationStateMachine
|
||||
{
|
||||
private final ImmutableMap<String, ITimeValue> parameters;
|
||||
private final ImmutableMap<String, IClip> clips;
|
||||
private final ImmutableList<String> states;
|
||||
private final ImmutableMultimap<String, String> transitions;
|
||||
@SerializedName("start_state")
|
||||
private final String startState;
|
||||
|
||||
private transient boolean shouldHandleSpecialEvents;
|
||||
private transient String currentStateName;
|
||||
private transient IClip currentState;
|
||||
private transient float lastPollTime;
|
||||
|
||||
private static final LoadingCache<Triple<? extends IClip, Float, Float>, Pair<IModelState, Iterable<Event>>> clipCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(100)
|
||||
.expireAfterWrite(100, TimeUnit.MILLISECONDS)
|
||||
.build(new CacheLoader<Triple<? extends IClip, Float, Float>, Pair<IModelState, Iterable<Event>>>()
|
||||
{
|
||||
@Override
|
||||
public Pair<IModelState, Iterable<Event>> load(Triple<? extends IClip, Float, Float> key) throws Exception
|
||||
{
|
||||
return Clips.apply(key.getLeft(), key.getMiddle(), key.getRight());
|
||||
}
|
||||
});
|
||||
|
||||
@Deprecated
|
||||
public AnimationStateMachine(ImmutableMap<String, ITimeValue> parameters, ImmutableMap<String, IClip> clips, ImmutableList<String> states, ImmutableMap<String, String> transitions, String startState)
|
||||
{
|
||||
this(parameters, clips, states, ImmutableMultimap.copyOf(Multimaps.newSetMultimap(Maps.transformValues(transitions, ImmutableSet::of), Sets::newHashSet)), startState);
|
||||
}
|
||||
|
||||
public AnimationStateMachine(ImmutableMap<String, ITimeValue> parameters, ImmutableMap<String, IClip> clips, ImmutableList<String> states, ImmutableMultimap<String, String> transitions, String startState)
|
||||
{
|
||||
this.parameters = parameters;
|
||||
this.clips = clips;
|
||||
this.states = states;
|
||||
this.transitions = transitions;
|
||||
this.startState = startState;
|
||||
}
|
||||
|
||||
/**
|
||||
* post-loading initialization hook.
|
||||
*/
|
||||
void initialize()
|
||||
{
|
||||
if(parameters == null)
|
||||
{
|
||||
throw new JsonParseException("Animation State Machine should contain \"parameters\" key.");
|
||||
}
|
||||
if(clips == null)
|
||||
{
|
||||
throw new JsonParseException("Animation State Machine should contain \"clips\" key.");
|
||||
}
|
||||
if(states == null)
|
||||
{
|
||||
throw new JsonParseException("Animation State Machine should contain \"states\" key.");
|
||||
}
|
||||
if(transitions == null)
|
||||
{
|
||||
throw new JsonParseException("Animation State Machine should contain \"transitions\" key.");
|
||||
}
|
||||
shouldHandleSpecialEvents = true;
|
||||
lastPollTime = Float.NEGATIVE_INFINITY;
|
||||
// setting the starting state
|
||||
IClip state = clips.get(startState);
|
||||
if(!clips.containsKey(startState) || !states.contains(startState))
|
||||
{
|
||||
throw new IllegalStateException("unknown state: " + startState);
|
||||
}
|
||||
currentStateName = startState;
|
||||
currentState = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<IModelState, Iterable<Event>> apply(float time)
|
||||
{
|
||||
if(lastPollTime == Float.NEGATIVE_INFINITY)
|
||||
{
|
||||
lastPollTime = time;
|
||||
}
|
||||
Pair<IModelState, Iterable<Event>> pair = clipCache.getUnchecked(Triple.of(currentState, lastPollTime, time));
|
||||
lastPollTime = time;
|
||||
boolean shouldFilter = false;
|
||||
if(shouldHandleSpecialEvents)
|
||||
{
|
||||
for(Event event : ImmutableList.copyOf(pair.getRight()).reverse())
|
||||
{
|
||||
if(event.event().startsWith("!"))
|
||||
{
|
||||
shouldFilter = true;
|
||||
if(event.event().startsWith("!transition:"))
|
||||
{
|
||||
String newState = event.event().substring("!transition:".length());
|
||||
transition(newState);
|
||||
}
|
||||
else
|
||||
{
|
||||
FMLLog.log.error("Unknown special event \"{}\", ignoring.", event.event());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!shouldFilter)
|
||||
{
|
||||
return pair;
|
||||
}
|
||||
return Pair.of(pair.getLeft(), Iterables.filter(pair.getRight(), new Predicate<Event>()
|
||||
{
|
||||
@Override
|
||||
public boolean apply(Event event)
|
||||
{
|
||||
return !event.event().startsWith("!");
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(String newState)
|
||||
{
|
||||
IClip nc = clips.get(newState);
|
||||
if(!clips.containsKey(newState) || !states.contains(newState))
|
||||
{
|
||||
throw new IllegalStateException("unknown state: " + newState);
|
||||
}
|
||||
if(!transitions.containsEntry(currentStateName, newState))
|
||||
{
|
||||
throw new IllegalArgumentException("no transition from current clip \"" + currentStateName + "\" to the clip \"" + newState + "\" found.");
|
||||
}
|
||||
currentStateName = newState;
|
||||
currentState = nc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentState()
|
||||
{
|
||||
return currentStateName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shouldHandleSpecialEvents(boolean value)
|
||||
{
|
||||
shouldHandleSpecialEvents = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a new instance if AnimationStateMachine at specified location, with specified custom parameters.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static IAnimationStateMachine load(IResourceManager manager, ResourceLocation location, ImmutableMap<String, ITimeValue> customParameters)
|
||||
{
|
||||
try (IResource resource = manager.getResource(location))
|
||||
{
|
||||
ClipResolver clipResolver = new ClipResolver();
|
||||
ParameterResolver parameterResolver = new ParameterResolver(customParameters);
|
||||
Clips.CommonClipTypeAdapterFactory.INSTANCE.setClipResolver(clipResolver);
|
||||
TimeValues.CommonTimeValueTypeAdapterFactory.INSTANCE.setValueResolver(parameterResolver);
|
||||
AnimationStateMachine asm = asmGson.fromJson(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8), AnimationStateMachine.class);
|
||||
clipResolver.asm = asm;
|
||||
parameterResolver.asm = asm;
|
||||
asm.initialize();
|
||||
//String json = asmGson.toJson(asm);
|
||||
//System.out.println(location + ": " + json);
|
||||
return asm;
|
||||
}
|
||||
catch(IOException | JsonParseException e)
|
||||
{
|
||||
FMLLog.log.error("Exception loading Animation State Machine {}, skipping", location, e);
|
||||
return missing;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Clips.CommonClipTypeAdapterFactory.INSTANCE.setClipResolver(null);
|
||||
TimeValues.CommonTimeValueTypeAdapterFactory.INSTANCE.setValueResolver(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static final AnimationStateMachine missing = new AnimationStateMachine(
|
||||
ImmutableMap.<String, ITimeValue>of(),
|
||||
ImmutableMap.of("missingno", (IClip)Clips.IdentityClip.INSTANCE),
|
||||
ImmutableList.of("missingno"),
|
||||
ImmutableMultimap.<String, String>of(),
|
||||
"missingno");
|
||||
|
||||
static
|
||||
{
|
||||
missing.initialize();
|
||||
}
|
||||
|
||||
public static AnimationStateMachine getMissing()
|
||||
{
|
||||
return missing;
|
||||
}
|
||||
|
||||
private static final class ClipResolver implements Function<String, IClip>
|
||||
{
|
||||
private AnimationStateMachine asm;
|
||||
|
||||
@Override
|
||||
public IClip apply(String name)
|
||||
{
|
||||
return asm.clips.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ParameterResolver implements Function<String, ITimeValue>
|
||||
{
|
||||
private final ImmutableMap<String, ITimeValue> customParameters;
|
||||
private AnimationStateMachine asm;
|
||||
|
||||
public ParameterResolver(ImmutableMap<String, ITimeValue> customParameters)
|
||||
{
|
||||
this.customParameters = customParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITimeValue apply(String name)
|
||||
{
|
||||
if(asm.parameters.containsKey(name))
|
||||
{
|
||||
return asm.parameters.get(name);
|
||||
}
|
||||
return customParameters.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Gson asmGson = new GsonBuilder()
|
||||
.registerTypeAdapter(ImmutableList.class, JsonUtils.ImmutableListTypeAdapter.INSTANCE)
|
||||
.registerTypeAdapter(ImmutableMap.class, JsonUtils.ImmutableMapTypeAdapter.INSTANCE)
|
||||
.registerTypeAdapterFactory(Clips.CommonClipTypeAdapterFactory.INSTANCE)
|
||||
//.registerTypeAdapterFactory(ClipProviders.CommonClipProviderTypeAdapterFactory.INSTANCE)
|
||||
.registerTypeAdapterFactory(TimeValues.CommonTimeValueTypeAdapterFactory.INSTANCE)
|
||||
.registerTypeAdapterFactory(TransitionsAdapterFactory.INSTANCE)
|
||||
.setPrettyPrinting()
|
||||
.enableComplexMapKeySerialization()
|
||||
.disableHtmlEscaping()
|
||||
.create();
|
||||
|
||||
private enum TransitionsAdapterFactory implements TypeAdapterFactory
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type)
|
||||
{
|
||||
if(type.getRawType() != ImmutableMultimap.class || !(type.getType() instanceof ParameterizedType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final Type[] typeArguments = ((ParameterizedType) type.getType()).getActualTypeArguments();
|
||||
if(typeArguments.length != 2 || typeArguments[0] != String.class || typeArguments[1] != String.class)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final TypeAdapter<Map<String, Collection<String>>> mapAdapter = gson.getAdapter(new TypeToken<Map<String, Collection<String>>>(){});
|
||||
final TypeAdapter<Collection<String>> collectionAdapter = gson.getAdapter(new TypeToken<Collection<String>>(){});
|
||||
return (TypeAdapter<T>)new TypeAdapter<ImmutableMultimap<String, String>>()
|
||||
{
|
||||
@Override
|
||||
public void write(JsonWriter out, ImmutableMultimap<String, String> value) throws IOException
|
||||
{
|
||||
mapAdapter.write(out, value.asMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableMultimap<String, String> read(JsonReader in) throws IOException
|
||||
{
|
||||
ImmutableMultimap.Builder<String, String> builder = ImmutableMultimap.builder();
|
||||
in.beginObject();
|
||||
while(in.hasNext())
|
||||
{
|
||||
String key = in.nextName();
|
||||
switch(in.peek())
|
||||
{
|
||||
case STRING:
|
||||
builder.put(key, in.nextString());
|
||||
break;
|
||||
case BEGIN_ARRAY:
|
||||
builder.putAll(key, collectionAdapter.read(in));
|
||||
break;
|
||||
default:
|
||||
throw new JsonParseException("Expected String or Array, got " + in.peek());
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
return builder.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.common.model.animation;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class CapabilityAnimation
|
||||
{
|
||||
@CapabilityInject(IAnimationStateMachine.class)
|
||||
public static Capability<IAnimationStateMachine> ANIMATION_CAPABILITY = null;
|
||||
|
||||
public static void register()
|
||||
{
|
||||
CapabilityManager.INSTANCE.register(IAnimationStateMachine.class, new Capability.IStorage<IAnimationStateMachine>()
|
||||
{
|
||||
@Override
|
||||
public NBTBase writeNBT(Capability<IAnimationStateMachine> capability, IAnimationStateMachine instance, EnumFacing side)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(Capability<IAnimationStateMachine> capability, IAnimationStateMachine instance, EnumFacing side, NBTBase nbt) {}
|
||||
}, AnimationStateMachine::getMissing);
|
||||
}
|
||||
|
||||
public static class DefaultItemAnimationCapabilityProvider implements ICapabilityProvider
|
||||
{
|
||||
private final IAnimationStateMachine asm;
|
||||
|
||||
public DefaultItemAnimationCapabilityProvider(IAnimationStateMachine asm)
|
||||
{
|
||||
this.asm = asm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
return capability == ANIMATION_CAPABILITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
if(capability == ANIMATION_CAPABILITY)
|
||||
{
|
||||
return ANIMATION_CAPABILITY.cast(asm);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* 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.common.model.animation;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||
import net.minecraft.util.IStringSerializable;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.IModel;
|
||||
import net.minecraftforge.client.model.ModelLoaderRegistry;
|
||||
import net.minecraftforge.common.animation.Event;
|
||||
import net.minecraftforge.common.animation.ITimeValue;
|
||||
import net.minecraftforge.common.model.IModelPart;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.function.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import java.util.Optional;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Various implementations of IClip, and utility methods.
|
||||
*/
|
||||
public final class Clips
|
||||
{
|
||||
/**
|
||||
* Clip that does nothing.
|
||||
*/
|
||||
public static enum IdentityClip implements IClip, IStringSerializable
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public IJointClip apply(IJoint joint)
|
||||
{
|
||||
return JointClips.IdentityJointClip.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Event> pastEvents(float lastPollTime, float time)
|
||||
{
|
||||
return ImmutableSet.<Event>of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "identity";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the clip from the model.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static IClip getModelClipNode(ResourceLocation modelLocation, String clipName)
|
||||
{
|
||||
IModel model = ModelLoaderRegistry.getModelOrMissing(modelLocation);
|
||||
Optional<? extends IClip> clip = model.getClip(clipName);
|
||||
if (clip.isPresent())
|
||||
{
|
||||
return new ModelClip(clip.get(), modelLocation, clipName);
|
||||
}
|
||||
FMLLog.log.error("Unable to find clip {} in the model {}", clipName, modelLocation);
|
||||
// FIXME: missing clip?
|
||||
return new ModelClip(IdentityClip.INSTANCE, modelLocation, clipName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for model clips; useful for debugging and serialization;
|
||||
*/
|
||||
public static final class ModelClip implements IClip
|
||||
{
|
||||
private final IClip childClip;
|
||||
private final ResourceLocation modelLocation;
|
||||
private final String clipName;
|
||||
|
||||
public ModelClip(IClip childClip, ResourceLocation modelLocation, String clipName)
|
||||
{
|
||||
this.childClip = childClip;
|
||||
this.modelLocation = modelLocation;
|
||||
this.clipName = clipName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IJointClip apply(IJoint joint)
|
||||
{
|
||||
return childClip.apply(joint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Event> pastEvents(float lastPollTime, float time)
|
||||
{
|
||||
return childClip.pastEvents(lastPollTime, time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(modelLocation, clipName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ModelClip other = (ModelClip) obj;
|
||||
return Objects.equal(modelLocation, other.modelLocation) && Objects.equal(clipName, other.clipName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clip with custom parameterization of the time.
|
||||
*/
|
||||
public static final class TimeClip implements IClip
|
||||
{
|
||||
private final IClip childClip;
|
||||
private final ITimeValue time;
|
||||
|
||||
public TimeClip(IClip childClip, ITimeValue time)
|
||||
{
|
||||
this.childClip = childClip;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IJointClip apply(final IJoint joint)
|
||||
{
|
||||
return new IJointClip()
|
||||
{
|
||||
private final IJointClip parent = childClip.apply(joint);
|
||||
@Override
|
||||
public TRSRTransformation apply(float time)
|
||||
{
|
||||
return parent.apply(TimeClip.this.time.apply(time));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Event> pastEvents(float lastPollTime, float time)
|
||||
{
|
||||
return childClip.pastEvents(this.time.apply(lastPollTime), this.time.apply(time));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(childClip, time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
TimeClip other = (TimeClip) obj;
|
||||
return Objects.equal(childClip, other.childClip) && Objects.equal(time, other.time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spherical linear blend between 2 clips.
|
||||
*/
|
||||
public static final class SlerpClip implements IClip
|
||||
{
|
||||
private final IClip from;
|
||||
private final IClip to;
|
||||
private final ITimeValue input;
|
||||
private final ITimeValue progress;
|
||||
|
||||
public SlerpClip(IClip from, IClip to, ITimeValue input, ITimeValue progress)
|
||||
{
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.input = input;
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IJointClip apply(IJoint joint)
|
||||
{
|
||||
IJointClip fromClip = from.apply(joint);
|
||||
IJointClip toClip = to.apply(joint);
|
||||
return blendClips(joint, fromClip, toClip, input, progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Event> pastEvents(float lastPollTime, float time)
|
||||
{
|
||||
float clipLastPollTime = input.apply(lastPollTime);
|
||||
float clipTime = input.apply(time);
|
||||
return Iterables.mergeSorted(ImmutableSet.of(
|
||||
from.pastEvents(clipLastPollTime, clipTime),
|
||||
to.pastEvents(clipLastPollTime, clipTime)
|
||||
), Ordering.<Event>natural());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(from, to, input, progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
SlerpClip other = (SlerpClip) obj;
|
||||
return Objects.equal(from, other.from) &&
|
||||
Objects.equal(to, other.to) &&
|
||||
Objects.equal(input, other.input) &&
|
||||
Objects.equal(progress, other.progress);
|
||||
}
|
||||
}
|
||||
|
||||
/*public static class AdditiveLerpClip implements IClip
|
||||
{
|
||||
private final IClip base;
|
||||
private final IClip add;
|
||||
private final IParameter progress;
|
||||
|
||||
public AdditiveLerpClip(IClip base, IClip add, IParameter progress)
|
||||
{
|
||||
this.base = base;
|
||||
this.add = add;
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
public IJointClip apply(IJoint joint)
|
||||
{
|
||||
throw new NotImplementedException("AdditiveLerpClip.apply");
|
||||
}
|
||||
}*/
|
||||
|
||||
private static IJointClip blendClips(final IJoint joint, final IJointClip fromClip, final IJointClip toClip, final ITimeValue input, final ITimeValue progress)
|
||||
{
|
||||
return new IJointClip()
|
||||
{
|
||||
@Override
|
||||
public TRSRTransformation apply(float time)
|
||||
{
|
||||
float clipTime = input.apply(time);
|
||||
return fromClip.apply(clipTime).slerp(toClip.apply(clipTime), MathHelper.clamp(progress.apply(time), 0, 1));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* IModelState wrapper for a Clip, sampled at specified time.
|
||||
*/
|
||||
public static Pair<IModelState, Iterable<Event>> apply(final IClip clip, final float lastPollTime, final float time)
|
||||
{
|
||||
return Pair.<IModelState, Iterable<Event>>of(new IModelState()
|
||||
{
|
||||
@Override
|
||||
public Optional<TRSRTransformation> apply(Optional<? extends IModelPart> part)
|
||||
{
|
||||
if(!part.isPresent() || !(part.get() instanceof IJoint))
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
IJoint joint = (IJoint)part.get();
|
||||
// TODO: Cache clip application?
|
||||
TRSRTransformation jointTransform = clip.apply(joint).apply(time).compose(joint.getInvBindPose());
|
||||
Optional<? extends IJoint> parent = joint.getParent();
|
||||
while(parent.isPresent())
|
||||
{
|
||||
TRSRTransformation parentTransform = clip.apply(parent.get()).apply(time);
|
||||
jointTransform = parentTransform.compose(jointTransform);
|
||||
parent = parent.get().getParent();
|
||||
}
|
||||
return Optional.of(jointTransform);
|
||||
}
|
||||
}, clip.pastEvents(lastPollTime, time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clip + Event, triggers when parameter becomes non-negative.
|
||||
*/
|
||||
public static final class TriggerClip implements IClip
|
||||
{
|
||||
private final IClip clip;
|
||||
private final ITimeValue parameter;
|
||||
private final String event;
|
||||
|
||||
public TriggerClip(IClip clip, ITimeValue parameter, String event)
|
||||
{
|
||||
this.clip = clip;
|
||||
this.parameter = parameter;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IJointClip apply(IJoint joint)
|
||||
{
|
||||
return clip.apply(joint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Event> pastEvents(float lastPollTime, float time)
|
||||
{
|
||||
if(parameter.apply(lastPollTime) < 0 && parameter.apply(time) >= 0)
|
||||
{
|
||||
return Iterables.mergeSorted(ImmutableSet.of(
|
||||
clip.pastEvents(lastPollTime, time),
|
||||
ImmutableSet.of(new Event(event, 0))
|
||||
), Ordering.<Event>natural());
|
||||
}
|
||||
return clip.pastEvents(lastPollTime, time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference to another clip.
|
||||
* Should only exist during debugging.
|
||||
*/
|
||||
public static final class ClipReference implements IClip, IStringSerializable
|
||||
{
|
||||
private final String clipName;
|
||||
private final Function<String, IClip> clipResolver;
|
||||
private IClip clip;
|
||||
|
||||
public ClipReference(String clipName, Function<String, IClip> clipResolver)
|
||||
{
|
||||
this.clipName = clipName;
|
||||
this.clipResolver = clipResolver;
|
||||
}
|
||||
|
||||
private void resolve()
|
||||
{
|
||||
if(clip == null)
|
||||
{
|
||||
if(clipResolver != null)
|
||||
{
|
||||
clip = clipResolver.apply(clipName);
|
||||
}
|
||||
if(clip == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Couldn't resolve clip " + clipName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IJointClip apply(final IJoint joint)
|
||||
{
|
||||
resolve();
|
||||
return clip.apply(joint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Event> pastEvents(float lastPollTime, float time)
|
||||
{
|
||||
resolve();
|
||||
return clip.pastEvents(lastPollTime, time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return clipName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
resolve();
|
||||
return clip.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ClipReference other = (ClipReference) obj;
|
||||
resolve();
|
||||
other.resolve();
|
||||
return Objects.equal(clip, other.clip);
|
||||
}
|
||||
}
|
||||
|
||||
public static enum CommonClipTypeAdapterFactory implements TypeAdapterFactory
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
private final ThreadLocal<Function<String, IClip>> clipResolver = new ThreadLocal<Function<String, IClip>>();
|
||||
|
||||
public void setClipResolver(@Nullable Function<String, IClip> clipResolver)
|
||||
{
|
||||
this.clipResolver.set(clipResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type)
|
||||
{
|
||||
if(type.getRawType() != IClip.class)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final TypeAdapter<ITimeValue> parameterAdapter = gson.getAdapter(ITimeValue.class);
|
||||
|
||||
return (TypeAdapter<T>)new TypeAdapter<IClip>()
|
||||
{
|
||||
@Override
|
||||
public void write(JsonWriter out, IClip clip) throws IOException
|
||||
{
|
||||
// IdentityClip + ClipReference
|
||||
if(clip instanceof IStringSerializable)
|
||||
{
|
||||
out.value("#" + ((IStringSerializable)clip).getName());
|
||||
return;
|
||||
}
|
||||
else if(clip instanceof TimeClip)
|
||||
{
|
||||
out.beginArray();
|
||||
out.value("apply");
|
||||
TimeClip timeClip = (TimeClip)clip;
|
||||
write(out, timeClip.childClip);
|
||||
parameterAdapter.write(out, timeClip.time);
|
||||
out.endArray();
|
||||
return;
|
||||
}
|
||||
else if(clip instanceof SlerpClip)
|
||||
{
|
||||
out.beginArray();
|
||||
out.value("slerp");
|
||||
SlerpClip slerpClip = (SlerpClip)clip;
|
||||
write(out, slerpClip.from);
|
||||
write(out, slerpClip.to);
|
||||
parameterAdapter.write(out, slerpClip.input);
|
||||
parameterAdapter.write(out, slerpClip.progress);
|
||||
out.endArray();
|
||||
return;
|
||||
}
|
||||
else if(clip instanceof TriggerClip)
|
||||
{
|
||||
out.beginArray();
|
||||
out.value("trigger_positive");
|
||||
TriggerClip triggerClip = (TriggerClip)clip;
|
||||
write(out, triggerClip.clip);
|
||||
parameterAdapter.write(out, triggerClip.parameter);
|
||||
out.value(triggerClip.event);
|
||||
out.endArray();
|
||||
return;
|
||||
}
|
||||
else if(clip instanceof ModelClip)
|
||||
{
|
||||
ModelClip modelClip = (ModelClip)clip;
|
||||
out.value(modelClip.modelLocation + "@" + modelClip.clipName);
|
||||
return;
|
||||
}
|
||||
// TODO custom clip writing?
|
||||
throw new NotImplementedException("unknown Clip to json: " + clip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IClip read(JsonReader in) throws IOException
|
||||
{
|
||||
switch(in.peek())
|
||||
{
|
||||
case BEGIN_ARRAY:
|
||||
in.beginArray();
|
||||
String type = in.nextString();
|
||||
IClip clip;
|
||||
// TimeClip
|
||||
if("apply".equals(type))
|
||||
{
|
||||
clip = new TimeClip(read(in), parameterAdapter.read(in));
|
||||
}
|
||||
else if("slerp".equals(type))
|
||||
{
|
||||
clip = new SlerpClip(read(in), read(in), parameterAdapter.read(in), parameterAdapter.read(in));
|
||||
}
|
||||
else if("trigger_positive".equals(type))
|
||||
{
|
||||
clip = new TriggerClip(read(in), parameterAdapter.read(in), in.nextString());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException("Unknown Clip type \"" + type + "\"");
|
||||
}
|
||||
in.endArray();
|
||||
return clip;
|
||||
case STRING:
|
||||
String string = in.nextString();
|
||||
// IdentityClip
|
||||
if(string.equals("#identity"))
|
||||
{
|
||||
return IdentityClip.INSTANCE;
|
||||
}
|
||||
// Clip reference
|
||||
if(string.startsWith("#"))
|
||||
{
|
||||
return new ClipReference(string.substring(1), clipResolver.get());
|
||||
}
|
||||
// ModelClip
|
||||
else
|
||||
{
|
||||
int at = string.lastIndexOf('@');
|
||||
String location = string.substring(0, at);
|
||||
String clipName = string.substring(at + 1, string.length());
|
||||
ResourceLocation model;
|
||||
if(location.indexOf('#') != -1)
|
||||
{
|
||||
model = new ModelResourceLocation(location);
|
||||
}
|
||||
else
|
||||
{
|
||||
model = new ResourceLocation(location);
|
||||
}
|
||||
return getModelClipNode(model, clipName);
|
||||
}
|
||||
default:
|
||||
throw new IOException("expected Clip, got " + in.peek());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.common.model.animation;
|
||||
|
||||
import net.minecraftforge.common.animation.Event;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
/**
|
||||
* State machine representing the model animation.
|
||||
*/
|
||||
public interface IAnimationStateMachine
|
||||
{
|
||||
/**
|
||||
* Sample the state and events at the current time.
|
||||
* Event iterable will contain all events that happened from the last invocation of this method, from most to least recent.
|
||||
* Event offset is relative to the previous event, and for the first event it's relative to the current time.
|
||||
*/
|
||||
Pair<IModelState, Iterable<Event>> apply(float time);
|
||||
|
||||
/**
|
||||
* Transition to a new state.
|
||||
*/
|
||||
void transition(String newState);
|
||||
|
||||
/**
|
||||
* Get current state name.
|
||||
*/
|
||||
String currentState();
|
||||
|
||||
/**
|
||||
* Set to true if the machine should handle special events that come from the clips (they start with '!').
|
||||
* Right now only implemented event is "!transition:<state_name>".
|
||||
* Default value is true.
|
||||
*/
|
||||
void shouldHandleSpecialEvents(boolean value);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.common.model.animation;
|
||||
|
||||
import net.minecraftforge.common.animation.Event;
|
||||
|
||||
|
||||
/**
|
||||
* Clip for a rigged model.
|
||||
*/
|
||||
public interface IClip
|
||||
{
|
||||
IJointClip apply(IJoint joint);
|
||||
|
||||
Iterable<Event> pastEvents(float lastPollTime, float time);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.common.model.animation;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import net.minecraftforge.common.model.IModelPart;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
|
||||
/**
|
||||
* Model part that's a part of the hierarchical skeleton.
|
||||
*/
|
||||
public interface IJoint extends IModelPart
|
||||
{
|
||||
TRSRTransformation getInvBindPose();
|
||||
|
||||
Optional<? extends IJoint> getParent();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.common.model.animation;
|
||||
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
|
||||
/**
|
||||
* Returns Local joint pose; animation clip for specific model part.
|
||||
*/
|
||||
public interface IJointClip
|
||||
{
|
||||
TRSRTransformation apply(float time);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.common.model.animation;
|
||||
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
|
||||
/**
|
||||
* Various implementations of IJointClip.
|
||||
*/
|
||||
public final class JointClips
|
||||
{
|
||||
public static enum IdentityJointClip implements IJointClip
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public TRSRTransformation apply(float time)
|
||||
{
|
||||
return TRSRTransformation.identity();
|
||||
}
|
||||
}
|
||||
|
||||
public static class NodeJointClip implements IJointClip
|
||||
{
|
||||
private final IJoint child;
|
||||
private final IClip clip;
|
||||
|
||||
public NodeJointClip(IJoint joint, IClip clip)
|
||||
{
|
||||
this.child = joint;
|
||||
this.clip = clip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TRSRTransformation apply(float time)
|
||||
{
|
||||
return clip.apply(child).apply(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.common.network;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraftforge.common.DimensionManager;
|
||||
import net.minecraftforge.common.network.ForgeMessage.DimensionRegisterMessage;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
|
||||
public class DimensionMessageHandler extends SimpleChannelInboundHandler<ForgeMessage.DimensionRegisterMessage>{
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, DimensionRegisterMessage msg) throws Exception
|
||||
{
|
||||
if (!DimensionManager.isDimensionRegistered(msg.dimensionId))
|
||||
{
|
||||
DimensionManager.registerDimension(msg.dimensionId, DimensionType.valueOf(msg.providerId));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
|
||||
{
|
||||
FMLLog.log.error("DimensionMessageHandler exception", cause);
|
||||
super.exceptionCaught(ctx, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.common.network;
|
||||
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
public class FluidIdRegistryMessageHandler extends SimpleChannelInboundHandler<ForgeMessage.FluidIdMapMessage> {
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ForgeMessage.FluidIdMapMessage msg) throws Exception
|
||||
{
|
||||
FluidRegistry.initFluidIDs(msg.fluidIds, msg.defaultFluids);
|
||||
}
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
|
||||
{
|
||||
FMLLog.log.error("FluidIdRegistryMessageHandler exception", cause);
|
||||
super.exceptionCaught(ctx, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.common.network;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.network.ByteBufUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public abstract class ForgeMessage {
|
||||
public static class DimensionRegisterMessage extends ForgeMessage {
|
||||
/** The dimension ID to register on client */
|
||||
int dimensionId;
|
||||
/** The provider ID to register with dimension on client */
|
||||
String providerId;
|
||||
|
||||
public DimensionRegisterMessage(){}
|
||||
public DimensionRegisterMessage(int dimensionId, String providerId)
|
||||
{
|
||||
this.dimensionId = dimensionId;
|
||||
this.providerId = providerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
void toBytes(ByteBuf bytes)
|
||||
{
|
||||
bytes.writeInt(this.dimensionId);
|
||||
byte[] data = this.providerId.getBytes(StandardCharsets.UTF_8);
|
||||
bytes.writeShort(data.length);
|
||||
bytes.writeBytes(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
void fromBytes(ByteBuf bytes)
|
||||
{
|
||||
dimensionId = bytes.readInt();
|
||||
byte[] data = new byte[bytes.readShort()];
|
||||
bytes.readBytes(data);
|
||||
providerId = new String(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FluidIdMapMessage extends ForgeMessage {
|
||||
BiMap<Fluid, Integer> fluidIds = HashBiMap.create();
|
||||
Set<String> defaultFluids = Sets.newHashSet();
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
void toBytes(ByteBuf bytes)
|
||||
{
|
||||
Map<Fluid, Integer> ids = FluidRegistry.getRegisteredFluidIDs();
|
||||
bytes.writeInt(ids.size());
|
||||
for (Map.Entry<Fluid, Integer> entry : ids.entrySet())
|
||||
{
|
||||
ByteBufUtils.writeUTF8String(bytes,entry.getKey().getName());
|
||||
bytes.writeInt(entry.getValue());
|
||||
}
|
||||
for (Map.Entry<Fluid, Integer> entry : ids.entrySet())
|
||||
{
|
||||
String defaultName = FluidRegistry.getDefaultFluidName(entry.getKey());
|
||||
ByteBufUtils.writeUTF8String(bytes, defaultName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void fromBytes(ByteBuf bytes)
|
||||
{
|
||||
int listSize = bytes.readInt();
|
||||
for (int i = 0; i < listSize; i++) {
|
||||
String fluidName = ByteBufUtils.readUTF8String(bytes);
|
||||
int fluidId = bytes.readInt();
|
||||
fluidIds.put(FluidRegistry.getFluid(fluidName), fluidId);
|
||||
}
|
||||
// do we have a defaults list?
|
||||
|
||||
if (bytes.isReadable())
|
||||
{
|
||||
for (int i = 0; i < listSize; i++)
|
||||
{
|
||||
defaultFluids.add(ByteBufUtils.readUTF8String(bytes));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FMLLog.log.info("Legacy server message contains no default fluid list - there may be problems with fluids");
|
||||
defaultFluids.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract void toBytes(ByteBuf bytes);
|
||||
abstract void fromBytes(ByteBuf bytes);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.common.network;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import net.minecraftforge.common.ForgeModContainer;
|
||||
import net.minecraftforge.fml.common.network.FMLEmbeddedChannel;
|
||||
import net.minecraftforge.fml.common.network.FMLOutboundHandler;
|
||||
import net.minecraftforge.fml.common.network.FMLOutboundHandler.OutboundTarget;
|
||||
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class ForgeNetworkHandler
|
||||
{
|
||||
private static EnumMap<Side, FMLEmbeddedChannel> channelPair;
|
||||
|
||||
public static void registerChannel(ForgeModContainer forgeModContainer, Side side)
|
||||
{
|
||||
channelPair = NetworkRegistry.INSTANCE.newChannel(forgeModContainer, "FORGE", new ForgeRuntimeCodec());
|
||||
if (side == Side.CLIENT)
|
||||
{
|
||||
addClientHandlers();
|
||||
}
|
||||
|
||||
FMLEmbeddedChannel serverChannel = channelPair.get(Side.SERVER);
|
||||
serverChannel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(OutboundTarget.NOWHERE);
|
||||
String handlerName = serverChannel.findChannelHandlerNameForType(ForgeRuntimeCodec.class);
|
||||
serverChannel.pipeline().addAfter(handlerName, "ServerToClientConnection", new ServerToClientConnectionEstablishedHandler());
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
private static void addClientHandlers()
|
||||
{
|
||||
FMLEmbeddedChannel clientChannel = channelPair.get(Side.CLIENT);
|
||||
String handlerName = clientChannel.findChannelHandlerNameForType(ForgeRuntimeCodec.class);
|
||||
clientChannel.pipeline().addAfter(handlerName, "DimensionHandler", new DimensionMessageHandler());
|
||||
clientChannel.pipeline().addAfter(handlerName, "FluidIdRegistryHandler", new FluidIdRegistryMessageHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.common.network;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import net.minecraftforge.fml.common.network.FMLIndexedMessageToMessageCodec;
|
||||
|
||||
public class ForgeRuntimeCodec extends FMLIndexedMessageToMessageCodec<ForgeMessage> {
|
||||
public ForgeRuntimeCodec()
|
||||
{
|
||||
addDiscriminator(1, ForgeMessage.DimensionRegisterMessage.class);
|
||||
addDiscriminator(2, ForgeMessage.FluidIdMapMessage.class);
|
||||
}
|
||||
@Override
|
||||
public void encodeInto(ChannelHandlerContext ctx, ForgeMessage msg, ByteBuf target) throws Exception
|
||||
{
|
||||
msg.toBytes(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decodeInto(ChannelHandlerContext ctx, ByteBuf source, ForgeMessage msg)
|
||||
{
|
||||
msg.fromBytes(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.common.network;
|
||||
|
||||
import net.minecraftforge.fml.common.network.NetworkHandshakeEstablished;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
public class ServerToClientConnectionEstablishedHandler extends ChannelInboundHandlerAdapter {
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception
|
||||
{
|
||||
if (evt instanceof NetworkHandshakeEstablished)
|
||||
{
|
||||
ctx.writeAndFlush(new ForgeMessage.FluidIdMapMessage());
|
||||
return;
|
||||
}
|
||||
// pass it forward
|
||||
ctx.fireUserEventTriggered(evt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.common.property;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.properties.IProperty;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
|
||||
import net.minecraft.block.state.BlockStateContainer.StateImplementation;
|
||||
|
||||
public class ExtendedBlockState extends BlockStateContainer
|
||||
{
|
||||
private final ImmutableSet<IUnlistedProperty<?>> unlistedProperties;
|
||||
|
||||
public ExtendedBlockState(Block blockIn, IProperty<?>[] properties, IUnlistedProperty<?>[] unlistedProperties)
|
||||
{
|
||||
super(blockIn, properties, buildUnlistedMap(unlistedProperties));
|
||||
ImmutableSet.Builder<IUnlistedProperty<?>> builder = ImmutableSet.builder();
|
||||
for(IUnlistedProperty<?> property : unlistedProperties)
|
||||
{
|
||||
builder.add(property);
|
||||
}
|
||||
this.unlistedProperties = builder.build();
|
||||
}
|
||||
|
||||
public Collection<IUnlistedProperty<?>> getUnlistedProperties()
|
||||
{
|
||||
return unlistedProperties;
|
||||
}
|
||||
|
||||
private static ImmutableMap<IUnlistedProperty<?>, Optional<?>> buildUnlistedMap(IUnlistedProperty<?>[] unlistedProperties)
|
||||
{
|
||||
ImmutableMap.Builder<IUnlistedProperty<?>, Optional<?>> builder = ImmutableMap.builder();
|
||||
for(IUnlistedProperty<?> p : unlistedProperties)
|
||||
{
|
||||
builder.put(p, Optional.empty());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
protected StateImplementation createState(@Nonnull Block block, @Nonnull ImmutableMap<IProperty<?>, Comparable<?>> properties, @Nullable ImmutableMap<IUnlistedProperty<?>, Optional<?>> unlistedProperties)
|
||||
{
|
||||
if (unlistedProperties == null || unlistedProperties.isEmpty()) return super.createState(block, properties, unlistedProperties);
|
||||
return new ExtendedStateImplementation(block, properties, unlistedProperties, null, null);
|
||||
}
|
||||
|
||||
protected static class ExtendedStateImplementation extends StateImplementation implements IExtendedBlockState
|
||||
{
|
||||
private final ImmutableMap<IUnlistedProperty<?>, Optional<?>> unlistedProperties;
|
||||
private IBlockState cleanState;
|
||||
|
||||
protected ExtendedStateImplementation(Block block, ImmutableMap<IProperty<?>, Comparable<?>> properties, ImmutableMap<IUnlistedProperty<?>, Optional<?>> unlistedProperties, @Nullable ImmutableTable<IProperty<?>, Comparable<?>, IBlockState> table, IBlockState clean)
|
||||
{
|
||||
super(block, properties, table);
|
||||
this.unlistedProperties = unlistedProperties;
|
||||
this.cleanState = clean == null ? this : clean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a version of this BlockState with the given Property now set to the given value
|
||||
*/
|
||||
@Override
|
||||
@Nonnull
|
||||
public <T extends Comparable<T>, V extends T> IBlockState withProperty(@Nonnull IProperty<T> property, @Nonnull V value)
|
||||
{
|
||||
IBlockState clean = super.withProperty(property, value);
|
||||
if (clean == this.cleanState) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (this == this.cleanState)
|
||||
{ // no dynamic properties present, looking up in the normal table
|
||||
return clean;
|
||||
}
|
||||
|
||||
return new ExtendedStateImplementation(getBlock(), clean.getProperties(), unlistedProperties, ((StateImplementation)clean).getPropertyValueTable(), this.cleanState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> IExtendedBlockState withProperty(IUnlistedProperty<V> property, @Nullable V value)
|
||||
{
|
||||
Optional<?> oldValue = unlistedProperties.get(property);
|
||||
if (oldValue == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot set unlisted property " + property + " as it does not exist in " + getBlock().getBlockState());
|
||||
}
|
||||
if (Objects.equals(oldValue.orElse(null), value))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
if (!property.isValid(value))
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot set unlisted property " + property + " to " + value + " on block " + Block.REGISTRY.getNameForObject(getBlock()) + ", it is not an allowed value");
|
||||
}
|
||||
boolean clean = true;
|
||||
ImmutableMap.Builder<IUnlistedProperty<?>, Optional<?>> builder = ImmutableMap.builder();
|
||||
for (Map.Entry<IUnlistedProperty<?>, Optional<?>> entry : unlistedProperties.entrySet())
|
||||
{
|
||||
IUnlistedProperty<?> key = entry.getKey();
|
||||
Optional<?> newValue = key.equals(property) ? Optional.ofNullable(value) : entry.getValue();
|
||||
if (newValue.isPresent()) clean = false;
|
||||
builder.put(key, newValue);
|
||||
}
|
||||
if (clean)
|
||||
{ // no dynamic properties, lookup normal state
|
||||
return (IExtendedBlockState) cleanState;
|
||||
}
|
||||
return new ExtendedStateImplementation(getBlock(), getProperties(), builder.build(), propertyValueTable, this.cleanState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<IUnlistedProperty<?>> getUnlistedNames()
|
||||
{
|
||||
return unlistedProperties.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <V> V getValue(IUnlistedProperty<V> property)
|
||||
{
|
||||
Optional<?> value = unlistedProperties.get(property);
|
||||
if (value == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot get unlisted property " + property + " as it does not exist in " + getBlock().getBlockState());
|
||||
}
|
||||
return property.getType().cast(value.orElse(null));
|
||||
}
|
||||
|
||||
public ImmutableMap<IUnlistedProperty<?>, Optional<?>> getUnlistedProperties()
|
||||
{
|
||||
return unlistedProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState getClean()
|
||||
{
|
||||
return cleanState;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.common.property;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
|
||||
import java.util.Optional;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public interface IExtendedBlockState extends IBlockState
|
||||
{
|
||||
Collection<IUnlistedProperty<?>> getUnlistedNames();
|
||||
|
||||
<V>V getValue(IUnlistedProperty<V> property);
|
||||
|
||||
<V>IExtendedBlockState withProperty(IUnlistedProperty<V> property, V value);
|
||||
|
||||
ImmutableMap<IUnlistedProperty<?>, Optional<?>> getUnlistedProperties();
|
||||
|
||||
IBlockState getClean();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.common.property;
|
||||
|
||||
public interface IUnlistedProperty<V>
|
||||
{
|
||||
String getName();
|
||||
|
||||
boolean isValid(V value);
|
||||
|
||||
Class<V> getType();
|
||||
|
||||
String valueToString(V value);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.common.property;
|
||||
|
||||
import net.minecraft.block.properties.IProperty;
|
||||
import net.minecraft.block.properties.PropertyBool;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
|
||||
public class Properties
|
||||
{
|
||||
/**
|
||||
* Property indicating if the model should be rendered in the static renderer or in the TESR. AnimationTESR sets it to false.
|
||||
*/
|
||||
public static final PropertyBool StaticProperty = PropertyBool.create("static");
|
||||
|
||||
/**
|
||||
* Property holding the IModelState used for animating the model in the TESR.
|
||||
*/
|
||||
public static final IUnlistedProperty<IModelState> AnimationProperty = new IUnlistedProperty<IModelState>()
|
||||
{
|
||||
@Override
|
||||
public String getName() { return "forge_animation"; }
|
||||
@Override
|
||||
public boolean isValid(IModelState state) { return true; }
|
||||
@Override
|
||||
public Class<IModelState> getType() { return IModelState.class; }
|
||||
@Override
|
||||
public String valueToString(IModelState state) { return state.toString(); }
|
||||
};
|
||||
|
||||
public static <V extends Comparable<V>> IUnlistedProperty<V> toUnlisted(IProperty<V> property)
|
||||
{
|
||||
return new PropertyAdapter<V>(property);
|
||||
}
|
||||
|
||||
public static class PropertyAdapter<V extends Comparable<V>> implements IUnlistedProperty<V>
|
||||
{
|
||||
private final IProperty<V> parent;
|
||||
|
||||
public PropertyAdapter(IProperty<V> parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return parent.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(V value)
|
||||
{
|
||||
return parent.getAllowedValues().contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<V> getType()
|
||||
{
|
||||
return parent.getValueClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(V value)
|
||||
{
|
||||
return parent.getName(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.common.property;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
public class PropertyFloat implements IUnlistedProperty<Float>
|
||||
{
|
||||
private final String name;
|
||||
private final Predicate<Float> validator;
|
||||
|
||||
public PropertyFloat(String name)
|
||||
{
|
||||
this(name, Predicates.alwaysTrue());
|
||||
}
|
||||
|
||||
public PropertyFloat(String name, float min, float max)
|
||||
{
|
||||
this(name, Range.closed(min, max));
|
||||
}
|
||||
|
||||
public PropertyFloat(String name, Predicate<Float> validator)
|
||||
{
|
||||
this.name = name;
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(Float value)
|
||||
{
|
||||
return validator.apply(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Float> getType()
|
||||
{
|
||||
return Float.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(Float value)
|
||||
{
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.registry.ForgeRegistries;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a captured snapshot of a block which will not change
|
||||
* automatically.
|
||||
* <p>
|
||||
* Unlike Block, which only one object can exist per coordinate, BlockSnapshot
|
||||
* can exist multiple times for any given Block.
|
||||
*/
|
||||
public class BlockSnapshot
|
||||
{
|
||||
private static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("forge.debugBlockSnapshot", "false"));
|
||||
|
||||
private final BlockPos pos;
|
||||
private final int dimId;
|
||||
@Nullable
|
||||
private IBlockState replacedBlock;
|
||||
private int flag;
|
||||
@Nullable
|
||||
private final NBTTagCompound nbt;
|
||||
@Nullable
|
||||
private WeakReference<World> world;
|
||||
private final ResourceLocation registryName;
|
||||
private final int meta;
|
||||
|
||||
public BlockSnapshot(World world, BlockPos pos, IBlockState state)
|
||||
{
|
||||
this(world, pos, state, getTileNBT(world.getTileEntity(pos)));
|
||||
}
|
||||
|
||||
public BlockSnapshot(World world, BlockPos pos, IBlockState state, @Nullable NBTTagCompound nbt)
|
||||
{
|
||||
this.setWorld(world);
|
||||
this.dimId = world.provider.getDimension();
|
||||
this.pos = pos.toImmutable();
|
||||
this.setReplacedBlock(state);
|
||||
this.registryName = state.getBlock().getRegistryName();
|
||||
this.meta = state.getBlock().getMetaFromState(state);
|
||||
this.setFlag(3);
|
||||
this.nbt = nbt;
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.printf("Created BlockSnapshot - [World: %s ][Location: %d,%d,%d ][Block: %s ][Meta: %d ]", world.getWorldInfo().getWorldName(), pos.getX(), pos.getY(), pos.getZ(), getRegistryName(), getMeta());
|
||||
}
|
||||
}
|
||||
|
||||
public BlockSnapshot(World world, BlockPos pos, IBlockState state, int flag)
|
||||
{
|
||||
this(world, pos, state);
|
||||
this.setFlag(flag);
|
||||
}
|
||||
|
||||
/** @deprecated use {@link #BlockSnapshot(int, BlockPos, ResourceLocation, int, int, NBTTagCompound)} */
|
||||
@Deprecated
|
||||
public BlockSnapshot(int dimension, BlockPos pos, String modId, String blockName, int meta, int flag, @Nullable NBTTagCompound nbt)
|
||||
{
|
||||
this(dimension, pos, new ResourceLocation(modId, blockName), meta, flag, nbt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw constructor designed for serialization usages.
|
||||
*/
|
||||
public BlockSnapshot(int dimension, BlockPos pos, ResourceLocation registryName, int meta, int flag, @Nullable NBTTagCompound nbt)
|
||||
{
|
||||
this.dimId = dimension;
|
||||
this.pos = pos.toImmutable();
|
||||
this.setFlag(flag);
|
||||
this.registryName = registryName;
|
||||
this.meta = meta;
|
||||
this.nbt = nbt;
|
||||
}
|
||||
|
||||
public static BlockSnapshot getBlockSnapshot(World world, BlockPos pos)
|
||||
{
|
||||
return new BlockSnapshot(world, pos, world.getBlockState(pos));
|
||||
}
|
||||
|
||||
public static BlockSnapshot getBlockSnapshot(World world, BlockPos pos, int flag)
|
||||
{
|
||||
return new BlockSnapshot(world, pos, world.getBlockState(pos), flag);
|
||||
}
|
||||
|
||||
public static BlockSnapshot readFromNBT(NBTTagCompound tag)
|
||||
{
|
||||
return new BlockSnapshot(
|
||||
tag.getInteger("dimension"),
|
||||
new BlockPos(tag.getInteger("posX"), tag.getInteger("posY"), tag.getInteger("posZ")),
|
||||
new ResourceLocation(tag.getString("blockMod"), tag.getString("blockName")),
|
||||
tag.getInteger("metadata"),
|
||||
tag.getInteger("flag"),
|
||||
tag.getBoolean("hasTE") ? tag.getCompoundTag("tileEntity") : null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static NBTTagCompound getTileNBT(@Nullable TileEntity te)
|
||||
{
|
||||
if (te == null) return null;
|
||||
NBTTagCompound nbt = new NBTTagCompound();
|
||||
te.writeToNBT(nbt);
|
||||
return nbt;
|
||||
}
|
||||
|
||||
public IBlockState getCurrentBlock()
|
||||
{
|
||||
return getWorld().getBlockState(getPos());
|
||||
}
|
||||
|
||||
public World getWorld()
|
||||
{
|
||||
World world = this.world != null ? this.world.get() : null;
|
||||
if (world == null)
|
||||
{
|
||||
world = FMLCommonHandler.instance().getMinecraftServerInstance().getWorld(getDimId());
|
||||
this.world = new WeakReference<World>(world);
|
||||
}
|
||||
return world;
|
||||
}
|
||||
|
||||
public IBlockState getReplacedBlock()
|
||||
{
|
||||
if (this.replacedBlock == null)
|
||||
{
|
||||
this.replacedBlock = ForgeRegistries.BLOCKS.getValue(getRegistryName()).getStateFromMeta(getMeta());
|
||||
}
|
||||
return this.replacedBlock;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TileEntity getTileEntity()
|
||||
{
|
||||
return getNbt() != null ? TileEntity.create(getWorld(), getNbt()) : null;
|
||||
}
|
||||
|
||||
public boolean restore()
|
||||
{
|
||||
return restore(false);
|
||||
}
|
||||
|
||||
public boolean restore(boolean force)
|
||||
{
|
||||
return restore(force, true);
|
||||
}
|
||||
|
||||
public boolean restore(boolean force, boolean notifyNeighbors)
|
||||
{
|
||||
return restoreToLocation(getWorld(), getPos(), force, notifyNeighbors);
|
||||
}
|
||||
|
||||
public boolean restoreToLocation(World world, BlockPos pos, boolean force, boolean notifyNeighbors)
|
||||
{
|
||||
IBlockState current = getCurrentBlock();
|
||||
IBlockState replaced = getReplacedBlock();
|
||||
|
||||
if (current.getBlock() != replaced.getBlock() || current.getBlock().getMetaFromState(current) != replaced.getBlock().getMetaFromState(replaced))
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
world.setBlockState(pos, replaced, notifyNeighbors ? 3 : 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
world.setBlockState(pos, replaced, notifyNeighbors ? 3 : 2);
|
||||
world.notifyBlockUpdate(pos, current, replaced, notifyNeighbors ? 3 : 2);
|
||||
|
||||
TileEntity te = null;
|
||||
if (getNbt() != null)
|
||||
{
|
||||
te = world.getTileEntity(pos);
|
||||
if (te != null)
|
||||
{
|
||||
te.readFromNBT(getNbt());
|
||||
te.markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.printf("Restored BlockSnapshot with data [World: %s ][Location: %d,%d,%d ][Meta: %d ][Block: %s ][TileEntity: %s ][force: %s ][notifyNeighbors: %s]", world.getWorldInfo().getWorldName(), pos.getX(), pos.getY(), pos.getZ(), replaced.getBlock().getMetaFromState(replaced), replaced.getBlock().delegate.name(), te, force, notifyNeighbors);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeToNBT(NBTTagCompound compound)
|
||||
{
|
||||
compound.setString("blockMod", getRegistryName().getResourceDomain());
|
||||
compound.setString("blockName", getRegistryName().getResourcePath());
|
||||
compound.setInteger("posX", getPos().getX());
|
||||
compound.setInteger("posY", getPos().getY());
|
||||
compound.setInteger("posZ", getPos().getZ());
|
||||
compound.setInteger("flag", getFlag());
|
||||
compound.setInteger("dimension", getDimId());
|
||||
compound.setInteger("metadata", getMeta());
|
||||
|
||||
compound.setBoolean("hasTE", getNbt() != null);
|
||||
|
||||
if (getNbt() != null)
|
||||
{
|
||||
compound.setTag("tileEntity", getNbt());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
final BlockSnapshot other = (BlockSnapshot) obj;
|
||||
if (this.getMeta() != other.getMeta())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this.getDimId() != other.getDimId())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!this.getPos().equals(other.getPos()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!this.getRegistryName().equals(other.getRegistryName()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.getNbt(), other.getNbt()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 7;
|
||||
hash = 73 * hash + this.getMeta();
|
||||
hash = 73 * hash + this.getDimId();
|
||||
hash = 73 * hash + this.getPos().hashCode();
|
||||
hash = 73 * hash + this.getRegistryName().hashCode();
|
||||
hash = 73 * hash + Objects.hashCode(this.getNbt());
|
||||
return hash;
|
||||
}
|
||||
|
||||
public BlockPos getPos() { return pos; }
|
||||
|
||||
public int getDimId() { return dimId; }
|
||||
|
||||
public void setReplacedBlock(IBlockState replacedBlock) { this.replacedBlock = replacedBlock; }
|
||||
|
||||
public int getFlag() { return flag; }
|
||||
|
||||
public void setFlag(int flag) { this.flag = flag; }
|
||||
|
||||
@Nullable
|
||||
public NBTTagCompound getNbt() { return nbt; }
|
||||
|
||||
public void setWorld(World world) { this.world = new WeakReference<World>(world); }
|
||||
|
||||
public ResourceLocation getRegistryName() { return registryName; }
|
||||
|
||||
public int getMeta() { return meta; }
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
// Sorter to load nearby chunks first
|
||||
public class ChunkCoordComparator implements java.util.Comparator<ChunkPos>
|
||||
{
|
||||
private int x;
|
||||
private int z;
|
||||
|
||||
public ChunkCoordComparator(EntityPlayerMP entityplayer)
|
||||
{
|
||||
x = (int) entityplayer.posX >> 4;
|
||||
z = (int) entityplayer.posZ >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ChunkPos a, ChunkPos b)
|
||||
{
|
||||
if (a.equals(b))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Subtract current position to set center point
|
||||
int ax = a.x - this.x;
|
||||
int az = a.z - this.z;
|
||||
int bx = b.x - this.x;
|
||||
int bz = b.z - this.z;
|
||||
int result = ((ax - bx) * (ax + bx)) + ((az - bz) * (az + bz));
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ax < 0)
|
||||
{
|
||||
if (bx < 0)
|
||||
{
|
||||
return bz - az;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bx < 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return az - bz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.datafix.DataFixer;
|
||||
import net.minecraft.util.datafix.FixTypes;
|
||||
import net.minecraft.util.datafix.IDataWalker;
|
||||
import net.minecraft.util.datafix.IFixType;
|
||||
import net.minecraft.util.datafix.IFixableData;
|
||||
|
||||
public class CompoundDataFixer extends DataFixer
|
||||
{
|
||||
private final ModFixs vanilla;
|
||||
private final Map<String, ModFixs> modFixers = Maps.newHashMap();
|
||||
private final Map<IFixType, List<IDataWalker>> walkers = Maps.newHashMap();
|
||||
|
||||
public CompoundDataFixer(DataFixer vanilla)
|
||||
{
|
||||
super(0);
|
||||
this.vanilla = init("minecraft", vanilla.version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound process(IFixType type, NBTTagCompound nbt)
|
||||
{
|
||||
final Map<String, Integer>versions = getVersions(nbt);
|
||||
final int mcversion = versions.get("minecraft") == null ? -1 : versions.get("minecraft");
|
||||
final IDataFixerData holder = new IDataFixerData()
|
||||
{
|
||||
@Override
|
||||
public NBTTagCompound process(IFixType type, NBTTagCompound nbt, int version)
|
||||
{
|
||||
for (Entry<String, ModFixs> e : modFixers.entrySet())
|
||||
{
|
||||
// This is a potential performance hot spot. As it walks all the data for all
|
||||
// of the fixers... But with the vanilla api there isn't a way to pass down
|
||||
// the mod specific version numbers, so redundant.. but not hacky...
|
||||
//Actually, this wont work as the data walkers take versions into account...
|
||||
ModFixs fixer = e.getValue();
|
||||
int ver = getVersion(e.getKey());
|
||||
if (ver < fixer.version)
|
||||
{
|
||||
for (IFixableData fix : fixer.getFixes(type))
|
||||
{
|
||||
if (fix.getFixVersion() > ver)
|
||||
nbt = fix.fixTagCompound(nbt);
|
||||
}
|
||||
|
||||
for (IDataWalker walker : getWalkers(type))
|
||||
nbt = walker.process(this, nbt, version); //We pass in the holder, in case a walker wants to know a mod version
|
||||
}
|
||||
}
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion(String mod)
|
||||
{
|
||||
Integer ret = versions.get(mod);
|
||||
return ret == null ? -1 : ret;
|
||||
}
|
||||
};
|
||||
return holder.process(type, nbt, mcversion);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated //MODDERS DO NOT CALL DIRECTLY! Only use from DataWalker!
|
||||
public NBTTagCompound process(IFixType type, NBTTagCompound nbt, int mcversion)
|
||||
{
|
||||
if (type != FixTypes.OPTIONS) //Options are vanilla only
|
||||
throw new IllegalStateException("Do not call recursive process directly on DataFixer!");
|
||||
|
||||
for (IFixableData fix : vanilla.getFixes(type))
|
||||
{
|
||||
if (fix.getFixVersion() > mcversion)
|
||||
nbt = fix.fixTagCompound(nbt);
|
||||
}
|
||||
//Options is a hack, and doesn't have any nested components
|
||||
//for (IDataWalker walker : getWalkers(type))
|
||||
// nbt = walker.process(this, nbt, version);
|
||||
return nbt;
|
||||
}
|
||||
|
||||
private List<IDataWalker> getWalkers(IFixType type)
|
||||
{
|
||||
return walkers.computeIfAbsent(type, k -> Lists.newArrayList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated //Modders do not use this, this will register you as vanilla. Use the ModID version below.
|
||||
public void registerFix(IFixType type, IFixableData fixable)
|
||||
{
|
||||
vanilla.registerFix(type, fixable);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Deprecated //Modders do not use this, use add below, To better allow custom fix types.
|
||||
public void registerWalker(FixTypes type, IDataWalker walker)
|
||||
{
|
||||
registerVanillaWalker(type, walker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not invoke this method, use registerWalker instead. It is expected to be removed in future versions.
|
||||
*/
|
||||
@Override
|
||||
public void registerVanillaWalker(IFixType type, IDataWalker walker)
|
||||
{
|
||||
getWalkers(type).add(walker);
|
||||
}
|
||||
|
||||
private void validateModId(String mod)
|
||||
{
|
||||
//String current = Loader.instance().activeModContainer() == null ? "minecraft" : Loader.instance().activeModContainer().getModId();
|
||||
//Test active modid?
|
||||
if (!mod.equals(mod.toLowerCase(Locale.ENGLISH)))
|
||||
throw new IllegalArgumentException("Mod ID is not lower case: " + mod);
|
||||
if (mod.length() > 64)
|
||||
throw new IllegalArgumentException("Mod ID is to long: " + mod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize your mod specific data fixer.
|
||||
*
|
||||
* @param modid You mod id, must be lower case.
|
||||
* @param version The current data version of your mod
|
||||
*/
|
||||
public ModFixs init(String modid, int version)
|
||||
{
|
||||
validateModId(modid);
|
||||
if (modFixers.containsKey(modid))
|
||||
throw new IllegalStateException("Attempted to initalize DataFixer for " + modid + " twice");
|
||||
ModFixs ret = new ModFixs(modid, version);
|
||||
modFixers.put(modid, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Map<String, Integer> getVersions(NBTTagCompound nbt)
|
||||
{
|
||||
Map<String, Integer> ret = Maps.newHashMap();
|
||||
ret.put("minecraft", nbt.hasKey("DataVersion", 99) ? nbt.getInteger("DataVersion") : -1);
|
||||
if (nbt.hasKey("ForgeDataVersion", 10))
|
||||
{
|
||||
NBTTagCompound sub = nbt.getCompoundTag("ForgeDataVersion");
|
||||
for (String key : sub.getKeySet())
|
||||
{
|
||||
ret.put(key, sub.hasKey(key, 99) ? sub.getInteger(key) : -1);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void writeVersionData(NBTTagCompound nbt)
|
||||
{
|
||||
//nbt.setInteger("DataVersion", vanilla.version);
|
||||
NBTTagCompound sub = new NBTTagCompound();
|
||||
nbt.setTag("ForgeDataVersion", sub);
|
||||
for (ModFixs mod : modFixers.values())
|
||||
sub.setInteger(mod.mod, mod.version);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
/**
|
||||
* A class containing constants for magic numbers used in the minecraft codebase.
|
||||
* Everything here should be checked each update, and have a comment relating to where to check it.
|
||||
*/
|
||||
public class Constants
|
||||
{
|
||||
/**
|
||||
* NBT Tag type IDS, used when storing the nbt to disc, Should align with NBTBase.getId,
|
||||
* table used in NBTBase.func_150283_g
|
||||
*
|
||||
* Main use is checking tag type in NBTTagCompound.hasKey(String, int)
|
||||
*
|
||||
*/
|
||||
public static class NBT
|
||||
{
|
||||
public static final int TAG_END = 0;
|
||||
public static final int TAG_BYTE = 1;
|
||||
public static final int TAG_SHORT = 2;
|
||||
public static final int TAG_INT = 3;
|
||||
public static final int TAG_LONG = 4;
|
||||
public static final int TAG_FLOAT = 5;
|
||||
public static final int TAG_DOUBLE = 6;
|
||||
public static final int TAG_BYTE_ARRAY = 7;
|
||||
public static final int TAG_STRING = 8;
|
||||
public static final int TAG_LIST = 9;
|
||||
public static final int TAG_COMPOUND = 10;
|
||||
public static final int TAG_INT_ARRAY = 11;
|
||||
public static final int TAG_LONG_ARRAY = 12;
|
||||
public static final int TAG_ANY_NUMERIC = 99;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.minecraftforge.fml.common.EnhancedRuntimeException;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraft.block.BlockPressurePlate.Sensitivity;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.enchantment.EnumEnchantmentType;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.EnumCreatureType;
|
||||
import net.minecraft.entity.item.EntityPainting.EnumArt;
|
||||
import net.minecraft.entity.passive.HorseArmorType;
|
||||
import net.minecraft.entity.passive.IAnimals;
|
||||
import net.minecraft.entity.player.EntityPlayer.SleepResult;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.Item.ToolMaterial;
|
||||
import net.minecraft.item.ItemArmor.ArmorMaterial;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.gen.structure.StructureStrongholdPieces.Stronghold.Door;
|
||||
import net.minecraftforge.classloading.FMLForgePlugin;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraftforge.fml.common.EnhancedRuntimeException.WrappedPrintStream;
|
||||
|
||||
public class EnumHelper
|
||||
{
|
||||
private static Object reflectionFactory = null;
|
||||
private static Method newConstructorAccessor = null;
|
||||
private static Method newInstance = null;
|
||||
private static Method newFieldAccessor = null;
|
||||
private static Method fieldAccessorSet = null;
|
||||
private static boolean isSetup = false;
|
||||
|
||||
//Some enums are decompiled with extra arguments, so lets check for that
|
||||
private static Class<?>[][] commonTypes =
|
||||
{
|
||||
{EnumAction.class},
|
||||
{ArmorMaterial.class, String.class, int.class, int[].class, int.class, SoundEvent.class, float.class},
|
||||
{EnumArt.class, String.class, int.class, int.class, int.class, int.class},
|
||||
{EnumCreatureAttribute.class},
|
||||
{EnumCreatureType.class, Class.class, int.class, Material.class, boolean.class, boolean.class},
|
||||
{Door.class},
|
||||
{EnumEnchantmentType.class, Predicate.class},
|
||||
{Sensitivity.class},
|
||||
{RayTraceResult.Type.class},
|
||||
{EnumSkyBlock.class, int.class},
|
||||
{SleepResult.class},
|
||||
{ToolMaterial.class, int.class, int.class, float.class, float.class, int.class},
|
||||
{EnumRarity.class, TextFormatting.class, String.class},
|
||||
{HorseArmorType.class, String.class, int.class},
|
||||
{EntityLiving.SpawnPlacementType.class, BiPredicate.class}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
public static EnumAction addAction(String name)
|
||||
{
|
||||
return addEnum(EnumAction.class, name);
|
||||
}
|
||||
@Nullable
|
||||
public static ArmorMaterial addArmorMaterial(String name, String textureName, int durability, int[] reductionAmounts, int enchantability, SoundEvent soundOnEquip, float toughness)
|
||||
{
|
||||
return addEnum(ArmorMaterial.class, name, textureName, durability, reductionAmounts, enchantability, soundOnEquip, toughness);
|
||||
}
|
||||
@Nullable
|
||||
public static EnumArt addArt(String name, String tile, int sizeX, int sizeY, int offsetX, int offsetY)
|
||||
{
|
||||
return addEnum(EnumArt.class, name, tile, sizeX, sizeY, offsetX, offsetY);
|
||||
}
|
||||
@Nullable
|
||||
public static EnumCreatureAttribute addCreatureAttribute(String name)
|
||||
{
|
||||
return addEnum(EnumCreatureAttribute.class, name);
|
||||
}
|
||||
@Nullable
|
||||
public static EnumCreatureType addCreatureType(String name, Class<? extends IAnimals> typeClass, int maxNumber, Material material, boolean peaceful, boolean animal)
|
||||
{
|
||||
return addEnum(EnumCreatureType.class, name, typeClass, maxNumber, material, peaceful, animal);
|
||||
}
|
||||
@Nullable
|
||||
public static Door addDoor(String name)
|
||||
{
|
||||
return addEnum(Door.class, name);
|
||||
}
|
||||
@Nullable
|
||||
public static EnumEnchantmentType addEnchantmentType(String name, Predicate<Item> delegate)
|
||||
{
|
||||
return addEnum(EnumEnchantmentType.class, name, delegate);
|
||||
}
|
||||
@Nullable
|
||||
public static Sensitivity addSensitivity(String name)
|
||||
{
|
||||
return addEnum(Sensitivity.class, name);
|
||||
}
|
||||
@Nullable
|
||||
public static RayTraceResult.Type addMovingObjectType(String name)
|
||||
{
|
||||
return addEnum(RayTraceResult.Type.class, name);
|
||||
}
|
||||
@Nullable
|
||||
public static EnumSkyBlock addSkyBlock(String name, int lightValue)
|
||||
{
|
||||
return addEnum(EnumSkyBlock.class, name, lightValue);
|
||||
}
|
||||
@Nullable
|
||||
public static SleepResult addStatus(String name)
|
||||
{
|
||||
return addEnum(SleepResult.class, name);
|
||||
}
|
||||
@Nullable
|
||||
public static ToolMaterial addToolMaterial(String name, int harvestLevel, int maxUses, float efficiency, float damage, int enchantability)
|
||||
{
|
||||
return addEnum(ToolMaterial.class, name, harvestLevel, maxUses, efficiency, damage, enchantability);
|
||||
}
|
||||
@Nullable
|
||||
public static EnumRarity addRarity(String name, TextFormatting color, String displayName)
|
||||
{
|
||||
return addEnum(EnumRarity.class, name, color, displayName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static EntityLiving.SpawnPlacementType addSpawnPlacementType(String name, BiPredicate<IBlockAccess, BlockPos> predicate)
|
||||
{
|
||||
return addEnum(EntityLiving.SpawnPlacementType.class, name, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name the name of the new {@code HorseArmorType}
|
||||
* @param textureLocation the path to the texture for this armor type. It must follow the format domain:path and be relative to the assets folder.
|
||||
* @param armorStrength how much protection this armor type should give
|
||||
* @return the new {@code HorseArmorType}, or null if it could not be created
|
||||
*/
|
||||
@Nullable
|
||||
public static HorseArmorType addHorseArmor(String name, String textureLocation, int armorStrength)
|
||||
{
|
||||
return addEnum(HorseArmorType.class, name, textureLocation, armorStrength);
|
||||
}
|
||||
|
||||
private static void setup()
|
||||
{
|
||||
if (isSetup)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory");
|
||||
reflectionFactory = getReflectionFactory.invoke(null);
|
||||
newConstructorAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newConstructorAccessor", Constructor.class);
|
||||
newInstance = Class.forName("sun.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", Object[].class);
|
||||
newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class);
|
||||
fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log.error("Error setting up EnumHelper.", e);
|
||||
}
|
||||
|
||||
isSetup = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Everything below this is found at the site below, and updated to be able to compile in Eclipse/Java 1.6+
|
||||
* Also modified for use in decompiled code.
|
||||
* Found at: http://niceideas.ch/roller2/badtrash/entry/java_create_enum_instances_dynamically
|
||||
*/
|
||||
private static Object getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes) throws Exception
|
||||
{
|
||||
Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
|
||||
parameterTypes[0] = String.class;
|
||||
parameterTypes[1] = int.class;
|
||||
System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
|
||||
return newConstructorAccessor.invoke(reflectionFactory, enumClass.getDeclaredConstructor(parameterTypes));
|
||||
}
|
||||
|
||||
private static < T extends Enum<? >> T makeEnum(Class<T> enumClass, @Nullable String value, int ordinal, Class<?>[] additionalTypes, @Nullable Object[] additionalValues) throws Exception
|
||||
{
|
||||
int additionalParamsCount = additionalValues == null ? 0 : additionalValues.length;
|
||||
Object[] params = new Object[additionalParamsCount + 2];
|
||||
params[0] = value;
|
||||
params[1] = ordinal;
|
||||
if (additionalValues != null)
|
||||
{
|
||||
System.arraycopy(additionalValues, 0, params, 2, additionalValues.length);
|
||||
}
|
||||
return enumClass.cast(newInstance.invoke(getConstructorAccessor(enumClass, additionalTypes), new Object[] {params}));
|
||||
}
|
||||
|
||||
public static void setFailsafeFieldValue(Field field, @Nullable Object target, @Nullable Object value) throws Exception
|
||||
{
|
||||
field.setAccessible(true);
|
||||
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false);
|
||||
fieldAccessorSet.invoke(fieldAccessor, target, value);
|
||||
}
|
||||
|
||||
private static void blankField(Class<?> enumClass, String fieldName) throws Exception
|
||||
{
|
||||
for (Field field : Class.class.getDeclaredFields())
|
||||
{
|
||||
if (field.getName().contains(fieldName))
|
||||
{
|
||||
field.setAccessible(true);
|
||||
setFailsafeFieldValue(field, enumClass, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void cleanEnumCache(Class<?> enumClass) throws Exception
|
||||
{
|
||||
blankField(enumClass, "enumConstantDirectory");
|
||||
blankField(enumClass, "enumConstants");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Object... paramValues)
|
||||
{
|
||||
setup();
|
||||
return addEnum(commonTypes, enumType, enumName, paramValues);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static <T extends Enum<? >> T addEnum(Class<?>[][] map, Class<T> enumType, String enumName, Object... paramValues)
|
||||
{
|
||||
for (Class<?>[] lookup : map)
|
||||
{
|
||||
if (lookup[0] == enumType)
|
||||
{
|
||||
Class<?>[] paramTypes = new Class<?>[lookup.length - 1];
|
||||
if (paramTypes.length > 0)
|
||||
{
|
||||
System.arraycopy(lookup, 1, paramTypes, 0, paramTypes.length);
|
||||
}
|
||||
return addEnum(enumType, enumName, paramTypes, paramValues);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//Tests an enum is compatible with these args, throws an error if not.
|
||||
public static void testEnum(Class<? extends Enum<?>> enumType, Class<?>[] paramTypes)
|
||||
{
|
||||
addEnum(true, enumType, null, paramTypes, (Object[])null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Class<?>[] paramTypes, Object... paramValues)
|
||||
{
|
||||
return addEnum(false, enumType, enumName, paramTypes, paramValues);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "serial" })
|
||||
@Nullable
|
||||
private static <T extends Enum<? >> T addEnum(boolean test, final Class<T> enumType, @Nullable String enumName, final Class<?>[] paramTypes, @Nullable Object[] paramValues)
|
||||
{
|
||||
if (!isSetup)
|
||||
{
|
||||
setup();
|
||||
}
|
||||
|
||||
Field valuesField = null;
|
||||
Field[] fields = enumType.getDeclaredFields();
|
||||
|
||||
for (Field field : fields)
|
||||
{
|
||||
String name = field.getName();
|
||||
if (name.equals("$VALUES") || name.equals("ENUM$VALUES")) //Added 'ENUM$VALUES' because Eclipse's internal compiler doesn't follow standards
|
||||
{
|
||||
valuesField = field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int flags = (FMLForgePlugin.RUNTIME_DEOBF ? Modifier.PUBLIC : Modifier.PRIVATE) | Modifier.STATIC | Modifier.FINAL | 0x1000 /*SYNTHETIC*/;
|
||||
if (valuesField == null)
|
||||
{
|
||||
String valueType = String.format("[L%s;", enumType.getName().replace('.', '/'));
|
||||
|
||||
for (Field field : fields)
|
||||
{
|
||||
if ((field.getModifiers() & flags) == flags &&
|
||||
field.getType().getName().replace('.', '/').equals(valueType)) //Apparently some JVMs return .'s and some don't..
|
||||
{
|
||||
valuesField = field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valuesField == null)
|
||||
{
|
||||
final List<String> lines = Lists.newArrayList();
|
||||
lines.add(String.format("Could not find $VALUES field for enum: %s", enumType.getName()));
|
||||
lines.add(String.format("Runtime Deobf: %s", FMLForgePlugin.RUNTIME_DEOBF));
|
||||
lines.add(String.format("Flags: %s", String.format("%16s", Integer.toBinaryString(flags)).replace(' ', '0')));
|
||||
lines.add( "Fields:");
|
||||
for (Field field : fields)
|
||||
{
|
||||
String mods = String.format("%16s", Integer.toBinaryString(field.getModifiers())).replace(' ', '0');
|
||||
lines.add(String.format(" %s %s: %s", mods, field.getName(), field.getType().getName()));
|
||||
}
|
||||
|
||||
for (String line : lines)
|
||||
FMLLog.log.fatal(line);
|
||||
|
||||
if (test)
|
||||
{
|
||||
throw new EnhancedRuntimeException("Could not find $VALUES field for enum: " + enumType.getName())
|
||||
{
|
||||
@Override
|
||||
protected void printStackTrace(WrappedPrintStream stream)
|
||||
{
|
||||
for (String line : lines)
|
||||
stream.println(line);
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (test)
|
||||
{
|
||||
Object ctr = null;
|
||||
Exception ex = null;
|
||||
try
|
||||
{
|
||||
ctr = getConstructorAccessor(enumType, paramTypes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ex = e;
|
||||
}
|
||||
if (ctr == null || ex != null)
|
||||
{
|
||||
throw new EnhancedRuntimeException(String.format("Could not find constructor for Enum %s", enumType.getName()), ex)
|
||||
{
|
||||
private String toString(Class<?>[] cls)
|
||||
{
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int x = 0; x < cls.length; x++)
|
||||
{
|
||||
b.append(cls[x].getName());
|
||||
if (x != cls.length - 1)
|
||||
b.append(", ");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
@Override
|
||||
protected void printStackTrace(WrappedPrintStream stream)
|
||||
{
|
||||
stream.println("Target Arguments:");
|
||||
stream.println(" java.lang.String, int, " + toString(paramTypes));
|
||||
stream.println("Found Constructors:");
|
||||
for (Constructor<?> ctr : enumType.getDeclaredConstructors())
|
||||
{
|
||||
stream.println(" " + toString(ctr.getParameterTypes()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
valuesField.setAccessible(true);
|
||||
|
||||
try
|
||||
{
|
||||
T[] previousValues = (T[])valuesField.get(enumType);
|
||||
T newValue = makeEnum(enumType, enumName, previousValues.length, paramTypes, paramValues);
|
||||
setFailsafeFieldValue(valuesField, null, ArrayUtils.add(previousValues, newValue));
|
||||
cleanEnumCache(enumType);
|
||||
|
||||
return newValue;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log.error("Error adding enum with EnumHelper.", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
if (!isSetup)
|
||||
{
|
||||
setup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.network.play.client.CPacketClientSettings;
|
||||
import net.minecraft.server.management.PlayerInteractionManager;
|
||||
import net.minecraft.stats.StatBase;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldServer;
|
||||
|
||||
//Preliminary, simple Fake Player class
|
||||
public class FakePlayer extends EntityPlayerMP
|
||||
{
|
||||
public FakePlayer(WorldServer world, GameProfile name)
|
||||
{
|
||||
super(FMLCommonHandler.instance().getMinecraftServerInstance(), world, name, new PlayerInteractionManager(world));
|
||||
}
|
||||
|
||||
@Override public Vec3d getPositionVector(){ return new Vec3d(0, 0, 0); }
|
||||
@Override public boolean canUseCommand(int i, String s){ return false; }
|
||||
@Override public void sendStatusMessage(ITextComponent chatComponent, boolean actionBar){}
|
||||
@Override public void sendMessage(ITextComponent component) {}
|
||||
@Override public void addStat(StatBase par1StatBase, int par2){}
|
||||
@Override public void openGui(Object mod, int modGuiId, World world, int x, int y, int z){}
|
||||
@Override public boolean isEntityInvulnerable(DamageSource source){ return true; }
|
||||
@Override public boolean canAttackPlayer(EntityPlayer player){ return false; }
|
||||
@Override public void onDeath(DamageSource source){ return; }
|
||||
@Override public void onUpdate(){ return; }
|
||||
@Override public Entity changeDimension(int dim, ITeleporter teleporter){ return this; }
|
||||
@Override public void handleClientSettings(CPacketClientSettings pkt){ return; }
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import net.minecraft.world.WorldServer;
|
||||
|
||||
//To be expanded for generic Mod fake players?
|
||||
public class FakePlayerFactory
|
||||
{
|
||||
private static GameProfile MINECRAFT = new GameProfile(UUID.fromString("41C82C87-7AfB-4024-BA57-13D2C99CAE77"), "[Minecraft]");
|
||||
// Map of all active fake player usernames to their entities
|
||||
private static Map<GameProfile, FakePlayer> fakePlayers = Maps.newHashMap();
|
||||
private static WeakReference<FakePlayer> MINECRAFT_PLAYER = null;
|
||||
|
||||
public static FakePlayer getMinecraft(WorldServer world)
|
||||
{
|
||||
FakePlayer ret = MINECRAFT_PLAYER != null ? MINECRAFT_PLAYER.get() : null;
|
||||
if (ret == null)
|
||||
{
|
||||
ret = FakePlayerFactory.get(world, MINECRAFT);
|
||||
MINECRAFT_PLAYER = new WeakReference<FakePlayer>(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a fake player with a given username,
|
||||
* Mods should either hold weak references to the return value, or listen for a
|
||||
* WorldEvent.Unload and kill all references to prevent worlds staying in memory.
|
||||
*/
|
||||
public static FakePlayer get(WorldServer world, GameProfile username)
|
||||
{
|
||||
if (!fakePlayers.containsKey(username))
|
||||
{
|
||||
FakePlayer fakePlayer = new FakePlayer(world, username);
|
||||
fakePlayers.put(username, fakePlayer);
|
||||
}
|
||||
|
||||
return fakePlayers.get(username);
|
||||
}
|
||||
|
||||
public static void unloadWorld(WorldServer world)
|
||||
{
|
||||
fakePlayers.entrySet().removeIf(entry -> entry.getValue().world == world);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import net.minecraft.util.datafix.IDataFixer;
|
||||
|
||||
public interface IDataFixerData extends IDataFixer
|
||||
{
|
||||
int getVersion(String mod);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
|
||||
/**
|
||||
* An interface designed to unify various things in the Minecraft
|
||||
* code base that can be serialized to and from a NBT tag.
|
||||
*/
|
||||
public interface INBTSerializable<T extends NBTBase>
|
||||
{
|
||||
T serializeNBT();
|
||||
void deserializeNBT(T nbt);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.Teleporter;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldProvider;
|
||||
|
||||
/**
|
||||
* Interface for handling the placement of entities during dimension change.
|
||||
*
|
||||
* An implementation of this interface can be used to place the entity
|
||||
* in a safe location, or generate a return portal, for instance.
|
||||
*
|
||||
* See the {@link net.minecraft.world.Teleporter} class, which has
|
||||
* been patched to implement this interface, for a vanilla example.
|
||||
*/
|
||||
public interface ITeleporter
|
||||
{
|
||||
/**
|
||||
* Called to handle placing the entity in the new world.
|
||||
*
|
||||
* The initial position of the entity will be its
|
||||
* position in the origin world, multiplied horizontally
|
||||
* by the computed cross-dimensional movement factor
|
||||
* (see {@link WorldProvider#getMovementFactor()}).
|
||||
*
|
||||
* Note that the supplied entity has not yet been spawned
|
||||
* in the destination world at the time.
|
||||
*
|
||||
* @param world the entity's destination
|
||||
* @param entity the entity to be placed
|
||||
* @param yaw the suggested yaw value to apply
|
||||
*/
|
||||
void placeEntity(World world, Entity entity, float yaw);
|
||||
|
||||
// used internally to handle vanilla hardcoding
|
||||
default boolean isVanilla()
|
||||
{
|
||||
return getClass() == Teleporter.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.reflect.TypeParameter;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import net.minecraft.nbt.JsonToNBT;
|
||||
import net.minecraft.nbt.NBTException;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class JsonUtils
|
||||
{
|
||||
// http://stackoverflow.com/questions/7706772/deserializing-immutablelist-using-gson/21677349#21677349
|
||||
public enum ImmutableListTypeAdapter implements JsonDeserializer<ImmutableList<?>>, JsonSerializer<ImmutableList<?>>
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public ImmutableList<?> deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
final Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
|
||||
final Type parametrizedType = listOf(typeArguments[0]).getType();
|
||||
final List<?> list = context.deserialize(json, parametrizedType);
|
||||
return ImmutableList.copyOf(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(ImmutableList<?> src, Type type, JsonSerializationContext context)
|
||||
{
|
||||
final Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
|
||||
final Type parametrizedType = listOf(typeArguments[0]).getType();
|
||||
return context.serialize(src, parametrizedType);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "serial", "unchecked" })
|
||||
private static <E> TypeToken<List<E>> listOf(final Type arg)
|
||||
{
|
||||
return new TypeToken<List<E>>() {}.where(new TypeParameter<E>() {}, (TypeToken<E>) TypeToken.of(arg));
|
||||
}
|
||||
|
||||
public enum ImmutableMapTypeAdapter implements JsonDeserializer<ImmutableMap<String, ?>>, JsonSerializer<ImmutableMap<String, ?>>
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public ImmutableMap<String, ?> deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
final Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
|
||||
final Type parameterizedType = mapOf(typeArguments[1]).getType();
|
||||
final Map<String, ?> map = context.deserialize(json, parameterizedType);
|
||||
return ImmutableMap.copyOf(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(ImmutableMap<String, ?> src, Type type, JsonSerializationContext context)
|
||||
{
|
||||
final Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
|
||||
final Type parameterizedType = mapOf(typeArguments[1]).getType();
|
||||
return context.serialize(src, parameterizedType);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static NBTTagCompound readNBT(JsonObject json, String key)
|
||||
{
|
||||
if (net.minecraft.util.JsonUtils.hasField(json, key))
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonToNBT.getTagFromJson(net.minecraft.util.JsonUtils.getString(json, key));
|
||||
} catch (NBTException e)
|
||||
{
|
||||
throw new JsonSyntaxException("Malformed NBT tag", e);
|
||||
}
|
||||
} else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "serial", "unchecked" })
|
||||
private static <E> TypeToken<Map<String, E>> mapOf(final Type arg)
|
||||
{
|
||||
return new TypeToken<Map<String, E>>() {}.where(new TypeParameter<E>() {}, (TypeToken<E>) TypeToken.of(arg));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.minecraft.util.datafix.IFixType;
|
||||
import net.minecraft.util.datafix.IFixableData;
|
||||
|
||||
public class ModFixs
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
final String mod;
|
||||
final int version;
|
||||
private final Map<IFixType, List<IFixableData>> fixes = Maps.newHashMap();
|
||||
|
||||
ModFixs(String mod, int version)
|
||||
{
|
||||
this.mod = mod;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public List<IFixableData> getFixes(IFixType type)
|
||||
{
|
||||
return this.fixes.computeIfAbsent(type, k -> Lists.newArrayList());
|
||||
}
|
||||
|
||||
public void registerFix(IFixType type, IFixableData fixer)
|
||||
{
|
||||
List<IFixableData> list = getFixes(type);
|
||||
int ver = fixer.getFixVersion();
|
||||
|
||||
if (ver > this.version)
|
||||
{
|
||||
LOGGER.warn("[{}] Ignored fix registered for version: {} as the DataVersion of the game is: {}", mod, ver, this.version);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!list.isEmpty() && list.get(list.size()-1).getFixVersion() > ver)
|
||||
{
|
||||
for (int x = 0; x < list.size(); ++x)
|
||||
{
|
||||
if (list.get(x).getFixVersion() > ver)
|
||||
{
|
||||
list.add(x, fixer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
list.add(fixer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
|
||||
public class PacketUtil
|
||||
{
|
||||
private PacketUtil() {}
|
||||
|
||||
/**
|
||||
* Most ItemStack serialization is Server to Client, and must go through PacketBuffer.writeItemStack which uses Item.getNBTShareTag.
|
||||
* One exception is items from the creative menu, which must be sent from Client to Server with their full NBT.
|
||||
* <br/>
|
||||
* This method matches PacketBuffer.writeItemStack but without the Item.getNBTShareTag patch.
|
||||
* It is compatible with PacketBuffer.readItemStack.
|
||||
*/
|
||||
public static void writeItemStackFromClientToServer(PacketBuffer buffer, ItemStack stack)
|
||||
{
|
||||
if (stack.isEmpty())
|
||||
{
|
||||
buffer.writeShort(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.writeShort(Item.getIdFromItem(stack.getItem()));
|
||||
buffer.writeByte(stack.getCount());
|
||||
buffer.writeShort(stack.getMetadata());
|
||||
NBTTagCompound nbttagcompound = null;
|
||||
|
||||
if (stack.getItem().isDamageable() || stack.getItem().getShareTag())
|
||||
{
|
||||
nbttagcompound = stack.getTagCompound();
|
||||
}
|
||||
|
||||
buffer.writeCompoundTag(nbttagcompound);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
public class RecipeMatcher
|
||||
{
|
||||
/**
|
||||
* Attempts to match inputs to the specified tests. In the best way that all inputs are used by one test.
|
||||
* Will return null in any of these cases:
|
||||
* input/test lengths don't match. This is only for matching paired outputs.
|
||||
* any input doesn't match a test
|
||||
* any test doesn't match a input
|
||||
* If we are unable to determine a proper pair
|
||||
*
|
||||
* @return An array mapping inputs to tests. ret[x] = y means input[x] = test[y]
|
||||
*/
|
||||
public static <T> int[] findMatches(List<T> inputs, List<? extends Predicate<T>> tests)
|
||||
{
|
||||
int elements = inputs.size();
|
||||
if (elements != tests.size())
|
||||
return null; // There will not be a 1:1 mapping of inputs -> tests
|
||||
|
||||
int[] ret = new int[elements];
|
||||
for (int x = 0; x < elements; x++)
|
||||
ret[x] = -1;
|
||||
|
||||
// [UnusedInputs] [UnusedIngredients] [IngredientMatchMask]...
|
||||
BitSet data = new BitSet((elements + 2) * elements);
|
||||
for (int x = 0; x < elements; x++)
|
||||
{
|
||||
int matched = 0;
|
||||
int offset = (x + 2) * elements;
|
||||
Predicate<T> test = tests.get(x);
|
||||
|
||||
for (int y = 0; y < elements; y++)
|
||||
{
|
||||
if (data.get(y))
|
||||
continue;
|
||||
|
||||
if (test.apply(inputs.get(y)))
|
||||
{
|
||||
data.set(offset + y);
|
||||
matched++;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched == 0)
|
||||
return null; //We have an test that matched non of the inputs
|
||||
|
||||
if (matched == 1)
|
||||
{
|
||||
if (!claim(ret, data, x, elements))
|
||||
return null; //We failed to claim this index, which means it caused something else to go to 0 matches, which makes the whole thing fail
|
||||
}
|
||||
}
|
||||
|
||||
if (data.nextClearBit(0) >= elements) //All items have been used, which means all tests have a match!
|
||||
return ret;
|
||||
|
||||
// We should be in a state where multiple tests are satified by multiple inputs. So we need to try a branching recursive test.
|
||||
// However for performance reasons, we should probably make that check a sub-set of the entire graph.
|
||||
if (backtrack(data, ret, 0, elements))
|
||||
return ret;
|
||||
|
||||
return null; //Backtrack failed, no matches, we cry and go home now :(
|
||||
}
|
||||
|
||||
// This is bad... need to think of a better cascade, recursion instead of stack?
|
||||
private static boolean claim(int[] ret, BitSet data, int claimed, int elements)
|
||||
{
|
||||
Queue<Integer> pending = new LinkedList<Integer>();
|
||||
pending.add(claimed);
|
||||
|
||||
while (pending.peek() != null)
|
||||
{
|
||||
int test = pending.poll();
|
||||
int offset = (test + 2) * elements;
|
||||
int used = data.nextSetBit(offset) - offset;
|
||||
|
||||
if (used >= elements || used < 0)
|
||||
throw new IllegalStateException("What? We matched something, but it wasn't set in the range of this test! Test: " + test + " Used: " + used);
|
||||
|
||||
data.set(used);
|
||||
data.set(elements + test);
|
||||
ret[used] = test;
|
||||
|
||||
for (int x = 0; x < elements; x++)
|
||||
{
|
||||
offset = (x + 2) * elements;
|
||||
if (data.get(offset + used) && !data.get(elements + x))
|
||||
{
|
||||
data.clear(offset + used);
|
||||
int count = 0;
|
||||
for (int y = offset; y < offset + elements; y++)
|
||||
if (data.get(y))
|
||||
count++;
|
||||
|
||||
if (count == 0)
|
||||
return false; //Claiming this caused another test to lose its last match..
|
||||
|
||||
if (count == 1)
|
||||
pending.add(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//We use recursion here, why? Because I feel like it. Also because we should only ever be working in data sets < 9
|
||||
private static boolean backtrack(BitSet data, int[] ret, int start, int elements)
|
||||
{
|
||||
int test = data.nextClearBit(elements + start) - elements;
|
||||
if (test >= elements)
|
||||
return true; //Could not find the next unused test.
|
||||
|
||||
if (test < 0)
|
||||
throw new IllegalStateException("This should never happen, negative test in backtrack!");
|
||||
|
||||
int offset = (test + 2) * elements;
|
||||
for (int x = 0; x < elements; x++)
|
||||
{
|
||||
if (!data.get(offset + x) || data.get(x))
|
||||
continue;
|
||||
|
||||
data.set(x);
|
||||
|
||||
if (backtrack(data, ret, test + 1, elements))
|
||||
{
|
||||
ret[x] = test;
|
||||
return true;
|
||||
}
|
||||
|
||||
data.clear(x);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Utility to format data into a textual (markdown-compliant) table.
|
||||
*/
|
||||
public class TextTable
|
||||
{
|
||||
public static Column column(String header)
|
||||
{
|
||||
return new Column(header);
|
||||
}
|
||||
|
||||
public static Column column(String header, Alignment alignment)
|
||||
{
|
||||
return new Column(header, alignment);
|
||||
}
|
||||
|
||||
private final List<Column> columns;
|
||||
private final List<Row> rows = new ArrayList<>();
|
||||
|
||||
public TextTable(List<Column> columns)
|
||||
{
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
public String build(String lineEnding)
|
||||
{
|
||||
StringBuilder destination = new StringBuilder();
|
||||
append(destination, lineEnding);
|
||||
return destination.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the data formatted as a table to the given string builder.
|
||||
* The padding character used for the column alignments is a single space (' '),
|
||||
* the separate between column headers and values is a dash ('-').
|
||||
* Note that you *have* to specify a line ending, '\n' isn't used by default.
|
||||
* <p>
|
||||
* The generated table is compliant with the markdown file format.
|
||||
*
|
||||
* @param destination a string builder to append the table to
|
||||
* @param lineEnding the line ending to use for each row of the table
|
||||
*/
|
||||
public void append(StringBuilder destination, String lineEnding)
|
||||
{
|
||||
List<String> headers = columns.stream().map(c -> c.formatHeader(" ")).collect(Collectors.toList());
|
||||
printRow(destination, headers);
|
||||
destination.append(lineEnding);
|
||||
printSeparators(destination);
|
||||
for (Row row : rows)
|
||||
{
|
||||
destination.append(lineEnding);
|
||||
printRow(destination, row.format(columns, " "));
|
||||
}
|
||||
}
|
||||
|
||||
private void printSeparators(StringBuilder destination)
|
||||
{
|
||||
destination.append('|');
|
||||
for (Column column : columns)
|
||||
{
|
||||
destination.append(column.alignment != Alignment.RIGHT ? ':' : ' ');
|
||||
destination.append(column.getSeparator('-'));
|
||||
destination.append(column.alignment != Alignment.LEFT ? ':' : ' ');
|
||||
destination.append('|');
|
||||
}
|
||||
}
|
||||
|
||||
private void printRow(StringBuilder destination, List<String> values)
|
||||
{
|
||||
destination.append('|');
|
||||
for (String value : values)
|
||||
{
|
||||
destination.append(' ');
|
||||
destination.append(value);
|
||||
destination.append(' ');
|
||||
destination.append('|');
|
||||
}
|
||||
}
|
||||
|
||||
public void add(@Nonnull Object... values)
|
||||
{
|
||||
if (values.length != columns.size())
|
||||
{
|
||||
throw new IllegalArgumentException("Received wrong amount of values for table row, expected " + columns.size() + ", received " + columns.size() + ".");
|
||||
}
|
||||
Row row = new Row();
|
||||
for (int i = 0; i < values.length; i++)
|
||||
{
|
||||
String value = Objects.toString(values[i]);
|
||||
row.values.add(value);
|
||||
columns.get(i).fit(value);
|
||||
}
|
||||
rows.add(row);
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
for (Column column : columns)
|
||||
{
|
||||
column.resetWidth();
|
||||
}
|
||||
rows.clear();
|
||||
}
|
||||
|
||||
public List<Column> getColumns()
|
||||
{
|
||||
return Collections.unmodifiableList(columns);
|
||||
}
|
||||
|
||||
public static class Column
|
||||
{
|
||||
private String header;
|
||||
private int width;
|
||||
private Alignment alignment;
|
||||
|
||||
public Column(String header)
|
||||
{
|
||||
this(header, Alignment.LEFT);
|
||||
}
|
||||
|
||||
public Column(String header, Alignment alignment)
|
||||
{
|
||||
this.header = header;
|
||||
this.width = header.length();
|
||||
this.alignment = alignment;
|
||||
}
|
||||
|
||||
public String formatHeader(String padding)
|
||||
{
|
||||
return format(header, padding);
|
||||
}
|
||||
|
||||
public String format(String value, String padding)
|
||||
{
|
||||
switch (alignment)
|
||||
{
|
||||
case LEFT:
|
||||
return StringUtils.rightPad(value, width, padding);
|
||||
case RIGHT:
|
||||
return StringUtils.leftPad(value, width, padding);
|
||||
default:
|
||||
int length = value.length();
|
||||
int left = (width - length) / 2;
|
||||
int leftWidth = left + length;
|
||||
return StringUtils.rightPad(StringUtils.leftPad(value, leftWidth, padding), width, padding);
|
||||
}
|
||||
}
|
||||
|
||||
public String getSeparator(char character)
|
||||
{
|
||||
return StringUtils.leftPad("", width, character);
|
||||
}
|
||||
|
||||
public void fit(String value)
|
||||
{
|
||||
if (value.length() > width)
|
||||
{
|
||||
width = value.length();
|
||||
}
|
||||
}
|
||||
|
||||
public void resetWidth()
|
||||
{
|
||||
this.width = header.length();
|
||||
}
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Row
|
||||
{
|
||||
private final ArrayList<String> values = new ArrayList<>();
|
||||
|
||||
public List<String> format(List<Column> columns, String padding)
|
||||
{
|
||||
if (columns.size() != values.size())
|
||||
{
|
||||
throw new IllegalArgumentException("Received wrong amount of columns for table row, expected " + columns.size() + ", received " + columns.size() + ".");
|
||||
}
|
||||
return Streams.zip(values.stream(), columns.stream(), (v, c) -> c.format(v, padding)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
public enum Alignment
|
||||
{
|
||||
LEFT, CENTER, RIGHT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.common.util;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.world.WorldProvider;
|
||||
import net.minecraft.world.storage.WorldSavedData;
|
||||
|
||||
public class WorldCapabilityData extends WorldSavedData
|
||||
{
|
||||
public static final String ID = "capabilities";
|
||||
|
||||
private INBTSerializable<NBTTagCompound> serializable;
|
||||
private NBTTagCompound capNBT = null;
|
||||
|
||||
public WorldCapabilityData(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
public WorldCapabilityData(INBTSerializable<NBTTagCompound> serializable)
|
||||
{
|
||||
super(ID);
|
||||
this.serializable = serializable;
|
||||
}
|
||||
|
||||
/**
|
||||
* reads in data from the NBTTagCompound into this MapDataBase
|
||||
*/
|
||||
@Override
|
||||
public void readFromNBT(NBTTagCompound nbt)
|
||||
{
|
||||
this.capNBT = nbt;
|
||||
if (serializable != null)
|
||||
{
|
||||
serializable.deserializeNBT(this.capNBT);
|
||||
this.capNBT = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound writeToNBT(NBTTagCompound nbt)
|
||||
{
|
||||
if (serializable != null)
|
||||
nbt = serializable.serializeNBT();
|
||||
return nbt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this MapDataBase needs saving to disk.
|
||||
*/
|
||||
@Override
|
||||
public boolean isDirty()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setCapabilities(WorldProvider provider, INBTSerializable<NBTTagCompound> capabilities)
|
||||
{
|
||||
this.serializable = capabilities;
|
||||
if (this.capNBT != null && serializable != null)
|
||||
{
|
||||
serializable.deserializeNBT(this.capNBT);
|
||||
this.capNBT = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user