package micdoodle8.mods.galacticraft.core.util;
import micdoodle8.mods.galacticraft.api.vector.BlockVec3;
import micdoodle8.mods.galacticraft.core.Constants;
import micdoodle8.mods.galacticraft.core.GalacticraftCore;
import micdoodle8.mods.galacticraft.core.client.DynamicTextureProper;
import micdoodle8.mods.galacticraft.core.client.screen.DrawGameScreen;
import micdoodle8.mods.galacticraft.core.network.PacketSimple;
import micdoodle8.mods.galacticraft.core.network.PacketSimple.EnumSimplePacket;
import micdoodle8.mods.galacticraft.core.proxy.ClientProxyCore;
import net.minecraft.block.Block;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.BlockPos;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraft.world.WorldProviderSurface;
import net.minecraft.world.WorldType;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.apache.commons.io.FileUtils;
import javax.imageio.*;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
public class MapUtil
{
//Mapgen management
public static AtomicBoolean calculatingMap = new AtomicBoolean();
public static AtomicBoolean resetClientFlag = new AtomicBoolean();
private static MapGen currentMap = null;
private static MapGen slowMap = null;
private static Thread threadCurrentMap = null;
private static Thread threadSlowMap = null;
public static boolean doneOverworldTexture = false;
private static LinkedList<MapGen> queuedMaps = new LinkedList();
public static LinkedList<String> clientRequests = new LinkedList();
public static ArrayList<BlockVec3> biomeColours = new ArrayList<BlockVec3>(40);
private static Random rand = new Random();
private static byte[] overworldImageBytesPart; //Used client side only
private static byte[] overworldImageCompressed = null;
//Map size definitions
private static final int SIZE_STD = 176;
public static final int SIZE_STD2 = SIZE_STD * 2;
public static final int OVERWORLD_LARGEMAP_WIDTH = 1536; //Do not make a large map whose raw binary exceeds 2MB otherwise sendMapPacket() will not send it. This raw binary is 576kB
private static final int OVERWORLD_LARGEMAP_HEIGHT = 960;
private static final int OVERWORLD_MAP_SCALE = 4; //Recommended is 4. This gives a large overworld map of size (1536 x 16) by (960 x 16): that extends 12000 blocks from spawn in both EW directions and 7600 blocks from spawn north and south
private static final int OVERWORLD_TEXTURE_WIDTH = 192; //Do not change - planet texture needs to be this size
private static final int OVERWORLD_TEXTURE_HEIGHT = 48; //Do not change - planet texture needs to be this size
private static final int OVERWORLD_TEXTURE_SCALE = 7;
private static final int LARGEMAP_MARKER = 30000001; //This is a marker to flag world map packets, it must be an impossible cx coordinate
//Color related constants
private static final int OCEAN_HEIGHT = 63;
private static final int DEEP_OCEAN = 56;
static
{
//TODO: Deal with mods like ExtraBiomes
setupColours();
}
public static void reset()
{
if (currentMap != null) currentMap.abort();
currentMap = null;
saveMapProgress();
threadCurrentMap = null;
threadSlowMap = null;
queuedMaps.clear();
calculatingMap.set(false);
doneOverworldTexture = false;
overworldImageCompressed = null;
}
@SideOnly(Side.CLIENT)
public static void resetClient()
{
resetClientFlag.set(true);
//Threadsafe
}
@SideOnly(Side.CLIENT)
public static void resetClientBody()
{
ClientProxyCore.overworldTexturesValid = false;
clientRequests.clear();
overworldImageBytesPart = null;
File baseFolder = new File(FMLClientHandler.instance().getClient().mcDataDir, "assets/galacticraftMaps");
if (baseFolder.exists() && baseFolder.isDirectory())
{
for (File f : baseFolder.listFiles())
{
if (f.isFile())
{
f.delete();
}
}
}
GalacticraftCore.packetPipeline.sendToServer(new PacketSimple(PacketSimple.EnumSimplePacket.S_REQUEST_OVERWORLD_IMAGE, GCCoreUtil.getDimensionID(FMLClientHandler.instance().getClient().theWorld), new Object[] {}));
ClientProxyCore.overworldTextureRequestSent = true;
DrawGameScreen.reusableMap = new DynamicTexture(MapUtil.SIZE_STD2, MapUtil.SIZE_STD2);
MapUtil.biomeColours.clear();
setupColours();
}
/**
* The BufferedImage needs to be already set up as a sized image of TYPE_INT_RGB
*/
public static void getLocalMap(World world, int chunkXPos, int chunkZPos, BufferedImage image)
{
for (int x0 = -12; x0 <= 12; x0++)
{
for (int z0 = -12; z0 <= 12; z0++)
{
Chunk chunk = world.getChunkFromChunkCoords(chunkXPos + x0, chunkZPos + z0);
if (chunk != null)
{
for (int z = 0; z < 16; z++)
{
for (int x = 0; x < 16; x++)
{
int l4 = chunk.getHeight(new BlockPos(x, 0, z)) + 1;
Block block = Blocks.air;
IBlockState i5 = null;
if (l4 > 1)
{
do
{
--l4;
block = chunk.getBlock(x, l4, z);
i5 = chunk.getBlockState(new BlockPos(x, l4, z));
}
while (block.getMapColor(i5) == MapColor.airColor && l4 > 0);
}
int col = block.getMapColor(i5).colorValue;
image.setRGB(x + (x0 + 12) * 16, z + (z0 + 12) * 16, col);
}
}
}
}
}
}
public static void makeOverworldTexture()
{
if (doneOverworldTexture)
{
return;
}
World overworld = WorldUtil.getProviderForDimensionServer(ConfigManagerCore.idDimensionOverworld).worldObj;
if (overworld == null)
{
return;
}
if (overworld.getWorldType() == WorldType.FLAT || !(overworld.provider instanceof WorldProviderSurface))
{
doneOverworldTexture = true;
return;
}
File baseFolder = new File(MinecraftServer.getServer().worldServerForDimension(0).getChunkSaveLocation(), "galacticraft/overworldMap");
if (!baseFolder.exists() && !baseFolder.mkdirs())
{
GCLog.severe("Base folder(s) could not be created: " + baseFolder.getAbsolutePath());
doneOverworldTexture = true;
return;
}
if (MapUtil.getBiomeMapForCoords(overworld, 0, 0, OVERWORLD_TEXTURE_SCALE, OVERWORLD_TEXTURE_WIDTH, OVERWORLD_TEXTURE_HEIGHT, baseFolder))
{
doneOverworldTexture = true;
}
//This will make the 'slow map', a map covering a large part of the world around spawn
//(On a typical modern PC, this should take 20-30 minutes to generate in its own thread)
MapUtil.getBiomeMapForCoords(overworld, 0, 0, OVERWORLD_MAP_SCALE, OVERWORLD_LARGEMAP_WIDTH, OVERWORLD_LARGEMAP_HEIGHT, baseFolder);
}
public static void sendOverworldToClient(EntityPlayerMP client)
{
if (doneOverworldTexture)
{
try
{
File baseFolder = new File(MinecraftServer.getServer().worldServerForDimension(0).getChunkSaveLocation(), "galacticraft/overworldMap");
if (!baseFolder.exists())
{
GCLog.severe("Base folder missing: " + baseFolder.getAbsolutePath());
return;
}
File file = new File(baseFolder, "Overworld" + OVERWORLD_TEXTURE_WIDTH + ".bin");
if (file.exists())
{
sendMapPacket(0, 0, client, FileUtils.readFileToByteArray(file));
}
file = new File(baseFolder, "Overworld" + OVERWORLD_LARGEMAP_WIDTH + ".bin");
if (file.exists())
{
sendMapPacket(LARGEMAP_MARKER, 0, client, FileUtils.readFileToByteArray(file));
}
}
catch (Exception ex)
{
System.err.println("Error sending overworld image to player.");
ex.printStackTrace();
}
}
}
public static void sendOrCreateMap(World world, int cx, int cz, EntityPlayerMP client)
{
if (world.getWorldType() == WorldType.FLAT || !(world.provider instanceof WorldProviderSurface))
{
doneOverworldTexture = true;
return;
}
try
{
File baseFolder = new File(MinecraftServer.getServer().worldServerForDimension(0).getChunkSaveLocation(), "galacticraft/overworldMap");
if (!baseFolder.exists())
{
GCLog.severe("Base folder missing: " + baseFolder.getAbsolutePath());
return;
}
File file = makeFileName(baseFolder, cx, cz);
if (!file.exists())
{
getBiomeMapForCoords(world, cx, cz, 1, SIZE_STD, SIZE_STD, baseFolder);
return;
}
sendMapPacket(cx, cz, client, FileUtils.readFileToByteArray(file));
}
catch (Exception ex)
{
System.err.println("Error sending map image to player.");
ex.printStackTrace();
}
}
public static void sendMapPacket(int cx, int cz, EntityPlayerMP client, byte[] largeMap) throws IOException
{
byte[] compressed;
if (cx == LARGEMAP_MARKER)
{
if (overworldImageCompressed == null)
{
overworldImageCompressed = zipCompress(largeMap);
}
compressed = overworldImageCompressed;
}
else
{
compressed = zipCompress(largeMap);
}
sendMapPacketCompressed(cx, cz, client, compressed);
}
public static void sendMapPacketToAll(int cx, int cz, byte[] largeMap)
{
byte[] compressed;
if (cx == LARGEMAP_MARKER)
{
if (overworldImageCompressed == null)
{
overworldImageCompressed = zipCompress(largeMap);
}
compressed = overworldImageCompressed;
}
else
{
compressed = zipCompress(largeMap);
}
sendMapPacketAllCompressed(cx, cz, compressed);
}
private static void sendMapPacketCompressed(int cx, int cz, EntityPlayerMP client, byte[] map) throws IOException
{
if (cx == LARGEMAP_MARKER && map.length < 2040000)
{
int halfSize = map.length / 2;
byte[] largeMapPartA = Arrays.copyOf(map, halfSize);
byte[] largeMapPartB = Arrays.copyOfRange(map, halfSize, map.length);
GalacticraftCore.packetPipeline.sendTo(new PacketSimple(EnumSimplePacket.C_SEND_OVERWORLD_IMAGE, GCCoreUtil.getDimensionID(client.worldObj), new Object[] { cx, map.length, largeMapPartA }), client);
GalacticraftCore.packetPipeline.sendTo(new PacketSimple(EnumSimplePacket.C_SEND_OVERWORLD_IMAGE, GCCoreUtil.getDimensionID(client.worldObj), new Object[] { cx + 1, map.length, largeMapPartB }), client);
}
else if (map.length < 1020000) //That's about the limit on a Forge packet length
{
GalacticraftCore.packetPipeline.sendTo(new PacketSimple(EnumSimplePacket.C_SEND_OVERWORLD_IMAGE, GCCoreUtil.getDimensionID(client.worldObj), new Object[] { cx, cz, map }), client);
}
}
private static void sendMapPacketAllCompressed(int cx, int cz, byte[] map)
{
if (cx == LARGEMAP_MARKER && map.length < 2040000)
{
int halfSize = map.length / 2;
byte[] largeMapPartA = Arrays.copyOf(map, halfSize);
byte[] largeMapPartB = Arrays.copyOfRange(map, halfSize, map.length);
GCCoreUtil.sendToAllDimensions(EnumSimplePacket.C_SEND_OVERWORLD_IMAGE, new Object[] { cx, map.length, largeMapPartA });
GCCoreUtil.sendToAllDimensions(EnumSimplePacket.C_SEND_OVERWORLD_IMAGE, new Object[] { cx + 1, map.length, largeMapPartB });
}
else if (map.length < 1020000) //That's about the limit on a Forge packet length
{
GCCoreUtil.sendToAllDimensions(EnumSimplePacket.C_SEND_OVERWORLD_IMAGE, new Object[] { cx, cz, map });
}
}
/**
* On a server, build any needed patchwork map files around co-ordinates (x, z)
* The needed files may already have been generated by previous calls on the same server
* Files are stored in the world save folder, subfolder galacticraft/overworldMap
*/
public static boolean buildMaps(World world, int x, int z)
{
if (world.getWorldType() == WorldType.FLAT || !(world.provider instanceof WorldProviderSurface))
{
return false;
}
File baseFolder = new File(MinecraftServer.getServer().worldServerForDimension(0).getChunkSaveLocation(), "galacticraft/overworldMap");
if (!baseFolder.exists() && !baseFolder.mkdirs())
{
GCLog.severe("Base folder(s) could not be created: " + baseFolder.getAbsolutePath());
return false;
}
int cx = convertMap(x);
int cz = convertMap(z);
getBiomeMapForCoords(world, cx, cz, 1, SIZE_STD, SIZE_STD, baseFolder);
getBiomeMapForCoords(world, cx + SIZE_STD2, cz, 1, SIZE_STD, SIZE_STD, baseFolder);
getBiomeMapForCoords(world, cx, cz + SIZE_STD2, 1, SIZE_STD, SIZE_STD, baseFolder);
getBiomeMapForCoords(world, cx - SIZE_STD2, cz, 1, SIZE_STD, SIZE_STD, baseFolder);
getBiomeMapForCoords(world, cx, cz - SIZE_STD2, 1, SIZE_STD, SIZE_STD, baseFolder);
getBiomeMapForCoords(world, cx + SIZE_STD2, cz + SIZE_STD2, 1, SIZE_STD, SIZE_STD, baseFolder);
getBiomeMapForCoords(world, cx - SIZE_STD2, cz + SIZE_STD2, 1, SIZE_STD, SIZE_STD, baseFolder);
getBiomeMapForCoords(world, cx - SIZE_STD2, cz - SIZE_STD2, 1, SIZE_STD, SIZE_STD, baseFolder);
getBiomeMapForCoords(world, cx + SIZE_STD2, cz - SIZE_STD2, 1, SIZE_STD, SIZE_STD, baseFolder);
return true;
}
private static int convertMap(int x)
{
int cx = x + SIZE_STD;
if (cx < 0)
{
cx -= SIZE_STD2 - 1;
}
cx /= SIZE_STD2;
return cx * SIZE_STD2;
}
public static boolean getBiomeMapForCoords(World world, int cx, int cz, int scale, int sizeX, int sizeZ, File baseFolder)
{
File outputFile;
if (sizeX != sizeZ)
{
outputFile = new File(baseFolder, "Overworld" + sizeX + ".bin");
if (sizeX == OVERWORLD_LARGEMAP_WIDTH)
{
MapGen newGen = new MapGen(world, sizeX, sizeZ, cx, cz, 1 << scale, outputFile);
if (newGen.mapNeedsCalculating)
{
slowMap = newGen;
calculatingMap.set(true);
}
return false;
}
}
else
{
outputFile = makeFileName(baseFolder, cx, cz);
}
MapGen newGen = new MapGen(world, sizeX, sizeZ, cx, cz, 1 << scale, outputFile);
if (newGen.mapNeedsCalculating)
{
if (calculatingMap.getAndSet(true))
{
queuedMaps.add(newGen);
}
else
{
currentMap = newGen;
}
return false;
}
return true;
}
public static void saveMapProgress()
{
if (slowMap != null)
{
slowMap.abort();
try
{
Thread.currentThread().sleep(90);
} catch (InterruptedException e)
{ }
slowMap.writeOutputFile(false);
slowMap = null;
}
}
/**
* Poll any map threads to see if they need starting or if they're finished
*
* Multi-threaded version - runs each MapGen in its own thread, polls MapGen.finishedCalculatingMap to know when finished
*/
public static void BiomeMapNextTick_MultiThreaded()
{
if (currentMap != null)
{
if (threadCurrentMap == null)
{
//Create the current map thread, pausing any slow map thread
if (slowMap != null)
{
slowMap.pause();
}
//TODO = should it use a re-usable thread pool?
threadCurrentMap = new Thread(currentMap);
threadCurrentMap.setName("Background world mapping");
threadCurrentMap.setPriority(Thread.NORM_PRIORITY - 1);
threadCurrentMap.start();
}
else if (currentMap.finishedCalculating.get())
{
//Finished the current map
threadCurrentMap = null;
currentMap.writeOutputFile(true);
if (queuedMaps.size() > 0)
{
currentMap = queuedMaps.removeFirst();
}
else
{
currentMap = null;
if (slowMap == null)
calculatingMap.set(false);
else
{
if (slowMap != null)
{
slowMap.resume();
}
}
}
}
return;
}
if (!queuedMaps.isEmpty())
{
if (slowMap != null)
{
slowMap.pause();
}
currentMap = queuedMaps.removeFirst();
return;
}
if (slowMap != null)
{
if (threadSlowMap == null)
{
//Create the slow map thread
threadSlowMap = new Thread(slowMap);
threadSlowMap.setName("Background world mapping");
threadSlowMap.setPriority(Thread.NORM_PRIORITY - 1);
threadSlowMap.start();
}
else if (slowMap.finishedCalculating.get())
{
//Finished the current map
threadSlowMap = null;
slowMap.writeOutputFile(true);
slowMap = null;
calculatingMap.set(false);
}
return;
}
}
//Single Threaded Version of the same code
//(Currently unused)
public static void BiomeMapNextTick_SingleThreaded()
{
MapGen map;
boolean doingSlow = false;
if (currentMap != null)
{
map = currentMap;
}
else if (slowMap != null)
{
map = slowMap;
doingSlow = true;
}
else
{
return;
}
//If single threade, allow GC background mapping around 9% of the server tick time if server running at full speed
//(on a slow server, it will be proportionately lower %)
long end = System.nanoTime() + 4500000L;
while (System.nanoTime() < end)
{
if (map.BiomeMapOneTick())
{
//Finished
map.writeOutputFile(true);
if (doingSlow)
{
slowMap = null;
}
else
{
currentMap = null;
if (queuedMaps.size() > 0)
{
currentMap = queuedMaps.removeFirst();
}
}
if (currentMap == null && slowMap == null)
{
calculatingMap.set(false);
}
return;
}
}
}
public static boolean backgroundMapping(Thread currentThread)
{
return currentThread == threadSlowMap || currentThread == threadCurrentMap;
}
/**
* Converts a 48px high image to a 12px high image with a palette chosen only from the colours in the paletteImage
*
* @param overworldImage Output image already created as a blank image, dimensions biomeMapSizeX x biomeMapSizeY
* @param paletteImage Palette image, dimensions must be a square with sides biomeMapSizeZ / 4
*/
public static BufferedImage convertTo12pxTexture(BufferedImage overworldImage, BufferedImage paletteImage)
{
BufferedImage result = new BufferedImage(overworldImage.getWidth(), overworldImage.getHeight(), BufferedImage.TYPE_INT_RGB);
TreeMap<Integer, Integer> mapColPos = new TreeMap();
TreeMap<Integer, Integer> mapColPosB = new TreeMap();
int count = 0;
for (int x = 0; x < overworldImage.getWidth(); x += 4)
{
for (int z = 0; z < overworldImage.getHeight(); z += 4)
{
int r = 0;
int g = 0;
int b = 0;
for (int xx = 0; xx < 4; xx++)
{
for (int zz = 0; zz < 4; zz++)
{
int col = overworldImage.getRGB(xx + x, zz + z);
r += (col >> 16);
g += (col >> 8) & 255;
b += col & 255;
}
}
while (mapColPos.containsKey(g - b))
{
g++;
}
mapColPos.put(g - b, count);
if (x < overworldImage.getHeight())
{
int col = paletteImage.getRGB(x + 1, z + 1);
r = (col >> 16);
g = (col >> 8) & 255;
b = col & 255;
while (mapColPosB.containsKey(g - b))
{
g++;
}
mapColPosB.put(g - b, col);
}
count++;
}
}
count = 0;
int newCol = 0;
Iterator<Integer> it = mapColPosB.keySet().iterator();
Iterator<Integer> itt = mapColPos.keySet().iterator();
int modulus = overworldImage.getHeight() / 4;
int mod2 = overworldImage.getWidth() / overworldImage.getHeight();
for (int x = 0; x < overworldImage.getWidth() / 4; x++)
{
for (int z = 0; z < modulus; z++)
{
if (count % mod2 == 0)
{
newCol = mapColPosB.get(it.next());
}
int position = mapColPos.get(itt.next());
int xx = position / modulus;
int zz = position % modulus;
for (int xxx = 0; xxx < 4; xxx++)
{
for (int zzz = 0; zzz < 4; zzz++)
{
result.setRGB(xx * 4 + xxx, zz * 4 + zzz, newCol);
}
}
count++;
}
}
return result;
}
//Unused
public static BufferedImage readImage(Object source) throws IOException
{
ImageInputStream stream = ImageIO.createImageInputStream(source);
ImageReader reader = ImageIO.getImageReaders(stream).next();
reader.setInput(stream);
ImageReadParam param = reader.getDefaultReadParam();
ImageTypeSpecifier typeToUse = null;
for (Iterator i = reader.getImageTypes(0); i.hasNext(); )
{
ImageTypeSpecifier type = (ImageTypeSpecifier) i.next();
if (type.getColorModel().getColorSpace().isCS_sRGB())
{
typeToUse = type;
}
}
if (typeToUse != null)
{
param.setDestinationType(typeToUse);
}
BufferedImage b = reader.read(0, param);
reader.dispose();
stream.close();
return b;
}
@SideOnly(Side.CLIENT)
public static void writeImgToFile(BufferedImage img, String name)
{
if (GalacticraftCore.enableJPEG)
{
File folder = new File(FMLClientHandler.instance().getClient().mcDataDir, "assets/galacticraftMaps");
try
{
ImageOutputStream outputStreamA = new FileImageOutputStream(new File(folder, name));
GalacticraftCore.jpgWriter.setOutput(outputStreamA);
GalacticraftCore.jpgWriter.write(null, new IIOImage(img, null, null), GalacticraftCore.writeParam);
outputStreamA.close();
}
catch (Exception e)
{
}
}
}
public static byte[] zipCompress(byte[] data)
{
Deflater deflater = new Deflater();
deflater.setLevel(Deflater.BEST_SPEED);
deflater.setInput(data);
deflater.finish();
ByteArrayOutputStream compressed = new ByteArrayOutputStream(data.length * 2 / 3);
byte[] miniBuffer = new byte[4096];
while (!deflater.finished())
{
int count = deflater.deflate(miniBuffer);
compressed.write(miniBuffer, 0, count);
}
return compressed.toByteArray();
}
private static byte[] zipDeCompress(byte[] data) throws DataFormatException
{
Inflater inflater = new Inflater();
inflater.setInput(data);
ByteArrayOutputStream deCompressed = new ByteArrayOutputStream(data.length * 2);
byte[] miniBuffer = new byte[4096];
while (!inflater.finished())
{
int count = inflater.inflate(miniBuffer);
deCompressed.write(miniBuffer, 0, count);
}
return deCompressed.toByteArray();
}
@SideOnly(Side.CLIENT)
public static void receiveOverworldImageCompressed(int cx, int cz, byte[] raw) throws IOException
{
if (cx == LARGEMAP_MARKER)
{
//Received large map part A
if (overworldImageBytesPart == null)
{
overworldImageBytesPart = raw;
return;
}
else
{
byte[] overWorldImageComplete = Arrays.copyOf(raw, cz);
int offsetPartB = cz / 2;
for (int i = offsetPartB; i < cz; i++)
overWorldImageComplete[i] = overworldImageBytesPart[i - offsetPartB];
overworldImageBytesPart = null;
raw = overWorldImageComplete;
}
}
else if (cx == LARGEMAP_MARKER + 1)
{
//Received large map part B
if (overworldImageBytesPart == null)
{
overworldImageBytesPart = raw;
return;
}
else
{
byte[] overWorldImageComplete = Arrays.copyOf(overworldImageBytesPart, cz);
int offsetPartB = cz / 2;
for (int i = offsetPartB; i < cz; i++)
overWorldImageComplete[i] = raw[i - offsetPartB];
overworldImageBytesPart = null;
raw = overWorldImageComplete;
}
}
try
{
getOverworldImageFromRaw(cx, cz, zipDeCompress(raw));
} catch (DataFormatException e)
{
GCLog.debug(e.toString());
GCLog.debug("Client received a corrupted map image data packet from server " + cx + "_" + cz);
}
}
@SideOnly(Side.CLIENT)
public static void getOverworldImageFromRaw(int cx, int cz, byte[] raw) throws IOException
{
File folder = MapUtil.getClientMapsFolder();
if (raw.length == OVERWORLD_LARGEMAP_WIDTH * OVERWORLD_LARGEMAP_HEIGHT * 2)
{
if (folder != null)
{
File file0 = new File(folder, "overworldRaw.bin");
if (!file0.exists() || (file0.canRead() && file0.canWrite()))
{
FileUtils.writeByteArrayToFile(file0, raw);
}
else
{
System.err.println("Cannot write to file %minecraft%/assets/galacticraftMaps/overworldRaw.bin");
}
}
else
{
System.err.println("No folder for file %minecraft%/assets/galacticraftMaps/overworldRaw.bin");
}
//raw is a WIDTH_WORLD x HEIGHT_WORLD array of 2 byte entries: biome type followed by height
//Here we will make a texture from that, but twice as large: 4 pixels for each data point, it just looks better that way when the texture is used
BufferedImage worldImageLarge = new BufferedImage(OVERWORLD_LARGEMAP_WIDTH * 2, OVERWORLD_LARGEMAP_HEIGHT * 2, BufferedImage.TYPE_INT_RGB);
ArrayList<Integer> cols = new ArrayList<Integer>();
int lastcol = -1;
int idx = 0;
for (int x = 0; x < OVERWORLD_LARGEMAP_WIDTH; x++)
{
for (int z = 0; z < OVERWORLD_LARGEMAP_HEIGHT; z++)
{
int arrayIndex = (x * OVERWORLD_LARGEMAP_HEIGHT + z) * 2;
int biome = ((int) raw[arrayIndex]) & 255;
int height = ((int) raw[arrayIndex + 1]) & 255;
if (height < OCEAN_HEIGHT && biome != 2 && biome != 10)
{
//Includes ponds, lakes and rivers in other biomes
biome = 0;
}
if (height < DEEP_OCEAN && biome == 0)
{
biome = 24;
}
worldImageLarge.setRGB(x * 2, z * 2, convertBiomeColour(biome, height));
worldImageLarge.setRGB(x * 2, z * 2 + 1, convertBiomeColour(biome, height));
worldImageLarge.setRGB(x * 2 + 1, z * 2, convertBiomeColour(biome, height));
worldImageLarge.setRGB(x * 2 + 1, z * 2 + 1, convertBiomeColour(biome, height));
}
}
//overworldTextureLarge is currently unused in beta
// if (ClientProxyCore.overworldTextureLarge == null)
// {
// ClientProxyCore.overworldTextureLarge = new DynamicTextureProper(WIDTH_WORLD * 2, HEIGHT_WORLD * 2);
// }
// ClientProxyCore.overworldTextureLarge.update(worldImageLarge);
//Write it to a .jpg file on client for beta preview
if (GalacticraftCore.enableJPEG && folder != null)
{
ImageOutputStream outputStream = new FileImageOutputStream(new File(folder, "large.jpg"));
GalacticraftCore.jpgWriter.setOutput(outputStream);
GalacticraftCore.jpgWriter.write(null, new IIOImage(worldImageLarge, null, null), GalacticraftCore.writeParam);
outputStream.close();
}
}
//This is the dimensions of the Overworld texture map
else if (raw.length == OVERWORLD_TEXTURE_WIDTH * OVERWORLD_TEXTURE_HEIGHT * 2)
{
//raw is a WIDTH_STD x HEIGHT_STD array of 2 byte entries: biome type followed by height
BufferedImage worldImage = new BufferedImage(OVERWORLD_TEXTURE_WIDTH, OVERWORLD_TEXTURE_HEIGHT, BufferedImage.TYPE_INT_RGB);
ArrayList<Integer> cols = new ArrayList<Integer>();
int lastcol = -1;
int idx = 0;
for (int x = 0; x < OVERWORLD_TEXTURE_WIDTH; x++)
{
for (int z = 0; z < OVERWORLD_TEXTURE_HEIGHT; z++)
{
int arrayIndex = (x * OVERWORLD_TEXTURE_HEIGHT + z) * 2;
int biome = ((int) raw[arrayIndex]) & 255;
int height = ((int) raw[arrayIndex + 1]) & 255;
if (height < OCEAN_HEIGHT && biome != 2 && biome != 10)
{
//Includes ponds, lakes and rivers in other biomes
biome = 0;
}
if (height < DEEP_OCEAN && biome == 0)
{
biome = 24;
}
worldImage.setRGB(x, z, convertBiomeColour(biome, height));
}
}
IResourceManager rm = Minecraft.getMinecraft().getResourceManager();
BufferedImage paletteImage = null;
try
{
InputStream in = rm.getResource(new ResourceLocation(Constants.ASSET_PREFIX, "textures/gui/celestialbodies/earth.png")).getInputStream();
paletteImage = ImageIO.read(in);
in.close();
paletteImage.getHeight();
}
catch (Exception e)
{
e.printStackTrace();
return;
}
BufferedImage result = convertTo12pxTexture(worldImage, paletteImage);
if (result != null)
{
if (ClientProxyCore.overworldTextureWide == null)
{
ClientProxyCore.overworldTextureWide = new DynamicTextureProper(OVERWORLD_TEXTURE_WIDTH, OVERWORLD_TEXTURE_HEIGHT);
}
if (ClientProxyCore.overworldTextureClient == null)
{
ClientProxyCore.overworldTextureClient = new DynamicTextureProper(OVERWORLD_TEXTURE_HEIGHT, OVERWORLD_TEXTURE_HEIGHT);
}
ClientProxyCore.overworldTextureWide.update(result);
ClientProxyCore.overworldTextureClient.update(result);
ClientProxyCore.overworldTexturesValid = true;
}
}
else if (folder != null)
{
File file0 = makeFileName(folder, cx, cz);
if (!file0.exists() || (file0.canRead() && file0.canWrite()))
{
FileUtils.writeByteArrayToFile(file0, raw);
}
}
else
{
System.err.println("No folder %minecraft%/assets/galacticraftMaps for local map file.");
}
}
@SideOnly(Side.CLIENT)
public static boolean getMap(int[] image, World world, BlockPos pos)
{
int xCoord = pos.getX();
int zCoord = pos.getZ();
int cx = convertMap(xCoord);
int cz = convertMap(zCoord);
File baseFolder = new File(FMLClientHandler.instance().getClient().mcDataDir, "assets/galacticraftMaps");
if (!baseFolder.exists() && !baseFolder.mkdirs())
{
GCLog.severe("Base folder(s) could not be created: " + baseFolder.getAbsolutePath());
return false;
}
int dim = GCCoreUtil.getDimensionID(world);
boolean result = true;
if (makeRGBimage(image, baseFolder, cx - SIZE_STD2, cz - SIZE_STD2, 0, 0, xCoord, zCoord, dim, result))
{
result = false;
}
if (makeRGBimage(image, baseFolder, cx - SIZE_STD2, cz, 0, SIZE_STD, xCoord, zCoord, dim, result))
{
result = false;
}
if (makeRGBimage(image, baseFolder, cx - SIZE_STD2, cz + SIZE_STD2, 0, SIZE_STD2, xCoord, zCoord, dim, result))
{
result = false;
}
if (makeRGBimage(image, baseFolder, cx, cz - SIZE_STD2, SIZE_STD, 0, xCoord, zCoord, dim, result))
{
result = false;
}
if (makeRGBimage(image, baseFolder, cx, cz, SIZE_STD, SIZE_STD, xCoord, zCoord, dim, result))
{
result = false;
}
if (makeRGBimage(image, baseFolder, cx, cz + SIZE_STD2, SIZE_STD, SIZE_STD2, xCoord, zCoord, dim, result))
{
result = false;
}
if (makeRGBimage(image, baseFolder, cx + SIZE_STD2, cz - SIZE_STD2, SIZE_STD2, 0, xCoord, zCoord, dim, result))
{
result = false;
}
if (makeRGBimage(image, baseFolder, cx + SIZE_STD2, cz, SIZE_STD2, SIZE_STD, xCoord, zCoord, dim, result))
{
result = false;
}
if (makeRGBimage(image, baseFolder, cx + SIZE_STD2, cz + SIZE_STD2, SIZE_STD2, SIZE_STD2, xCoord, zCoord, dim, result))
{
result = false;
}
return result;
}
@SideOnly(Side.CLIENT)
private static boolean makeRGBimage(int[] array, File baseFolder, int cx, int cz, int offsetX, int offsetZ, int xCoord, int zCoord, int dim, boolean prevResult)
{
File filename = makeFileName(baseFolder, cx, cz);
if (!filename.exists())
{
if (clientRequests.contains(filename.getName()))
{
//GCLog.debug("Info: Server not yet ready to send map file " + baseFolder.getName() + "/" + filename.getName());
}
else
{
clientRequests.add(filename.getName());
//GCLog.debug("Info: Client requested map file" + filename.getName());
GalacticraftCore.packetPipeline.sendToServer(new PacketSimple(PacketSimple.EnumSimplePacket.S_REQUEST_MAP_IMAGE, GCCoreUtil.getDimensionID(FMLClientHandler.instance().getClient().theWorld), new Object[] { dim, cx, cz }));
}
return true;
}
if (!prevResult)
{
return true;
}
int ox = (convertMap(xCoord) - xCoord - SIZE_STD) / 2;
int oz = (convertMap(zCoord) - zCoord - SIZE_STD) / 2;
byte[] raw = null;
try
{
raw = FileUtils.readFileToByteArray(filename);
}
catch (IOException e)
{
GCLog.severe("Problem reading map file: " + baseFolder.getAbsolutePath() + filename.getName());
return true;
}
if (raw == null || raw.length != SIZE_STD * SIZE_STD * 2)
{
GCLog.debug("Warning: unexpected map size is " + raw.length + " for file " + filename.toString());
return true;
}
int xstart = Math.max(0, -offsetX - ox);
int zstart = Math.max(0, -offsetZ - oz);
for (int x = xstart; x < SIZE_STD; x++)
{
int imagex = x + offsetX + ox;
if (imagex >= SIZE_STD2)
{
break;
}
for (int z = zstart; z < SIZE_STD; z++)
{
int imageZ = z + oz + offsetZ;// + SIZE_STD - 1 - z;
if (imageZ >= SIZE_STD2)
{
break;
}
int arrayIndex = (x * SIZE_STD + z) * 2;
int biome = ((int) raw[arrayIndex]) & 255;
int height = ((int) raw[arrayIndex + 1]) & 255;
if (height < OCEAN_HEIGHT && biome != 2 && biome != 10)
{
//Includes ponds, lakes and rivers in other biomes
biome = 0;
}
if (height < DEEP_OCEAN && biome == 0)
{
biome = 24;
}
if (imagex < 0 || imageZ < 0)
{
GCLog.debug("Outside image " + imagex + "," + imageZ + " - " + "x=" + x + " z=" + z + " offsetX=" + offsetX + " offsetZ = " + offsetZ + " ox=" + ox + " oz=" + oz);
}
else
{
array[imagex + SIZE_STD2 * imageZ] = convertBiomeColour(biome, height) + 0xff000000;
}
}
}
return false;
}
private static File makeFileName(File folder, int cx, int cz)
{
return new File(folder, "overworld" + cx / SIZE_STD2 + "_" + cz / SIZE_STD2 + ".bin");
}
private static int getBiomeBaseColour(int biomeId)
{
BiomeGenBase[] biomeList = BiomeGenBase.getBiomeGenArray();
BiomeGenBase biomegenbase = null;
if (biomeId >= 0 && biomeId <= biomeList.length)
{
biomegenbase = biomeList[biomeId];
}
return biomegenbase == null ? BiomeGenBase.ocean.color : biomegenbase.color;
}
public static int convertBiomeColour(int in, int height)
{
int rv;
int s = MapUtil.biomeColours.size();
if (in >= 128 && in < 128 + s)
{
in -= 128;
}
if (in >= s)
{
rv = getBiomeBaseColour(in);
}
else
{
BlockVec3 bv = MapUtil.biomeColours.get(in);
if (bv == null)
{
rv = getBiomeBaseColour(in);
}
else
{
if (bv.z > 0 && MapUtil.rand.nextInt(100) < bv.z)
{
rv = bv.y;
}
else
{
rv = bv.x;
}
}
}
if (rv == 0x9c2424 && MapUtil.rand.nextInt(2) == 0)
{
rv = 0xbfa384;
}
if (height < OCEAN_HEIGHT)
{
return rv;
}
if (height > 92 && (in == 3 || in == 20 || in == 31 || in == 33 || in == 34))
{
if (MapUtil.rand.nextInt(8) > 98 - height)
{
rv = Material.snow.getMaterialMapColor().colorValue;
}
}
float factor = (height - 68F) / 94F;
return ColorUtil.lighten(rv, factor);
}
private static void setupColours()
{
//ocean = Ocean(0) colour(112) "Ocean"
MapUtil.biomeColours.add(new BlockVec3(Material.water.getMaterialMapColor().colorValue, 0, 0));
//plains = Plains(1) colour(9286496) "Plains"
MapUtil.biomeColours.add(new BlockVec3(0x497436, 0, 0));
//desert = Desert(2) colour(16421912) "Desert"
MapUtil.biomeColours.add(new BlockVec3(0xd4cd98, Material.cactus.getMaterialMapColor().colorValue, 3));
//extremeHills = Hills(3, false) colour(6316128) "Extreme Hills"
MapUtil.biomeColours.add(new BlockVec3(0x4d654c, Material.rock.getMaterialMapColor().colorValue, 15));
//forest = Forest(4, 0) colour(353825) "Forest"
MapUtil.biomeColours.add(new BlockVec3(0x3c7521, 0x295416, 45));
//taiga = Taiga(5, 0) colour(747097) "Taiga"
MapUtil.biomeColours.add(new BlockVec3(0x627e61, 0x172a17, 18));
//swampland = Swamp(6) colour(522674) "Swampland"
MapUtil.biomeColours.add(new BlockVec3(0x43541b, 0x111309, 25));
//river = River(7) colour(255) "River"
MapUtil.biomeColours.add(new BlockVec3(0x497436, 0, 0));
MapUtil.biomeColours.add(new BlockVec3(0, 0, 0));
MapUtil.biomeColours.add(new BlockVec3(0, 0, 0));
//frozenOcean = Ocean(10) colour(9474208) "FrozenOcean"
MapUtil.biomeColours.add(new BlockVec3(Material.ice.getMaterialMapColor().colorValue, 0, 0));
//frozenRiver = River(11) colour(10526975) "FrozenRiver"
MapUtil.biomeColours.add(new BlockVec3(Material.ice.getMaterialMapColor().colorValue, 0, 0));
//icePlains = Snow(12, false) colour(16777215) "Ice Plains"
MapUtil.biomeColours.add(new BlockVec3(Material.snow.getMaterialMapColor().colorValue, 0x497436, 3));
//iceMountains = Snow(13, false) colour(10526880) "Ice Mountains"
MapUtil.biomeColours.add(new BlockVec3(Material.snow.getMaterialMapColor().colorValue, Material.ice.getMaterialMapColor().colorValue, 5));
//mushroomIsland = MushroomIsland(14) colour(16711935) "MushroomIsland"
MapUtil.biomeColours.add(new BlockVec3(0x63565f, 0x7c1414, 10));
//mushroomIslandShore = MushroomIsland(15) colour(10486015) "MushroomIslandShore"
MapUtil.biomeColours.add(new BlockVec3(0x6a6066, 0, 0));
//beach = Beach(16) colour(16440917) "Beach"
MapUtil.biomeColours.add(new BlockVec3(Material.sand.getMaterialMapColor().colorValue, 0, 0));
//desertHills = Desert(17) colour(13786898) "DesertHills"
MapUtil.biomeColours.add(new BlockVec3(0xd4cd98, 0, 0));
//forestHills = Forest(18, 0) colour(2250012) "ForestHills"
MapUtil.biomeColours.add(new BlockVec3(0x3c7521, 0x295416, 35));
//taigaHills = Taiga(19, 0) colour(1456435) "TaigaHills"
MapUtil.biomeColours.add(new BlockVec3(0x627e61, 0x172a17, 14));
//extremeHillsEdge = Hills(20, true) colour(7501978) "Extreme Hills Edge"
MapUtil.biomeColours.add(new BlockVec3(0x4d654c, 0x497436, 50));
//jungle = Jungle(21, false) colour(5470985) "Jungle"
MapUtil.biomeColours.add(new BlockVec3(0x176c03, 0x0f4502, 25));
//jungleHills = Jungle(22, false) colour(2900485) "JungleHills"
MapUtil.biomeColours.add(new BlockVec3(0x176c03, 0x0f4502, 25));
//jungleEdge = Jungle(23, true) colour(6458135) "JungleEdge"
MapUtil.biomeColours.add(new BlockVec3(0x176c03, 0x0f4502, 25));
//deepOcean = Ocean(24) colour(48) "Deep Ocean"
MapUtil.biomeColours.add(new BlockVec3(0x2f2fd4, 0, 0));
//stoneBeach = StoneBeach(25) colour(10658436) "Stone Beach"
MapUtil.biomeColours.add(new BlockVec3(Material.rock.getMaterialMapColor().colorValue, 0, 0));
//coldBeach = Beach(26) colour(16445632) "Cold Beach"
MapUtil.biomeColours.add(new BlockVec3(Material.sand.getMaterialMapColor().colorValue, Material.snow.getMaterialMapColor().colorValue, 75));
//birchForest = Forest(27, 2)) colour(3175492) "Birch Forest"
MapUtil.biomeColours.add(new BlockVec3(0x516b36, 0x497436, 65));
//birchForestHills = Forest(28, 2)) colour(2055986) "Birch Forest Hills"
MapUtil.biomeColours.add(new BlockVec3(0x516b36, 0x497436, 55));
//roofedForest = Forest(29, 3) colour(4215066) "Roofed Forest"
MapUtil.biomeColours.add(new BlockVec3(0x9c2424, 0x1e2e18, 98));
//coldTaiga = Taiga(30, 0) colour(3233098) "Cold Taiga"
MapUtil.biomeColours.add(new BlockVec3(Material.snow.getMaterialMapColor().colorValue, 0x172a17, 12));
//coldTaigaHills = Taiga(31, 0) colour(2375478) "Cold Taiga Hills"
MapUtil.biomeColours.add(new BlockVec3(Material.snow.getMaterialMapColor().colorValue, 0x172a17, 12));
//megaTaiga = Taiga(32, 1) colour(5858897) "Mega Taiga"
MapUtil.biomeColours.add(new BlockVec3(0x172a17, 0x6e4e35, 12));
//megaTaigaHills = Taiga(33, 1) colour(4542270) "Mega Taiga Hills"
MapUtil.biomeColours.add(new BlockVec3(0x172a17, 0x6e4e35, 12));
//extremeHillsPlus = Hills(34, true) colour(5271632) "Extreme Hills+"
MapUtil.biomeColours.add(new BlockVec3(0x7b7978, 0x497436, 10));
//savanna = Savanna(35) colour(12431967) "Savanna"
MapUtil.biomeColours.add(new BlockVec3(0x565529, 0x262304, 20));
//savannaPlateau = Savanna(36) colour(10984804) "Savanna Plateau"
MapUtil.biomeColours.add(new BlockVec3(0x565529, 0x262304, 14));
//mesa = Mesa(37, false, false) colour(14238997) "Mesa"
MapUtil.biomeColours.add(new BlockVec3(0xa0521f, 0x712f23, 14));
//mesaPlateau_F = Mesa(38, false, true) colour(11573093) "Mesa Plateau F"
MapUtil.biomeColours.add(new BlockVec3(0xa0521f, 0x712f23, 17));
//mesaPlateau = Mesa(39, false, false) colour(13274213) "Mesa Plateau"
MapUtil.biomeColours.add(new BlockVec3(0xa0521f, 0x712f23, 20));
}
public static void makeVanillaMap(int dim, int chunkXPos, int chunkZPos, File baseFolder, BufferedImage image)
{
for (int x0 = -12; x0 <= 12; x0++)
{
for (int z0 = -12; z0 <= 12; z0++)
{
Chunk chunk = MinecraftServer.getServer().worldServerForDimension(dim).getChunkFromChunkCoords(chunkXPos + x0, chunkZPos + z0);
if (chunk != null)
{
for (int z = 0; z < 16; z++)
{
for (int x = 0; x < 16; x++)
{
int l4 = chunk.getHeightValue(x, z) + 1;
Block block = Blocks.air;
IBlockState i5 = block.getDefaultState();
if (l4 > 1)
{
do
{
--l4;
BlockPos pos = new BlockPos(x, l4, z);
block = chunk.getBlock(pos);
i5 = chunk.getBlockState(pos);
}
while (block.getMapColor(i5) == MapColor.airColor && l4 > 0);
}
int col = block.getMapColor(i5).colorValue;
image.setRGB(x + (x0 + 12) * 16, z + (z0 + 12) * 16, col);
}
}
}
}
}
try
{
File outputFile = new File(baseFolder, dim + "_" + chunkXPos + "_" + chunkZPos + ".jpg");
if (!outputFile.exists() || (outputFile.canWrite() && outputFile.canRead()))
{
ImageIO.write(image, "jpg", outputFile);
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
@SideOnly(Side.CLIENT)
public static File getClientMapsFolder()
{
File folder = new File(FMLClientHandler.instance().getClient().mcDataDir, "assets/galacticraftMaps");
try
{
if (folder.exists() || folder.mkdirs())
{
return folder;
}
else
{
System.err.println("Cannot create directory %minecraft%/assets/galacticraftMaps! : " + folder.toString());
}
}
catch (Exception e)
{
System.err.println(folder.toString());
e.printStackTrace();
}
return null;
}
}