Java tutorial
/* * This file is part of Minecraft Editor For Functions. * Copyright (c) 2017 suncrasher <suncrasher@outlook.com> * * Minecraft Editor For Functions is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Minecraft Editor For Functions is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Minecraft Editor For Functions. If not, see <http://www.gnu.org/licenses/>. */ package meff; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; import com.google.gson.Gson; public class Function { private final String id; // Format: world;namespace:path/to/func private FunctionData data; private final Settings s; public Function(String id, Settings settings) { this.id = id; s = settings; data = new FunctionData(); } public boolean load() { File file = getFile(true); if (file.exists()) { data = getGson().fromJson(U.readFileToOneLine(file.getAbsolutePath()), FunctionData.class); } else { file = getFile(false); if (file.exists()) { data = new FunctionData(); data.lines = U.readFile(file.getAbsolutePath()); } else { data = new FunctionData(); save(); } } return true; } public boolean save() { boolean success = true; Map<String, String> bin = compile(); if (!getFile(true).getParentFile().exists()) getFile(true).getParentFile().mkdirs(); success &= U.saveFile(getFile(true).getAbsolutePath(), getGson().toJson(data)); File dir = getFile().getParentFile(); for (Map.Entry<String, String> entry : bin.entrySet()) { success &= U.saveFile(new File(dir, entry.getKey()).getAbsolutePath(), entry.getValue()); } File advFile = getAdvFile(); if (data.adv == Advancement.NONE) { if (advFile.exists()) advFile.delete(); return success; } String trigger; switch (data.adv) { case TICK: trigger = "minecraft:tick"; break; case SECOND: trigger = "minecraft:location"; break; default: trigger = "minecraft:impossible"; } String advBody = "{\"criteria\":{\"" + getHalfId() + "\":{\"trigger\":\"" + trigger + "\"}}, \"rewards\":{\"function\":\"" + getHalfId() + "\"}}"; if (!advFile.getParentFile().exists()) advFile.getParentFile().mkdirs(); success &= U.saveFile(advFile.getAbsolutePath(), advBody); return success; } private Map<String, String> compile() { class Compiler { private Map<String, StringBuilder> functions; private String currentFunctionName; private StringBuilder currentFunction; Compiler(String functionName) { functions = new HashMap<>(); currentFunctionName = functionName; currentFunction = new StringBuilder(); functions.put(currentFunctionName, currentFunction); } void compileLine(StringBuilder line) { // Manage score value features { Matcher m = Pattern.compile("(score_\\w+)(>=|>|<=|<|==)(-?\\d+)").matcher(line); while (m.find()) { int offset = m.start(); String beginning = m.group(1); String operator = m.group(2); String end = m.group(3); StringBuilder newScore = new StringBuilder(); switch (operator) { case ">=": newScore.append(beginning).append("_min=").append(end); break; case ">": newScore.append(beginning).append("_min=").append(Integer.parseInt(end) + 1); break; case "<=": newScore.append(beginning).append("=").append(end); break; case "<": newScore.append(beginning).append("=").append(Integer.parseInt(end) - 1); break; case "==": newScore.append(beginning).append("_min=").append(end).append(",").append(beginning) .append("=").append(end); break; } line.replace(offset, offset + m.group().length(), newScore.toString()); m.reset(); // Need to reset the matcher, so it updates the length of the text } } currentFunction.append(line).append("\n"); } Map<String, String> getOutput() { Map<String, String> outputMap = new HashMap<>(functions.size()); for (Map.Entry<String, StringBuilder> entry : functions.entrySet()) { outputMap.put(entry.getKey() + ".mcfunction", entry.getValue().toString()); } return outputMap; } } Compiler compiler = new Compiler(getName()); boolean comment = false; StringBuilder sb = new StringBuilder(); for (String line : data.lines) { line = line.trim(); if (line.startsWith("#") || line.startsWith("//")) { continue; } if (line.isEmpty()) { continue; } int si = 0; // start index int bci = -1; // block comment index int bcei = -1; // block comment end index int prevBci = -1; int prevBcei = -1; while ((!comment && (bci = line.indexOf("/*", si)) > -1) || (comment && (bcei = line.indexOf("*/", si)) > -1)) { if (comment) { if (line.charAt(bcei - 1) == '\\') { si = bcei + 2; bcei = prevBcei; continue; } comment = false; si = bcei + 2; } else { if (line.charAt(bci - 1) == '\\') { sb.append(line.substring(si, bci - 1)).append("/*"); si = bci + 2; bci = prevBci; continue; } sb.append(line.substring(si, bci)); comment = true; si = bci + 2; } prevBci = bci; prevBcei = bcei; } if (comment) { continue; } if (line.endsWith("\\")) { line = line.substring(si, line.length() - 1); sb.append(line); } else { sb.append(line.substring(si)); compiler.compileLine(sb); sb.delete(0, sb.length()); } } return compiler.getOutput(); } public void delete() { try { File f = getFile(false); if (f.isDirectory()) { FileUtils.deleteDirectory(f); FileUtils.deleteDirectory(getFile(true)); FileUtils.deleteDirectory(getAdvFile().getParentFile()); } else { String prefix = getName() + "$"; f.delete(); f = f.getParentFile(); for (File ff : f.listFiles()) if (ff.getName().startsWith(prefix)) ff.delete(); f = getFile(true); if (f.exists()) f.delete(); f = getAdvFile(); if (f.exists()) f.delete(); } } catch (Exception e) { e.printStackTrace(); } } public boolean isFunction() { return id.contains(";"); } public void setLines(List<String> lines) { data.lines = lines; } public List<String> getLines() { return data.lines; } public void setAdv(Advancement adv) { data.adv = adv; } public Advancement getAdv() { return data.adv; } public String getId() { return id; } public String getHalfId() { String[] split = id.split(";"); if (split.length == 1) return ""; String half = split[1]; if (half.endsWith(":")) half = half.substring(0, half.length() - 1); return half; } public File getFile() { return getFile(false); } public File getAdvFile() { String[] split = id.split(";"); File f = new File(s.minecraftSavesLocation.getAbsolutePath() + "/" + split[0] + "/data/advancements/" + split[1].replace(":", "/")); if (f.isDirectory()) return f; return new File(f.getAbsolutePath() + ".json"); } public File getFile(boolean source) { if (source) { String[] split = id.split(";"); File f = new File(s.minecraftSavesLocation.getAbsolutePath() + "/" + split[0] + "/data/functions/.meff/" + split[1].replace(":", "/")); if (f.isDirectory()) return f; return new File(f.getAbsolutePath() + ".json"); } String[] split = id.split(";"); if (split.length > 1) { String world = split[0]; String funcPath = split[1].replace(':', '/'); File file = new File( s.minecraftSavesLocation.getAbsolutePath() + "/" + world + "/data/functions/" + funcPath); if (file.isDirectory()) return file; else return new File(file.getAbsolutePath() + ".mcfunction"); } return new File(s.minecraftSavesLocation.getAbsolutePath() + "/" + split[0] + "/data/functions"); } public String getName() { if (id.contains("/")) { return id.substring(id.lastIndexOf('/') + 1); } if (!id.contains(":")) { return id; } if (id.length() > id.indexOf(":") + 1) { return id.split(":")[1]; } return id.split(";")[1].split(":")[0]; } @Override public String toString() { return getName(); } private Gson getGson() { return new Gson(); } public enum Advancement { NONE, TICK, SECOND } private static class FunctionData { List<String> lines; Advancement adv; FunctionData() { lines = new ArrayList<>(); lines.add("# Your commands here"); adv = Advancement.NONE; } } }