mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-19 14:41:34 +01:00
Rendering API v1
This commit is contained in:
parent
02b57a43d0
commit
07aaab77a9
@ -35,6 +35,7 @@ public class Main {
|
|||||||
commandManager.register(new GamemodeCommand());
|
commandManager.register(new GamemodeCommand());
|
||||||
commandManager.register(new DimensionCommand());
|
commandManager.register(new DimensionCommand());
|
||||||
commandManager.register(new ShutdownCommand());
|
commandManager.register(new ShutdownCommand());
|
||||||
|
commandManager.register(new TestItemFrame());
|
||||||
|
|
||||||
MapAnimationDemo.init();
|
MapAnimationDemo.init();
|
||||||
|
|
||||||
|
38
src/main/java/fr/themode/demo/commands/TestItemFrame.java
Normal file
38
src/main/java/fr/themode/demo/commands/TestItemFrame.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package fr.themode.demo.commands;
|
||||||
|
|
||||||
|
import fr.themode.demo.map.MapAnimationDemo;
|
||||||
|
import net.minestom.server.command.CommandProcessor;
|
||||||
|
import net.minestom.server.command.CommandSender;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.entity.type.EntityItemFrame;
|
||||||
|
import net.minestom.server.item.ItemStack;
|
||||||
|
import net.minestom.server.item.Material;
|
||||||
|
import net.minestom.server.item.metadata.MapMeta;
|
||||||
|
|
||||||
|
public class TestItemFrame implements CommandProcessor {
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "itemframe";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getAliases() {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean process(CommandSender sender, String command, String[] args) {
|
||||||
|
Player player = (Player)sender;
|
||||||
|
EntityItemFrame frame = new EntityItemFrame(player.getPosition(), EntityItemFrame.ItemFrameOrientation.SOUTH);
|
||||||
|
ItemStack map = new ItemStack(Material.FILLED_MAP, (byte) 1);
|
||||||
|
map.setItemMeta(new MapMeta(MapAnimationDemo.MAP_ID));
|
||||||
|
frame.setItemStack(map);
|
||||||
|
frame.setInstance(player.getInstance());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAccess(Player player) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -6,10 +6,17 @@ import net.minestom.server.item.ItemStack;
|
|||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
import net.minestom.server.item.metadata.MapMeta;
|
import net.minestom.server.item.metadata.MapMeta;
|
||||||
import net.minestom.server.map.MapColors;
|
import net.minestom.server.map.MapColors;
|
||||||
|
import net.minestom.server.map.framebuffers.Graphics2DFramebuffer;
|
||||||
import net.minestom.server.network.packet.server.play.MapDataPacket;
|
import net.minestom.server.network.packet.server.play.MapDataPacket;
|
||||||
import net.minestom.server.timer.SchedulerManager;
|
import net.minestom.server.timer.SchedulerManager;
|
||||||
import net.minestom.server.utils.time.TimeUnit;
|
import net.minestom.server.utils.time.TimeUnit;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class MapAnimationDemo {
|
public class MapAnimationDemo {
|
||||||
|
|
||||||
public static final int MAP_ID = 1;
|
public static final int MAP_ID = 1;
|
||||||
@ -27,30 +34,48 @@ public class MapAnimationDemo {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Graphics2DFramebuffer framebuffer = new Graphics2DFramebuffer();
|
||||||
|
|
||||||
|
private static float time = 0f;
|
||||||
|
private static long lastTime = System.currentTimeMillis();
|
||||||
|
|
||||||
public static void tick() {
|
public static void tick() {
|
||||||
|
Graphics2D renderer = framebuffer.getRenderer();
|
||||||
|
renderer.setColor(Color.BLACK);
|
||||||
|
renderer.clearRect(0, 0, 128, 128);
|
||||||
|
renderer.setColor(Color.WHITE);
|
||||||
|
renderer.drawString("Hello from", 0, 10);
|
||||||
|
renderer.drawString("Graphics2D!", 0, 20);
|
||||||
|
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
long l = currentTime / 60;
|
||||||
|
if(l % 2 == 0) {
|
||||||
|
renderer.setColor(Color.RED);
|
||||||
|
}
|
||||||
|
renderer.fillRect(128-10, 0, 10, 10);
|
||||||
|
|
||||||
|
renderer.setColor(Color.GREEN);
|
||||||
|
float dt = (currentTime-lastTime)/1000.0f;
|
||||||
|
lastTime = currentTime;
|
||||||
|
time += dt;
|
||||||
|
float speed = 10f;
|
||||||
|
int x = (int) (Math.cos(time*speed) * 10 + 64) - 25;
|
||||||
|
int y = (int) (Math.sin(time*speed) * 10 + 64) - 10;
|
||||||
|
renderer.fillRoundRect(x, y, 50, 20, 10, 10);
|
||||||
|
|
||||||
MapDataPacket mapDataPacket = new MapDataPacket();
|
MapDataPacket mapDataPacket = new MapDataPacket();
|
||||||
mapDataPacket.mapId = MAP_ID;
|
mapDataPacket.mapId = MAP_ID;
|
||||||
mapDataPacket.columns = 127;
|
mapDataPacket.columns = 128;
|
||||||
mapDataPacket.rows = 127;
|
mapDataPacket.rows = 128;
|
||||||
mapDataPacket.icons = new MapDataPacket.Icon[0];
|
mapDataPacket.icons = new MapDataPacket.Icon[0];
|
||||||
mapDataPacket.x = 0;
|
mapDataPacket.x = 0;
|
||||||
mapDataPacket.z = 0;
|
mapDataPacket.z = 0;
|
||||||
mapDataPacket.scale = 0;
|
mapDataPacket.scale = 0;
|
||||||
mapDataPacket.locked = true;
|
mapDataPacket.locked = true;
|
||||||
mapDataPacket.trackingPosition = true;
|
mapDataPacket.trackingPosition = true;
|
||||||
byte[] colors = new byte[128*128];
|
mapDataPacket.data = framebuffer.toMapColors();
|
||||||
for (int x = 0; x < 128; x++) {
|
|
||||||
for (int z = 0; z < 128; z++) {
|
|
||||||
int r = (int) (Math.random() * MapColors.values().length);
|
|
||||||
MapColors baseColor = MapColors.values()[r];
|
|
||||||
int m = (int) (Math.random() * 4);
|
|
||||||
byte colorID = (byte) ((baseColor.ordinal() << 2) + m);
|
|
||||||
colors[x+z*128] = colorID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mapDataPacket.data = colors;
|
|
||||||
MinecraftServer.getConnectionManager().getOnlinePlayers().forEach(p -> {
|
MinecraftServer.getConnectionManager().getOnlinePlayers().forEach(p -> {
|
||||||
p.sendPacketToViewersAndSelf(mapDataPacket);
|
p.getPlayerConnection().sendPacket(mapDataPacket);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
src/main/java/net/minestom/server/map/Framebuffer.java
Normal file
21
src/main/java/net/minestom/server/map/Framebuffer.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package net.minestom.server.map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Framebuffer to render to a map
|
||||||
|
*/
|
||||||
|
public interface Framebuffer {
|
||||||
|
|
||||||
|
int WIDTH = 128;
|
||||||
|
int HEIGHT = 128;
|
||||||
|
|
||||||
|
byte[] toMapColors();
|
||||||
|
|
||||||
|
static int index(int x, int z) {
|
||||||
|
return index(x, z, WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int index(int x, int z, int stride) {
|
||||||
|
return z*stride + x;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
package net.minestom.server.map;
|
package net.minestom.server.map;
|
||||||
|
|
||||||
|
import net.minestom.server.utils.thread.MinestomThread;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public enum MapColors {
|
public enum MapColors {
|
||||||
@ -68,6 +72,50 @@ public enum MapColors {
|
|||||||
private final int green;
|
private final int green;
|
||||||
private final int blue;
|
private final int blue;
|
||||||
|
|
||||||
|
private static final ConcurrentHashMap<Integer, PreciseMapColor> rgbMap = new ConcurrentHashMap<>();
|
||||||
|
// only used if mappingStrategy == ColorMappingStrategy.PRECISE
|
||||||
|
private static PreciseMapColor[] rgbArray = null;
|
||||||
|
|
||||||
|
private static final ColorMappingStrategy mappingStrategy;
|
||||||
|
private static final String MAPPING_ARGUMENT = "minestom.map.rgbmapping";
|
||||||
|
// only used if MAPPING_ARGUMENT is "approximate"
|
||||||
|
private static final String REDUCTION_ARGUMENT = "minestom.map.rgbreduction";
|
||||||
|
private static final int colorReduction;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ColorMappingStrategy strategy;
|
||||||
|
String strategyStr = System.getProperty(MAPPING_ARGUMENT);
|
||||||
|
if(strategyStr == null) {
|
||||||
|
strategy = ColorMappingStrategy.LAZY;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
strategy = ColorMappingStrategy.valueOf(strategyStr.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
System.err.println("Unknown color mapping strategy: "+strategyStr);
|
||||||
|
System.err.println("Defaulting to LAZY.");
|
||||||
|
strategy = ColorMappingStrategy.LAZY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mappingStrategy = strategy;
|
||||||
|
|
||||||
|
int reduction = 10;
|
||||||
|
String reductionStr = System.getProperty(REDUCTION_ARGUMENT);
|
||||||
|
if(reductionStr != null) {
|
||||||
|
try {
|
||||||
|
reduction = Integer.parseInt(reductionStr);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
System.err.println("Invalid integer in reduction argument: "+reductionStr);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reduction < 0 || reduction >= 255) {
|
||||||
|
System.err.println("Reduction was found to be invalid: "+reduction+". Must in 0-255, defaulting to 10.");
|
||||||
|
reduction = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
colorReduction = reduction;
|
||||||
|
}
|
||||||
|
|
||||||
MapColors(int red, int green, int blue) {
|
MapColors(int red, int green, int blue) {
|
||||||
this.red = red;
|
this.red = red;
|
||||||
this.green = green;
|
this.green = green;
|
||||||
@ -121,8 +169,101 @@ public enum MapColors {
|
|||||||
return blue;
|
return blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PreciseMapColor closestColor(int rgb) {
|
private static void fillRGBMap() {
|
||||||
throw new UnsupportedOperationException("TODO");
|
for(MapColors base : values()) {
|
||||||
|
if(base == NONE)
|
||||||
|
continue;
|
||||||
|
for(Multiplier m : Multiplier.values()) {
|
||||||
|
PreciseMapColor preciseMapColor = new PreciseMapColor(base, m);
|
||||||
|
int rgb = preciseMapColor.toRGB();
|
||||||
|
|
||||||
|
if(mappingStrategy == ColorMappingStrategy.APPROXIMATE) {
|
||||||
|
rgb = reduceColor(rgb);
|
||||||
|
}
|
||||||
|
rgbMap.put(rgb, preciseMapColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fillRGBArray() {
|
||||||
|
rgbArray = new PreciseMapColor[0xFFFFFF+1];
|
||||||
|
MinestomThread threads = new MinestomThread(Runtime.getRuntime().availableProcessors(), "RGBMapping", true);
|
||||||
|
for (int rgb = 0; rgb <= 0xFFFFFF; rgb++) {
|
||||||
|
int finalRgb = rgb;
|
||||||
|
threads.execute(() -> rgbArray[finalRgb] = mapColor(finalRgb));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
threads.shutdown();
|
||||||
|
threads.awaitTermination(100, TimeUnit.MINUTES);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
System.out.println("done mapping."); // todo: remove, debug only
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PreciseMapColor closestColor(int argb) {
|
||||||
|
int noAlpha = argb & 0xFFFFFF;
|
||||||
|
if (mappingStrategy == ColorMappingStrategy.PRECISE) {
|
||||||
|
if(rgbArray == null) {
|
||||||
|
synchronized (MapColors.class) {
|
||||||
|
if(rgbArray == null) {
|
||||||
|
fillRGBArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rgbArray[noAlpha];
|
||||||
|
}
|
||||||
|
if(rgbMap.isEmpty()) {
|
||||||
|
synchronized (rgbMap) {
|
||||||
|
if(rgbMap.isEmpty()) {
|
||||||
|
fillRGBMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(mappingStrategy == ColorMappingStrategy.APPROXIMATE) {
|
||||||
|
noAlpha = reduceColor(noAlpha);
|
||||||
|
}
|
||||||
|
return rgbMap.computeIfAbsent(noAlpha, MapColors::mapColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int reduceColor(int rgb) {
|
||||||
|
int red = (rgb >> 16) & 0xFF;
|
||||||
|
int green = (rgb >> 8) & 0xFF;
|
||||||
|
int blue = rgb & 0xFF;
|
||||||
|
|
||||||
|
red = red/colorReduction;
|
||||||
|
green = green/colorReduction;
|
||||||
|
blue = blue/colorReduction;
|
||||||
|
return (red << 16) | (green << 8) | blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PreciseMapColor mapColor(int rgb) {
|
||||||
|
PreciseMapColor closest = null;
|
||||||
|
int closestDistance = Integer.MAX_VALUE;
|
||||||
|
for(MapColors base : values()) {
|
||||||
|
if (base == NONE)
|
||||||
|
continue;
|
||||||
|
for (Multiplier m : Multiplier.values()) {
|
||||||
|
int rgbKey = PreciseMapColor.toRGB(base, m);
|
||||||
|
int redKey = (rgbKey >> 16) & 0xFF;
|
||||||
|
int greenKey = (rgbKey >> 8) & 0xFF;
|
||||||
|
int blueKey = rgbKey & 0xFF;
|
||||||
|
|
||||||
|
int red = (rgb >> 16) & 0xFF;
|
||||||
|
int green = (rgb >> 8) & 0xFF;
|
||||||
|
int blue = rgb & 0xFF;
|
||||||
|
|
||||||
|
int dr = redKey - red;
|
||||||
|
int dg = greenKey - green;
|
||||||
|
int db = blueKey - blue;
|
||||||
|
int dist = (dr * dr + dg * dg + db * db);
|
||||||
|
if (dist < closestDistance) {
|
||||||
|
closest = new PreciseMapColor(base, m);
|
||||||
|
closestDistance = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PreciseMapColor {
|
public static class PreciseMapColor {
|
||||||
@ -145,23 +286,69 @@ public enum MapColors {
|
|||||||
public byte getIndex() {
|
public byte getIndex() {
|
||||||
return multiplier.apply(baseColor);
|
return multiplier.apply(baseColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int toRGB() {
|
||||||
|
return toRGB(baseColor, multiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int toRGB(MapColors baseColor, Multiplier multiplier) {
|
||||||
|
double r = baseColor.red();
|
||||||
|
double g = baseColor.green();
|
||||||
|
double b = baseColor.blue();
|
||||||
|
|
||||||
|
r *= multiplier.multiplier();
|
||||||
|
g *= multiplier.multiplier();
|
||||||
|
b *= multiplier.multiplier();
|
||||||
|
|
||||||
|
int red = (int) r;
|
||||||
|
int green = (int) g;
|
||||||
|
int blue = (int) b;
|
||||||
|
return (red << 16) | (green << 8) | blue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Multiplier {
|
enum Multiplier {
|
||||||
x1_00(MapColors::baseColor),
|
x1_00(MapColors::baseColor, 1.00),
|
||||||
x0_53(MapColors::multiply53),
|
x0_53(MapColors::multiply53, 0.53),
|
||||||
x0_71(MapColors::multiply71),
|
x0_71(MapColors::multiply71, 0.71),
|
||||||
x0_86(MapColors::multiply86),
|
x0_86(MapColors::multiply86, 0.86),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final Function<MapColors, Byte> indexGetter;
|
private final Function<MapColors, Byte> indexGetter;
|
||||||
|
private final double multiplier;
|
||||||
|
|
||||||
Multiplier(Function<MapColors, Byte> indexGetter) {
|
Multiplier(Function<MapColors, Byte> indexGetter, double multiplier) {
|
||||||
this.indexGetter = indexGetter;
|
this.indexGetter = indexGetter;
|
||||||
|
this.multiplier = multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double multiplier() {
|
||||||
|
return multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte apply(MapColors baseColor) {
|
public byte apply(MapColors baseColor) {
|
||||||
return indexGetter.apply(baseColor);
|
return indexGetter.apply(baseColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How does Minestom compute RGB->MapColor transitions?
|
||||||
|
*/
|
||||||
|
public enum ColorMappingStrategy {
|
||||||
|
/**
|
||||||
|
* If already computed, send the result. Otherwise, compute the closest color in a RGB Map, and add it to the map
|
||||||
|
*/
|
||||||
|
LAZY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All colors are already in the map after the first call. Heavy hit on the memory:
|
||||||
|
* (2^24) * 4 bytes at the min (~64MB)
|
||||||
|
*/
|
||||||
|
PRECISE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RGB components are divided by 10 before issuing a lookup (as with the PRECISE strategy), but saves on memory usage
|
||||||
|
*/
|
||||||
|
APPROXIMATE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package net.minestom.server.map.framebuffers;
|
||||||
|
|
||||||
|
import net.minestom.server.map.Framebuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Framebuffer with direct access to the colors array
|
||||||
|
*/
|
||||||
|
public class DirectFramebuffer implements Framebuffer {
|
||||||
|
|
||||||
|
private final byte[] colors = new byte[WIDTH*HEIGHT];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutable colors array
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public byte[] getColors() {
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte get(int x, int z) {
|
||||||
|
return colors[Framebuffer.index(x, z)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectFramebuffer set(int x, int z, byte color) {
|
||||||
|
colors[Framebuffer.index(x, z)] = color;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toMapColors() {
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package net.minestom.server.map.framebuffers;
|
||||||
|
|
||||||
|
import net.minestom.server.map.Framebuffer;
|
||||||
|
import net.minestom.server.map.MapColors;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.DataBufferInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Framebuffer that embeds a BufferedImage, allowing for rendering directly via Graphics2D or its pixel array
|
||||||
|
*/
|
||||||
|
public class Graphics2DFramebuffer implements Framebuffer {
|
||||||
|
|
||||||
|
private final byte[] colors = new byte[WIDTH*HEIGHT];
|
||||||
|
private final BufferedImage backingImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
|
||||||
|
private final Graphics2D renderer;
|
||||||
|
private final int[] pixels;
|
||||||
|
|
||||||
|
public Graphics2DFramebuffer() {
|
||||||
|
renderer = backingImage.createGraphics();
|
||||||
|
pixels = ((DataBufferInt)backingImage.getRaster().getDataBuffer()).getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Graphics2D getRenderer() {
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage getBackingImage() {
|
||||||
|
return backingImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int get(int x, int z) {
|
||||||
|
return pixels[x+z*WIDTH]; // stride is always the width of the image
|
||||||
|
}
|
||||||
|
|
||||||
|
public Graphics2DFramebuffer set(int x, int z, int rgb) {
|
||||||
|
pixels[x+z*WIDTH] = rgb;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toMapColors() {
|
||||||
|
// TODO: update subparts only
|
||||||
|
for (int x = 0; x < 128; x++) {
|
||||||
|
for (int z = 0; z < 128; z++) {
|
||||||
|
colors[Framebuffer.index(x, z)] = MapColors.closestColor(get(x, z)).getIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
}
|
@ -14,8 +14,8 @@ public class MapDataPacket implements ServerPacket {
|
|||||||
|
|
||||||
public Icon[] icons;
|
public Icon[] icons;
|
||||||
|
|
||||||
public byte columns;
|
public short columns;
|
||||||
public byte rows;
|
public short rows;
|
||||||
public byte x;
|
public byte x;
|
||||||
public byte z;
|
public byte z;
|
||||||
public byte[] data;
|
public byte[] data;
|
||||||
@ -36,12 +36,12 @@ public class MapDataPacket implements ServerPacket {
|
|||||||
writer.writeVarInt(0);
|
writer.writeVarInt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.writeByte(columns);
|
writer.writeByte((byte)columns);
|
||||||
if (columns <= 0) {
|
if (columns <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.writeByte(rows);
|
writer.writeByte((byte)rows);
|
||||||
writer.writeByte(x);
|
writer.writeByte(x);
|
||||||
writer.writeByte(z);
|
writer.writeByte(z);
|
||||||
if (data != null && data.length > 0) {
|
if (data != null && data.length > 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user