diff --git a/src/main/java/electrosphere/main/client/syncmanager/methods/UpdateEntityState.java b/src/main/java/electrosphere/main/client/syncmanager/methods/UpdateEntityState.java index 7223cfa..58f10ac 100644 --- a/src/main/java/electrosphere/main/client/syncmanager/methods/UpdateEntityState.java +++ b/src/main/java/electrosphere/main/client/syncmanager/methods/UpdateEntityState.java @@ -33,45 +33,10 @@ public class UpdateEntityState implements VirtualMethod { for(SynchronizedField field : serverTree.getSynchronizedFields()){ String treeName = clientEquivalent.getClassName(); String fieldIdVariable = "TREE_" + serverTree.getName().toUpperCase() + "_SYNCEDFIELD_" + field.getFieldName().toUpperCase() + "_ID"; - switch(field.getTypeName()){ - case "int": { - updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; - updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; - updateCases = updateCases + " tree." + field.getSetterName() + "(message.getintValue());\n"; - updateCases = updateCases + " } break;\n"; - } break; - case "long": { - updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; - updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; - updateCases = updateCases + " tree." + field.getSetterName() + "(message.getlongValue());\n"; - updateCases = updateCases + " } break;\n"; - } break; - case "float": { - updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; - updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; - updateCases = updateCases + " tree." + field.getSetterName() + "(message.getfloatValue());\n"; - updateCases = updateCases + " } break;\n"; - } break; - case "double": { - updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; - updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; - updateCases = updateCases + " tree." + field.getSetterName() + "(message.getdoubleValue());\n"; - updateCases = updateCases + " } break;\n"; - } break; - case "String": { - updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; - updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; - updateCases = updateCases + " tree." + field.getSetterName() + "(message.getstringValue());\n"; - updateCases = updateCases + " } break;\n"; - } break; - default: { - SynchronizedType type = projectStructure.getType(field.getTypeName()); - String typeClass = type.getTargetFile().getSource().getName(); - updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; - updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; - updateCases = updateCases + " tree." + field.getSetterName() + "(" + typeClass + "." + type.getFromShortConversionMethodName() + "((short)message.getbTreeValue()));\n"; - updateCases = updateCases + " } break;\n"; - } break; + if(field.isSerialized()){ + updateCases = this.getSerializeBody(updateCases, projectStructure, treeName, field, fieldIdVariable); + } else { + updateCases = this.getSetterBody(updateCases, projectStructure, treeName, field, fieldIdVariable); } } updateCases = updateCases + " }\n"; @@ -82,6 +47,76 @@ public class UpdateEntityState implements VirtualMethod { return fullReplacementText; } + /** + * Gets the body for synchronizing based on a serialized payload + * @param updateCases + * @param projectStructure + * @param treeName + * @param field + * @param fieldIdVariable + */ + private String getSerializeBody(String updateCases, ProjectStructure projectStructure, String treeName, SynchronizedField field, String fieldIdVariable){ + updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; + updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; + updateCases = updateCases + " " + field.getTypeName() + " result = Utilities.deserialize(message.getstringValue(), " + field.getTypeName() + ".class);\n"; + updateCases = updateCases + " tree." + field.getPusherName() + "(tree." + field.getGetterName() + "(),result);\n"; + updateCases = updateCases + " tree." + field.getSetterName() + "(result);\n"; + updateCases = updateCases + " } break;\n"; + return updateCases; + } + + /** + * Gets the body for setting a field directly + * @param updateCases + * @param projectStructure + * @param treeName + * @param field + * @param fieldIdVariable + */ + private String getSetterBody(String updateCases, ProjectStructure projectStructure, String treeName, SynchronizedField field, String fieldIdVariable){ + switch(field.getTypeName()){ + case "int": { + updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; + updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; + updateCases = updateCases + " tree." + field.getSetterName() + "(message.getintValue());\n"; + updateCases = updateCases + " } break;\n"; + } break; + case "long": { + updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; + updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; + updateCases = updateCases + " tree." + field.getSetterName() + "(message.getlongValue());\n"; + updateCases = updateCases + " } break;\n"; + } break; + case "float": { + updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; + updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; + updateCases = updateCases + " tree." + field.getSetterName() + "(message.getfloatValue());\n"; + updateCases = updateCases + " } break;\n"; + } break; + case "double": { + updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; + updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; + updateCases = updateCases + " tree." + field.getSetterName() + "(message.getdoubleValue());\n"; + updateCases = updateCases + " } break;\n"; + } break; + case "String": { + updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; + updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; + updateCases = updateCases + " tree." + field.getSetterName() + "(message.getstringValue());\n"; + updateCases = updateCases + " } break;\n"; + } break; + default: { + SynchronizedType type = projectStructure.getType(field.getTypeName()); + String typeClass = type.getTargetFile().getSource().getName(); + updateCases = updateCases + " case FieldIdEnums." + fieldIdVariable + ":{\n"; + updateCases = updateCases + " " + treeName + " tree = " + treeName + ".get" + treeName + "(entity);\n"; + updateCases = updateCases + " tree." + field.getSetterName() + "(" + typeClass + "." + type.getFromShortConversionMethodName() + "((short)message.getbTreeValue()));\n"; + updateCases = updateCases + " } break;\n"; + } break; + } + return updateCases; + } + @Override public List getImports(ProjectStructure projectStructure) { List rVal = new LinkedList(); @@ -92,6 +127,10 @@ public class UpdateEntityState implements VirtualMethod { rVal.add(bTree.getTargetFile().getQualifiedPath()); } } + + //add utilities + rVal.add("electrosphere.util.Utilities"); + return rVal; } diff --git a/src/main/java/electrosphere/main/core/btree/methods/ClientFieldPusher.java b/src/main/java/electrosphere/main/core/btree/methods/ClientFieldPusher.java new file mode 100644 index 0000000..481bbba --- /dev/null +++ b/src/main/java/electrosphere/main/core/btree/methods/ClientFieldPusher.java @@ -0,0 +1,62 @@ +package electrosphere.main.core.btree.methods; + +import java.util.Arrays; +import java.util.List; + +import electrosphere.main.core.btree.BehaviorTree; +import electrosphere.main.project.ProjectStructure; +import electrosphere.main.source.VirtualMethod; +import electrosphere.main.util.TemplateInjectionUtils; +import electrosphere.main.util.Utilities; + +/** + * Pushes a serialized object into the client component + */ +public class ClientFieldPusher implements VirtualMethod { + + //the name of the field + String fieldName; + + //the name of the type of the field + String typeName; + + //The parent behavior tree + BehaviorTree parent; + + /** + * Constructor + * @param tree The parent behavior tree + * @param typeName The name of the type of the field + * @param fieldName The name of the field + */ + public ClientFieldPusher(BehaviorTree tree, String typeName, String fieldName){ + this.parent = tree; + this.typeName = typeName; + this.fieldName = fieldName; + } + + @Override + public String getName(ProjectStructure projectStructure) { + String rVal = ""; + rVal = "push" + Utilities.camelCase(fieldName); + return rVal; + } + + @Override + public String getContent(ProjectStructure projectStructure) { + String rVal = TemplateInjectionUtils.getFragmentWithReplacement("/client/Pusher.java", fieldName, Utilities.camelCase(fieldName), typeName); + return rVal; + } + + @Override + public List getImports(ProjectStructure projectStructure) { + List rVal = Arrays.asList(new String[]{ + }); + return rVal; + } + + @Override + public boolean shouldOverwrite(){ + return false; + } +} diff --git a/src/main/java/electrosphere/main/core/btree/methods/ServerFieldSetter.java b/src/main/java/electrosphere/main/core/btree/methods/ServerFieldSetter.java index bca95f9..8af668f 100644 --- a/src/main/java/electrosphere/main/core/btree/methods/ServerFieldSetter.java +++ b/src/main/java/electrosphere/main/core/btree/methods/ServerFieldSetter.java @@ -45,7 +45,9 @@ public class ServerFieldSetter implements VirtualMethod { @Override public String getContent(ProjectStructure projectStructure){ String rVal = null; - if(shouldUseStateTransitionFlow(projectStructure)){ + if(shouldUseSerializedPacketFlow(projectStructure)){ + rVal = getSerializedPacketContent(projectStructure); + } else if(shouldUseStateTransitionFlow(projectStructure)){ rVal = getStateTransitionContent(projectStructure); } else { rVal = getImmediatePacketContent(projectStructure); @@ -81,6 +83,20 @@ public class ServerFieldSetter implements VirtualMethod { return false; } } + + /** + * Checks if the setter should use the serialized packet flow + * @param projectStructure The project structure + * @return true if should use serialized packet flow + */ + private boolean shouldUseSerializedPacketFlow(ProjectStructure projectStructure){ + SynchronizedField field = projectStructure.getField(parent.getName(),fieldName); + if(field.isSerialized()){ + return true; + } else { + return false; + } + } /** * Gets the method content for a setter that uses the state transition flow @@ -157,4 +173,22 @@ public class ServerFieldSetter implements VirtualMethod { return rVal; } + /** + * Gets the content for a serialized packet based setter + * @param projectStructure The project structure + * @return The method content for an serialized packet based setter + */ + private String getSerializedPacketContent(ProjectStructure projectStructure){ + String rVal = null; + String bTreeIdVariable = "BehaviorTreeIdEnums.BTREE_" + this.parent.getName().toUpperCase() + "_ID"; + String fieldIdVariable = "FieldIdEnums.TREE_" + this.parent.getName().toUpperCase() + "_SYNCEDFIELD_" + this.fieldName.toUpperCase() + "_ID"; + String packetContentFiller = ""; + packetContentFiller = packetContentFiller + " String value = Utilities.stringify(" + fieldName + ");\n"; + packetContentFiller = packetContentFiller + " if(DataCellSearchUtils.getEntityDataCell(parent) != null){\n"; + packetContentFiller = packetContentFiller + " DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStringStateMessage(parent.getId(), " + bTreeIdVariable + ", " + fieldIdVariable + ", value));\n"; + packetContentFiller = packetContentFiller + " }"; + rVal = TemplateInjectionUtils.getFragmentWithReplacement("/server/Setter.java", fieldName, Utilities.camelCase(fieldName), typeName, packetContentFiller); + return rVal; + } + } diff --git a/src/main/java/electrosphere/main/core/syncfield/SynchronizedField.java b/src/main/java/electrosphere/main/core/syncfield/SynchronizedField.java index 4913543..136870b 100644 --- a/src/main/java/electrosphere/main/core/syncfield/SynchronizedField.java +++ b/src/main/java/electrosphere/main/core/syncfield/SynchronizedField.java @@ -21,6 +21,11 @@ public class SynchronizedField { //The name of the type (int, short, String, etc, or the name of the enum eg IdleTreeState) String typeName; + /** + * Tracks whether the field is serialized during transit via json or binary + */ + boolean isSerialized = false; + //The file this type appears in TargetFile targetFile; @@ -65,6 +70,16 @@ public class SynchronizedField { return rVal; } + /** + * Gets the name of the push method for this field + * @return The name of the push method + */ + public String getPusherName(){ + String rVal = ""; + rVal = "push" + Utilities.camelCase(fieldName); + return rVal; + } + /** * Gets parent tree of this field * @param tree @@ -105,6 +120,23 @@ public class SynchronizedField { return this.typeName; } + + /** + * Gets whether this field is serialized via json before transit or not + * @return true if is serialized via json, false otherwise + */ + public boolean isSerialized(){ + return this.isSerialized; + } + + /** + * Sets whether this field is serialized via json before transit or not + * @param isSerialized true if is serialized via json, false otherwise + */ + public void setIsSerialized(boolean isSerialized){ + this.isSerialized = isSerialized; + } + /** * Adds an annoration to this field * @param annotationName The name of the annotation diff --git a/src/main/java/electrosphere/main/core/transport/methods/ApplyStateCollection.java b/src/main/java/electrosphere/main/core/transport/methods/ApplyStateCollection.java index d3d1213..5d071dd 100644 --- a/src/main/java/electrosphere/main/core/transport/methods/ApplyStateCollection.java +++ b/src/main/java/electrosphere/main/core/transport/methods/ApplyStateCollection.java @@ -34,25 +34,10 @@ public class ApplyStateCollection implements VirtualMethod { String fieldIdVariable = "TREE_" + serverTree.getName().toUpperCase() + "_SYNCEDFIELD_" + field.getFieldName().toUpperCase() + "_ID"; updateCases = updateCases + " case(FieldIdEnums." + fieldIdVariable + "): {\n"; String getFieldValue = ""; - switch(field.getTypeName()){ - case "int": { - getFieldValue = "((Double)syncedValue.getValue()).intValue()"; - } break; - case "long": { - getFieldValue = "((Double)syncedValue.getValue()).longValue()"; - } break; - case "float": { - getFieldValue = "((Double)syncedValue.getValue()).floatValue()"; - } break; - case "double": { - getFieldValue = "(double)syncedValue.getValue()"; - } break; - case "String": { - getFieldValue = "(String)syncedValue.getValue()"; - } break; - default : { - getFieldValue = clientEquivalent.getClassName() + ".get" + Utilities.camelCase(field.getTypeName()) + "ShortAsEnum(((Double)syncedValue.getValue()).shortValue())"; - } break; + if(field.isSerialized()){ + getFieldValue = getFieldValueSerialized(field, clientEquivalent, getFieldValue); + } else { + getFieldValue = getFieldValueNonSerialized(field, clientEquivalent, getFieldValue); } updateCases = updateCases + " tree." + field.getSetterName() + "(" + getFieldValue + ");\n"; updateCases = updateCases + " } break;\n"; @@ -65,6 +50,49 @@ public class ApplyStateCollection implements VirtualMethod { return fullReplacementText; } + /** + * Gets the field's value as a serialized string which will itself be serialized + * @param field + * @param clientEquivalent + * @param getFieldValue + * @return + */ + private String getFieldValueSerialized(SynchronizedField field, BehaviorTree clientEquivalent, String getFieldValue){ + getFieldValue = "Utilities.deserialize((String)syncedValue.getValue()," + field.getTypeName() + ".class)"; + return getFieldValue; + } + + /** + * Gets the field's value as a number or string directly + * @param field + * @param getFieldValue + * @param clientEquivalent + * @return + */ + private String getFieldValueNonSerialized(SynchronizedField field, BehaviorTree clientEquivalent, String getFieldValue){ + switch(field.getTypeName()){ + case "int": { + getFieldValue = "((Double)syncedValue.getValue()).intValue()"; + } break; + case "long": { + getFieldValue = "((Double)syncedValue.getValue()).longValue()"; + } break; + case "float": { + getFieldValue = "((Double)syncedValue.getValue()).floatValue()"; + } break; + case "double": { + getFieldValue = "(double)syncedValue.getValue()"; + } break; + case "String": { + getFieldValue = "(String)syncedValue.getValue()"; + } break; + default : { + getFieldValue = clientEquivalent.getClassName() + ".get" + Utilities.camelCase(field.getTypeName()) + "ShortAsEnum(((Double)syncedValue.getValue()).shortValue())"; + } break; + } + return getFieldValue; + } + @Override public List getImports(ProjectStructure projectStructure) { List rVal = new LinkedList(); diff --git a/src/main/java/electrosphere/main/core/transport/methods/GetStateCollection.java b/src/main/java/electrosphere/main/core/transport/methods/GetStateCollection.java index 100a721..c2e4413 100644 --- a/src/main/java/electrosphere/main/core/transport/methods/GetStateCollection.java +++ b/src/main/java/electrosphere/main/core/transport/methods/GetStateCollection.java @@ -31,17 +31,10 @@ public class GetStateCollection implements VirtualMethod { //update each field for(SynchronizedField field : serverTree.getSynchronizedFields()){ String getFieldValue = ""; - switch(field.getTypeName()){ - case "int": - case "long": - case "float": - case "double": - case "String": { - getFieldValue = "tree.get" + Utilities.camelCase(field.getFieldName()) + "()"; - } break; - default : { - getFieldValue = clientEquivalent.getClassName() + ".get" + Utilities.camelCase(field.getTypeName()) + "EnumAsShort(tree.get" + Utilities.camelCase(field.getFieldName()) + "())"; - } break; + if(field.isSerialized()){ + getFieldValue = getFieldValueSerialized(field, clientEquivalent, getFieldValue); + } else { + getFieldValue = getFieldValueNonSerialized(field, clientEquivalent, getFieldValue); } String fieldIdVariable = "TREE_" + serverTree.getName().toUpperCase() + "_SYNCEDFIELD_" + field.getFieldName().toUpperCase() + "_ID"; updateCases = updateCases + " collection.setValue(new SynchronizedFieldValue(BehaviorTreeIdEnums." + BTreeIdEnum.getTreeIdEnum(serverTree) + ",FieldIdEnums." + fieldIdVariable + "," + getFieldValue + "));\n"; @@ -53,6 +46,41 @@ public class GetStateCollection implements VirtualMethod { return fullReplacementText; } + /** + * Gets the field's value as a serialized string which will itself be serialized + * @param field + * @param clientEquivalent + * @param getFieldValue + * @return + */ + private String getFieldValueSerialized(SynchronizedField field, BehaviorTree clientEquivalent, String getFieldValue){ + getFieldValue = "Utilities.stringify(tree.get" + Utilities.camelCase(field.getFieldName()) + "())"; + return getFieldValue; + } + + /** + * Gets the field's value as a number or string directly + * @param field + * @param getFieldValue + * @param clientEquivalent + * @return + */ + private String getFieldValueNonSerialized(SynchronizedField field, BehaviorTree clientEquivalent, String getFieldValue){ + switch(field.getTypeName()){ + case "int": + case "long": + case "float": + case "double": + case "String": { + getFieldValue = "tree.get" + Utilities.camelCase(field.getFieldName()) + "()"; + } break; + default : { + getFieldValue = clientEquivalent.getClassName() + ".get" + Utilities.camelCase(field.getTypeName()) + "EnumAsShort(tree.get" + Utilities.camelCase(field.getFieldName()) + "())"; + } break; + } + return getFieldValue; + } + @Override public List getImports(ProjectStructure projectStructure) { List rVal = new LinkedList(); @@ -63,6 +91,10 @@ public class GetStateCollection implements VirtualMethod { rVal.add(bTree.getTargetFile().getQualifiedPath()); } } + + //add utilities + rVal.add("electrosphere.util.Utilities"); + return rVal; } diff --git a/src/main/java/electrosphere/main/project/parsers/FieldParser.java b/src/main/java/electrosphere/main/project/parsers/FieldParser.java index f98b3d9..8ee129c 100644 --- a/src/main/java/electrosphere/main/project/parsers/FieldParser.java +++ b/src/main/java/electrosphere/main/project/parsers/FieldParser.java @@ -52,6 +52,11 @@ public class FieldParser { scrapeAnnotations(field,syncAnnotation); this.synchronizedFields.add(field); rVal.add(field); + + AnnotationSource serializableAnnotation = fieldSource.getAnnotation("SerializableField"); + if(serializableAnnotation != null){ + field.setIsSerialized(true); + } } } } diff --git a/src/main/java/electrosphere/main/project/parsers/MainParser.java b/src/main/java/electrosphere/main/project/parsers/MainParser.java index b9dd5e0..1c9463c 100644 --- a/src/main/java/electrosphere/main/project/parsers/MainParser.java +++ b/src/main/java/electrosphere/main/project/parsers/MainParser.java @@ -5,6 +5,7 @@ import java.util.List; import electrosphere.main.core.VirtualProject; import electrosphere.main.core.btree.BehaviorTree; import electrosphere.main.core.btree.methods.ClientFieldGetter; +import electrosphere.main.core.btree.methods.ClientFieldPusher; import electrosphere.main.core.btree.methods.ClientFieldSetter; import electrosphere.main.core.btree.methods.ClientTransition; import electrosphere.main.core.btree.methods.FromTypeConversion; @@ -101,6 +102,9 @@ public class MainParser { if(field.containsAnnotation("serverSendTransitionPacket")){ tree.addMethod(new ClientTransition(tree, field.getTypeName(), field.getFieldName())); } + if(field.isSerialized()){ + tree.addMethod(new ClientFieldPusher(tree, field.getTypeName(), field.getFieldName())); + } } } for(SynchronizedType type : types){ diff --git a/src/main/java/electrosphere/main/util/ClassSourceUtils.java b/src/main/java/electrosphere/main/util/ClassSourceUtils.java index cac7904..cd6f40e 100644 --- a/src/main/java/electrosphere/main/util/ClassSourceUtils.java +++ b/src/main/java/electrosphere/main/util/ClassSourceUtils.java @@ -33,7 +33,7 @@ public class ClassSourceUtils { for(MethodSource methodRaw : methods){ if(methodRaw.getName().equals(methodName)){ if(methodSource != null){ - throw new UnknownError("Two methods in a source file have the same name. This is unsupported by the addOrReplaceMethod function at this time!"); + throw new UnknownError("Two methods in a source file have the same name. This is unsupported by the addOrReplaceMethod function at this time! " + methodName); } methodSource = methodRaw; } diff --git a/src/main/resources/client/Pusher.java b/src/main/resources/client/Pusher.java new file mode 100644 index 0000000..fbad432 --- /dev/null +++ b/src/main/resources/client/Pusher.java @@ -0,0 +1,14 @@ +/** + *

(Initially) Automatically generated

+ *

+ * Pushes a new value to REPLACE_0_ME. This provides a place to perform client side logic on a value change of a serialized variable. + *

+ *

+ * Note: The value REPLACE_0_ME will be set to newValue after this function is called. + *

+ * @param oldValue The old value of REPLACE_0_ME. + * @param newValue The value to set REPLACE_0_ME to. + */ +public void pushREPLACE_1_ME(REPLACE_2_ME oldValue, REPLACE_2_ME newValue){ + //TODO: client side logic here +} \ No newline at end of file