support for serialized-during-transit fields
All checks were successful
studiorailgun/highlevel-netcode-gen/pipeline/head This commit looks good

This commit is contained in:
austin 2024-09-27 18:33:55 -04:00
parent af8e7cad75
commit 2c638fca2f
10 changed files with 321 additions and 71 deletions

View File

@ -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<String> getImports(ProjectStructure projectStructure) {
List<String> rVal = new LinkedList<String>();
@ -92,6 +127,10 @@ public class UpdateEntityState implements VirtualMethod {
rVal.add(bTree.getTargetFile().getQualifiedPath());
}
}
//add utilities
rVal.add("electrosphere.util.Utilities");
return rVal;
}

View File

@ -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<String> getImports(ProjectStructure projectStructure) {
List<String> rVal = Arrays.asList(new String[]{
});
return rVal;
}
@Override
public boolean shouldOverwrite(){
return false;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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<String> getImports(ProjectStructure projectStructure) {
List<String> rVal = new LinkedList<String>();

View File

@ -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<String> getImports(ProjectStructure projectStructure) {
List<String> rVal = new LinkedList<String>();
@ -63,6 +91,10 @@ public class GetStateCollection implements VirtualMethod {
rVal.add(bTree.getTargetFile().getQualifiedPath());
}
}
//add utilities
rVal.add("electrosphere.util.Utilities");
return rVal;
}

View File

@ -52,6 +52,11 @@ public class FieldParser {
scrapeAnnotations(field,syncAnnotation);
this.synchronizedFields.add(field);
rVal.add(field);
AnnotationSource<JavaClassSource> serializableAnnotation = fieldSource.getAnnotation("SerializableField");
if(serializableAnnotation != null){
field.setIsSerialized(true);
}
}
}
}

View File

@ -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){

View File

@ -33,7 +33,7 @@ public class ClassSourceUtils {
for(MethodSource<JavaClassSource> 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;
}

View File

@ -0,0 +1,14 @@
/**
* <p> (Initially) Automatically generated </p>
* <p>
* 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.
* </p>
* <p>
* Note: The value REPLACE_0_ME will be set to newValue after this function is called.
* </p>
* @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
}