Compare commits

...

7 Commits

Author SHA1 Message Date
Mike Primm f80b456b2c Shift marker persistence off of server thread (lag reduction) 2015-01-05 23:02:09 -06:00
Mike Primm 41d1de1fbb Fix biome temp/humidity on newer biomes 2015-01-03 17:23:40 -06:00
Mike Primm 274a363a97 Avoid trap in Spigot for light levels outside max height 2014-12-14 20:49:38 -06:00
Mike Primm b0d9f5541a Start Spigot 1.8 support (doesn't work yet....) 2014-11-28 11:08:44 -06:00
Mike Primm 0f722da05d Update to use Bukkit 1.7.10-R0.1 API, rip out old SpoutPlugin support 2014-10-13 09:41:14 -05:00
Mike Primm af1bcd1094 Bump to 2.1 2014-10-13 09:12:01 -05:00
Mike Primm a7f0218d0f Prepare for 2.0.0. release 2014-10-12 17:40:14 -05:00
9 changed files with 94 additions and 371 deletions
+2 -10
View File
@@ -96,14 +96,6 @@
</plugins>
</build>
<repositories>
<repository>
<releases>
</releases>
<snapshots>
</snapshots>
<id>bukkit-repo</id>
<url>http://repo.bukkit.org/content/repositories/releases/</url>
</repository>
<repository>
<releases>
</releases>
@@ -122,7 +114,7 @@
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.4.7-R1.0</version>
<version>1.7.10-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>us.dynmap</groupId>
@@ -162,5 +154,5 @@
<version>2.10.1</version>
</dependency>
</dependencies>
<version>2.0.0-SNAPSHOT</version>
<version>2.1-SNAPSHOT</version>
</project>
@@ -142,5 +142,5 @@ public abstract class BukkitVersionHelper {
/**
* Get player health
*/
public abstract int getHealth(Player p);
public abstract double getHealth(Player p);
}
@@ -5,6 +5,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Server;
@@ -58,7 +59,7 @@ public class BukkitVersionHelperCB extends BukkitVersionHelperGeneric {
biomebaselist = getPrivateField(biomebase, new String[] { "biomes" }, biomebasearray);
biomebasetemp = getField(biomebase, new String[] { "temperature", "F" }, float.class);
biomebasehumi = getField(biomebase, new String[] { "humidity", "G" }, float.class);
biomebaseidstring = getField(biomebase, new String[] { "y", "af" }, String.class);
biomebaseidstring = getField(biomebase, new String[] { "y", "af", "ah" }, String.class);
biomebaseid = getField(biomebase, new String[] { "id" }, int.class);
/* n.m.s.World */
nmsworld = getNMSClass("net.minecraft.server.WorldServer");
@@ -82,7 +83,7 @@ public class BukkitVersionHelperCB extends BukkitVersionHelperGeneric {
nmschunk = getNMSClass("net.minecraft.server.Chunk");
nmsc_removeentities = getMethod(nmschunk, new String[] { "removeEntities" }, new Class[0]);
nmsc_tileentities = getField(nmschunk, new String[] { "tileEntities" }, Map.class);
nmsc_inhabitedticks = getFieldNoFail(nmschunk, new String[] { "s", "q" }, long.class);
nmsc_inhabitedticks = getFieldNoFail(nmschunk, new String[] { "s", "q", "u" }, long.class);
if (nmsc_inhabitedticks == null) {
Log.info("inhabitedTicks field not found - inhabited shader not functional");
}
@@ -111,9 +112,18 @@ public class BukkitVersionHelperCB extends BukkitVersionHelperGeneric {
/** Tile entity */
nms_tileentity = getNMSClass("net.minecraft.server.TileEntity");
nmst_readnbt = getMethod(nms_tileentity, new String[] { "b" }, new Class[] { nbttagcompound });
nmst_x = getField(nms_tileentity, new String[] { "x" }, int.class);
nmst_y = getField(nms_tileentity, new String[] { "y" }, int.class);
nmst_z = getField(nms_tileentity, new String[] { "z" }, int.class);
nmst_getposition = getMethodNoFail(nms_tileentity, new String[] { "getPosition" }, new Class[0]); // Try 1.8 method
if (nmst_getposition == null) {
nmst_x = getField(nms_tileentity, new String[] { "x" }, int.class);
nmst_y = getField(nms_tileentity, new String[] { "y" }, int.class);
nmst_z = getField(nms_tileentity, new String[] { "z" }, int.class);
}
else { /* BlockPosition */
nms_blockposition = getNMSClass("net.minecraft.server.BlockPosition");
nmsbp_getx = getMethod(nms_blockposition, new String[] { "getX" }, new Class[0]);
nmsbp_gety = getMethod(nms_blockposition, new String[] { "getY" }, new Class[0]);
nmsbp_getz = getMethod(nms_blockposition, new String[] { "getZ" }, new Class[0]);
}
}
@Override
public void unloadChunkNoSave(World w, Chunk c, int cx, int cz) {
@@ -83,6 +83,13 @@ public abstract class BukkitVersionHelperGeneric extends BukkitVersionHelper {
protected Field nmst_x;
protected Field nmst_y;
protected Field nmst_z;
protected Method nmst_getposition;
/** BlockPosition */
protected Class<?> nms_blockposition;
protected Method nmsbp_getx;
protected Method nmsbp_gety;
protected Method nmsbp_getz;
/** Server */
protected Method server_getonlineplayers;
/** Player */
@@ -231,6 +238,17 @@ public abstract class BukkitVersionHelperGeneric extends BukkitVersionHelper {
failed = true;
return null;
}
protected Method getMethodNoFail(Class<?> cls, String[] ids, Class[] args) {
if(cls == null) return null;
for(String id : ids) {
try {
return cls.getMethod(id, args);
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
}
}
return null;
}
private Object callMethod(Object obj, Method meth, Object[] args, Object def) {
if((obj == null) || (meth == null)) {
return def;
@@ -333,19 +351,37 @@ public abstract class BukkitVersionHelperGeneric extends BukkitVersionHelper {
* Get X coordinate of tile entity
*/
public int getTileEntityX(Object te) {
return (Integer)getFieldValue(te, nmst_x, 0);
if (nmst_getposition == null) {
return (Integer)getFieldValue(te, nmst_x, 0);
}
else {
Object pos = callMethod(te, nmst_getposition, nullargs, null);
return (Integer) callMethod(pos, nmsbp_getx, nullargs, null);
}
}
/**
* Get Y coordinate of tile entity
*/
public int getTileEntityY(Object te) {
return (Integer)getFieldValue(te, nmst_y, 0);
if (nmst_getposition == null) {
return (Integer)getFieldValue(te, nmst_y, 0);
}
else {
Object pos = callMethod(te, nmst_getposition, nullargs, null);
return (Integer) callMethod(pos, nmsbp_gety, nullargs, null);
}
}
/**
* Get Z coordinate of tile entity
*/
public int getTileEntityZ(Object te) {
return (Integer)getFieldValue(te, nmst_z, 0);
if (nmst_getposition == null) {
return (Integer)getFieldValue(te, nmst_z, 0);
}
else {
Object pos = callMethod(te, nmst_getposition, nullargs, null);
return (Integer) callMethod(pos, nmsbp_getz, nullargs, null);
}
}
/**
* Read tile entity NBT
@@ -416,7 +452,8 @@ public abstract class BukkitVersionHelperGeneric extends BukkitVersionHelper {
/**
* Get player health
*/
public int getHealth(Player p) {
@Override
public double getHealth(Player p) {
Object health = callMethod(p, player_gethealth, nullargs, null);
if (health instanceof Integer) {
return (Integer) health;
@@ -444,7 +444,7 @@ public class BukkitVersionHelperGlowstone extends BukkitVersionHelper {
}
@Override
public int getHealth(Player p) {
public double getHealth(Player p) {
return p.getHealth();
}
@@ -11,6 +11,7 @@ import java.util.List;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.dynmap.DynmapChunk;
@@ -124,7 +125,10 @@ public class BukkitWorld extends DynmapWorld {
@Override
public int getLightLevel(int x, int y, int z) {
if(world != null) {
return world.getBlockAt(x, y, z).getLightLevel();
if ((y >= 0) && (y < this.worldheight)) {
return world.getBlockAt(x, y, z).getLightLevel();
}
return 0;
}
else {
return -1;
@@ -149,7 +153,12 @@ public class BukkitWorld extends DynmapWorld {
@Override
public int getSkyLightLevel(int x, int y, int z) {
if(world != null) {
return world.getBlockAt(x, y, z).getLightFromSky();
if ((y >= 0) && (y < this.worldheight)) {
return world.getBlockAt(x, y, z).getLightFromSky();
}
else {
return 15;
}
}
else {
return -1;
@@ -2,11 +2,9 @@ package org.dynmap.bukkit;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -103,11 +101,9 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
private PermissionProvider permissions;
private String version;
public SnapshotCache sscache;
private boolean has_spout = false;
public PlayerList playerList;
private MapManager mapManager;
public static DynmapPlugin plugin;
public SpoutPluginBlocks spb;
public PluginManager pm;
private Metrics metrics;
private BukkitEnableCoreCallback enabCoreCB = new BukkitEnableCoreCallback();
@@ -169,26 +165,10 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
private class BukkitEnableCoreCallback extends DynmapCore.EnableCoreCallbacks {
@Override
public void configurationLoaded() {
/* Check for Spout */
if(detectSpout()) {
if(core.configuration.getBoolean("spout/enabled", true)) {
has_spout = true;
Log.info("Detected Spout");
if(spb == null) {
spb = new SpoutPluginBlocks(DynmapPlugin.this);
}
modsused.add("SpoutPlugin");
}
else {
Log.info("Detected Spout - Support Disabled");
}
File st = new File(core.getDataFolder(), "renderdata/spout-texture.txt");
if(st.exists()) {
st.delete();
}
if(!has_spout) { /* If not, clean up old spout texture, if needed */
File st = new File(core.getDataFolder(), "renderdata/spout-texture.txt");
if(st.exists())
st.delete();
}
}
}
@@ -276,6 +256,11 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
return true;
return false;
}
@Override
public boolean isServerThread() {
return Bukkit.getServer().isPrimaryThread();
}
@Override
public String stripChatColor(String s) {
return ChatColor.stripColor(s);
@@ -634,7 +619,7 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
return false;
}
@Override
public int getHealth() {
public double getHealth() {
if(player != null)
return helper.getHealth(player);
else
@@ -738,19 +723,24 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
Object[] biomelist = helper.getBiomeBaseList();
/* Loop through list, skipping well known biomes */
for(int i = 0; i < biomelist.length; i++) {
if (!BiomeMap.byBiomeID(i).isDefault()) continue;
Object bb = biomelist[i];
if(bb != null) {
String id = helper.getBiomeBaseIDString(bb);
if(id == null) {
id = "BIOME_" + i;
}
float tmp = helper.getBiomeBaseTemperature(bb);
float hum = helper.getBiomeBaseHumidity(bb);
BiomeMap m = new BiomeMap(i, id, tmp, hum);
Log.verboseinfo("Add custom biome [" + m.toString() + "] (" + i + ")");
cnt++;
BiomeMap bmap = BiomeMap.byBiomeID(i);
if (bmap.isDefault()) {
String id = helper.getBiomeBaseIDString(bb);
if(id == null) {
id = "BIOME_" + i;
}
BiomeMap m = new BiomeMap(i, id, tmp, hum);
Log.verboseinfo("Add custom biome [" + m.toString() + "] (" + i + ")");
cnt++;
}
else {
bmap.setTemperature(tmp);
bmap.setRainfall(hum);
}
}
}
if(cnt > 0) {
@@ -842,7 +832,6 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
@EventHandler(priority=EventPriority.MONITOR, ignoreCancelled=true)
public void onPluginEnabled(PluginEnableEvent evt) {
if (!readyToEnable()) {
spb.markPluginEnabled(evt.getPlugin());
if (readyToEnable()) { /* If we;re ready now, finish enable */
doEnable(); /* Finish enable */
}
@@ -867,18 +856,10 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
}
private boolean readyToEnable() {
if (spb != null) {
return spb.isReady();
}
return true;
}
private void doEnable() {
/* Prep spout support, if needed */
if(spb != null) {
spb.processSpoutBlocks(this, core);
}
/* Enable core */
if(!core.enableCore(enabCoreCB)) {
this.setEnabled(false);
@@ -1515,18 +1496,6 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
}
}
private boolean detectSpout() {
Plugin p = this.getServer().getPluginManager().getPlugin("Spout");
if(p != null) {
return p.isEnabled();
}
return false;
}
public boolean hasSpout() {
return has_spout;
}
@Override
public void assertPlayerInvisibility(String player, boolean is_invisible,
String plugin_id) {
@@ -1577,14 +1546,6 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
return 0;
}
});
features.addPlotter(new Metrics.Plotter("Spout") {
@Override
public int getValue() {
if(plugin.has_spout)
return 1;
return 0;
}
});
features.addPlotter(new Metrics.Plotter("Login Security") {
@Override
public int getValue() {
@@ -22,14 +22,12 @@ import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.VisibilityLimit;
import org.getspout.spoutapi.block.SpoutChunk;
/**
* Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread
*/
public class NewMapChunkCache extends MapChunkCache {
private static boolean init = false;
private static boolean use_spout = false;
private World w;
private DynmapWorld dw;
@@ -737,8 +735,6 @@ public class NewMapChunkCache extends MapChunkCache {
*/
public NewMapChunkCache() {
if(!init) {
use_spout = DynmapPlugin.plugin.hasSpout();
init = true;
}
}
@@ -781,17 +777,6 @@ public class NewMapChunkCache extends MapChunkCache {
isSectionNotEmpty = new boolean[snapcnt][];
}
private ChunkSnapshot checkSpoutData(Chunk c, ChunkSnapshot ss) {
if(c instanceof SpoutChunk) {
SpoutChunk sc = (SpoutChunk)c;
short[] custids = sc.getCustomBlockIds();
if(custids != null) {
return new SpoutChunkSnapshot(ss, custids, c.getWorld().getMaxHeight());
}
}
return ss;
}
public int loadChunks(int max_to_load) {
if(dw.isLoaded() == false)
return 0;
@@ -884,9 +869,6 @@ public class NewMapChunkCache extends MapChunkCache {
else {
if(blockdata || highesty) {
ss = c.getChunkSnapshot(highesty, biome, biomeraw);
if(use_spout) {
ss = checkSpoutData(c, ss);
}
/* Get tile entity data */
List<Object> vals = new ArrayList<Object>();
Map<?,?> tileents = helper.getTileEntitiesForChunk(c);
@@ -1,268 +0,0 @@
package org.dynmap.bukkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import javax.imageio.ImageIO;
import org.bukkit.Material;
import org.bukkit.plugin.Plugin;
import org.dynmap.DynmapCore;
import org.dynmap.Log;
import org.getspout.spoutapi.block.design.BlockDesign;
import org.getspout.spoutapi.block.design.GenericBlockDesign;
import org.getspout.spoutapi.block.design.GenericCuboidBlockDesign;
import org.getspout.spoutapi.block.design.Texture;
import org.getspout.spoutapi.material.CustomBlock;
import org.getspout.spoutapi.material.MaterialData;
/**
* Handler for pulling spout-defined custom blocks into dynmap
*
* Generates and maintains custom render data file (renderdata/spout-texture.txt) and
* directory of downloaded image files (texturepacks/standard/spout/plugin.blockid.png)
*/
public class SpoutPluginBlocks {
private Field textXPosField; /* float[][] textXPos */
private Field textYPosField; /* float[][] textYPos */
private HashSet<String> plugins_pending = new HashSet<String>();
public SpoutPluginBlocks(Plugin plugin) {
/* First, see if any spout plugins that need to be enabled */
for(Plugin p : plugin.getServer().getPluginManager().getPlugins()) {
List<String> dep = p.getDescription().getDepend();
if((dep != null) && (dep.contains("Spout"))) {
Log.info("Found Spout plugin: " + p.getName());
if(p.isEnabled() == false) {
plugins_pending.add(p.getName());
}
}
}
}
public boolean isReady() {
return plugins_pending.isEmpty();
}
public void markPluginEnabled(Plugin p) {
plugins_pending.remove(p.getName());
}
private boolean initSpoutAccess() {
boolean success = false;
try {
textXPosField = GenericBlockDesign.class.getDeclaredField("textXPos");
textXPosField.setAccessible(true);
textYPosField = GenericBlockDesign.class.getDeclaredField("textYPos");
textYPosField.setAccessible(true);
success = true;
} catch (NoSuchFieldException nsfx) {
Log.severe("Cannot access needed Spout custom block fields!");
Log.severe(nsfx);
}
return success;
}
private void addDefaultBlock(StringBuilder sb, CustomBlock blk) {
if(blk.isOpaque())
sb.append("block:id=" + blk.getCustomId() + ",data=*,allfaces=1\n");
else
sb.append("block:id=" + blk.getCustomId() + ",allfaces=12049,transparency=TRANSPARENT\n");
}
private static String fixIDString(String id) {
StringBuilder sb = new StringBuilder();
int len = id.length();
sb.setLength(len);
for(int i = 0; i < len; i++) {
char c = id.charAt(i);
if(Character.isJavaIdentifierStart(c)) {
sb.setCharAt(i, c);
}
else {
sb.setCharAt(i, '_');
}
}
return sb.toString();
}
/* Process spout blocks - return true if something changed */
public boolean processSpoutBlocks(DynmapPlugin plugin, DynmapCore core) {
File datadir = core.getDataFolder();
if(textYPosField == null) {
if(initSpoutAccess() == false)
return false;
}
HashMap<String, String> texturelist = new HashMap<String, String>();
boolean use_existing_texture = core.configuration.getBoolean("spout/use-existing-textures", true);
int cnt = 0;
File f = new File(datadir, "texturepacks/standard/spout");
if(f.exists() == false)
f.mkdirs();
List<CustomBlock> blks = new ArrayList<CustomBlock>();
CustomBlock[] cb = MaterialData.getCustomBlocks();
/* Build new texture file as string */
StringBuilder sb = new StringBuilder();
/* Loop through blocks - try to freshen files, if needed */
for(CustomBlock b : cb) {
BlockDesign bd = b.getBlockDesign();
if(bd == null) continue;
String txtplug = bd.getTexturePlugin();
if(txtplug == null) continue;
String blkname = b.getName();
if(blkname == null) continue;
String blkid = txtplug + "." + fixIDString(blkname);
/* If not GenericCubiodBlockDesign, we don't handle it */
if((bd instanceof GenericCuboidBlockDesign) == false) {
Log.info("Block " + blkid + " not suppored - only cubiod blocks");
addDefaultBlock(sb, b);
continue;
}
/* Get texture info */
Texture txt = bd.getTexture();
int w = txt.getWidth();
int h = txt.getHeight();
int sz = txt.getSpriteSize();
GenericCuboidBlockDesign gbd = (GenericCuboidBlockDesign)bd;
int[] txtidx = new int[6];
/* Fetch quad fields - figure out which texture for which side */
try {
float[][] textXPos = (float[][])textXPosField.get(gbd);
float[][] textYPos = (float[][])textYPosField.get(gbd);
/* Quads on cuboid are bottom, west, south, east, north, top */
for(int i = 0; i < 6; i++) {
float minx = 1.0F;
float miny = 1.0F;
for(int j = 0; j < 4; j++) {
minx = Math.min(minx, textXPos[i][j]);
miny = Math.min(miny, textYPos[i][j]);
}
txtidx[i] = (int)((minx * w)/sz) + (w/sz)*(int)((miny * h)/sz);
}
} catch (Exception iax) {
addDefaultBlock(sb, b);
continue;
}
String txname = bd.getTexureURL();
String txtid = texturelist.get(txname); /* Get texture */
if(txtid == null) { /* Not found yet */
File imgfile = new File(f, blkid + ".png");
/* If not reusing loaded textures OR not previously loaded */
if((!use_existing_texture) || (!imgfile.exists())) {
BufferedImage img = null;
boolean urlloaded = false;
try {
URL url = new URL(txname);
img = ImageIO.read(url); /* Load skin for player */
urlloaded = true;
} catch (IOException iox) {
if(txname.startsWith("http") == false) { /* Not URL - try file */
File tf = new File(txname);
if(tf.exists() == false) {
/* Horrible hack - try to find temp file (some SpoutMaterials versions) */
try {
File tmpf = File.createTempFile("dynmap", "test");
tf = new File(tmpf.getParent(), txname);
tmpf.delete();
} catch (IOException iox2) {}
}
if(tf.exists()) {
try {
img = ImageIO.read(tf);
urlloaded = true;
} catch (IOException iox3) {
}
}
}
if(img == null) {
Log.severe("Error loading texture for custom block '" + blkid + "' (" + b.getCustomId() + ") from " + txname + "(" + iox.getMessage() + ")");
if(imgfile.exists()) {
try {
img = ImageIO.read(imgfile); /* Load existing */
Log.info("Loaded cached texture file for " + blkid);
} catch (IOException iox2) {
Log.severe("Error loading cached texture file for " + blkid + " - " + iox2.getMessage());
}
}
}
}
if(img != null) {
try {
if(urlloaded)
ImageIO.write(img, "png", imgfile);
} catch (IOException iox) {
Log.severe("Error writing " + blkid + ".png");
} finally {
img.flush();
}
}
}
if(imgfile.exists()) { /* If exists now, log it */
String tfid = "txtid" + texturelist.size();
sb.append("texturefile:id=" + tfid + ",filename=spout/" + blkid + ".png,xcount=" + w/sz + ",ycount=" + h/sz + "\n");
texturelist.put(txname, tfid);
}
}
String txfileid = texturelist.get(txname);
if(txfileid != null) {
blks.add(b);
sb.append("block:id=" + b.getCustomId() + ",data=*,bottom=" + txtidx[0] + ",west=" +txtidx[1] + ",south=" + txtidx[2] + ",east=" + txtidx[3] + ",north="+txtidx[4]+",top="+txtidx[5]);
if(b.getBlockId() == Material.GLASS.getId())
sb.append(",transparency=TRANSPARENT");
sb.append(",txtid=" + txfileid + "\n");
cnt++;
}
}
String rslt = sb.toString();
/* Now, generate spout texture file - see if changed */
File renderdata = new File(datadir, "renderdata");
if (renderdata.exists() == false)
renderdata.mkdirs();
File st = new File(renderdata, "spout-texture.txt");
if(st.exists()) {
FileReader fr = null;
StringBuilder sbold = new StringBuilder();
try {
fr = new FileReader(st);
int len;
char[] buf = new char[512];
while((len = fr.read(buf)) > 0) {
sbold.append(buf, 0, len);
}
} catch (IOException iox) {
} finally {
if(fr != null) { try { fr.close(); } catch (IOException iox) {} }
}
/* If same, no changes */
if(sbold.equals(rslt)) {
Log.info("Loaded " + cnt + " Spout custom blocks");
return false;
}
}
FileWriter fw = null;
try {
fw = new FileWriter(st);
fw.write(rslt);
} catch (IOException iox) {
Log.severe("Error opening spout texture file - " + st.getPath());
return false;
} finally {
if(fw != null) { try { fw.close(); } catch (IOException iox) {} }
}
Log.info("Loaded " + cnt + " Spout custom blocks");
return false;
}
}