/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.pepsoft.worldpainter.biomeschemes;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.pepsoft.util.Checksum;
import org.pepsoft.util.FileUtils;
import org.pepsoft.worldpainter.BiomeScheme;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An abstract base class for {@link BiomeScheme}s which can invoke Minecraft
* code from a Minecraft jar file for version 1.2 through 1.6 to calculate
* biomes.
*
* @author pepijn
*/
public abstract class Minecraft1_2JarBiomeScheme extends AbstractMinecraft1_2BiomeScheme {
public Minecraft1_2JarBiomeScheme(File minecraftJar, File libDir, Checksum md5Sum, Map<Checksum, String[]> hashesToClassNames, String version) {
if (logger.isDebugEnabled()) {
logger.debug("Creating biome scheme using Minecraft jar {}", minecraftJar);
}
if (md5Sum == null) {
try {
md5Sum = FileUtils.getMD5(minecraftJar);
} catch (IOException e) {
throw new RuntimeException("I/O error calculating hash for " + minecraftJar, e);
}
}
String[] classNames = hashesToClassNames.get(md5Sum);
String landscapeClassName = classNames[0];
String bufferManagerClassName = classNames[1];
String worldGeneratorClassName = classNames[2];
try {
List<URL> classpath = new ArrayList<>();
classpath.add(minecraftJar.toURI().toURL());
if ((libDir != null) && libDir.isDirectory()) {
List<URL> jars = new ArrayList<>();
scanDir(libDir, jars);
classpath.addAll(jars);
}
ClassLoader classLoader = new URLClassLoader(classpath.toArray(new URL[classpath.size()]));
Class<?> landscapeClass = classLoader.loadClass(landscapeClassName);
worldGeneratorClass = classLoader.loadClass(worldGeneratorClassName);
getLandscapesMethod = landscapeClass.getMethod("a", long.class, worldGeneratorClass);
getBiomesMethod = landscapeClass.getMethod("a", int.class, int.class, int.class, int.class);
Class<?> bufferManagerClass = classLoader.loadClass(bufferManagerClassName);
clearBuffersMethod = bufferManagerClass.getMethod("a");
} catch (MalformedURLException e) {
throw new RuntimeException("Malformed URL exception while loading minecraft.jar", e);
} catch (ClassNotFoundException | NoSuchMethodException e) {
throw new RuntimeException("Not a valid " + version + " minecraft.jar", e);
}
}
@Override
public void setSeed(long seed) {
if ((seed != this.seed) || (landscape == null)) {
try {
landscape = ((Object[]) getLandscapesMethod.invoke(null, seed, null))[1];
this.seed = seed;
} catch (IllegalAccessException e) {
throw new RuntimeException("Access denied while trying to set the seed", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Exception thrown while trying to set the seed", e);
}
}
}
@Override
public final synchronized void getBiomes(int x, int y, int width, int height, int[] buffer) {
try {
int[] biomes = (int[]) getBiomesMethod.invoke(landscape, x, y, width, height);
clearBuffersMethod.invoke(null);
System.arraycopy(biomes, 0, buffer, 0, Math.min(biomes.length, buffer.length));
} catch (IllegalAccessException e) {
throw new RuntimeException("Access denied while trying to calculate biomes", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Exception thrown while trying to calculate biomes", e);
}
}
private void scanDir(File dir, List<URL> classpath) {
File[] files = dir.listFiles(pathname -> pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".jar"));
for (File file: files) {
if (file.isDirectory()) {
scanDir(file, classpath);
} else {
try {
classpath.add(file.toURI().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException("Malformed URL", e);
}
}
}
}
final Class<?> worldGeneratorClass;
final Method getLandscapesMethod, getBiomesMethod, clearBuffersMethod;
Object landscape;
long seed = Long.MIN_VALUE;
private static final Logger logger = LoggerFactory.getLogger(Minecraft1_2JarBiomeScheme.class);
}