├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitmodules ├── src ├── main │ └── java │ │ └── de │ │ └── johni0702 │ │ └── mc │ │ └── protocolgen │ │ ├── Packet.java │ │ ├── types │ │ ├── Rotation.java │ │ ├── Position.java │ │ ├── ItemStack.java │ │ └── EntityMetadata.java │ │ └── NetUtils.java └── test │ ├── java │ └── de.johni0702.mc.protocolgen │ │ ├── container │ │ ├── TestContainerSimple.java │ │ ├── TestContainerMulti.java │ │ └── TestContainerOuterCount.java │ │ ├── PacketReadWriteTest.java │ │ ├── condition │ │ ├── TestSwitchNoDefault.java │ │ ├── TestSwitchSimple.java │ │ ├── TestSwitchBoolean.java │ │ ├── TestSwitchArray.java │ │ ├── TestSwitchString.java │ │ ├── TestSwitchDefault.java │ │ ├── TestSwitchRange.java │ │ └── TestSwitchRangeMulti.java │ │ ├── TestArray.java │ │ ├── TestBuffer.java │ │ ├── TestArrayCountType.java │ │ ├── TestBufferCountType.java │ │ └── TestSimpleFields.java │ └── resources │ └── test_protocol.json ├── README.md ├── gradlew.bat ├── gradlew └── generator.gvy /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ProtocolGen' 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Johni0702/ProtocolGen/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "minecraft-data"] 2 | path = minecraft-data 3 | url = https://github.com/PrismarineJS/minecraft-data 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jul 31 14:34:10 CEST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip 7 | -------------------------------------------------------------------------------- /src/main/java/de/johni0702/mc/protocolgen/Packet.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | import java.io.IOException; 6 | 7 | public interface Packet { 8 | void read(ByteBuf in) throws IOException; 9 | void write(ByteBuf out) throws IOException; 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Reads the json protocol info (currently from [here](https://github.com/PrismarineJS/minecraft-data/blob/1.8/enums/protocol.json)) and turns it into java source files. 2 | 3 | To convert json to Java run `./gradlew genPacketSource`. 4 | To compile the generated files into a jar file run `./gradlew build`. 5 | 6 | Uses [OpenNBT](https://github.com/Steveice10/OpenNBT) for parsing of ItemStack NBT data. 7 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/container/TestContainerSimple.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.container; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestContainerSimple; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertNotNull; 12 | 13 | public class TestContainerSimple extends PacketReadWriteTest { 14 | private PacketTestContainerSimple packet; 15 | 16 | @Before 17 | public void init() { 18 | packet = new PacketTestContainerSimple(); 19 | } 20 | 21 | @Test 22 | public void testRead() throws IOException { 23 | read(packet, 1); 24 | assertNotNull("Container object is null.", packet.content); 25 | assertEquals(1, packet.content.inner); 26 | } 27 | 28 | @Test 29 | public void testWrite() throws IOException { 30 | packet.content = new PacketTestContainerSimple.Content(); 31 | packet.content.inner = 1; 32 | write(packet, 1); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/container/TestContainerMulti.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.container; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestContainerMulti; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertNotNull; 12 | 13 | public class TestContainerMulti extends PacketReadWriteTest { 14 | private PacketTestContainerMulti packet; 15 | 16 | @Before 17 | public void init() { 18 | packet = new PacketTestContainerMulti(); 19 | } 20 | 21 | @Test 22 | public void testRead() throws IOException { 23 | read(packet, 1); 24 | assertNotNull("Container object is null.", packet.content); 25 | assertNotNull("Inner container object is null.", packet.content.innerContent); 26 | assertEquals(1, packet.content.innerContent.inner); 27 | } 28 | 29 | @Test 30 | public void testWrite() throws IOException { 31 | packet.content = new PacketTestContainerMulti.Content(); 32 | packet.content.innerContent = new PacketTestContainerMulti.Content.InnerContent(); 33 | packet.content.innerContent.inner = 1; 34 | write(packet, 1); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/PacketReadWriteTest.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | 6 | import java.io.IOException; 7 | 8 | import static org.junit.Assert.assertArrayEquals; 9 | 10 | public class PacketReadWriteTest { 11 | 12 | protected byte[] bytes(int...bytes) { 13 | byte[] data = new byte[bytes.length]; 14 | for (int i = 0; i < data.length; i++) { 15 | data[i] = (byte) bytes[i]; 16 | } 17 | return data; 18 | } 19 | 20 | protected void read(Packet packet, int...bytes) throws IOException { 21 | read(packet, bytes(bytes)); 22 | } 23 | 24 | protected void read(Packet packet, byte...bytes) throws IOException { 25 | packet.read(Unpooled.wrappedBuffer(bytes)); 26 | } 27 | 28 | protected void write(Packet packet, int...expected) throws IOException { 29 | write(packet, bytes(expected)); 30 | } 31 | 32 | protected void write(Packet packet, byte...expected) throws IOException { 33 | ByteBuf buf = Unpooled.buffer(); 34 | try { 35 | packet.write(buf); 36 | 37 | byte[] data = new byte[buf.readableBytes()]; 38 | buf.readBytes(data); 39 | assertArrayEquals(expected, data); 40 | } finally { 41 | buf.release(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/condition/TestSwitchNoDefault.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.condition; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestSwitchNoDefault; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class TestSwitchNoDefault extends PacketReadWriteTest { 13 | private PacketTestSwitchNoDefault packet; 14 | 15 | @Before 16 | public void init() { 17 | packet = new PacketTestSwitchNoDefault(); 18 | } 19 | 20 | @Test(expected = IllegalArgumentException.class) 21 | public void testReadInactive() throws IOException { 22 | read(packet, 1, 2); 23 | } 24 | 25 | @Test 26 | public void testReadActive() throws IOException { 27 | read(packet, 2, 3, 4); 28 | assertEquals(2, packet.before); 29 | assertEquals(3, packet.inner); 30 | assertEquals(4, packet.after); 31 | } 32 | 33 | @Test(expected = IllegalArgumentException.class) 34 | public void testWriteInactive() throws IOException { 35 | packet.before = 1; 36 | packet.after = 2; 37 | write(packet, 1, 2); 38 | } 39 | 40 | @Test 41 | public void testWriteActive() throws IOException { 42 | packet.before = 2; 43 | packet.inner = 3; 44 | packet.after = 4; 45 | write(packet, 2, 3, 4); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/container/TestContainerOuterCount.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.container; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestContainerOuterCondition; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertNotNull; 12 | 13 | public class TestContainerOuterCount extends PacketReadWriteTest { 14 | private PacketTestContainerOuterCondition packet; 15 | 16 | @Before 17 | public void init() { 18 | packet = new PacketTestContainerOuterCondition(); 19 | } 20 | 21 | @Test 22 | public void testRead() throws IOException { 23 | read(packet, 2); 24 | assertEquals(2, packet.outer); 25 | assertNotNull("Container object is null.", packet.content); 26 | assertEquals(0, packet.content.inner); 27 | read(packet, 1, 2); 28 | assertEquals(1, packet.outer); 29 | assertNotNull("Container object is null.", packet.content); 30 | assertEquals(2, packet.content.inner); 31 | } 32 | 33 | @Test 34 | public void testWrite() throws IOException { 35 | packet.content = new PacketTestContainerOuterCondition.Content(); 36 | packet.outer = 2; 37 | write(packet, 2); 38 | packet.outer = 1; 39 | packet.content.inner = 2; 40 | write(packet, 1, 2); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/condition/TestSwitchSimple.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.condition; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestSwitchSimple; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class TestSwitchSimple extends PacketReadWriteTest { 13 | private PacketTestSwitchSimple packet; 14 | 15 | @Before 16 | public void init() { 17 | packet = new PacketTestSwitchSimple(); 18 | } 19 | 20 | @Test 21 | public void testReadInactive() throws IOException { 22 | read(packet, 1, 2); 23 | assertEquals(1, packet.before); 24 | assertEquals(0, packet.inner); 25 | assertEquals(2, packet.after); 26 | } 27 | 28 | @Test 29 | public void testReadActive() throws IOException { 30 | read(packet, 2, 3, 4); 31 | assertEquals(2, packet.before); 32 | assertEquals(3, packet.inner); 33 | assertEquals(4, packet.after); 34 | } 35 | 36 | @Test 37 | public void testWriteInactive() throws IOException { 38 | packet.before = 1; 39 | packet.after = 2; 40 | write(packet, 1, 2); 41 | } 42 | 43 | @Test 44 | public void testWriteActive() throws IOException { 45 | packet.before = 2; 46 | packet.inner = 3; 47 | packet.after = 4; 48 | write(packet, 2, 3, 4); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/condition/TestSwitchBoolean.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.condition; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestSwitchBoolean; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class TestSwitchBoolean extends PacketReadWriteTest { 13 | private PacketTestSwitchBoolean packet; 14 | 15 | @Before 16 | public void init() { 17 | packet = new PacketTestSwitchBoolean(); 18 | } 19 | 20 | @Test 21 | public void testReadInactive() throws IOException { 22 | read(packet, 0, 2); 23 | assertEquals(false, packet.before); 24 | assertEquals(0, packet.inner); 25 | assertEquals(2, packet.after); 26 | } 27 | 28 | @Test 29 | public void testReadActive() throws IOException { 30 | read(packet, 1, 3, 4); 31 | assertEquals(true, packet.before); 32 | assertEquals(3, packet.inner); 33 | assertEquals(4, packet.after); 34 | } 35 | 36 | @Test 37 | public void testWriteInactive() throws IOException { 38 | packet.before = false; 39 | packet.after = 2; 40 | write(packet, 0, 2); 41 | } 42 | 43 | @Test 44 | public void testWriteActive() throws IOException { 45 | packet.before = true; 46 | packet.inner = 3; 47 | packet.after = 4; 48 | write(packet, 1, 3, 4); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/TestArray.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen; 2 | 3 | import de.johni0702.mc.protocolgen.test.PacketTestArray; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | 9 | import java.io.IOException; 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | 13 | import static org.junit.Assert.assertArrayEquals; 14 | 15 | @RunWith(Parameterized.class) 16 | public class TestArray extends PacketReadWriteTest { 17 | @Parameterized.Parameters(name = "{0}") 18 | public static Collection params() { 19 | return Arrays.asList(new Object[][] {{0}, {1}, {2}, {3}, {5}}); 20 | } 21 | 22 | private final byte[] bytes; 23 | private final short[] array; 24 | private PacketTestArray packet; 25 | 26 | public TestArray(int length) { 27 | array = new short[length]; 28 | bytes = new byte[1 + length * 2]; 29 | bytes[0] = (byte) length; 30 | for (int i = 0; i < length; i++) { 31 | array[i] = (byte) i; 32 | bytes[2 + i * 2] = (byte) i; 33 | } 34 | } 35 | 36 | @Before 37 | public void init() { 38 | packet = new PacketTestArray(); 39 | } 40 | 41 | @Test 42 | public void testRead() throws IOException { 43 | read(packet, bytes); 44 | assertArrayEquals(array, packet.inner); 45 | } 46 | 47 | @Test 48 | public void testWrite() throws IOException { 49 | packet.inner = array; 50 | write(packet, bytes); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/TestBuffer.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen; 2 | 3 | import de.johni0702.mc.protocolgen.test.PacketTestBuffer; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | 9 | import java.io.IOException; 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | 13 | import static org.junit.Assert.assertArrayEquals; 14 | 15 | @RunWith(Parameterized.class) 16 | public class TestBuffer extends PacketReadWriteTest { 17 | @Parameterized.Parameters(name = "{0}") 18 | public static Collection params() { 19 | return Arrays.asList(new Object[][] {{0}, {1}, {2}, {3}, {5}}); 20 | } 21 | 22 | private final byte[] bytes; 23 | private final byte[] buffer; 24 | private PacketTestBuffer packet; 25 | 26 | public TestBuffer(int length) { 27 | buffer = new byte[length]; 28 | bytes = new byte[1 + length]; 29 | bytes[0] = (byte) length; 30 | for (int i = 0; i < length; i++) { 31 | buffer[i] = (byte) i; 32 | bytes[1 + i] = (byte) i; 33 | } 34 | } 35 | 36 | @Before 37 | public void init() { 38 | packet = new PacketTestBuffer(); 39 | } 40 | 41 | @Test 42 | public void testRead() throws IOException { 43 | read(packet, bytes); 44 | assertArrayEquals(buffer, packet.inner); 45 | } 46 | 47 | @Test 48 | public void testWrite() throws IOException { 49 | packet.inner = buffer; 50 | write(packet, bytes); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/TestArrayCountType.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen; 2 | 3 | import de.johni0702.mc.protocolgen.test.PacketTestArrayCountType; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | 9 | import java.io.IOException; 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | 13 | import static org.junit.Assert.assertArrayEquals; 14 | 15 | @RunWith(Parameterized.class) 16 | public class TestArrayCountType extends PacketReadWriteTest { 17 | @Parameterized.Parameters(name = "{0}") 18 | public static Collection params() { 19 | return Arrays.asList(new Object[][] {{0}, {1}, {2}, {3}, {5}}); 20 | } 21 | 22 | private final byte[] bytes; 23 | private final short[] array; 24 | private PacketTestArrayCountType packet; 25 | 26 | public TestArrayCountType(int length) { 27 | array = new short[length]; 28 | bytes = new byte[1 + length * 2]; 29 | bytes[0] = (byte) length; 30 | for (int i = 0; i < length; i++) { 31 | array[i] = (byte) i; 32 | bytes[2 + i * 2] = (byte) i; 33 | } 34 | } 35 | 36 | @Before 37 | public void init() { 38 | packet = new PacketTestArrayCountType(); 39 | } 40 | 41 | @Test 42 | public void testRead() throws IOException { 43 | read(packet, bytes); 44 | assertArrayEquals(array, packet.inner); 45 | } 46 | 47 | @Test 48 | public void testWrite() throws IOException { 49 | packet.inner = array; 50 | write(packet, bytes); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/TestBufferCountType.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen; 2 | 3 | import de.johni0702.mc.protocolgen.test.PacketTestBufferCountType; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | 9 | import java.io.IOException; 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | 13 | import static org.junit.Assert.assertArrayEquals; 14 | 15 | @RunWith(Parameterized.class) 16 | public class TestBufferCountType extends PacketReadWriteTest { 17 | @Parameterized.Parameters(name = "{0}") 18 | public static Collection params() { 19 | return Arrays.asList(new Object[][] {{0}, {1}, {2}, {3}, {5}}); 20 | } 21 | 22 | private final byte[] bytes; 23 | private final byte[] buffer; 24 | private PacketTestBufferCountType packet; 25 | 26 | public TestBufferCountType(int length) { 27 | buffer = new byte[length]; 28 | bytes = new byte[1 + length]; 29 | bytes[0] = (byte) length; 30 | for (int i = 0; i < length; i++) { 31 | buffer[i] = (byte) i; 32 | bytes[1 + i] = (byte) i; 33 | } 34 | } 35 | 36 | @Before 37 | public void init() { 38 | packet = new PacketTestBufferCountType(); 39 | } 40 | 41 | @Test 42 | public void testRead() throws IOException { 43 | read(packet, bytes); 44 | assertArrayEquals(buffer, packet.inner); 45 | } 46 | 47 | @Test 48 | public void testWrite() throws IOException { 49 | packet.inner = buffer; 50 | write(packet, bytes); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/de/johni0702/mc/protocolgen/types/Rotation.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.types; 2 | 3 | public final class Rotation { 4 | private final float pitch; 5 | private final float yaw; 6 | private final float roll; 7 | 8 | public Rotation(float pitch, float yaw, float roll) { 9 | this.pitch = pitch; 10 | this.yaw = yaw; 11 | this.roll = roll; 12 | } 13 | 14 | public float getPitch() { 15 | return pitch; 16 | } 17 | 18 | public float getYaw() { 19 | return yaw; 20 | } 21 | 22 | public float getRoll() { 23 | return roll; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) return true; 29 | if (o == null || getClass() != o.getClass()) return false; 30 | 31 | Rotation rotation = (Rotation) o; 32 | 33 | if (Float.compare(rotation.pitch, pitch) != 0) return false; 34 | if (Float.compare(rotation.yaw, yaw) != 0) return false; 35 | return Float.compare(rotation.roll, roll) == 0; 36 | 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | int result = (pitch != +0.0f ? Float.floatToIntBits(pitch) : 0); 42 | result = 31 * result + (yaw != +0.0f ? Float.floatToIntBits(yaw) : 0); 43 | result = 31 * result + (roll != +0.0f ? Float.floatToIntBits(roll) : 0); 44 | return result; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "Rotation{" + 50 | "pitch=" + pitch + 51 | ", yaw=" + yaw + 52 | ", roll=" + roll + 53 | '}'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/condition/TestSwitchArray.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.condition; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestSwitchArray; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertArrayEquals; 11 | import static org.junit.Assert.assertEquals; 12 | 13 | public class TestSwitchArray extends PacketReadWriteTest { 14 | private PacketTestSwitchArray packet; 15 | 16 | @Before 17 | public void init() { 18 | packet = new PacketTestSwitchArray(); 19 | } 20 | 21 | @Test 22 | public void testReadInactive() throws IOException { 23 | read(packet, 1, 2); 24 | assertEquals(1, packet.before); 25 | assertEquals(null, packet.inner); 26 | assertEquals(2, packet.after); 27 | } 28 | 29 | @Test 30 | public void testReadActive() throws IOException { 31 | read(packet, 2, 4, 7, 11, 13, 17, 4); 32 | assertEquals(2, packet.before); 33 | assertArrayEquals(new byte[]{7, 11, 13, 17}, packet.inner); 34 | assertEquals(4, packet.after); 35 | } 36 | 37 | @Test 38 | public void testWriteInactive() throws IOException { 39 | packet.before = 1; 40 | packet.after = 2; 41 | write(packet, 1, 2); 42 | } 43 | 44 | @Test 45 | public void testWriteActive() throws IOException { 46 | packet.before = 2; 47 | packet.inner = new byte[]{7, 11, 13, 17}; 48 | packet.after = 4; 49 | write(packet, 2, 4, 7, 11, 13, 17, 4); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/condition/TestSwitchString.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.condition; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestSwitchString; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class TestSwitchString extends PacketReadWriteTest { 13 | private PacketTestSwitchString packet; 14 | 15 | @Before 16 | public void init() { 17 | packet = new PacketTestSwitchString(); 18 | } 19 | 20 | @Test 21 | public void testReadInactive() throws IOException { 22 | read(packet, 7, 'I', 'n', 'v', 'a', 'l', 'i', 'd', 2); 23 | assertEquals("Invalid", packet.before); 24 | assertEquals(0, packet.inner); 25 | assertEquals(2, packet.after); 26 | } 27 | 28 | @Test 29 | public void testReadActive() throws IOException { 30 | read(packet, 5, 'V', 'a', 'l', 'i', 'd', 3, 4); 31 | assertEquals("Valid", packet.before); 32 | assertEquals(3, packet.inner); 33 | assertEquals(4, packet.after); 34 | } 35 | 36 | @Test 37 | public void testWriteInactive() throws IOException { 38 | packet.before = "Invalid"; 39 | packet.after = 2; 40 | write(packet, 7, 'I', 'n', 'v', 'a', 'l', 'i', 'd', 2); 41 | } 42 | 43 | @Test 44 | public void testWriteActive() throws IOException { 45 | packet.before = "Valid"; 46 | packet.inner = 3; 47 | packet.after = 4; 48 | write(packet, 5, 'V', 'a', 'l', 'i', 'd', 3, 4); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/condition/TestSwitchDefault.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.condition; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestSwitchDefault; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class TestSwitchDefault extends PacketReadWriteTest { 13 | private PacketTestSwitchDefault packet; 14 | 15 | @Before 16 | public void init() { 17 | packet = new PacketTestSwitchDefault(); 18 | } 19 | 20 | @Test 21 | public void testReadInactive() throws IOException { 22 | read(packet, 2, 3); 23 | assertEquals(2, packet.before); 24 | assertEquals(0, packet.inner); 25 | assertEquals(3, packet.after); 26 | } 27 | 28 | @Test 29 | public void testReadActive() throws IOException { 30 | read(packet, 1, 3, 4); 31 | assertEquals(1, packet.before); 32 | assertEquals(3, packet.inner); 33 | assertEquals(4, packet.after); 34 | read(packet, 3, 4, 5); 35 | assertEquals(3, packet.before); 36 | assertEquals(4, packet.inner); 37 | assertEquals(5, packet.after); 38 | } 39 | 40 | @Test 41 | public void testWriteInactive() throws IOException { 42 | packet.before = 2; 43 | packet.after = 3; 44 | write(packet, 2, 3); 45 | } 46 | 47 | @Test 48 | public void testWriteActive() throws IOException { 49 | packet.before = 1; 50 | packet.inner = 3; 51 | packet.after = 4; 52 | write(packet, 1, 3, 4); 53 | packet.before = 3; 54 | packet.inner = 4; 55 | packet.after = 5; 56 | write(packet, 3, 4, 5); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/de/johni0702/mc/protocolgen/types/Position.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.types; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | import java.io.IOException; 6 | 7 | public final class Position { 8 | private final int x; 9 | private final int y; 10 | private final int z; 11 | 12 | public Position(int x, int y, int z) { 13 | this.x = x; 14 | this.y = y; 15 | this.z = z; 16 | } 17 | 18 | public int getX() { 19 | return x; 20 | } 21 | 22 | public int getY() { 23 | return y; 24 | } 25 | 26 | public int getZ() { 27 | return z; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) return true; 33 | if (o == null || getClass() != o.getClass()) return false; 34 | 35 | Position position = (Position) o; 36 | 37 | if (x != position.x) return false; 38 | if (y != position.y) return false; 39 | return z == position.z; 40 | 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | int result = x; 46 | result = 31 * result + y; 47 | result = 31 * result + z; 48 | return result; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "Position{" + 54 | "x=" + x + 55 | ", y=" + y + 56 | ", z=" + z + 57 | '}'; 58 | } 59 | 60 | public static Position read(ByteBuf in) throws IOException { 61 | long val = in.readLong(); 62 | return new Position((int) (val >> 38), (int) ((val >> 26) & 0xfff), (int) (val << 38 >> 38)); 63 | } 64 | 65 | public void write(ByteBuf out) throws IOException { 66 | out.writeLong((((long)x & 0x3FFFFFF) << 38) | (((long)y & 0xFFF) << 26) | (z & 0x3FFFFFF)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/de/johni0702/mc/protocolgen/NetUtils.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | import java.io.IOException; 6 | import java.util.UUID; 7 | 8 | /** 9 | * Provides utility methods for reading and writing of additional data types. 10 | */ 11 | public class NetUtils { 12 | public static int readVarInt(ByteBuf in) throws IOException { 13 | int result = 0; 14 | int bits = 0; 15 | int b; 16 | do { 17 | result |= ((b = in.readByte()) & 0x7F) << bits; 18 | if ((bits += 7) > 35) { 19 | throw new IOException("VarInt longer than 5 bytes"); 20 | } 21 | } while ((b & 0x80) == 0x80); 22 | return result; 23 | } 24 | 25 | public static byte[] readBytes(ByteBuf in, int length) throws IOException { 26 | byte[] bytes = new byte[length]; 27 | in.readBytes(bytes); 28 | return bytes; 29 | } 30 | 31 | public static String readString(ByteBuf in) throws IOException { 32 | return new String(readBytes(in, readVarInt(in)), "UTF-8"); 33 | } 34 | 35 | public static UUID readUUID(ByteBuf in) throws IOException { 36 | return new UUID(in.readLong(), in.readLong()); 37 | } 38 | 39 | public static void writeVarInt(ByteBuf out, int i) throws IOException { 40 | while ((i & ~0x7F) != 0) { 41 | out.writeByte((i & 0x7F) | 0x80); 42 | i >>>= 7; 43 | } 44 | out.writeByte(i); 45 | } 46 | 47 | public static void writeString(ByteBuf out, String string) throws IOException { 48 | byte[] bytes = string.getBytes("UTF-8"); 49 | writeVarInt(out, bytes.length); 50 | out.writeBytes(bytes); 51 | } 52 | 53 | public static void writeUUID(ByteBuf out, UUID uuid) throws IOException { 54 | out.writeLong(uuid.getMostSignificantBits()); 55 | out.writeLong(uuid.getLeastSignificantBits()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/condition/TestSwitchRange.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.condition; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestSwitchRange; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class TestSwitchRange extends PacketReadWriteTest { 13 | private PacketTestSwitchRange packet; 14 | 15 | @Before 16 | public void init() { 17 | packet = new PacketTestSwitchRange(); 18 | } 19 | 20 | @Test 21 | public void testReadInactive() throws IOException { 22 | read(packet, 1, 2); 23 | assertEquals(1, packet.before); 24 | assertEquals(0, packet.inner); 25 | assertEquals(2, packet.after); 26 | 27 | read(packet, 5, 2); 28 | assertEquals(5, packet.before); 29 | assertEquals(0, packet.inner); 30 | assertEquals(2, packet.after); 31 | } 32 | 33 | @Test 34 | public void testReadActive() throws IOException { 35 | read(packet, 2, 3, 4); 36 | assertEquals(2, packet.before); 37 | assertEquals(3, packet.inner); 38 | assertEquals(4, packet.after); 39 | 40 | read(packet, 3, 4, 5); 41 | assertEquals(3, packet.before); 42 | assertEquals(4, packet.inner); 43 | assertEquals(5, packet.after); 44 | 45 | read(packet, 4, 5, 6); 46 | assertEquals(4, packet.before); 47 | assertEquals(5, packet.inner); 48 | assertEquals(6, packet.after); 49 | } 50 | 51 | @Test 52 | public void testWriteInactive() throws IOException { 53 | packet.before = 1; 54 | packet.after = 2; 55 | write(packet, 1, 2); 56 | 57 | packet.before = 5; 58 | packet.after = 2; 59 | write(packet, 5, 2); 60 | } 61 | 62 | @Test 63 | public void testWriteActive() throws IOException { 64 | packet.before = 2; 65 | packet.inner = 3; 66 | packet.after = 4; 67 | write(packet, 2, 3, 4); 68 | 69 | packet.before = 3; 70 | packet.inner = 4; 71 | packet.after = 5; 72 | write(packet, 3, 4, 5); 73 | 74 | packet.before = 4; 75 | packet.inner = 5; 76 | packet.after = 6; 77 | write(packet, 4, 5, 6); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/condition/TestSwitchRangeMulti.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.condition; 2 | 3 | import de.johni0702.mc.protocolgen.PacketReadWriteTest; 4 | import de.johni0702.mc.protocolgen.test.PacketTestSwitchRangeMulti; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class TestSwitchRangeMulti extends PacketReadWriteTest { 13 | private PacketTestSwitchRangeMulti packet; 14 | 15 | @Before 16 | public void init() { 17 | packet = new PacketTestSwitchRangeMulti(); 18 | } 19 | 20 | @Test 21 | public void testReadInactive() throws IOException { 22 | read(packet, 0, 2); 23 | assertEquals(0, packet.before); 24 | assertEquals(0, packet.inner); 25 | assertEquals(2, packet.after); 26 | 27 | read(packet, 3, 2); 28 | assertEquals(3, packet.before); 29 | assertEquals(0, packet.inner); 30 | assertEquals(2, packet.after); 31 | 32 | read(packet, 6, 2); 33 | assertEquals(6, packet.before); 34 | assertEquals(0, packet.inner); 35 | assertEquals(2, packet.after); 36 | } 37 | 38 | @Test 39 | public void testReadActive() throws IOException { 40 | read(packet, 1, 2, 3); 41 | assertEquals(1, packet.before); 42 | assertEquals(2, packet.inner); 43 | assertEquals(3, packet.after); 44 | 45 | read(packet, 2, 3, 4); 46 | assertEquals(2, packet.before); 47 | assertEquals(3, packet.inner); 48 | assertEquals(4, packet.after); 49 | 50 | read(packet, 4, 5, 6); 51 | assertEquals(4, packet.before); 52 | assertEquals(5, packet.inner); 53 | assertEquals(6, packet.after); 54 | 55 | read(packet, 5, 6, 7); 56 | assertEquals(5, packet.before); 57 | assertEquals(6, packet.inner); 58 | assertEquals(7, packet.after); 59 | } 60 | 61 | @Test 62 | public void testWriteInactive() throws IOException { 63 | packet.before = 0; 64 | packet.after = 2; 65 | write(packet, 0, 2); 66 | 67 | packet.before = 3; 68 | packet.after = 2; 69 | write(packet, 3, 2); 70 | 71 | packet.before = 6; 72 | packet.after = 2; 73 | write(packet, 6, 2); 74 | } 75 | 76 | @Test 77 | public void testWriteActive() throws IOException { 78 | packet.before = 1; 79 | packet.inner = 2; 80 | packet.after = 3; 81 | write(packet, 1, 2, 3); 82 | 83 | packet.before = 2; 84 | packet.inner = 3; 85 | packet.after = 4; 86 | write(packet, 2, 3, 4); 87 | 88 | packet.before = 4; 89 | packet.inner = 5; 90 | packet.after = 6; 91 | write(packet, 4, 5, 6); 92 | 93 | packet.before = 5; 94 | packet.inner = 6; 95 | packet.after = 7; 96 | write(packet, 5, 6, 7); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/de/johni0702/mc/protocolgen/types/ItemStack.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.types; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import org.spacehq.opennbt.NBTIO; 5 | import org.spacehq.opennbt.tag.builtin.CompoundTag; 6 | 7 | import java.io.*; 8 | 9 | public final class ItemStack { 10 | private final int id; 11 | private final int amount; 12 | private final int data; 13 | private final CompoundTag nbt; 14 | 15 | public ItemStack(int id) { 16 | this(id, 1); 17 | } 18 | 19 | public ItemStack(int id, int amount) { 20 | this(id, amount, 0); 21 | } 22 | 23 | public ItemStack(int id, int amount, int data) { 24 | this(id, amount, data, null); 25 | } 26 | 27 | public ItemStack(int id, int amount, int data, CompoundTag nbt) { 28 | this.id = id; 29 | this.amount = amount; 30 | this.data = data; 31 | this.nbt = nbt; 32 | } 33 | 34 | public int getId() { 35 | return id; 36 | } 37 | 38 | public int getAmount() { 39 | return amount; 40 | } 41 | 42 | public int getData() { 43 | return data; 44 | } 45 | 46 | public CompoundTag getNbt() { 47 | return nbt; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object o) { 52 | if (this == o) return true; 53 | if (o == null || getClass() != o.getClass()) return false; 54 | 55 | ItemStack itemStack = (ItemStack) o; 56 | 57 | if (id != itemStack.id) return false; 58 | if (amount != itemStack.amount) return false; 59 | if (data != itemStack.data) return false; 60 | return !(nbt != null ? !nbt.equals(itemStack.nbt) : itemStack.nbt != null); 61 | 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | int result = id; 67 | result = 31 * result + amount; 68 | result = 31 * result + data; 69 | result = 31 * result + (nbt != null ? nbt.hashCode() : 0); 70 | return result; 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return "ItemStack{" + 76 | "id=" + id + 77 | ", amount=" + amount + 78 | ", data=" + data + 79 | ", nbt=" + nbt + 80 | '}'; 81 | } 82 | 83 | public static ItemStack read(final ByteBuf in) throws IOException { 84 | int id = in.readShort(); 85 | if (id == -1) { 86 | return null; 87 | } else { 88 | int amount = in.readByte(); 89 | int data = in.readShort(); 90 | CompoundTag nbt = (CompoundTag) NBTIO.readTag(new DataInputStream(new InputStream() { 91 | @Override 92 | public int read() throws IOException { 93 | return in.readUnsignedByte(); 94 | } 95 | })); 96 | return new ItemStack(id, amount, data, nbt); 97 | } 98 | } 99 | 100 | public static void write(ItemStack is, final ByteBuf out) throws IOException { 101 | if (is == null) { 102 | out.writeShort(-1); 103 | } else { 104 | out.writeShort(is.id); 105 | out.writeByte(is.amount); 106 | out.writeShort(is.data); 107 | if (is.nbt == null) { 108 | out.writeByte(0); 109 | } else { 110 | NBTIO.writeTag(new DataOutputStream(new OutputStream() { 111 | @Override 112 | public void write(int b) throws IOException { 113 | out.writeByte(b); 114 | } 115 | }), is.nbt); 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/test/java/de.johni0702.mc.protocolgen/TestSimpleFields.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen; 2 | 3 | import de.johni0702.mc.protocolgen.test.PacketTestBool; 4 | import de.johni0702.mc.protocolgen.test.PacketTestByte; 5 | import de.johni0702.mc.protocolgen.test.PacketTestDouble; 6 | import de.johni0702.mc.protocolgen.test.PacketTestEntityMetadata; 7 | import de.johni0702.mc.protocolgen.test.PacketTestFloat; 8 | import de.johni0702.mc.protocolgen.test.PacketTestInt; 9 | import de.johni0702.mc.protocolgen.test.PacketTestLong; 10 | import de.johni0702.mc.protocolgen.test.PacketTestPosition; 11 | import de.johni0702.mc.protocolgen.test.PacketTestRestBuffer; 12 | import de.johni0702.mc.protocolgen.test.PacketTestShort; 13 | import de.johni0702.mc.protocolgen.test.PacketTestSlot; 14 | import de.johni0702.mc.protocolgen.test.PacketTestString; 15 | import de.johni0702.mc.protocolgen.test.PacketTestUbyte; 16 | import de.johni0702.mc.protocolgen.test.PacketTestUshort; 17 | import de.johni0702.mc.protocolgen.test.PacketTestUuid; 18 | import de.johni0702.mc.protocolgen.test.PacketTestVarint; 19 | import de.johni0702.mc.protocolgen.types.EntityMetadata; 20 | import de.johni0702.mc.protocolgen.types.ItemStack; 21 | import de.johni0702.mc.protocolgen.types.Position; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.junit.runners.Parameterized; 25 | 26 | import java.io.IOException; 27 | import java.lang.reflect.Field; 28 | import java.util.Arrays; 29 | import java.util.Collection; 30 | import java.util.UUID; 31 | 32 | import static org.junit.Assert.assertArrayEquals; 33 | import static org.junit.Assert.assertEquals; 34 | 35 | @RunWith(Parameterized.class) 36 | public class TestSimpleFields extends PacketReadWriteTest { 37 | private static final UUID theUuid = new UUID(0x0102030405060708L, 0x090a0b0c0d0e0f10L); 38 | 39 | @Parameterized.Parameters(name = "{0}") 40 | public static Collection params() { 41 | return Arrays.asList(new Object[][] { 42 | {PacketTestInt.class, new byte[]{1, 2, 3, 4}, 0x01020304}, 43 | {PacketTestVarint.class, new byte[]{(byte) 0b10000100, 0b10}, 0b100000100}, 44 | {PacketTestString.class, new byte[]{4, 'T', 'e', 's', 't'}, "Test"}, 45 | {PacketTestShort.class, new byte[]{1, 2}, (short) 0x0102}, 46 | {PacketTestUshort.class, new byte[]{-1, 2}, 0xFF02}, 47 | {PacketTestLong.class, new byte[]{1, 2, 3, 4, 5, 6, 7, 8}, 0x0102030405060708L}, 48 | {PacketTestByte.class, new byte[]{-1}, (byte) -1}, 49 | {PacketTestUbyte.class, new byte[]{-1}, 0xff}, 50 | {PacketTestFloat.class, new byte[]{0, 0, 0, 0}, 0F}, // TODO Replace with proper test data 51 | {PacketTestDouble.class, new byte[]{0, 0, 0, 0, 0, 0, 0, 0}, 0D}, // TODO Replace with proper test data 52 | {PacketTestBool.class, new byte[]{0}, false}, 53 | {PacketTestUuid.class, new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, theUuid}, 54 | {PacketTestPosition.class, new byte[]{0, 0, 0, 0, 0, 0, 0, 0}, new Position(0, 0, 0)}, // TODO Replace with proper test data 55 | {PacketTestSlot.class, new byte[]{1, 2, 3, 4, 5, 0}, new ItemStack(0x0102, 0x03, 0x0405)}, 56 | {PacketTestEntityMetadata.class, new byte[]{127}, new EntityMetadata()}, 57 | {PacketTestRestBuffer.class, new byte[]{1, 2, 3, 4, 5, 6, 7}, new byte[]{1, 2, 3, 4, 5, 6, 7}}, 58 | }); 59 | } 60 | 61 | private final Packet packet; 62 | private final Field testField; 63 | private final byte[] bytes; 64 | private final Object value; 65 | 66 | public TestSimpleFields(Class packet, byte[] bytes, Object value) throws IllegalAccessException, InstantiationException, NoSuchFieldException { 67 | this.packet = packet.newInstance(); 68 | this.testField = packet.getField("test"); 69 | this.bytes = bytes; 70 | this.value = value; 71 | } 72 | 73 | @Test 74 | public void testRead() throws IOException, IllegalAccessException { 75 | read(packet, bytes); 76 | 77 | if (value instanceof byte[]) { 78 | assertArrayEquals((byte[]) value, (byte[]) testField.get(packet)); 79 | } else { 80 | assertEquals(value, testField.get(packet)); 81 | } 82 | } 83 | 84 | @Test 85 | public void testWrite() throws IOException, IllegalAccessException { 86 | testField.set(packet, value); 87 | 88 | write(packet, bytes); 89 | } 90 | } -------------------------------------------------------------------------------- /src/main/java/de/johni0702/mc/protocolgen/types/EntityMetadata.java: -------------------------------------------------------------------------------- 1 | package de.johni0702.mc.protocolgen.types; 2 | 3 | import de.johni0702.mc.protocolgen.NetUtils; 4 | import io.netty.buffer.ByteBuf; 5 | 6 | import java.io.DataInput; 7 | import java.io.DataOutput; 8 | import java.io.IOException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class EntityMetadata { 13 | private final Map data = new HashMap(); 14 | 15 | public boolean hasData(int key) { 16 | return data.containsKey(key); 17 | } 18 | 19 | public Object getData(int key) { 20 | return data.get(key); 21 | } 22 | 23 | public void putData(int key, Object obj) { 24 | data.put(key, obj); 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) return true; 30 | if (o == null || getClass() != o.getClass()) return false; 31 | 32 | EntityMetadata metadata = (EntityMetadata) o; 33 | 34 | return data.equals(metadata.data); 35 | 36 | } 37 | 38 | @Override 39 | public int hashCode() { 40 | return data.hashCode(); 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "EntityMetadata{" + 46 | "data=" + data + 47 | '}'; 48 | } 49 | 50 | public static EntityMetadata read(ByteBuf in) throws IOException { 51 | EntityMetadata metadata = new EntityMetadata(); 52 | int b; 53 | while ((b = in.readUnsignedByte()) != 127) { 54 | int type = (b & 0xE0) >> 5; 55 | int key = b & 0x1F; 56 | Object value; 57 | switch (type) { 58 | case 0: 59 | value = in.readByte(); 60 | break; 61 | case 1: 62 | value = in.readShort(); 63 | break; 64 | case 2: 65 | value = in.readInt(); 66 | break; 67 | case 3: 68 | value = in.readFloat(); 69 | break; 70 | case 4: 71 | value = NetUtils.readString(in); 72 | break; 73 | case 5: 74 | value = ItemStack.read(in); 75 | break; 76 | case 6: 77 | value = new Position(in.readInt(), in.readInt(), in.readInt()); 78 | break; 79 | case 7: 80 | value = new Rotation(in.readFloat(), in.readFloat(), in.readFloat()); 81 | break; 82 | default: 83 | throw new IllegalArgumentException("Unknown metadata type: " + type); 84 | } 85 | metadata.putData(key, value); 86 | } 87 | return metadata; 88 | } 89 | 90 | public void write(ByteBuf out) throws IOException { 91 | for (Map.Entry e : data.entrySet()) { 92 | int key = e.getKey(); 93 | Object value = e.getValue(); 94 | int type; 95 | if (value instanceof Byte) type = 0; 96 | else if (value instanceof Short) type = 1; 97 | else if (value instanceof Integer) type = 2; 98 | else if (value instanceof Float) type = 3; 99 | else if (value instanceof String) type = 4; 100 | else if (value instanceof ItemStack) type = 5; 101 | else if (value instanceof Position) type = 6; 102 | else if (value instanceof Rotation) type = 7; 103 | else throw new IllegalArgumentException("Unknown metadata type: " + value.getClass()); 104 | 105 | out.writeByte(type << 5 | key & 0x1F); 106 | 107 | switch (type) { 108 | case 0: 109 | out.writeByte((Byte) value); 110 | break; 111 | case 1: 112 | out.writeShort((Short) value); 113 | break; 114 | case 2: 115 | out.writeInt((Integer) value); 116 | break; 117 | case 3: 118 | out.writeFloat((Float) value); 119 | break; 120 | case 4: 121 | NetUtils.writeString(out, (String) value); 122 | break; 123 | case 5: 124 | ItemStack.write((ItemStack) value, out); 125 | break; 126 | case 6: 127 | ((Position) value).write(out); 128 | break; 129 | case 7: 130 | Rotation rotation = (Rotation) value; 131 | out.writeFloat(rotation.getPitch()); 132 | out.writeFloat(rotation.getYaw()); 133 | out.writeFloat(rotation.getRoll()); 134 | break; 135 | default: 136 | throw new IllegalArgumentException("Unknown metadata type: " + type); 137 | } 138 | } 139 | out.writeByte(127); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /src/test/resources/test_protocol.json: -------------------------------------------------------------------------------- 1 | { 2 | "test_int": { 3 | "id": "0x00", 4 | "fields": [ 5 | { 6 | "name": "test", 7 | "type": "int" 8 | } 9 | ] 10 | }, 11 | "test_varint": { 12 | "id": "0x00", 13 | "fields": [ 14 | { 15 | "name": "test", 16 | "type": "varint" 17 | } 18 | ] 19 | }, 20 | "test_string": { 21 | "id": "0x00", 22 | "fields": [ 23 | { 24 | "name": "test", 25 | "type": "string" 26 | } 27 | ] 28 | }, 29 | "test_short": { 30 | "id": "0x00", 31 | "fields": [ 32 | { 33 | "name": "test", 34 | "type": "short" 35 | } 36 | ] 37 | }, 38 | "test_ushort": { 39 | "id": "0x00", 40 | "fields": [ 41 | { 42 | "name": "test", 43 | "type": "ushort" 44 | } 45 | ] 46 | }, 47 | "test_long": { 48 | "id": "0x00", 49 | "fields": [ 50 | { 51 | "name": "test", 52 | "type": "long" 53 | } 54 | ] 55 | }, 56 | "test_byte": { 57 | "id": "0x00", 58 | "fields": [ 59 | { 60 | "name": "test", 61 | "type": "byte" 62 | } 63 | ] 64 | }, 65 | "test_ubyte": { 66 | "id": "0x00", 67 | "fields": [ 68 | { 69 | "name": "test", 70 | "type": "ubyte" 71 | } 72 | ] 73 | }, 74 | "test_float": { 75 | "id": "0x00", 76 | "fields": [ 77 | { 78 | "name": "test", 79 | "type": "float" 80 | } 81 | ] 82 | }, 83 | "test_double": { 84 | "id": "0x00", 85 | "fields": [ 86 | { 87 | "name": "test", 88 | "type": "double" 89 | } 90 | ] 91 | }, 92 | "test_bool": { 93 | "id": "0x00", 94 | "fields": [ 95 | { 96 | "name": "test", 97 | "type": "bool" 98 | } 99 | ] 100 | }, 101 | "test_uuid": { 102 | "id": "0x00", 103 | "fields": [ 104 | { 105 | "name": "test", 106 | "type": "uuid" 107 | } 108 | ] 109 | }, 110 | "test_restBuffer": { 111 | "id": "0x00", 112 | "fields": [ 113 | { 114 | "name": "test", 115 | "type": "restBuffer" 116 | } 117 | ] 118 | }, 119 | "test_position": { 120 | "id": "0x00", 121 | "fields": [ 122 | { 123 | "name": "test", 124 | "type": "position" 125 | } 126 | ] 127 | }, 128 | "test_slot": { 129 | "id": "0x00", 130 | "fields": [ 131 | { 132 | "name": "test", 133 | "type": "slot" 134 | } 135 | ] 136 | }, 137 | "test_entityMetadata": { 138 | "id": "0x00", 139 | "fields": [ 140 | { 141 | "name": "test", 142 | "type": "entityMetadata" 143 | } 144 | ] 145 | }, 146 | "test_switch_simple": { 147 | "id": "0x00", 148 | "fields": [ 149 | { 150 | "name": "before", 151 | "type": "byte" 152 | }, 153 | { 154 | "name": "inner", 155 | "type": [ 156 | "switch", 157 | { 158 | "compareTo": "before", 159 | "fields": { 160 | "2": "byte" 161 | }, 162 | "default": "void" 163 | } 164 | ] 165 | }, 166 | { 167 | "name": "after", 168 | "type": "byte" 169 | } 170 | ] 171 | }, 172 | "test_switch_string": { 173 | "id": "0x00", 174 | "fields": [ 175 | { 176 | "name": "before", 177 | "type": "string" 178 | }, 179 | { 180 | "name": "inner", 181 | "type": [ 182 | "switch", 183 | { 184 | "compareTo": "before", 185 | "fields": { 186 | "Valid": "byte" 187 | }, 188 | "default": "void" 189 | } 190 | ] 191 | }, 192 | { 193 | "name": "after", 194 | "type": "byte" 195 | } 196 | ] 197 | }, 198 | "test_switch_boolean": { 199 | "id": "0x00", 200 | "fields": [ 201 | { 202 | "name": "before", 203 | "type": "bool" 204 | }, 205 | { 206 | "name": "inner", 207 | "type": [ 208 | "switch", 209 | { 210 | "compareTo": "before", 211 | "fields": { 212 | "true": "byte" 213 | }, 214 | "default": "void" 215 | } 216 | ] 217 | }, 218 | { 219 | "name": "after", 220 | "type": "byte" 221 | } 222 | ] 223 | }, 224 | "test_switch_default": { 225 | "id": "0x00", 226 | "fields": [ 227 | { 228 | "name": "before", 229 | "type": "byte" 230 | }, 231 | { 232 | "name": "inner", 233 | "type": [ 234 | "switch", 235 | { 236 | "compareTo": "before", 237 | "fields": { 238 | "2": "void" 239 | }, 240 | "default": "byte" 241 | } 242 | ] 243 | }, 244 | { 245 | "name": "after", 246 | "type": "byte" 247 | } 248 | ] 249 | }, 250 | "test_switch_no_default": { 251 | "id": "0x00", 252 | "fields": [ 253 | { 254 | "name": "before", 255 | "type": "byte" 256 | }, 257 | { 258 | "name": "inner", 259 | "type": [ 260 | "switch", 261 | { 262 | "compareTo": "before", 263 | "fields": { 264 | "2": "byte" 265 | } 266 | } 267 | ] 268 | }, 269 | { 270 | "name": "after", 271 | "type": "byte" 272 | } 273 | ] 274 | }, 275 | "test_switch_array": { 276 | "id": "0x00", 277 | "fields": [ 278 | { 279 | "name": "before", 280 | "type": "byte" 281 | }, 282 | { 283 | "name": "inner", 284 | "type": [ 285 | "switch", 286 | { 287 | "compareTo": "before", 288 | "fields": { 289 | "2": [ 290 | "array", 291 | { 292 | "type": "byte", 293 | "countType": "byte" 294 | } 295 | ] 296 | }, 297 | "default": "void" 298 | } 299 | ] 300 | }, 301 | { 302 | "name": "after", 303 | "type": "byte" 304 | } 305 | ] 306 | }, 307 | "test_switch_array_multi": { 308 | "comment": "This packet does not have its own test. It merely exists to make sure variable declaration within switch cases work fine.", 309 | "id": "0x00", 310 | "fields": [ 311 | { 312 | "name": "before", 313 | "type": "byte" 314 | }, 315 | { 316 | "name": "inner", 317 | "type": [ 318 | "switch", 319 | { 320 | "compareTo": "before", 321 | "fields": { 322 | "2": [ 323 | "array", 324 | { 325 | "type": "byte", 326 | "countType": "byte" 327 | } 328 | ], 329 | "3": [ 330 | "array", 331 | { 332 | "type": "byte", 333 | "countType": "byte" 334 | } 335 | ] 336 | }, 337 | "default": "void" 338 | } 339 | ] 340 | }, 341 | { 342 | "name": "after", 343 | "type": "byte" 344 | } 345 | ] 346 | }, 347 | "test_switch_range": { 348 | "id": "0x00", 349 | "fields": [ 350 | { 351 | "name": "before", 352 | "type": "byte" 353 | }, 354 | { 355 | "name": "inner", 356 | "type": [ 357 | "switch", 358 | { 359 | "compareTo": "before", 360 | "fields": { 361 | "2": "byte", 362 | "3": "byte", 363 | "4": "byte" 364 | }, 365 | "default": "void" 366 | } 367 | ] 368 | }, 369 | { 370 | "name": "after", 371 | "type": "byte" 372 | } 373 | ] 374 | }, 375 | "test_switch_range_multi": { 376 | "id": "0x00", 377 | "fields": [ 378 | { 379 | "name": "before", 380 | "type": "byte" 381 | }, 382 | { 383 | "name": "inner", 384 | "type": [ 385 | "switch", 386 | { 387 | "compareTo": "before", 388 | "fields": { 389 | "1": "byte", 390 | "2": "byte", 391 | "3": "void", 392 | "4": "byte", 393 | "5": "byte" 394 | }, 395 | "default": "void" 396 | } 397 | ] 398 | }, 399 | { 400 | "name": "after", 401 | "type": "byte" 402 | } 403 | ] 404 | }, 405 | "test_array": { 406 | "id": "0x00", 407 | "fields": [ 408 | { 409 | "name": "before", 410 | "type": [ 411 | "count", 412 | { 413 | "type": "byte", 414 | "countFor": "inner" 415 | } 416 | ] 417 | }, 418 | { 419 | "name": "inner", 420 | "type": [ 421 | "array", 422 | { 423 | "count": "before", 424 | "type": "short" 425 | } 426 | ] 427 | } 428 | ] 429 | }, 430 | "test_buffer": { 431 | "id": "0x00", 432 | "fields": [ 433 | { 434 | "name": "before", 435 | "type": [ 436 | "count", 437 | { 438 | "type": "byte", 439 | "countFor": "inner" 440 | } 441 | ] 442 | }, 443 | { 444 | "name": "inner", 445 | "type": [ 446 | "buffer", 447 | { 448 | "count": "before" 449 | } 450 | ] 451 | } 452 | ] 453 | }, 454 | "test_array_count_type": { 455 | "id": "0x00", 456 | "fields": [ 457 | { 458 | "name": "inner", 459 | "type": [ 460 | "array", 461 | { 462 | "countType": "byte", 463 | "type": "short" 464 | } 465 | ] 466 | } 467 | ] 468 | }, 469 | "test_buffer_count_type": { 470 | "id": "0x00", 471 | "fields": [ 472 | { 473 | "name": "inner", 474 | "type": [ 475 | "buffer", 476 | { 477 | "countType": "byte" 478 | } 479 | ] 480 | } 481 | ] 482 | }, 483 | "test_container_simple": { 484 | "id": "0x00", 485 | "fields": [ 486 | { 487 | "name": "content", 488 | "type": [ 489 | "container", 490 | [ 491 | { 492 | "name": "inner", 493 | "type": "byte" 494 | } 495 | ] 496 | ] 497 | } 498 | ] 499 | }, 500 | "test_container_multi": { 501 | "id": "0x00", 502 | "fields": [ 503 | { 504 | "name": "content", 505 | "type": [ 506 | "container", 507 | [ 508 | { 509 | "name": "innerContent", 510 | "type": [ 511 | "container", 512 | [ 513 | { 514 | "name": "inner", 515 | "type": "byte" 516 | } 517 | ] 518 | ] 519 | } 520 | ] 521 | ] 522 | } 523 | ] 524 | }, 525 | "test_container_outer_condition": { 526 | "id": "0x00", 527 | "fields": [ 528 | { 529 | "name": "outer", 530 | "type": "byte" 531 | }, 532 | { 533 | "name": "content", 534 | "type": [ 535 | "container", 536 | [ 537 | { 538 | "name": "inner", 539 | "type": [ 540 | "switch", 541 | { 542 | "compareTo": "outer", 543 | "fields": { 544 | "1": "byte" 545 | }, 546 | "default": "void" 547 | } 548 | ] 549 | } 550 | ] 551 | ] 552 | } 553 | ] 554 | } 555 | } -------------------------------------------------------------------------------- /generator.gvy: -------------------------------------------------------------------------------- 1 | import groovy.json.JsonSlurper 2 | import groovy.transform.InheritConstructors 3 | 4 | task genPacketSources() { 5 | def protocolFile = file('minecraft-data/enums/protocol.json') 6 | def outputFolder = file('src/gen/java') 7 | inputs.file protocolFile 8 | outputs.dir outputFolder 9 | doLast { 10 | def jsonSlurper = new JsonSlurper() 11 | def jsonString = protocolFile.text 12 | // ugly hack to prevent uppercase field names 13 | jsonString = jsonString.replace('"UUID"', '"uuid"') 14 | def root = jsonSlurper.parseText(jsonString) 15 | 16 | root.each { 17 | def pckg = 'de.johni0702.mc.protocolgen.' + it.key 18 | def name = it.key.capitalize() 19 | generateProtocol(outputFolder, 'Server' + name, pckg + '.server', it.value.toServer) 20 | generateProtocol(outputFolder, 'Client' + name, pckg + '.client', it.value.toClient) 21 | } 22 | } 23 | } 24 | 25 | task genPacketTestSources() { 26 | def protocolFile = file('src/test/resources/test_protocol.json') 27 | def outputFolder = file('src/test/gen') 28 | inputs.file protocolFile 29 | outputs.dir outputFolder 30 | doLast { 31 | def jsonSlurper = new JsonSlurper() 32 | def jsonString = protocolFile.text 33 | // ugly hack to prevent uppercase field names 34 | jsonString = jsonString.replace('"UUID"', '"uuid"') 35 | def root = jsonSlurper.parseText(jsonString) 36 | generateProtocol(outputFolder, 'Test', 'de.johni0702.mc.protocolgen.test', root) 37 | } 38 | } 39 | 40 | def generateProtocol(sourceFolder, protocolName, pckg, root) { 41 | def folder = new File(sourceFolder, pckg.replace('.', '/')) 42 | if (!folder.exists()) folder.mkdirs() 43 | protocolName = 'Protocol' + protocolName 44 | def f = new File(folder, protocolName + '.java') 45 | if (f.exists()) f.delete() 46 | 47 | f << """// This file was generated automatically. Do not edit. 48 | package $pckg; 49 | 50 | import de.johni0702.mc.protocolgen.Packet; 51 | 52 | import java.util.HashMap; 53 | 54 | public class $protocolName extends HashMap> { 55 | 56 | \tpublic $protocolName() { 57 | """ 58 | 59 | root.sort{ it.value.id }.each { 60 | def name = 'Packet' + it.key.capitalize().replaceAll(/_\w/){ it[1].toUpperCase() } 61 | generatePacket(folder, pckg, name, it.value) 62 | f << "\t\tput($it.value.id, ${name}.class);\n" 63 | } 64 | 65 | f << '\t}\n}\n' 66 | } 67 | 68 | def generatePacket(folder, pckg, name, root) { 69 | println "Generating $pckg.$name" 70 | if (!folder.exists()) folder.mkdirs() 71 | def f = new File(folder, name + '.java') 72 | if (f.exists()) f.delete() 73 | 74 | def containers = new HashSet() 75 | def fields = Parser.parseFields(root.fields, [:], new HashSet<>(), null, containers) 76 | containers.each { it.parseClass(fields) } 77 | generatePacketHeader(f, pckg, name) 78 | Parser.generateBody(f, '\t', fields, Collections.emptySet(), containers) 79 | generatePacketFooter(f) 80 | } 81 | 82 | class Parser { 83 | static Map parseFields(root, Map parentFields, Set referencedFields, Field parent, Set containers) { 84 | def fields = [:] 85 | root.each { 86 | def referenced = new HashSet() 87 | def field = parseFieldWithName(it, parent, fields + parentFields, referenced, containers) 88 | referencedFields.addAll(referenced) 89 | fields.put(it.name, field) 90 | } 91 | return fields 92 | } 93 | 94 | static Field parseFieldWithName(it, Field parent, Map fields, Set referenced, Set containers) { 95 | parseField(it.type, it.name, parent, fields, referenced, containers) 96 | } 97 | 98 | static Field parseField(type, name, Field parent, Map fields, Set referenced, Set containers) { 99 | def typeArgs = null 100 | if (!(type instanceof String)) { 101 | typeArgs = type[1] 102 | type = type[0] 103 | } 104 | switch (type) { 105 | case 'count': 106 | def field = new CountField(parent, name) 107 | field.child = parseFieldWithName(typeArgs, field, fields, referenced, containers) 108 | return field 109 | case 'buffer': 110 | if (typeArgs.countType == null) { 111 | Field count 112 | CountTransformation countTransformation 113 | (count, countTransformation) = parseCount(typeArgs.count, fields) 114 | referenced.add(count) 115 | return new BufferField(parent, name, count, countTransformation) 116 | } else { 117 | return new CountedBufferField(parent, name, Type.fromJson(typeArgs.countType)) 118 | } 119 | case 'switch': 120 | def compareTo = typeArgs.compareTo; 121 | if (compareTo.startsWith('this.')) { 122 | compareTo = compareTo.substring('this.'.length()) 123 | } 124 | compareTo = fields.get(compareTo) 125 | referenced.add(compareTo) 126 | def voids = [] 127 | Field contentField = null 128 | SwitchField field = new SwitchField(parent, name, compareTo) 129 | typeArgs.fields.each { 130 | def f = parseField(it.value, null, field, fields, referenced, containers) 131 | if (f instanceof VoidField) { 132 | voids << f 133 | } else { 134 | contentField = f 135 | } 136 | field.cases.put(it.key, f) 137 | } 138 | if (typeArgs.default != null) { 139 | field.defaultCase = parseField(typeArgs.default, null, field, fields, referenced, containers) 140 | if (field.defaultCase instanceof VoidField) { 141 | voids << field.defaultCase 142 | } else { 143 | contentField = field.defaultCase 144 | } 145 | } 146 | voids.each { 147 | it.type = contentField.type 148 | } 149 | field.child = contentField 150 | return field 151 | case 'array': 152 | def field 153 | if (typeArgs.countType == null) { 154 | Field count 155 | CountTransformation countTransformation 156 | (count, countTransformation) = parseCount(typeArgs.count, fields) 157 | referenced.add(count) 158 | field = new ArrayField(parent, name, count, countTransformation) 159 | field.child = parseFieldWithName(typeArgs, field, fields, referenced, containers) 160 | } else { 161 | field = new CountedArrayField(parent, name, Type.fromJson(typeArgs.countType)) 162 | field.counted.child = parseFieldWithName(typeArgs, field, fields, referenced, containers) 163 | } 164 | return field 165 | case 'container': 166 | def field = new ContainerField(parent, name, typeArgs) 167 | containers.add(field) 168 | return field 169 | case 'void': 170 | return new VoidField(parent, name) 171 | default: 172 | return new SimpleField(parent, Type.fromJson(type), name) 173 | } 174 | } 175 | 176 | static def parseCount(root, fields) { 177 | CountTransformation transform 178 | def count 179 | if (root instanceof String) { 180 | transform = new NoCountTransformation() 181 | count = root 182 | } else { 183 | transform = new SwitchTransformation(root.default, root.map) 184 | count = root.field 185 | } 186 | if (count.startsWith("this.")) { 187 | count = count.substring("this.".length()) 188 | } 189 | Field countField = fields.get(count) 190 | if (countField == null) { 191 | throw new InvalidUserDataException("Can't find count field '$count'") 192 | } 193 | return [countField, transform] 194 | } 195 | 196 | static def generateBody(File f, String indent, Map fields, Set referenced, Set containers) { 197 | // Fields 198 | fields.values().each { it.generateDeclaration(f, indent) } 199 | f << '\n' 200 | 201 | // Read 202 | def args = referenced.collect { ", $it.type.java $it.name" }.join('') 203 | f << indent + (indent.length() == 1 ? 'public' : 'private') + " void read(ByteBuf in$args) throws IOException {\n" 204 | fields.values().each { it.generateLocalDeclaration(f, indent + '\t') } 205 | fields.values().each { it.generateRead(f, indent + '\t', it.name) } 206 | f << indent + '}\n' 207 | f << '\n' 208 | 209 | // Write 210 | args = referenced.collect { ", $it.type.java $it.name" }.join('') 211 | f << indent + (indent.length() == 1 ? 'public' : 'private') + " void write(ByteBuf out$args) throws IOException {\n" 212 | fields.values().each { it.generateWrite(f, indent + '\t', it.name) } 213 | f << indent + '}\n' 214 | f << '\n' 215 | 216 | // Containers / Inner classes 217 | containers.each { it.generateClass(f, indent) } 218 | } 219 | } 220 | 221 | class Type { 222 | static List values = [] 223 | 224 | final String java 225 | final String json 226 | final String read 227 | final Closure write 228 | final String defaultValue 229 | 230 | Type(String java, String json, String read, Closure write, String defaultValue) { 231 | this.java = java 232 | this.json = json 233 | this.read = read 234 | this.write = write 235 | this.defaultValue = defaultValue 236 | } 237 | 238 | public static Type fromJson(String jsonType) { 239 | values.find {it.json == jsonType} 240 | } 241 | 242 | static { 243 | values << new Type('int', 'int', 'in.readInt()', {"out.writeInt($it)"}, "0"); 244 | values << new Type('int', 'varint', 'NetUtils.readVarInt(in)', {"NetUtils.writeVarInt(out, $it)"}, "0"); 245 | values << new Type('String', 'string', 'NetUtils.readString(in)', {"NetUtils.writeString(out, $it)"}, "null"); 246 | values << new Type('short', 'short', 'in.readShort()', {"out.writeShort($it)"}, "0"); 247 | values << new Type('int', 'ushort', 'in.readUnsignedShort()', {"out.writeShort($it)"}, "0"); 248 | values << new Type('long', 'long', 'in.readLong()', {"out.writeLong($it)"}, "0"); 249 | values << new Type('byte', 'byte', 'in.readByte()', {"out.writeByte($it)"}, "0"); 250 | values << new Type('int', 'ubyte', 'in.readUnsignedByte()', {"out.writeByte($it)"}, "0"); 251 | values << new Type('float', 'float', 'in.readFloat()', {"out.writeFloat($it)"}, "0"); 252 | values << new Type('double', 'double', 'in.readDouble()', {"out.writeDouble($it)"}, "0"); 253 | values << new Type('boolean', 'bool', 'in.readBoolean()', {"out.writeBoolean($it)"}, "false"); 254 | values << new Type('UUID', 'uuid', 'NetUtils.readUUID(in)', {"NetUtils.writeUUID(out, $it)"}, "null"); 255 | values << new Type('byte[]', 'restBuffer', 'NetUtils.readBytes(in, in.readableBytes())', {"out.writeBytes($it)"}, "null"); 256 | values << new Type('Position', 'position', 'Position.read(in)', {"${it}.write(out)"}, "null"); 257 | values << new Type('ItemStack', 'slot', 'ItemStack.read(in)', {"ItemStack.write($it, out)"}, "null"); 258 | values << new Type('EntityMetadata', 'entityMetadata', 'EntityMetadata.read(in)', {"${it}.write(out)"}, "null"); 259 | } 260 | } 261 | 262 | abstract class Field { 263 | Field parent 264 | Field child 265 | String name 266 | 267 | Field(Field parent, String name) { 268 | this.parent = parent 269 | this.name = name 270 | } 271 | 272 | Type getType() { 273 | parent?.type 274 | } 275 | 276 | String getName() { 277 | name != null ? name : parent?.getName() 278 | } 279 | 280 | abstract void generateDeclaration(File file, String indent) 281 | abstract void generateLocalDeclaration(File file, String indent) 282 | abstract void generateRead(File file, String indent, String name) 283 | abstract void generateWrite(File file, String indent, String name) 284 | } 285 | 286 | class SimpleField extends Field { 287 | Type type 288 | 289 | SimpleField(Field parent, Type type, String name) { 290 | super(parent, name) 291 | this.type = type 292 | } 293 | 294 | @Override 295 | Type getType() { 296 | type 297 | } 298 | 299 | @Override 300 | void generateDeclaration(File file, String indent) { 301 | file << "${indent}public $type.java $name;\n" 302 | } 303 | 304 | @Override 305 | void generateLocalDeclaration(File file, String indent) { 306 | // Only count fields require local declaration 307 | } 308 | 309 | @Override 310 | void generateRead(File file, String indent, String name) { 311 | file << indent + name + ' = ' + type.read + ";\n" 312 | } 313 | 314 | @Override 315 | void generateWrite(File file, String indent, String name) { 316 | file << indent + type.write(name) + ";\n" 317 | } 318 | } 319 | 320 | class VoidField extends Field { 321 | Type type 322 | 323 | VoidField(Field parent, String name) { 324 | super(parent, name) 325 | } 326 | 327 | @Override 328 | Type getType() { 329 | type 330 | } 331 | 332 | @Override 333 | void generateDeclaration(File file, String indent) { 334 | file << "${indent}public $type.java $name;\n" 335 | } 336 | 337 | @Override 338 | void generateLocalDeclaration(File file, String indent) { 339 | } 340 | 341 | @Override 342 | void generateRead(File file, String indent, String name) { 343 | file << indent + name + ' = ' + type.defaultValue + ";\n" 344 | } 345 | 346 | @Override 347 | void generateWrite(File file, String indent, String name) { 348 | } 349 | } 350 | 351 | @InheritConstructors 352 | class CountField extends Field { 353 | interface For { 354 | String getCount() 355 | } 356 | For forField 357 | CountTransformation countTransformation; 358 | 359 | @Override 360 | Type getType() { 361 | child.type 362 | } 363 | 364 | @Override 365 | void generateDeclaration(File file, String indent) { 366 | // Count field is local only 367 | } 368 | 369 | @Override 370 | void generateLocalDeclaration(File file, String indent) { 371 | file << "${indent}int $name = 0;\n" 372 | } 373 | 374 | @Override 375 | void generateRead(File file, String indent, String name) { 376 | child.generateRead(file, indent, name) 377 | } 378 | 379 | @Override 380 | void generateWrite(File file, String indent, String name) { 381 | def transformed = countTransformation.transform(file, indent, forField.count) 382 | file << "${indent}int $name = $transformed;\n" 383 | child.generateWrite(file, indent, name) 384 | } 385 | } 386 | 387 | class CountedField extends Field { 388 | Field count 389 | Field counted 390 | 391 | CountedField(Field parent, String name, Type countType) { 392 | super(parent, name) 393 | count = new CountField(this, "${->this.name}\$count") 394 | count.child = new SimpleField(count, countType, null) 395 | } 396 | 397 | @Override 398 | Type getType() { 399 | counted.type 400 | } 401 | 402 | @Override 403 | void generateDeclaration(File file, String indent) { 404 | counted.generateDeclaration(file, indent) 405 | } 406 | 407 | @Override 408 | void generateLocalDeclaration(File file, String indent) { 409 | count.generateLocalDeclaration(file, indent) 410 | } 411 | 412 | @Override 413 | void generateRead(File file, String indent, String name) { 414 | count.generateRead(file, indent, name + '$count') 415 | counted.generateRead(file, indent, name) 416 | } 417 | 418 | @Override 419 | void generateWrite(File file, String indent, String name) { 420 | count.generateWrite(file, indent, name + '$count') 421 | counted.generateWrite(file, indent, name) 422 | } 423 | } 424 | 425 | class CountedBufferField extends CountedField { 426 | CountedBufferField(Field parent, String name, Type countType) { 427 | super(parent, name, countType) 428 | counted = new BufferField(this, name, count, new NoCountTransformation()) 429 | } 430 | } 431 | 432 | class CountedArrayField extends CountedField { 433 | CountedArrayField(Field parent, String name, Type countType) { 434 | super(parent, name, countType) 435 | counted = new ArrayField(this, name, count, new NoCountTransformation()) 436 | } 437 | } 438 | 439 | class BufferField extends Field implements CountField.For { 440 | static Type type = new Type('byte[]', 'buffer', null, null, "null") 441 | Field count 442 | CountTransformation countTransformation 443 | 444 | BufferField(Field parent, String name, Field count, CountTransformation countTransformation) { 445 | super(parent, name) 446 | this.count = count 447 | this.countTransformation = countTransformation 448 | 449 | while (!(count instanceof CountField)) { 450 | count = count.child 451 | if (count == null) return 452 | } 453 | count.forField = this 454 | count.countTransformation = countTransformation 455 | } 456 | 457 | @Override 458 | Type getType() { 459 | type 460 | } 461 | 462 | @Override 463 | void generateDeclaration(File file, String indent) { 464 | file << "${indent}public byte[] $name;\n" 465 | } 466 | 467 | @Override 468 | void generateLocalDeclaration(File file, String indent) { 469 | // None 470 | } 471 | 472 | @Override 473 | void generateRead(File file, String indent, String name) { 474 | file << indent + "$name = NetUtils.readBytes(in, $count.name);\n" 475 | } 476 | 477 | @Override 478 | void generateWrite(File file, String indent, String name) { 479 | file << indent + "out.writeBytes($name);\n" 480 | } 481 | 482 | @Override 483 | String getCount() { 484 | name + '.length' 485 | } 486 | } 487 | 488 | @InheritConstructors 489 | class SwitchField extends Field { 490 | Field compareTo 491 | Map cases = [:] 492 | Field defaultCase 493 | 494 | SwitchField(Field parent, String name, Field compareTo) { 495 | super(parent, name) 496 | this.compareTo = compareTo 497 | 498 | } 499 | 500 | @Override 501 | Type getType() { 502 | child.type 503 | } 504 | 505 | @Override 506 | void generateDeclaration(File file, String indent) { 507 | child.generateDeclaration(file, indent) 508 | } 509 | 510 | @Override 511 | void generateLocalDeclaration(File file, String indent) { 512 | child.generateLocalDeclaration(file, indent) 513 | } 514 | 515 | @Override 516 | void generateRead(File file, String indent, String name) { 517 | if (compareTo.type.java == 'boolean') { 518 | def onTrue = cases.containsKey("true") && !(cases.get("true") instanceof VoidField) ? cases.get("true") : defaultCase 519 | def onFalse = cases.containsKey("false") && !(cases.get("false") instanceof VoidField) ? cases.get("false") : defaultCase 520 | file << "${indent}if ($compareTo.name) {\n" 521 | onTrue.generateRead(file, indent + '\t', name) 522 | file << indent + '} else {\n' 523 | onFalse.generateRead(file, indent + '\t', name) 524 | file << indent + '}\n' 525 | } else { 526 | file << "${indent}switch ($compareTo.name) {\n" 527 | cases.each { 528 | if (compareTo.type.java == 'String') { 529 | file << "$indent\tcase \"$it.key\": {\n" 530 | } else { 531 | file << "$indent\tcase $it.key: {\n" 532 | } 533 | it.value.generateRead(file, indent + '\t\t', name) 534 | file << "$indent\t\tbreak;\n" 535 | file << "$indent\t}\n" 536 | } 537 | file << "$indent\tdefault:\n" 538 | if (defaultCase != null) { 539 | defaultCase.generateRead(file, indent + '\t\t', name) 540 | } else { 541 | file << "$indent\t\tthrow new IllegalArgumentException(String.valueOf($compareTo.name));\n" 542 | } 543 | file << indent + '}\n' 544 | } 545 | } 546 | 547 | @Override 548 | void generateWrite(File file, String indent, String name) { 549 | if (compareTo.type.java == 'boolean') { 550 | def onTrue = cases.containsKey("true") && !(cases.get("true") instanceof VoidField) ? cases.get("true") : defaultCase 551 | def onFalse = cases.containsKey("false") && !(cases.get("false") instanceof VoidField) ? cases.get("false") : defaultCase 552 | file << "${indent}if ($compareTo.name) {\n" 553 | onTrue.generateWrite(file, indent + '\t', name) 554 | file << indent + '} else {\n' 555 | onFalse.generateWrite(file, indent + '\t', name) 556 | file << indent + '}\n' 557 | } else { 558 | file << "${indent}switch ($compareTo.name) {\n" 559 | cases.each { 560 | if (compareTo.type.java == 'String') { 561 | file << "$indent\tcase \"$it.key\": {\n" 562 | } else { 563 | file << "$indent\tcase $it.key: {\n" 564 | } 565 | it.value.generateWrite(file, indent + '\t\t', name) 566 | file << "$indent\t\tbreak;\n" 567 | file << "$indent\t}\n" 568 | } 569 | file << "$indent\tdefault:\n" 570 | if (defaultCase != null) { 571 | defaultCase.generateWrite(file, indent + '\t\t', name) 572 | } else { 573 | file << "$indent\t\tthrow new IllegalArgumentException(String.valueOf($compareTo.name));\n" 574 | } 575 | file << indent + '}\n' 576 | } 577 | } 578 | } 579 | 580 | @InheritConstructors 581 | class ArrayField extends Field implements CountField.For { 582 | Field count 583 | CountTransformation countTransformation 584 | 585 | ArrayField(Field parent, String name, Field count, CountTransformation countTransformation) { 586 | super(parent, name) 587 | this.count = count 588 | this.countTransformation = countTransformation 589 | 590 | while (!(count instanceof CountField)) { 591 | count = count.child 592 | if (count == null) return 593 | } 594 | count.forField = this 595 | count.countTransformation = countTransformation 596 | } 597 | 598 | @Override 599 | Type getType() { 600 | new Type("$child.type.java[]", null, null, null, "null") 601 | } 602 | 603 | @Override 604 | void generateDeclaration(File file, String indent) { 605 | file << "${indent}public $type.java $name;\n" 606 | } 607 | 608 | @Override 609 | void generateLocalDeclaration(File file, String indent) { 610 | child.generateLocalDeclaration(file, indent) 611 | } 612 | 613 | @Override 614 | void generateRead(File file, String indent, String name) { 615 | def i = 'i_' + name 616 | file << indent + "int max_$i = ${countTransformation.transform(file, indent, count.name)};\n" 617 | file << indent + "$name = new $child.type.java[max_$i];\n" 618 | file << indent + "for (int $i = 0; $i < max_$i; $i++) {\n" 619 | child.generateRead(file, indent + '\t', "$name[$i]") 620 | file << indent + '}\n' 621 | } 622 | 623 | @Override 624 | void generateWrite(File file, String indent, String name) { 625 | file << indent + "for ($child.type.java $name : this.$name) {\n" 626 | child.generateWrite(file, indent + '\t', name) 627 | file << indent + '}\n' 628 | } 629 | 630 | @Override 631 | String getCount() { 632 | name + '.length' 633 | } 634 | } 635 | 636 | @InheritConstructors 637 | class ContainerField extends Field { 638 | 639 | def root 640 | def containers = new HashSet() 641 | def referenced = new HashSet() 642 | def fields 643 | 644 | ContainerField(Field parent, String name, root) { 645 | super(parent, name) 646 | this.root = root 647 | } 648 | 649 | @Override 650 | Type getType() { 651 | def name = name 652 | def parent = parent 653 | while (parent != null) { 654 | if (parent instanceof ArrayField) { 655 | if (name.endsWith('s')) { 656 | if (name.endsWith('ies')) { 657 | name = name.substring(0, name.length() - 3) + 'y' 658 | } else { 659 | name = name.substring(0, name.length() - 1) 660 | } 661 | } else { 662 | name = name + 'Element' 663 | } 664 | } 665 | parent = parent.parent 666 | } 667 | return new Type(name.capitalize(), null, null, null, "null") 668 | } 669 | 670 | @Override 671 | void generateDeclaration(File file, String indent) { 672 | file << "${indent}public $type.java $name;\n" 673 | } 674 | 675 | @Override 676 | void generateLocalDeclaration(File file, String indent) { 677 | // None 678 | } 679 | 680 | @Override 681 | void generateRead(File file, String indent, String name) { 682 | def args = referenced.collect { ", $it.name" }.join('') 683 | file << "$indent${name} = new $type.java();\n" 684 | file << "$indent${name}.read(in$args);\n" 685 | } 686 | 687 | @Override 688 | void generateWrite(File file, String indent, String name) { 689 | def args = referenced.collect { ", $it.name" }.join('') 690 | file << "$indent${name}.write(out$args);\n" 691 | } 692 | 693 | void parseClass(outerFields) { 694 | fields = Parser.parseFields(root, outerFields, referenced, null, containers) 695 | referenced.retainAll(outerFields.values()) 696 | containers.each { 697 | it.parseClass(fields + outerFields) 698 | it.referenced.each { 699 | if (outerFields.containsValue(it)) { 700 | referenced.add(it) 701 | } 702 | } 703 | } 704 | } 705 | 706 | void generateClass(File file, String indent) { 707 | file << indent + "public static class $type.java {\n" 708 | Parser.generateBody(file, indent + '\t', fields, referenced, containers) 709 | file << "$indent}\n" 710 | } 711 | } 712 | 713 | interface CountTransformation { 714 | String transform(File file, String intend, String from) 715 | } 716 | 717 | class NoCountTransformation implements CountTransformation { 718 | @Override 719 | String transform(File file, String intend, String from) { 720 | from 721 | } 722 | } 723 | 724 | class SwitchTransformation implements CountTransformation { 725 | int defaultValue 726 | Map map 727 | 728 | SwitchTransformation(int defaultValue, Map map) { 729 | this.defaultValue = defaultValue 730 | this.map = map 731 | } 732 | 733 | @Override 734 | String transform(File file, String intend, String from) { 735 | file << intend + "int s_$from = $defaultValue;\n" 736 | file << intend + "switch ($from) {\n" 737 | map.each { 738 | file << "$intend\tcase $it.key: s_$from = $it.value; break;\n" 739 | } 740 | file << intend + '}\n' 741 | return "s_$from" 742 | } 743 | } 744 | 745 | def generatePacketHeader(f, pckg, name) { 746 | f << """// This file was generated automatically. Do not edit. 747 | package $pckg; 748 | 749 | import de.johni0702.mc.protocolgen.Packet; 750 | import de.johni0702.mc.protocolgen.NetUtils; 751 | import de.johni0702.mc.protocolgen.types.*; 752 | import io.netty.buffer.ByteBuf; 753 | 754 | import java.io.IOException; 755 | import java.util.UUID; 756 | 757 | public class $name implements Packet { 758 | """ 759 | } 760 | 761 | def generatePacketFooter(f) { 762 | f << "}\n" 763 | } 764 | --------------------------------------------------------------------------------