diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 7af35cdf..1a6b5d1d 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -44,6 +44,7 @@ import org.bukkit.event.entity.EntityListener; import org.bukkit.event.player.PlayerChatEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerListener; +import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.world.ChunkLoadEvent; @@ -74,6 +75,7 @@ public class DynmapPlugin extends JavaPlugin { public HashSet enabledTriggers = new HashSet(); public PermissionProvider permissions; public ComponentManager componentManager = new ComponentManager(); + public PlayerFaces playerfacemgr; public Events events = new Events(); public String deftemplatesuffix = ""; /* Flag to let code know that we're doing reload - make sure we don't double-register event handlers */ @@ -252,6 +254,8 @@ public class DynmapPlugin extends JavaPlugin { mapManager = new MapManager(this, configuration); mapManager.startRendering(); + playerfacemgr = new PlayerFaces(this); + loadWebserver(); enabledTriggers.clear(); @@ -342,6 +346,7 @@ public class DynmapPlugin extends JavaPlugin { List ll = event_handlers.get(t); ll.clear(); /* Empty list - we use presence of list to remember that we've registered with Bukkit */ } + playerfacemgr = null; Debug.clearDebuggers(); } @@ -992,6 +997,16 @@ public class DynmapPlugin extends JavaPlugin { } } } + @Override + public void onPlayerLogin(PlayerLoginEvent event) { + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((PlayerListener)l).onPlayerLogin(event); + } + } + } @Override public void onPlayerMove(PlayerMoveEvent event) { diff --git a/src/main/java/org/dynmap/PlayerFaces.java b/src/main/java/org/dynmap/PlayerFaces.java new file mode 100644 index 00000000..ef66d128 --- /dev/null +++ b/src/main/java/org/dynmap/PlayerFaces.java @@ -0,0 +1,135 @@ +package org.dynmap; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import javax.imageio.ImageIO; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event.Type; +import org.bukkit.event.player.PlayerListener; +import org.bukkit.event.player.PlayerLoginEvent; +import org.dynmap.MapType.ImageFormat; +import org.dynmap.debug.Debug; +import org.dynmap.utils.DynmapBufferedImage; +import org.dynmap.utils.FileLockManager; + +/** + * Listen for player logins, and process player faces by fetching skins * + */ +public class PlayerFaces { + private DynmapPlugin plugin; + private File facesdir; + private File faces8x8dir; + private File faces16x16dir; + private File faces32x32dir; + + + private class LoadPlayerImages implements Runnable { + public String playername; + public LoadPlayerImages(String playername) { + this.playername = playername; + } + public void run() { + BufferedImage img = null; + try { + URL url = new URL("http://s3.amazonaws.com/MinecraftSkins/" + playername + ".png"); + img = ImageIO.read(url); /* Load skin for player */ + } catch (IOException iox) { + Debug.debug("Error loading skin for '" + playername + "' - " + iox); + } + if(img == null) { + try { + InputStream in = getClass().getResourceAsStream("/char.png"); + img = ImageIO.read(in); /* Load generic skin for player */ + in.close(); + } catch (IOException iox) { + Debug.debug("Error loading default skin for '" + playername + "' - " + iox); + } + } + if(img == null) { /* No image to process? Quit */ + return; + } + int[] faceaccessory = new int[64]; /* 8x8 of face accessory */ + /* Get buffered image for face at 8x8 */ + DynmapBufferedImage face8x8 = DynmapBufferedImage.allocateBufferedImage(8, 8); + img.getRGB(8, 8, 8, 8, face8x8.argb_buf, 0, 8); /* Read face from image */ + img.getRGB(40, 8, 8, 8, faceaccessory, 0, 8); /* Read face accessory from image */ + /* Apply accessory to face: first element is transparency color so only ones not matching it */ + for(int i = 0; i < 64; i++) { + if(faceaccessory[i] != faceaccessory[0]) + face8x8.argb_buf[i] = faceaccessory[i]; + } + /* Write 8x8 file */ + File img_8x8 = new File(faces8x8dir, playername + ".png"); + FileLockManager.getWriteLock(img_8x8); + try { + FileLockManager.imageIOWrite(face8x8.buf_img, ImageFormat.FORMAT_PNG, img_8x8); + } catch (IOException iox) { + Log.severe("Cannot write player icon " + img_8x8.getPath()); + } + FileLockManager.releaseWriteLock(img_8x8); + /* Make 16x16 version */ + DynmapBufferedImage face16x16 = DynmapBufferedImage.allocateBufferedImage(16, 16); + for(int i = 0; i < 16; i++) { + for(int j = 0; j < 16; j++) { + face16x16.argb_buf[i*16+j] = face8x8.argb_buf[(i/2)*8 + (j/2)]; + } + } + /* Write 16x16 file */ + File img_16x16 = new File(faces16x16dir, playername + ".png"); + FileLockManager.getWriteLock(img_16x16); + try { + FileLockManager.imageIOWrite(face16x16.buf_img, ImageFormat.FORMAT_PNG, img_16x16); + } catch (IOException iox) { + Log.severe("Cannot write player icon " + img_16x16.getPath()); + } + FileLockManager.releaseWriteLock(img_16x16); + DynmapBufferedImage.freeBufferedImage(face16x16); + + /* Make 32x32 version */ + DynmapBufferedImage face32x32 = DynmapBufferedImage.allocateBufferedImage(32, 32); + for(int i = 0; i < 32; i++) { + for(int j = 0; j < 32; j++) { + face32x32.argb_buf[i*32+j] = face8x8.argb_buf[(i/4)*8 + (j/4)]; + } + } + /* Write 32x32 file */ + File img_32x32 = new File(faces32x32dir, playername + ".png"); + FileLockManager.getWriteLock(img_32x32); + try { + FileLockManager.imageIOWrite(face32x32.buf_img, ImageFormat.FORMAT_PNG, img_32x32); + } catch (IOException iox) { + Log.severe("Cannot write player icon " + img_32x32.getPath()); + } + FileLockManager.releaseWriteLock(img_32x32); + DynmapBufferedImage.freeBufferedImage(face32x32); + + DynmapBufferedImage.freeBufferedImage(face8x8); + /* TODO: signal update for player icon to client */ + } + } + private class LoginListener extends PlayerListener { + @Override + public void onPlayerLogin(PlayerLoginEvent event) { + MapManager.scheduleDelayedJob(new LoadPlayerImages(event.getPlayer().getName()), 0); + } + } + public PlayerFaces(DynmapPlugin plugin) { + this.plugin = plugin; + plugin.registerEvent(Type.PLAYER_LOGIN, new LoginListener()); + facesdir = new File(plugin.tilesDirectory, "faces"); + facesdir.mkdirs(); /* Make sure directory exists */ + faces8x8dir = new File(facesdir, "8x8"); + faces8x8dir.mkdirs(); + faces16x16dir = new File(facesdir, "16x16"); + faces16x16dir.mkdirs(); + faces32x32dir = new File(facesdir, "32x32"); + faces32x32dir.mkdirs(); + } + + +} diff --git a/src/main/resources/char.png b/src/main/resources/char.png new file mode 100644 index 00000000..d02b7180 Binary files /dev/null and b/src/main/resources/char.png differ diff --git a/web/js/map.js b/web/js/map.js index 0bccc1ba..5f4e73b8 100644 --- a/web/js/map.js +++ b/web/js/map.js @@ -41,7 +41,6 @@ DynMap.prototype = { inittime: new Date().getTime(), followingPlayer: '', missedupdates: 0, - canvassupport: !!document.createElement('canvas').getContext, formatUrl: function(name, options) { var url = this.options.url[name]; $.each(options, function(n,v) { @@ -133,9 +132,6 @@ DynMap.prototype = { me.maptype.updateTileSize(me.map.zoom); $(me).trigger('zoomchanged'); }; - - if(me.canvassupport == false) - me.options.showplayerfacesinmenu = false; /*google.maps.event.addListener(map, 'dragstart', function(mEvent) { me.followPlayer(null); diff --git a/web/js/minecraft.js b/web/js/minecraft.js index 6605683e..28038695 100644 --- a/web/js/minecraft.js +++ b/web/js/minecraft.js @@ -1,61 +1,19 @@ -var cloneCanvas = function(self) { - var c = document.createElement('canvas'); - c.width = self.width; - c.height = self.height; - var cxt = c.getContext('2d'); - cxt.drawImage(self,0,0); - return c; -}; - -function blitImage(ctx, image, sx ,sy, sw, sh, dx, dy, dw, dh) { - var x; var y; - for (x=0;x