diff --git a/.travis.yml b/.travis.yml index 2390a3f..65fab6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ jdk: install: true script: - mvn clean package - - java -jar ./target/protogen-1.0-SNAPSHOT-shaded.jar all + - java -jar ./target/protogen-1.0-SNAPSHOT-shaded.jar --type all cache: directories: diff --git a/pom.xml b/pom.xml index 8b84cb9..e98a500 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,26 @@ gson 2.8.5 + + net.sf.jopt-simple + jopt-simple + 5.0.4 + + + org.ow2.asm + asm + 7.1 + + + org.ow2.asm + asm-tree + 7.1 + + + org.ow2.asm + asm-commons + 7.1 + diff --git a/src/main/java/org/feathercore/protogen/Bootstrap.java b/src/main/java/org/feathercore/protogen/Bootstrap.java index cd36905..10572a2 100644 --- a/src/main/java/org/feathercore/protogen/Bootstrap.java +++ b/src/main/java/org/feathercore/protogen/Bootstrap.java @@ -16,16 +16,22 @@ package org.feathercore.protogen; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; +import joptsimple.util.EnumConverter; import org.feathercore.protogen.burger.BurgerReader; import org.feathercore.protogen.generate.MaterialGenerator; import org.feathercore.protogen.generate.PacketGenerator; import org.feathercore.protogen.generate.ParticleGenerator; import org.feathercore.protogen.generate.SoundGenerator; import org.feathercore.protogen.util.FileUtil; +import org.feathercore.protogen.version.MinecraftVersion; import org.feathercore.protogen.wiki.WikiPacketInfo; import org.feathercore.protogen.wiki.WikiReader; -import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,71 +40,90 @@ * @author xtrafrancyz */ public class Bootstrap { - private static final File VALID_DIR = new File("gen"); - private static final File BROKEN_DIR = new File("gen-broken"); + private static final Path VALID_DIR; + private static final Path BROKEN_DIR; - private static final CachedDataSource BURGER = new CachedDataSource<>(BurgerReader::new); - private static final CachedDataSource WIKI = new CachedDataSource<>(WikiReader::new); + private static final MinecraftVersion DEFAULT_VERSION = MinecraftVersion._1_13_2; + private static final CachedDataSource BURGER = new CachedDataSource<>(); + private static final CachedDataSource WIKI = new CachedDataSource<>(); - private static final Map GENERATORS = new HashMap() {{ - put("materials", Bootstrap::genMaterials); - put("sounds", Bootstrap::genSounds); - put("particles", Bootstrap::genParticles); - put("packets", Bootstrap::genPackets); + private static final Map GENERATORS = new HashMap() {{ + put(GeneratorType.MATERIALS, Bootstrap::genMaterials); + put(GeneratorType.SOUNDS, Bootstrap::genSounds); + put(GeneratorType.PARTICLES, Bootstrap::genParticles); + put(GeneratorType.PACKETS, Bootstrap::genPackets); + put(GeneratorType.ALL, () -> { + Bootstrap.genMaterials(); + Bootstrap.genSounds(); + Bootstrap.genParticles(); + Bootstrap.genPackets(); + }); }}; public static void main(String[] args) throws Exception { - if (args.length == 0) { - System.out.println("Choose what to generate:"); - System.out.println(" " + String.join(", ", GENERATORS.keySet())); - System.out.println(" or"); - System.out.println(" all"); - return; - } + OptionParser parser = new OptionParser(); + OptionSpec specType = parser.accepts("type") + .withRequiredArg() + .ofType(GeneratorType.class) + .withValuesConvertedBy(new EnumConverter(GeneratorType.class) {}) + .required(); + OptionSpec specVersion = parser.accepts("version") + .withRequiredArg() + .ofType(MinecraftVersion.class) + .withValuesConvertedBy(new EnumConverter(MinecraftVersion.class) { + @Override + public MinecraftVersion convert(String value) { + return super.convert("_".concat(value.replace('.', '_'))); + } + }) + .defaultsTo(DEFAULT_VERSION); + OptionSet options = parser.parse(args); + GeneratorType type = options.valueOf(specType); + MinecraftVersion version = options.valueOf(specVersion); + + System.out.println("Generator: " + type + ", version: " + version); FileUtil.deleteRecursive(VALID_DIR); FileUtil.deleteRecursive(BROKEN_DIR); - if (args.length == 1 && args[0].equalsIgnoreCase("all")) { - for (GenRunnable gen : GENERATORS.values()) { - gen.run(); - } - } else { - for (final String arg : args) { - GenRunnable gen = GENERATORS.get(arg.toLowerCase()); - if (gen != null) { - gen.run(); - } - } - } + BURGER.setHandle(() -> new BurgerReader(version)); + // TODO workaround please + WIKI.setHandle(() -> new WikiReader(version.getLink())); + GENERATORS.get(type).run(); } private static void genMaterials() throws Exception { String generated = new MaterialGenerator(BURGER.get()).generate(); - FileUtil.writeFile(new File(VALID_DIR, MaterialGenerator.CLASS_NAME + ".java"), generated); + FileUtil.writeFile(VALID_DIR.resolve(MaterialGenerator.CLASS_NAME + ".java"), generated); } private static void genParticles() throws Exception { //String generated = new ParticleGenerator(new WikiReader(new File("proto.html")).getParticles()).generate(); String generated = new ParticleGenerator(WIKI.get().getParticles()).generate(); - FileUtil.writeFile(new File(VALID_DIR, ParticleGenerator.CLASS_NAME + ".java"), generated); + FileUtil.writeFile(VALID_DIR.resolve(ParticleGenerator.CLASS_NAME + ".java"), generated); } private static void genSounds() throws Exception { String generated = new SoundGenerator(BURGER.get().getSounds()).generate(); - FileUtil.writeFile(new File(VALID_DIR, SoundGenerator.CLASS_NAME + ".java"), generated); + FileUtil.writeFile(VALID_DIR.resolve(SoundGenerator.CLASS_NAME + ".java"), generated); } private static void genPackets() throws Exception { List packets = WIKI.get().getPackets(); for (WikiPacketInfo info : packets) { String generated = new PacketGenerator(info).generate(); - File protoDir = new File(info.isBroken() ? BROKEN_DIR : VALID_DIR, info.getProtocol().name().toLowerCase()); - File finalDir = new File(protoDir, info.getSender().name().toLowerCase()); - FileUtil.writeFile(new File(finalDir, info.getStandardClassName() + ".java"), generated); + Path protoDir = (info.isBroken() ? BROKEN_DIR : VALID_DIR).resolve(info.getProtocol().name().toLowerCase()); + Path finalDir = protoDir.resolve(info.getSender().name().toLowerCase()); + FileUtil.writeFile(finalDir.resolve(info.getStandardClassName() + ".java"), generated); } } + static { + Path workingDir = Paths.get(System.getProperty("user.dir")); + VALID_DIR = workingDir.resolve("gen"); + BROKEN_DIR = workingDir.resolve("gen-broken"); + } + private interface GenRunnable { void run() throws Exception; } diff --git a/src/main/java/org/feathercore/protogen/CachedDataSource.java b/src/main/java/org/feathercore/protogen/CachedDataSource.java index 7f3a1ac..64d8c19 100644 --- a/src/main/java/org/feathercore/protogen/CachedDataSource.java +++ b/src/main/java/org/feathercore/protogen/CachedDataSource.java @@ -23,10 +23,6 @@ public class CachedDataSource { private Creator creator; private T cache; - public CachedDataSource(Creator creator) { - this.creator = creator; - } - public T get() throws Exception { if (cache == null) { cache = creator.create(); @@ -34,6 +30,10 @@ public T get() throws Exception { return cache; } + public void setHandle(Creator creator) { + this.creator = creator; + } + public interface Creator { T create() throws Exception; } diff --git a/src/main/java/org/feathercore/protogen/GeneratorType.java b/src/main/java/org/feathercore/protogen/GeneratorType.java new file mode 100644 index 0000000..29a8384 --- /dev/null +++ b/src/main/java/org/feathercore/protogen/GeneratorType.java @@ -0,0 +1,26 @@ +/* + * Copyright 2019 Feather Core + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.feathercore.protogen; + +public enum GeneratorType { + + MATERIALS, + SOUNDS, + PARTICLES, + PACKETS, + ALL +} diff --git a/src/main/java/org/feathercore/protogen/burger/BurgerItem.java b/src/main/java/org/feathercore/protogen/burger/BurgerItem.java index 5eb38ec..a01ef42 100644 --- a/src/main/java/org/feathercore/protogen/burger/BurgerItem.java +++ b/src/main/java/org/feathercore/protogen/burger/BurgerItem.java @@ -22,11 +22,14 @@ * @author xtrafrancyz */ public class BurgerItem { + //private static boolean warned; + private String textId; private String enumName; private int id; private int maxStackSize; - + private boolean solid; + public BurgerItem(JsonObject json) { this.textId = json.get("text_id").getAsString(); this.id = json.get("numeric_id").getAsInt(); @@ -49,4 +52,8 @@ public int getId() { public int getMaxStackSize() { return maxStackSize; } + + public boolean isSolid() { + return solid; + } } diff --git a/src/main/java/org/feathercore/protogen/burger/BurgerReader.java b/src/main/java/org/feathercore/protogen/burger/BurgerReader.java index 8186dd3..881b1c0 100644 --- a/src/main/java/org/feathercore/protogen/burger/BurgerReader.java +++ b/src/main/java/org/feathercore/protogen/burger/BurgerReader.java @@ -21,11 +21,9 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.feathercore.protogen.util.NetworkUtil; +import org.feathercore.protogen.version.MinecraftVersion; -import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.*; /** @@ -34,21 +32,18 @@ * @author xtrafrancyz */ public class BurgerReader { - public static final String STANDARD_URL = "https://pokechu22.github.io/Burger/1.13.2.json"; + + public static final String URL = "https://pokechu22.github.io/Burger/%s.json"; private List sounds; private List items; private Map blocks; - public BurgerReader() throws IOException { - parse(NetworkUtil.get(STANDARD_URL)); - } - - public BurgerReader(File file) throws IOException { - parse(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)); + public BurgerReader(MinecraftVersion version) throws IOException { + parse(NetworkUtil.get(String.format(URL, version.getName())), version); } - private void parse(String str) { + private void parse(String str, MinecraftVersion version) { JsonObject root = new JsonParser() .parse(str) .getAsJsonArray() @@ -62,6 +57,8 @@ private void parse(String str) { JsonObject sound = entry.getValue().getAsJsonObject(); this.sounds.add(new BurgerSound(entry.getKey(), sound.get("id").getAsInt())); } + JsonObject classes = root.getAsJsonObject("classes"); + version.setBlockClass(classes.getAsJsonPrimitive("block.register").getAsString()); // Items this.items = new ArrayList<>(); diff --git a/src/main/java/org/feathercore/protogen/generate/MaterialGenerator.java b/src/main/java/org/feathercore/protogen/generate/MaterialGenerator.java index 0a91d2e..187baac 100644 --- a/src/main/java/org/feathercore/protogen/generate/MaterialGenerator.java +++ b/src/main/java/org/feathercore/protogen/generate/MaterialGenerator.java @@ -63,6 +63,7 @@ protected void generateClass() { sb.append(T1).append("@Getter(AccessLevel.NONE) int id;").append(LF); sb.append(T1).append("int maxStackSize;").append(LF); sb.append(T1).append("boolean block;").append(LF); + sb.append(T2).append("boolean solid;").append(LF); sb.append(LF); @@ -90,7 +91,8 @@ private void appendMaterials() { sb.append(T1).append(item.getEnumName()) .append("(").append(item.getId()).append(", ") .append(item.getMaxStackSize()).append(", ") - .append(blocks.containsKey(item.getTextId())) + .append(blocks.containsKey(item.getTextId())).append(", ") + .append(item.isSolid()) .append(")"); if (i == this.items.size() - 1) { diff --git a/src/main/java/org/feathercore/protogen/util/FileUtil.java b/src/main/java/org/feathercore/protogen/util/FileUtil.java index 8cc6506..8b23ee4 100644 --- a/src/main/java/org/feathercore/protogen/util/FileUtil.java +++ b/src/main/java/org/feathercore/protogen/util/FileUtil.java @@ -16,31 +16,51 @@ package org.feathercore.protogen.util; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; /** * @author xtrafrancyz */ -@SuppressWarnings({"ConstantConditions", "ResultOfMethodCallIgnored"}) -public class FileUtil { - public static void writeFile(File file, String content) { - file.getParentFile().mkdirs(); - try (FileOutputStream fos = new FileOutputStream(file)) { - fos.write(content.getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - e.printStackTrace(); +public final class FileUtil { + + private FileUtil() { } + + public static void writeFile(Path file, String content) { + try { + Path parent = file.getParent(); + if(parent != null && !Files.isDirectory(parent)) { + Files.createDirectories(parent); + } + Files.write(file, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); + } catch (IOException ex) { + ex.printStackTrace(); } } - public static void deleteRecursive(File file) { - if (file.isDirectory()) { - for (File f : file.listFiles()) { - deleteRecursive(f); + public static void deleteRecursive(Path dir) { + if (Files.isDirectory(dir)) { + try { + Files.walkFileTree(dir, new SimpleFileVisitor() { + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + FileVisitResult result = super.postVisitDirectory(dir, exc); + Files.delete(dir); + return result; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return super.visitFile(file, attrs); + } + }); + } catch (IOException ex) { + ex.printStackTrace(); } } - file.delete(); } } diff --git a/src/main/java/org/feathercore/protogen/util/MaterialUtil.java b/src/main/java/org/feathercore/protogen/util/MaterialUtil.java new file mode 100644 index 0000000..2feb5fe --- /dev/null +++ b/src/main/java/org/feathercore/protogen/util/MaterialUtil.java @@ -0,0 +1,83 @@ +/* + * Copyright 2019 Feather Core + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.feathercore.protogen.util; + +import org.feathercore.protogen.version.MinecraftVersion; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +public final class MaterialUtil { + + private MaterialUtil() { } + + public static Set buildSolidMaterials(Path jar, MinecraftVersion version) + throws IOException { + if (version.getMaterialSlot() == -1) { + System.err.println("isSolid is unsupported for specified version, skipping"); + return Collections.emptySet(); + } + // Let's rock! + ClassLoader loader = MaterialUtil.class.getClassLoader(); + try { + Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + method.setAccessible(true); + method.invoke(loader, jar.toUri().toURL()); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) { + System.err.println("Exception occurred while injecting jar into class path, skipping isSolid"); + ex.printStackTrace(); + return Collections.emptySet(); + } + Class klass; + try { + klass = Class.forName(version.getBlockClass(), true, loader); + } catch (ClassNotFoundException ex) { + System.err.println("Class loader failed to load class, skipping isSolid"); + ex.printStackTrace(); + return Collections.emptySet(); + } + // Call init + Optional optional = Arrays.stream(klass.getDeclaredMethods()) + .filter(m -> Modifier.isPublic(m.getModifiers())) + .filter(m -> Modifier.isStatic(m.getModifiers())) + .filter(m -> Void.TYPE == m.getReturnType()) + .findFirst(); + if (!optional.isPresent()) { + System.err.println("No init method in Block class, skipping isSolid"); + return Collections.emptySet(); + } + try { + optional.get().invoke(null); + } catch (IllegalAccessException | InvocationTargetException ex) { + System.err.println("Init method invocation failed, skipping isSolid"); + ex.printStackTrace(); + return Collections.emptySet(); + } + // Search for material field + // TODO + return Collections.emptySet(); + } +} diff --git a/src/main/java/org/feathercore/protogen/util/NetworkUtil.java b/src/main/java/org/feathercore/protogen/util/NetworkUtil.java index 1488660..a0ef7fe 100644 --- a/src/main/java/org/feathercore/protogen/util/NetworkUtil.java +++ b/src/main/java/org/feathercore/protogen/util/NetworkUtil.java @@ -21,11 +21,15 @@ import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; +import java.util.stream.Collectors; /** * @author xtrafrancyz */ -public class NetworkUtil { +public final class NetworkUtil { + + private NetworkUtil() { } + public static String get(String url) throws IOException { HttpURLConnection conn = null; try { @@ -43,22 +47,14 @@ public static String get(String url) throws IOException { } break; } - StringBuilder response = new StringBuilder(); - String line; if (conn.getResponseCode() / 200 == 1) { - BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); - while ((line = reader.readLine()) != null) { - response.append(line); + try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + return br.lines().collect(Collectors.joining()); } } else { - BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getErrorStream())); - while ((line = reader.readLine()) != null) { - response.append(line); - } conn.getInputStream(); return null; } - return response.toString(); } finally { if (conn != null) { conn.disconnect(); diff --git a/src/main/java/org/feathercore/protogen/version/MinecraftVersion.java b/src/main/java/org/feathercore/protogen/version/MinecraftVersion.java new file mode 100644 index 0000000..24e63a4 --- /dev/null +++ b/src/main/java/org/feathercore/protogen/version/MinecraftVersion.java @@ -0,0 +1,87 @@ +/* + * Copyright 2019 Feather Core + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.feathercore.protogen.version; + +public enum MinecraftVersion { + _1_8_8(47, createOldIdLink("7368"), "1.8.8", null, -1, -1), + _1_8_9(47, createOldIdLink("7368"), "1.8.9", null, -1, -1), + _1_9_4(110, createOldIdLink("7959"), "1.9.4", null, -1, -1), + _1_10_2(210, createOldIdLink("8235"), "1.10.2", null, -1, -1), + _1_11_2(316, createOldIdLink("8543"), "1.11.2", null, -1, -1), + _1_12_2(340, createOldIdLink("14204"), "1.12.2", null, -1, 2), + _1_13_2(404, "http://wiki.vg/Protocol", "1.13.2", "bza", 11, 3); + + private static final String FORMAT = "http://wiki.vg/index.php?title=Protocol&oldid=%s"; + private final int protocolVersion; + private final String link; + private final String name; + private String blockClass; + private final String materialClass; + private final int materialSlot; + private final int registrySlot; + + MinecraftVersion(int protocolVersion, String link, String name, String materialClass, + int materialSlot, final int registrySlot) { + this.protocolVersion = protocolVersion; + this.link = link; + this.name = name; + this.materialClass = materialClass; + this.materialSlot = materialSlot; + this.registrySlot = registrySlot; + } + + public int getProtocolVersion() { + return protocolVersion; + } + + public String getLink() { + return link; + } + + public String getName() { + return name; + } + + private static String createOldIdLink(String id) { + return String.format(FORMAT, id); + } + + public int getMaterialSlot() { + return materialSlot; + } + + public String getBlockClass() { + return blockClass; + } + + public String getMaterialClass() { + return materialClass; + } + + public int getRegistrySlot() { + return registrySlot; + } + + public void setBlockClass(String blockClass) { + this.blockClass = blockClass; + } + + @Override + public String toString() { + return name + "/" + protocolVersion; + } +} diff --git a/src/main/java/org/feathercore/protogen/wiki/WikiReader.java b/src/main/java/org/feathercore/protogen/wiki/WikiReader.java index 86adf74..aa37375 100644 --- a/src/main/java/org/feathercore/protogen/wiki/WikiReader.java +++ b/src/main/java/org/feathercore/protogen/wiki/WikiReader.java @@ -31,7 +31,6 @@ import org.jsoup.nodes.Element; import org.jsoup.select.Elements; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; @@ -44,24 +43,16 @@ * @author Kristian */ public class WikiReader { - public static final String STANDARD_URL = "http://www.wiki.vg/Protocol"; + public static final String URL = "http://wiki.vg/index.php?title=Protocol&oldid=%s"; // Stored packet information private List packets; private List particles; - public WikiReader() throws IOException { - this(STANDARD_URL); - } - public WikiReader(String url) throws IOException { load(Jsoup.connect(url).get()); } - public WikiReader(File file) throws IOException { - load(Jsoup.parse(file, null)); - } - private void load(Document doc) { packets = loadPackets(doc); particles = loadParticles(doc);