Development resources/Example Minecraft Classic Level Editing Class
To compile and use this class, have your classpath include the path to the LevelEditor.java file and the minecraft-server.jar file.
javac -classpath .:minecraft-server.jar LevelEditor.java
java -classpath .:minecraft-server.jar LevelEditor
or
java -classpath .:minecraft-server.jar LevelEditor new_server_level.dat
import java.io.*;
import java.text.DateFormat;
import java.util.zip.GZIPOutputStream;
import java.util.zip.GZIPInputStream;
public class LevelEditor {
private com.mojang.minecraft.level.Level level = null;
LevelEditor() {
}
// load from file called filename
public void load(String filename) {
FileInputStream fis = null;
GZIPInputStream gzis = null;
ObjectInputStream in = null;
DataInputStream inputstream = null;
try {
fis = new FileInputStream(filename);
gzis = new GZIPInputStream(fis);
inputstream = new DataInputStream(gzis);
if((inputstream.readInt()) != 0x271bb788) {
return;
}
if((inputstream.readByte()) > 2) {
System.out.println("Error: Level version > 2, this is unexpected!");
return;
}
in = new ObjectInputStream(gzis);
level = (com.mojang.minecraft.level.Level)in.readObject();
inputstream.close();
in.close();
System.out.println("Loading level "+filename+" successful");
} catch(IOException ex) {
ex.printStackTrace();
} catch(ClassNotFoundException ex) {
ex.printStackTrace();
}
level.initTransient();
}
// save in file called filename
public void save(String filename) {
FileOutputStream fos = null;
GZIPOutputStream gzos = null;
ObjectOutputStream out = null;
DataOutputStream outputstream = null;
try {
fos = new FileOutputStream(filename);
gzos = new GZIPOutputStream(fos);
outputstream = new DataOutputStream(gzos);
outputstream.writeInt(0x271bb788);
outputstream.writeByte(2);
out = new ObjectOutputStream(gzos);
out.writeObject(level);
outputstream.close();
out.close();
System.out.println("Saving level "+filename+" successful");
} catch(IOException ex) {
ex.printStackTrace();
}
}
// prints all there is to know about a level, except for the blocks data
public void printInfo() {
if (level == null) {
return;
}
System.out.println("Level info:");
System.out.println("name: "+level.name);
System.out.println("creator: "+level.creator);
System.out.println("createTime: "+(DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL).format(level.createTime)));
System.out.println("width: "+level.width);
System.out.println("height: "+level.height);
System.out.println("depth: "+level.depth);
System.out.println("spawnpoint: ["+level.xSpawn+","+level.ySpawn+","+level.zSpawn+"]");
System.out.println("spawn rotation: "+level.rotSpawn);
}
// safe to use method, return value let's you know if anything was changed
public boolean setTile(int x, int y, int z, int t) {
if (
x >=0 && x < level.width &&
y >=0 && y < level.depth &&
z >=0 && z < level.height &&
t >= 0 && t <= 37
) {
if (t == 8 || t == 10) {
level.setTile(x,y,z,t);
} else if (t >= 0 && t <= 18) {
level.setTileNoUpdate(x,y,z,t);
}
return true;
}
return false;
}
// gets you the level coordinates from the blocks array index
public int[] getCoords(int index) {
int x = index % level.width;
index = (index-x) / level.width;
int z = index % level.height;
int y = (index-z) / level.height;
return new int[] {x, y, z};
}
public void clearBlocks() {
for (int i=0; i<level.blocks.length; i++) {
level.blocks[i] = 0;
}
}
public void floor(int y, int type) {
for (int i=0; i<level.width; i++) {
for (int j=0; j<level.height; j++) {
setTile(i,y,j,type);
}
}
}
public void wallX(int x1, int x2, int z, int y, int height, int type) {
for (int i=x1; i<=x2; i++) {
for (int j=y; j<y+height; j++) {
if (!setTile(i,j,z,type)) {
System.out.println("Warning: a tile got ignored while building a wallX: ["+i+","+j+","+z+"]");
}
}
}
}
public void wallZ(int x, int z1, int z2, int y, int height, int type) {
for (int i=z1; i<=z2; i++) {
for (int j=y; j<y+height; j++) {
if (!setTile(x,j,i,type)) {
System.out.println("Warning: a tile got ignored while building a wallZ: ["+x+","+j+","+i+"]");
}
}
}
}
// substitute all of block type 'from' to 'to' :) returning the number of blocks altered
public int substitute(byte from, byte to) {
int count=0;
for (int i=0; i<level.blocks.length; i++) {
if (level.blocks[i] == from) {
level.blocks[i] = to;
count++;
}
}
return count;
}
public void setSize(int x, int y, int z) {
level.setData(x, y, z, new byte[x*y*z]);
}
public static void main(String [] args) {
LevelEditor le = new LevelEditor();
String filename = "server_level.dat";
if(args.length > 0) {
filename = args[0];
le.load(filename);
if (le.level == null) {
System.out.println("Loading level "+filename+" failed");
return;
}
} else {
le.level = new com.mojang.minecraft.level.Level();
}
// Do some fancy editing here
// set a custom size: 256 wide, 128 high, 512 length
le.setSize(256, 128, 512);
// first, let's clear out the field
le.clearBlocks();
// add a bunch of walls to fill the lower part of the level
for (int i=0; i<le.level.width; i++) {
le.wallX(0,le.level.height-1,i,0,(int)le.level.getWaterLevel()-1,3);
}
// lay the floor
le.floor((int)le.level.getWaterLevel()-1,2);
// let the level find a spawn location for us
le.level.findSpawn();
// Leave our fingerprint
le.level.creator = "Minecrafter";
le.level.name = "A Custom World";
le.level.createTime = System.currentTimeMillis();
le.save(filename);
le.printInfo();
}
}
To help with getting the correct bytes for different block types, you can also use this class
public class Blocks
{
public static final byte air = (byte)0;
public static final byte rock = (byte)1;
public static final byte grass = (byte)2;
public static final byte dirt = (byte)3;
public static final byte stone = (byte)4;
public static final byte wood = (byte)5;
public static final byte shrub = (byte)6;
public static final byte blackrock = (byte)7;
public static final byte water = (byte)8;
public static final byte waterstill = (byte)9;
public static final byte lava = (byte)10;
public static final byte lavastill = (byte)11;
public static final byte sand = (byte)12;
public static final byte gravel = (byte)13;
public static final byte goldrock = (byte)14;
public static final byte ironrock = (byte)15;
public static final byte coal = (byte)16;
public static final byte trunk = (byte)17;
public static final byte leaf = (byte)18;
public static final byte sponge = (byte)19;
public static final byte glass = (byte)20;
public static final byte red = (byte)21;
public static final byte orange = (byte)22;
public static final byte yellow = (byte)23;
public static final byte lightgreen = (byte)24;
public static final byte green = (byte)25;
public static final byte aquagreen = (byte)26;
public static final byte cyan = (byte)27;
public static final byte lightblue = (byte)28;
public static final byte blue = (byte)29;
public static final byte purple = (byte)30;
public static final byte lightpurple = (byte)31;
public static final byte pink = (byte)32;
public static final byte darkpink = (byte)33;
public static final byte darkgrey = (byte)34;
public static final byte lightgrey = (byte)35;
public static final byte white = (byte)36;
public static final byte yellowflower = (byte)37;
public static final byte redflower = (byte)38;
public static final byte mushroom = (byte)39;
public static final byte redmushroom = (byte)40;
public static final byte goldsolid = (byte)41;
public static final byte iron = (byte)42;
public static final byte staircasefull = (byte)43;
public static final byte staircasestep = (byte)44;
public static final byte brick = (byte)45;
public static final byte tnt = (byte)46;
public static final byte bookcase = (byte)47;
public static final byte stonevine = (byte)48;
public static final byte obsidian = (byte)49;
}