diff --git a/src/main/java/electrosphere/main/Main.java b/src/main/java/electrosphere/main/Main.java index 0751c79..c81d4e5 100644 --- a/src/main/java/electrosphere/main/Main.java +++ b/src/main/java/electrosphere/main/Main.java @@ -3,13 +3,21 @@ package electrosphere.main; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.jboss.forge.roaster.Roaster; +import org.jboss.forge.roaster.model.ValuePair; +import org.jboss.forge.roaster.model.source.AnnotationSource; +import org.jboss.forge.roaster.model.source.EnumConstantSource; import org.jboss.forge.roaster.model.source.FieldSource; import org.jboss.forge.roaster.model.source.JavaClassSource; import org.jboss.forge.roaster.model.source.JavaDocSource; +import org.jboss.forge.roaster.model.source.JavaEnumSource; +import org.jboss.forge.roaster.model.source.JavaSource; +import org.jboss.forge.roaster.model.source.MethodSource; import electrosphere.main.targets.NetcodeGenTarget; import electrosphere.main.targets.TargetFile; @@ -20,30 +28,177 @@ import electrosphere.main.targets.TargetFile; */ public class Main { + static String topLevelFolderPath = "C:\\Users\\satellite\\Documents\\Renderer\\src\\main\\java\\electrosphere"; + static String[] targetFiles = new String[]{ "C:\\Users\\satellite\\Documents\\Renderer\\src\\main\\java\\electrosphere\\entity\\state\\IdleTree.java", }; + + static int bTreeIterator = 0; + + //maps the btree annotation "name" to an id + static Map bTreeIdMap = new HashMap(); public static void main(String args[]){ - System.out.println("Hello, world!"); - for(String targetFilePath : targetFiles){ - String content = ""; - try { - content = Files.readString(new File(targetFilePath).toPath()); - } catch (IOException e){ - e.printStackTrace(); + File topLevelFolder = new File(topLevelFolderPath); + List fileQueue = new LinkedList(); + List fileQueueOpenSet = new LinkedList(); + List fileQueueClosedSet = new LinkedList(); + fileQueue.add(topLevelFolder); + while(fileQueue.size() > 0){ + for(File currentFile : fileQueue){ + if(currentFile.isDirectory()){ + for(File childFile : currentFile.listFiles()){ + fileQueueOpenSet.add(childFile); + } + } else { + if(currentFile.getName().contains(".java")){ + String content = ""; + try { + content = Files.readString(currentFile.toPath()); + if(content.contains("@BehaviorTreeAnnotation")){ + //parse + JavaClassSource source = parseJavaFile(content); + //generate code + generateCode(source, currentFile, content); + } + } catch (IOException e){ + e.printStackTrace(); + } + } + } + + + fileQueueClosedSet.add(currentFile); } - TargetFile targetFile = new TargetFile(targetFilePath, content); - for(NetcodeGenTarget target : parseGenerationTargets(targetFile)){ - System.out.println(target.getName()); + for(File currentClosedFile : fileQueueOpenSet){ + fileQueue.add(currentClosedFile); + } + fileQueueOpenSet.clear(); + for(File currentClosedFile : fileQueueClosedSet){ + fileQueue.remove(currentClosedFile); } } } + static JavaClassSource parseJavaFile(String content){ + return Roaster.parse(JavaClassSource.class, content); + } + + static void generateCode(JavaClassSource source, File file, String content){ + String bTreeName = source.getAnnotation("BehaviorTreeAnnotation").getStringValue("name"); + bTreeIdMap.put(bTreeName,bTreeIterator); + bTreeIterator++; + + TargetFile targetFile = new TargetFile(file.getAbsolutePath(), content, bTreeName, source); + StringBuilder outputContent = new StringBuilder(content); + List targets = parseGenerationTargets(targetFile); + for(NetcodeGenTarget target : targets){ + //check if has getter + MethodSource getter = null; + MethodSource setter = null; + MethodSource enumToIntMapper = null; + MethodSource intToEnumMapper = null; + for(MethodSource method : targetFile.getSource().getMethods()){ + if(method.getName().equals(getGetterName(target.getName()))){ + getter = method; + } + if(method.getName().equals(getSetterName(target.getName()))){ + setter = method; + } + if(method.getName().equals(getEnumToIntMapperName(target.getName()))){ + enumToIntMapper = method; + } + if(method.getName().equals(getIntToEnumMapperName(target.getName()))){ + intToEnumMapper = method; + } + if(getter != null && setter != null && enumToIntMapper != null && intToEnumMapper != null){ + break; + } + } + //check if need to make enum to int mapper function + if(target.getAnnotation().getStringValue("isEnum") != null){ + //find enum in current file + JavaSource enumSource = null; + for(JavaSource nestedSource : targetFile.getSource().getNestedTypes()){ + if(nestedSource.getName().equals(target.getTypeName())){ + enumSource = (JavaSource)nestedSource; + break; + } + } + List enumConstNames = new LinkedList(); + if(enumSource != null){ + for(EnumConstantSource enumConstant : enumSource.getOrigin().getEnumConstants()){ + enumConstNames.add(enumConstant.getName()); + } + } + //mapper enum->int + if(enumToIntMapper != null){ + //regenerate + int startChar = enumToIntMapper.getStartPosition(); + int endChar = enumToIntMapper.getEndPosition(); + outputContent = outputContent.replace(startChar, endChar, generateEnumToIntMapperCode(target,enumConstNames)); + } else { + //generate + int positionJustBeforeClassEnd = targetFile.getSource().getEndPosition() - 1; + outputContent.insert(positionJustBeforeClassEnd, generateEnumToIntMapperCode(target,enumConstNames)); + } + //mapper int->enum + if(intToEnumMapper != null){ + //regenerate + int startChar = intToEnumMapper.getStartPosition(); + int endChar = intToEnumMapper.getEndPosition(); + outputContent = outputContent.replace(startChar, endChar, generateIntToEnumMapperCode(target,enumConstNames)); + } else { + //generate + int positionJustBeforeClassEnd = targetFile.getSource().getEndPosition() - 1; + outputContent.insert(positionJustBeforeClassEnd, generateIntToEnumMapperCode(target,enumConstNames)); + } + } + //getter + if(getter != null){ + //regenerate + int startChar = getter.getStartPosition(); + int endChar = getter.getEndPosition(); + outputContent = outputContent.replace(startChar, endChar, generateGetterCode(target.getName(),target)); + } else { + //generate + int positionJustBeforeClassEnd = targetFile.getSource().getEndPosition() - 1; + outputContent.insert(positionJustBeforeClassEnd, generateGetterCode(target.getName(),target)); + } + //setter + if(setter != null){ + //regenerate + int startChar = setter.getStartPosition(); + int endChar = setter.getEndPosition(); + outputContent = outputContent.replace(startChar, endChar, generateSetterCode(targetFile,target.getName(),target)); + } else { + //generate + int positionJustBeforeClassEnd = targetFile.getSource().getEndPosition() - 1; + outputContent.insert(positionJustBeforeClassEnd, generateSetterCode(targetFile,target.getName(),target)); + } + //message parsing on behavior tree in general + //...TODO + + } + //message parser generation + int positionJustBeforeClassEnd = targetFile.getSource().getEndPosition() - 1; + outputContent.insert(positionJustBeforeClassEnd, generateParseBTreeMessages(targetFile, targets)); + + System.out.println(outputContent); + } + static List parseGenerationTargets(TargetFile targetFile){ + int targetIdIterator = 0; List targets = new LinkedList(); for(FieldSource field : targetFile.getSource().getFields()){ - System.out.println(field.getName()); + List> annotations = field.getAnnotations(); + for(AnnotationSource annotation : annotations){ + if(annotation.getName().equals("SyncedField")){ + targets.add(new NetcodeGenTarget(targetIdIterator, field.getName(), field.getType().getName(), field, annotation)); + targetIdIterator++; + } + } } return targets; } @@ -79,4 +234,140 @@ public class Main { System.out.println(javaClass); } + static String getGetterName(String fieldName){ + return "get" + camelCase(fieldName); + } + + static String getSetterName(String fieldName){ + return "set" + camelCase(fieldName); + } + + static String getEnumToIntMapperName(String fieldName){ + return fieldName + "ToInt"; + } + + static String getIntToEnumMapperName(String fieldName){ + return fieldName + "FromInt"; + } + + static String camelCase(String input){ + return Character.toUpperCase(input.charAt(0)) + input.substring(1); + } + + static String generateGetterCode(String variableName, NetcodeGenTarget target){ + return Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/Getter.java")) + .replace("REPLACENAMECAMEL",camelCase(variableName)) + .replace("REPLACETYPE",target.getTypeName()) + .replace("REPLACENAMENOTCAMEL",variableName); + } + + static String generateSetterCode(TargetFile targetFile, String variableName, NetcodeGenTarget target){ + String messageConstructor = ""; + switch(target.getTypeName()){ + case "int": { + messageConstructor = Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/SetterInt.java")) + .replace("REPLACEBTREEID",bTreeIdMap.get(targetFile.getName()) + "") + .replace("REPLACEPROPERTYID",target.getId() + "") + .replace("REPLACENAMENOTCAMEL",variableName); + } break; + case "double": { + messageConstructor = Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/SetterDouble.java")) + .replace("REPLACEBTREEID",bTreeIdMap.get(targetFile.getName()) + "") + .replace("REPLACEPROPERTYID",target.getId() + "") + .replace("REPLACENAMENOTCAMEL",variableName); + } break; + case "float": { + messageConstructor = Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/SetterFloat.java")) + .replace("REPLACEBTREEID",bTreeIdMap.get(targetFile.getName()) + "") + .replace("REPLACEPROPERTYID",target.getId() + "") + .replace("REPLACENAMENOTCAMEL",variableName); + } break; + case "String": { + messageConstructor = Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/SetterString.java")) + .replace("REPLACEBTREEID",bTreeIdMap.get(targetFile.getName()) + "") + .replace("REPLACEPROPERTYID",target.getId() + "") + .replace("REPLACENAMENOTCAMEL",variableName); + } break; + } + if(target.getAnnotation().getStringValue("isEnum") != null){ + messageConstructor = Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/SetterEnum.java")) + .replace("REPLACEBTREEID",bTreeIdMap.get(targetFile.getName()) + "") + .replace("REPLACEPROPERTYID",target.getId() + "") + .replace("REPLACENAMECAMEL",camelCase(variableName)) + .replace("REPLACENAMENOTCAMEL",variableName); + } + return Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/Setter.java")) + .replace("REPLACENAMECAMEL",camelCase(variableName)) + .replace("REPLACETYPE",target.getTypeName()) + .replace("REPLACENAMENOTCAMEL",variableName) + .replace("REPLACEMESSAGECONSTRUCTOR",messageConstructor); + } + + static String generateEnumToIntMapperCode(NetcodeGenTarget target, List enumConstNames){ + String switchCases = ""; + int i = 0; + for(String enumConstName : enumConstNames){ + switchCases = switchCases + "case " + enumConstName + ":\n" + + "return " + i + ";\n"; + i++; + } + return Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/EnumToIntMapper.java")) + .replace("REPLACEENUMTYPE",target.getTypeName()) + .replace("REPLACENAMECAMEL",camelCase(target.getName())) + .replace("REPLACECASE",switchCases) + .replace("REPLACETYPENAME",target.getTypeName()); + } + + + static String generateIntToEnumMapperCode(NetcodeGenTarget target, List enumConstNames){ + String switchCases = ""; + int i = 0; + for(String enumConstName : enumConstNames){ + switchCases = switchCases + "case " + i + ":\n" + + "return " + target.getTypeName() + "." + enumConstName + ";\n"; + i++; + } + return Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/IntToEnumMapper.java")) + .replace("REPLACEENUMTYPE",target.getTypeName()) + .replace("REPLACENAMECAMEL",camelCase(target.getName())) + .replace("REPLACECASE",switchCases); + } + + static String generateParseBTreeMessages(TargetFile targetFile, List targets){ + String setCases = ""; + for(NetcodeGenTarget target : targets){ + String base = "case REPLACE_PROPERTY_ID: {\n" + + "setREPLACE_VARIABLE_NAME_CAMEL(REPLACE_GET_PROPERTY_VALUE);\n" + + "} break;\n"; + String propertyValueFetcher = ""; + switch(target.getTypeName()){ + case "int": { + propertyValueFetcher = "message.getpropertyValueInt()"; + } break; + case "float": { + propertyValueFetcher = "message.getpropertyValueFloat()"; + } break; + case "double": { + propertyValueFetcher = "message.getpropertyValueDouble()"; + } break; + case "String": { + propertyValueFetcher = "message.getpropertyValueString()"; + } break; + //enum + default: { + propertyValueFetcher = "getEnumIntValueREPLACENAMECAMEL(message.getpropertyValueInt())".replace("REPLACENAMECAMEL",camelCase(target.getName())); + } break; + } + String replaced = base + .replace("REPLACE_PROPERTY_ID",target.getId() + "") + .replace("REPLACE_GET_PROPERTY_VALUE",propertyValueFetcher) + .replace("REPLACE_VARIABLE_NAME_CAMEL",camelCase(target.getName())); + System.out.println(replaced); + setCases = setCases + replaced; + } + return Utilities.readBakedResourceToString(Main.class.getResourceAsStream("/ParseBTreeMessages.java")) + .replace("REPLACE_WITH_CASES",setCases); + } + + } diff --git a/src/main/java/electrosphere/main/Utilities.java b/src/main/java/electrosphere/main/Utilities.java new file mode 100644 index 0000000..747b5b3 --- /dev/null +++ b/src/main/java/electrosphere/main/Utilities.java @@ -0,0 +1,53 @@ +package electrosphere.main; + + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.concurrent.TimeUnit; + +public class Utilities { + + static final int maxReadFails = 3; + static final int READ_TIMEOUT_DURATION = 5; + public static String readBakedResourceToString(InputStream resourceInputStream){ + String rVal = ""; + BufferedReader reader; + try { + reader = new BufferedReader(new InputStreamReader(resourceInputStream)); + int failCounter = 0; + boolean reading = true; + StringBuilder builder = new StringBuilder(""); + while(reading){ + if(reader.ready()){ + failCounter = 0; + int nextValue = reader.read(); + if(nextValue == -1){ + reading = false; + } else { + builder.append((char)nextValue); + } + } else { + failCounter++; + if(failCounter > maxReadFails){ + reading = false; + } else { + try { + TimeUnit.MILLISECONDS.sleep(READ_TIMEOUT_DURATION); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + } + } + rVal = builder.toString(); + } catch (IOException ex) { + ex.printStackTrace(); + } + return rVal; + } + + +} diff --git a/src/main/java/electrosphere/main/targets/NetcodeGenTarget.java b/src/main/java/electrosphere/main/targets/NetcodeGenTarget.java index b1c68ee..e142ae1 100644 --- a/src/main/java/electrosphere/main/targets/NetcodeGenTarget.java +++ b/src/main/java/electrosphere/main/targets/NetcodeGenTarget.java @@ -1,15 +1,43 @@ package electrosphere.main.targets; +import org.jboss.forge.roaster.model.source.AnnotationSource; +import org.jboss.forge.roaster.model.source.FieldSource; +import org.jboss.forge.roaster.model.source.JavaClassSource; + public class NetcodeGenTarget { + int id; String name; + String typeName; + FieldSource field; + AnnotationSource annotation; - public NetcodeGenTarget(String name){ + public NetcodeGenTarget(int id, String name, String typeName, FieldSource field, AnnotationSource annotation){ + this.id = id; this.name = name; + this.typeName = typeName; + this.field = field; + this.annotation = annotation; + } + + public int getId(){ + return id; } public String getName(){ return name; } + public String getTypeName(){ + return typeName; + } + + public FieldSource getField(){ + return field; + } + + public AnnotationSource getAnnotation(){ + return annotation; + } + } diff --git a/src/main/java/electrosphere/main/targets/TargetFile.java b/src/main/java/electrosphere/main/targets/TargetFile.java index 801d000..36b41cf 100644 --- a/src/main/java/electrosphere/main/targets/TargetFile.java +++ b/src/main/java/electrosphere/main/targets/TargetFile.java @@ -8,15 +8,21 @@ public class TargetFile { String path; String content; JavaClassSource source; + String name; - public TargetFile(String path, String content){ + public TargetFile(String path, String content, String name, JavaClassSource source){ this.path = path; this.content = content; - this.source = Roaster.parse(JavaClassSource.class, content); + this.name = name; + this.source = source; } public JavaClassSource getSource(){ return source; } + public String getName(){ + return name; + } + } diff --git a/src/main/resources/EnumToIntMapper.java b/src/main/resources/EnumToIntMapper.java new file mode 100644 index 0000000..aa26852 --- /dev/null +++ b/src/main/resources/EnumToIntMapper.java @@ -0,0 +1,7 @@ +private int getEnumIntValueREPLACENAMECAMEL(REPLACEENUMTYPE value){ + int rVal = -1; + switch(value){ + REPLACECASE + } + return rVal; +} \ No newline at end of file diff --git a/src/main/resources/Getter.java b/src/main/resources/Getter.java new file mode 100644 index 0000000..eb1a822 --- /dev/null +++ b/src/main/resources/Getter.java @@ -0,0 +1,3 @@ +public REPLACETYPE getREPLACENAMECAMEL(){ + return REPLACENAMENOTCAMEL; +} \ No newline at end of file diff --git a/src/main/resources/IntToEnumMapper.java b/src/main/resources/IntToEnumMapper.java new file mode 100644 index 0000000..4ec1d67 --- /dev/null +++ b/src/main/resources/IntToEnumMapper.java @@ -0,0 +1,7 @@ +private REPLACEENUMTYPE getEnumIntValueREPLACENAMECAMEL(int value){ + REPLACEENUMTYPE rVal = null; + switch(value){ + REPLACECASE + } + return rVal; +} \ No newline at end of file diff --git a/src/main/resources/ParseBTreeMessages.java b/src/main/resources/ParseBTreeMessages.java new file mode 100644 index 0000000..f81e2c9 --- /dev/null +++ b/src/main/resources/ParseBTreeMessages.java @@ -0,0 +1,7 @@ +private void parseBTreeMessages(List messages){ + for(EntityMessage message : messages){ + switch(message.getpropertyID()){ + REPLACE_WITH_CASES + } + } +} \ No newline at end of file diff --git a/src/main/resources/Setter.java b/src/main/resources/Setter.java new file mode 100644 index 0000000..957f3c9 --- /dev/null +++ b/src/main/resources/Setter.java @@ -0,0 +1,17 @@ +public void setREPLACENAMECAMEL(REPLACETYPE REPLACENAMENOTCAMEL){ + this.REPLACENAMENOTCAMEL = REPLACENAMENOTCAMEL; + if(Globals.RUN_SERVER){ + //only server + Globals.dataCellManager.sendNetworkMessageToChunk( + REPLACEMESSAGECONSTRUCTOR, + Globals.serverWorldData.convertRealToChunkSpace(position.x), + Globals.serverWorldData.convertRealToChunkSpace(position.z) + ); + if(Globals.RUN_CLIENT){ + //server and client so alert client entity that this value has been updated + } + } else if(Globals.RUN_CLIENT){ + //only client + this.REPLACENAMENOTCAMEL = REPLACENAMENOTCAMEL; + } +} \ No newline at end of file diff --git a/src/main/resources/SetterDouble.java b/src/main/resources/SetterDouble.java new file mode 100644 index 0000000..8cddfcd --- /dev/null +++ b/src/main/resources/SetterDouble.java @@ -0,0 +1 @@ +EntityMessage.constructsetBTreePropertyDoubleMessage(parent.getId(), Main.getCurrentFrame(), REPLACEBTREEID, REPLACEPROPERTYID, REPLACENAMENOTCAMEL) \ No newline at end of file diff --git a/src/main/resources/SetterEnum.java b/src/main/resources/SetterEnum.java new file mode 100644 index 0000000..bb7dd8c --- /dev/null +++ b/src/main/resources/SetterEnum.java @@ -0,0 +1 @@ +EntityMessage.constructsetBTreePropertyEnumMessage(parent.getId(), Main.getCurrentFrame(), REPLACEBTREEID, REPLACEPROPERTYID, getEnumIntValueREPLACENAMECAMEL(REPLACENAMENOTCAMEL)) \ No newline at end of file diff --git a/src/main/resources/SetterFloat.java b/src/main/resources/SetterFloat.java new file mode 100644 index 0000000..1bd80b0 --- /dev/null +++ b/src/main/resources/SetterFloat.java @@ -0,0 +1 @@ +EntityMessage.constructsetBTreePropertyFloatMessage(parent.getId(), Main.getCurrentFrame(), REPLACEBTREEID, REPLACEPROPERTYID, REPLACENAMENOTCAMEL) \ No newline at end of file diff --git a/src/main/resources/SetterInt.java b/src/main/resources/SetterInt.java new file mode 100644 index 0000000..94da8b2 --- /dev/null +++ b/src/main/resources/SetterInt.java @@ -0,0 +1 @@ +EntityMessage.constructsetBTreePropertyIntMessage(parent.getId(), Main.getCurrentFrame(), REPLACEBTREEID, REPLACEPROPERTYID, REPLACENAMENOTCAMEL) \ No newline at end of file diff --git a/src/main/resources/SetterString.java b/src/main/resources/SetterString.java new file mode 100644 index 0000000..bc8ee76 --- /dev/null +++ b/src/main/resources/SetterString.java @@ -0,0 +1 @@ +EntityMessage.constructsetBTreePropertyStringMessage(parent.getId(), Main.getCurrentFrame(), REPLACEBTREEID, REPLACEPROPERTYID, REPLACENAMENOTCAMEL) \ No newline at end of file