diff --git a/samples/ayunami2000/VideoMapPacketCodec.java b/samples/ayunami2000/VideoMapPacketCodec.java index 09673f6..e44165c 100644 --- a/samples/ayunami2000/VideoMapPacketCodec.java +++ b/samples/ayunami2000/VideoMapPacketCodec.java @@ -40,7 +40,7 @@ public class VideoMapPacketCodec { this.requiresFullResetPacket = true; this.isDisabled = true; } - + /** * @param mapIds 2D grid of map IDs that make up the screen (mapIds[y][x]) * @param posX audio playback X coord @@ -48,7 +48,14 @@ public class VideoMapPacketCodec { * @param posZ audio playback Z coord */ public VideoMapPacketCodec(int[][] mapIds, double posX, double posY, double posZ) { - this(mapIds, posX, posY, posZ, 1.0f); + this(mapIds, posX, posY, posZ, 0.5f); + } + + /** + * @param mapIds 2D grid of map IDs that make up the screen (mapIds[y][x]) + */ + public VideoMapPacketCodec(int[][] mapIds) { + this(mapIds, 0, 100, 0, 0.5f); } /** diff --git a/samples/ayunami2000/VideoMapPacketCodecBukkit.java b/samples/ayunami2000/VideoMapPacketCodecBukkit.java index 4edbd46..b782cae 100644 --- a/samples/ayunami2000/VideoMapPacketCodecBukkit.java +++ b/samples/ayunami2000/VideoMapPacketCodecBukkit.java @@ -20,7 +20,7 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec { public VideoMapPacketCodecBukkit(int[][] mapIds, double posX, double posY, double posZ, float volume) { super(mapIds, posX, posY, posZ, volume); } - + /** * @param mapIds 2D grid of map IDs that make up the screen (mapIds[y][x]) * @param posX audio playback X coord @@ -28,13 +28,23 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec { * @param posZ audio playback Z coord */ public VideoMapPacketCodecBukkit(int[][] mapIds, double posX, double posY, double posZ) { - super(mapIds, posX, posY, posZ, 1.0f); + super(mapIds, posX, posY, posZ); + } + + /** + * @param mapIds 2D grid of map IDs that make up the screen (mapIds[y][x]) + */ + public VideoMapPacketCodecBukkit(int[][] mapIds) { + super(mapIds); } public static class VideoMapPacket { protected final Object packet; protected VideoMapPacket(byte[] packet) { - this.packet = new Packet131ItemData((short)104, (short)0, packet); + this(packet, false); + } + protected VideoMapPacket(byte[] packet, boolean image) { + this.packet = new Packet131ItemData((short)(104 + (image ? 1 : 0)), (short)0, packet); } public Object getNativePacket() { return packet; @@ -72,7 +82,15 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec { public VideoMapPacket disableVideoBukkit() { return new VideoMapPacket(disableVideo()); } - + + /** + * unloads image and resets all map object to vanilla renderer + * @return packet to send to players + */ + public VideoMapPacket disableImageBukkit() { + return new VideoMapPacket(disableVideo(), true); + } + /** * syncs the server side video timestamp with players * @return packet to send to players @@ -80,7 +98,15 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec { public VideoMapPacket syncPlaybackWithPlayersBukkit() { return new VideoMapPacket(syncPlaybackWithPlayers()); } - + + /** + * syncs the server side image with players + * @return packet to send to players + */ + public VideoMapPacket syncPlaybackWithPlayersImageBukkit() { + return new VideoMapPacket(syncPlaybackWithPlayers(), true); + } + /** * @param url URL to an MP4 or other HTML5 supported video file * @param loop If the video file should loop @@ -90,7 +116,15 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec { public VideoMapPacket beginPlaybackBukkit(String url, boolean loop, float duration) { return new VideoMapPacket(beginPlayback(url, loop, duration)); } - + + /** + * @param url URL to a PNG, JPEG, GIF, or other HTML5 supported image file + * @return packet to send to players + */ + public VideoMapPacket beginPlaybackImageBukkit(String url) { + return new VideoMapPacket(beginPlayback(url, loop, duration)); + } + /** * Tells the browser to pre-load a URL to a video to be played in the future * @param url the URL of the video @@ -101,6 +135,16 @@ public class VideoMapPacketCodecBukkit extends VideoMapPacketCodec { return new VideoMapPacket(bufferVideo(url, ttl)); } + /** + * Tells the browser to pre-load a URL to an image to be played in the future + * @param url the URL of the image + * @param ttl the amount of time the image should stay loaded + * @return packet to send to players + */ + public static VideoMapPacket bufferImageBukkit(String url, int ttl) { + return new VideoMapPacket(bufferVideo(url, ttl), true); + } + /** * @param time time in seconds to seek the video to */ diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java b/src/lwjgl/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java index 091fb65..8bc8fc9 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java @@ -675,6 +675,43 @@ public class EaglerAdapterImpl2 { throw new UnsupportedOperationException("Video is not supported in LWJGL runtime"); } + public static final boolean isImageSupported() { + return false; + } + public static final void loadImage(String src) { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final void loadImage(String src, String setJavascriptPointer) { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final void loadImage(String src, String setJavascriptPointer, String javascriptOnloadFunction) { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final void bufferImage(String src, int ttl) { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final void unloadImage() { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final boolean isImageLoaded() { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final void updateImageTexture() { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final void bindImageTexture() { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final int getImageWidth() { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final int getImageHeight() { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + public static final void setImageFrameRate(float seconds) { + throw new UnsupportedOperationException("Image is not supported in LWJGL runtime"); + } + // ======================================================================================= // ======================================================================================= // ======================================================================================= diff --git a/src/main/java/net/minecraft/src/ItemMap.java b/src/main/java/net/minecraft/src/ItemMap.java index f2f5a5c..b2512d0 100644 --- a/src/main/java/net/minecraft/src/ItemMap.java +++ b/src/main/java/net/minecraft/src/ItemMap.java @@ -360,4 +360,64 @@ public class ItemMap extends ItemMapBase { e.printStackTrace(); } } + + public static void processImageMap(WorldClient theWorld, byte[] data) { + if(!EaglerAdapter.isImageSupported()) { + return; + } + try { + DataInputStream dat = new DataInputStream(new ByteArrayInputStream(data)); + int op = dat.read(); + if(op == 0) { + int count = dat.read(); + int w = (count >> 4) & 0xF; + int h = count & 0xF; + for(int y = 0; y < h; ++y) { + for(int x = 0; x < w; ++x) { + getMapById(theWorld, dat.readUnsignedShort()).enableVideoPlayback = false; + } + } + EaglerAdapter.unloadImage(); + }else if(op == 8) { + int ttl = dat.readInt(); + String src = dat.readUTF(); + EaglerAdapter.bufferImage(src, ttl); + }else { + boolean fullResetPacket = (op & 2) == 2; + boolean positionPacket = (op & 4) == 4; + + int fps = 0; + int len = 0; + String url = null; + if(fullResetPacket) { + int count = dat.read(); + int w = (count >> 4) & 0xF; + int h = count & 0xF; + float wf = 1.0f / w; + float hf = 1.0f / h; + for(int y = 0; y < h; ++y) { + for(int x = 0; x < w; ++x) { + MapData mp = getMapById(theWorld, dat.readUnsignedShort()); + mp.videoX1 = x * wf; + mp.videoY1 = y * hf; + mp.videoX2 = mp.videoX1 + wf; + mp.videoY2 = mp.videoY1 + hf; + mp.enableVideoPlayback = true; + } + } + fps = dat.read(); + len = dat.readInt(); + url = dat.readUTF(); + } + + if(fullResetPacket) { + EaglerAdapter.setImageFrameRate(fps); + EaglerAdapter.loadImage(url); + } + } + }catch(IOException e) { + System.err.println("Failed to read image map packet! " + e.toString()); + e.printStackTrace(); + } + } } diff --git a/src/main/java/net/minecraft/src/MapData.java b/src/main/java/net/minecraft/src/MapData.java index 0806af2..d903932 100644 --- a/src/main/java/net/minecraft/src/MapData.java +++ b/src/main/java/net/minecraft/src/MapData.java @@ -1,6 +1,5 @@ package net.minecraft.src; -import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/net/minecraft/src/MapItemRenderer.java b/src/main/java/net/minecraft/src/MapItemRenderer.java index 04d0d38..e9ca39b 100644 --- a/src/main/java/net/minecraft/src/MapItemRenderer.java +++ b/src/main/java/net/minecraft/src/MapItemRenderer.java @@ -30,7 +30,9 @@ public class MapItemRenderer { float texX2 = 1.0f; float texY1 = 0.0f; float texY2 = 1.0f; - boolean isVideoMode = EaglerAdapter.isVideoSupported() && par3MapData.enableVideoPlayback && EaglerAdapter.isVideoLoaded(); + boolean isVideoOrImageMode = EaglerAdapter.isVideoSupported() && par3MapData.enableVideoPlayback; + boolean isVideoMode = isVideoOrImageMode && EaglerAdapter.isVideoLoaded(); + boolean isImageMode = isVideoOrImageMode && EaglerAdapter.isImageLoaded(); if(isVideoMode) { EaglerAdapter.glEnable(EaglerAdapter.EAG_SWAP_RB); EaglerAdapter.updateVideoTexture(); @@ -39,32 +41,39 @@ public class MapItemRenderer { texY1 = par3MapData.videoY1; texX2 = par3MapData.videoX2; texY2 = par3MapData.videoY2; + }else if(isImageMode) { + EaglerAdapter.updateImageTexture(); + EaglerAdapter.bindImageTexture(); + texX1 = par3MapData.videoX1; + texY1 = par3MapData.videoY1; + texX2 = par3MapData.videoX2; + texY2 = par3MapData.videoY2; }else { if(par3MapData.enableAyunami) { System.arraycopy(par3MapData.ayunamiPixels, 0, intArray, 0, intArray.length); }else { for (int var4 = 0; var4 < 16384; ++var4) { byte var5 = par3MapData.colors[var4]; - + if (var5 / 4 == 0) { this.intArray[var4] = (var4 + var4 / 128 & 1) * 8 + 16 << 24; } else { int var6 = MapColor.mapColorArray[var5 / 4].colorValue; int var7 = var5 & 3; short var8 = 220; - + if (var7 == 2) { var8 = 255; } - + if (var7 == 0) { var8 = 180; } - + int var9 = (var6 >> 16 & 255) * var8 / 255; int var10 = (var6 >> 8 & 255) * var8 / 255; int var11 = (var6 & 255) * var8 / 255; - + if (this.gameSettings.anaglyph) { int var12 = (var9 * 30 + var10 * 59 + var11 * 11) / 100; int var13 = (var9 * 30 + var10 * 70) / 100; @@ -73,14 +82,14 @@ public class MapItemRenderer { var10 = var13; var11 = var14; } - + this.intArray[var4] = -16777216 | var9 << 16 | var10 << 8 | var11; } } } par2RenderEngine.createTextureFromBytes(this.intArray, 128, 128, this.bufferedImage); } - + byte var15 = 0; byte var16 = 0; Tessellator var17 = Tessellator.instance; @@ -97,15 +106,15 @@ public class MapItemRenderer { EaglerAdapter.glEnable(EaglerAdapter.GL_ALPHA_TEST); EaglerAdapter.glDisable(EaglerAdapter.GL_BLEND); par2RenderEngine.resetBoundTexture(); - + if(isVideoMode) { EaglerAdapter.glDisable(EaglerAdapter.EAG_SWAP_RB); } - + if(!par3MapData.enableAyunami && !isVideoMode) { mapicons.bindTexture(); int var19 = 0; - + for (Iterator var20 = par3MapData.playersVisibleOnMap.values().iterator(); var20.hasNext(); ++var19) { MapCoord var21 = (MapCoord) var20.next(); EaglerAdapter.glPushMatrix(); diff --git a/src/main/java/net/minecraft/src/NetClientHandler.java b/src/main/java/net/minecraft/src/NetClientHandler.java index 86c91dc..99a9a80 100644 --- a/src/main/java/net/minecraft/src/NetClientHandler.java +++ b/src/main/java/net/minecraft/src/NetClientHandler.java @@ -1017,6 +1017,8 @@ public class NetClientHandler extends NetHandler { ItemMap.readAyunamiMapPacket(this.mc.theWorld, par1Packet131MapData.uniqueID, par1Packet131MapData.itemData); } else if (par1Packet131MapData.itemID == 104) { ItemMap.processVideoMap(this.mc.theWorld, par1Packet131MapData.itemData); + } else if (par1Packet131MapData.itemID == 105) { + ItemMap.processImageMap(this.mc.theWorld, par1Packet131MapData.itemData); } else { System.err.println("Unknown itemid: " + par1Packet131MapData.itemID); } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java index d697b89..745db88 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java @@ -35,6 +35,7 @@ import org.teavm.jso.dom.html.HTMLCanvasElement; import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLElement; import org.teavm.jso.dom.html.HTMLVideoElement; +import org.teavm.jso.dom.html.HTMLImageElement; import org.teavm.jso.media.MediaError; import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.Float32Array; @@ -886,7 +887,7 @@ public class EaglerAdapterImpl2 { public static final boolean isWindows() { return getString("window.navigator.platform").toLowerCase().contains("win"); } - + private static HTMLVideoElement currentVideo = null; private static TextureGL videoTexture = null; private static boolean videoIsLoaded = false; @@ -1188,6 +1189,171 @@ public class EaglerAdapterImpl2 { frameRate = 1; } } + + private static HTMLImageElement currentImage = null; + private static TextureGL imageTexture = null; + private static boolean imageIsLoaded = false; + private static boolean imageTexIsInitialized = false; + private static int imageFrameRate = 33; + private static long imageFrameTimer = 0l; + + public static final boolean isImageSupported() { + return true; + } + public static final void loadImage(String src) { + loadImage(src, null); + } + public static final void loadImage(String src, String setJavascriptPointer) { + loadImage(src, setJavascriptPointer, null); + } + + @JSBody(params = { "ptr", "el" }, script = "window[ptr] = el;") + private static native void setImagePointer(String ptr, HTMLImageElement el); + @JSBody(params = { "ptr", "el" }, script = "window[ptr](el);") + private static native void callImageLoadEvent(String ptr, HTMLImageElement el); + + public static final void loadImage(String src, String setJavascriptPointer, final String javascriptOnloadFunction) { + imageIsLoaded = false; + imageTexIsInitialized = false; + if(imageTexture == null) { + imageTexture = _wglGenTextures(); + } + if(currentImage != null) { + currentImage.setSrc(""); + } + + BufferedImageElem img = imagesBuffer.get(src); + + if(img != null) { + currentImage = img.imageElement; + imagesBuffer.remove(src); + }else { + currentImage = (HTMLImageElement) win.getDocument().createElement("img"); + currentImage.setAttribute("crossorigin", "anonymous"); + } + + if(setJavascriptPointer != null) { + setImagePointer(setJavascriptPointer, currentImage); + } + + currentImage.addEventListener("load", new EventListener() { + @Override + public void handleEvent(Event evt) { + imageIsLoaded = true; + if(javascriptOnloadFunction != null) { + callImageLoadEvent(javascriptOnloadFunction, currentImage); + } + } + }); + + if(img == null) { + currentImage.setSrc(src); + } + } + + private static class BufferedImageElem { + + protected final HTMLImageElement imageElement; + protected final String url; + protected final long requestedTime; + protected final int ttl; + + public BufferedImageElem(HTMLImageElement imageElement, String url, int ttl) { + this.imageElement = imageElement; + this.url = url; + this.requestedTime = System.currentTimeMillis(); + this.ttl = ttl; + } + + } + + private static final HashMap imagesBuffer = new HashMap(); + + public static final void bufferImage(String src, int ttl) { + if(!imagesBuffer.containsKey(src)) { + HTMLImageElement image = (HTMLImageElement) win.getDocument().createElement("img"); + image.setAttribute("crossorigin", "anonymous"); + image.setSrc(src); + imagesBuffer.put(src, new BufferedImageElem(image, src, ttl)); + } + } + + public static final void unloadImage() { + if(imageTexture != null) { + _wglDeleteTextures(imageTexture); + imageTexture = null; + } + if(currentImage != null) { + currentImage.setSrc(""); + currentImage = null; + } + } + public static final boolean isImageLoaded() { + return imageTexture != null && currentImage != null && imageIsLoaded; + } + + @JSBody( + params = {"ctx", "target", "internalformat", "format", "type", "image"}, + script = "ctx.texImage2D(target, 0, internalformat, format, type, image);" + ) + private static native void html5ImageTexImage2D(WebGL2RenderingContext ctx, int target, int internalformat, int format, int type, HTMLImageElement image); + + @JSBody( + params = {"ctx", "target", "format", "type", "image"}, + script = "ctx.texSubImage2D(target, 0, 0, 0, format, type, image);" + ) + private static native void html5ImageTexSubImage2D(WebGL2RenderingContext ctx, int target, int format, int type, HTMLImageElement image); + + public static final void updateImageTexture() { + long ms = System.currentTimeMillis(); + if(ms - imageFrameTimer < imageFrameRate && imageTexIsInitialized) { + return; + } + imageFrameTimer = ms; + if(currentImage != null && imageTexture != null && imageIsLoaded) { + try { + _wglBindTexture(_wGL_TEXTURE_2D, imageTexture); + if(imageTexIsInitialized) { + html5ImageTexSubImage2D(webgl, _wGL_TEXTURE_2D, _wGL_RGBA, _wGL_UNSIGNED_BYTE, currentImage); + }else { + html5ImageTexImage2D(webgl, _wGL_TEXTURE_2D, _wGL_RGBA, _wGL_RGBA, _wGL_UNSIGNED_BYTE, currentImage); + _wglTexParameteri(_wGL_TEXTURE_2D, _wGL_TEXTURE_WRAP_S, _wGL_CLAMP); + _wglTexParameteri(_wGL_TEXTURE_2D, _wGL_TEXTURE_WRAP_T, _wGL_CLAMP); + _wglTexParameteri(_wGL_TEXTURE_2D, _wGL_TEXTURE_MIN_FILTER, _wGL_LINEAR); + _wglTexParameteri(_wGL_TEXTURE_2D, _wGL_TEXTURE_MAG_FILTER, _wGL_LINEAR); + imageTexIsInitialized = true; + } + }catch(Throwable t) { + // rip + } + } + } + public static final void bindImageTexture() { + if(imageTexture != null) { + _wglBindTexture(_wGL_TEXTURE_2D, imageTexture); + } + } + public static final int getImageWidth() { + if(currentImage != null && imageIsLoaded) { + return currentImage.getWidth(); + }else { + return -1; + } + } + public static final int getImageHeight() { + if(currentImage != null && imageIsLoaded) { + return currentImage.getHeight(); + }else { + return -1; + } + } + + public static final void setImageFrameRate(float fps) { + frameRate = (int)(1000.0f / fps); + if(frameRate < 1) { + frameRate = 1; + } + } private static MouseEvent currentEvent = null; private static KeyboardEvent currentEventK = null;