From 2c42de3882e024273b3f9e181e7510c7b839b9c5 Mon Sep 17 00:00:00 2001
From: austin
Date: Fri, 2 Aug 2024 15:57:11 -0400
Subject: [PATCH] state interrupts and transition packets
---
net/synchronization.json | 10 +++
.../entity/btree/StateTransitionUtil.java | 81 +++++++++++++++++++
.../entity/state/block/ClientBlockTree.java | 27 ++++++-
.../entity/state/block/ServerBlockTree.java | 4 +-
.../protocol/SynchronizationProtocol.java | 1 +
.../parser/net/message/NetworkMessage.java | 5 ++
.../net/message/SynchronizationMessage.java | 50 ++++++++++++
.../net/parser/net/message/TypeBytes.java | 8 +-
.../protocol/SynchronizationProtocol.java | 2 +
.../ClientSynchronizationManager.java | 29 +++++++
.../ServerSynchronizationManager.java | 1 +
.../annotation/SyncedField.java | 4 +
12 files changed, 216 insertions(+), 6 deletions(-)
diff --git a/net/synchronization.json b/net/synchronization.json
index 81d2cac4..51cda9a8 100644
--- a/net/synchronization.json
+++ b/net/synchronization.json
@@ -113,6 +113,16 @@
"bTreeValue"
]
},
+ {
+ "messageName" : "ServerNotifyBTreeTransition",
+ "description" : "Packet from the server to the client notifying the client that it should transition a btree from one state to another",
+ "data" : [
+ "entityId",
+ "bTreeId",
+ "fieldId",
+ "bTreeValue"
+ ]
+ },
{
"messageName" : "AttachTree",
"description" : "Attaches a btree to an entity on the client",
diff --git a/src/main/java/electrosphere/entity/btree/StateTransitionUtil.java b/src/main/java/electrosphere/entity/btree/StateTransitionUtil.java
index 3f12e10c..022a3084 100644
--- a/src/main/java/electrosphere/entity/btree/StateTransitionUtil.java
+++ b/src/main/java/electrosphere/entity/btree/StateTransitionUtil.java
@@ -191,6 +191,87 @@ public class StateTransitionUtil {
}
}
+ /**
+ * Interrupts a given state
+ * @param stateEnum The state enum
+ */
+ public void interrupt(Object stateEnum){
+ StateTransitionUtilItem state = null;
+ for(StateTransitionUtilItem targetState : states){
+ if(targetState.stateEnum == stateEnum){
+ state = targetState;
+ break;
+ }
+ }
+ if(state == null){
+ LoggerInterface.loggerEngine.DEBUG("Skipping state " + stateEnum + " because there is not a state registered to that enum value!");
+ } else {
+ if(this.isServer){
+ interruptServerState(this.parent,state);
+ } else {
+ interruptClientState(this.parent,state);
+ }
+ }
+ }
+
+ /**
+ * Interrupts animation logic for client tree
+ * @param parent The parent entity
+ * @param state The state
+ */
+ private static void interruptClientState(Entity parent, StateTransitionUtilItem state){
+ Actor actor = EntityUtils.getActor(parent);
+ if(actor != null){
+
+ //get the animation to play
+ TreeDataAnimation animation = state.animation;
+ if(state.getAnimation != null && state.getAnimation.get() != null){
+ animation = state.getAnimation.get();
+ }
+
+
+ //
+ //Interrupt main animation
+ //
+ if(animation != null && actor.isPlayingAnimation() && actor.isPlayingAnimation(animation)){
+ actor.interruptAnimation(animation, true);
+ }
+
+ //
+ //Interrupt animation in first person
+ //
+ if(animation != null){
+ FirstPersonTree.conditionallyInterruptAnimation(Globals.firstPersonEntity, animation);
+ }
+ }
+ }
+
+ /**
+ * Interrupts animation logic for server tree
+ * @param parent The parent entity
+ * @param state The state
+ */
+ private static void interruptServerState(Entity parent, StateTransitionUtilItem state){
+ PoseActor poseActor = EntityUtils.getPoseActor(parent);
+ if(poseActor != null){
+
+ //get the animation to play
+ TreeDataAnimation animation = state.animation;
+ if(state.getAnimation != null && state.getAnimation.get() != null){
+ animation = state.getAnimation.get();
+ }
+
+
+
+ //
+ //Interrupt main animation
+ //
+ if(animation != null && poseActor.isPlayingAnimation() && poseActor.isPlayingAnimation(animation)){
+ poseActor.interruptAnimation(animation, true);
+ }
+ }
+ }
+
/**
* A parameter used to construct a StateTransitionUtil
*/
diff --git a/src/main/java/electrosphere/entity/state/block/ClientBlockTree.java b/src/main/java/electrosphere/entity/state/block/ClientBlockTree.java
index 581007af..59a9a852 100644
--- a/src/main/java/electrosphere/entity/state/block/ClientBlockTree.java
+++ b/src/main/java/electrosphere/entity/state/block/ClientBlockTree.java
@@ -31,7 +31,7 @@ public class ClientBlockTree implements BehaviorTree {
NOT_BLOCKING,
}
- @SyncedField
+ @SyncedField(serverSendTransitionPacket = true)
BlockState state = BlockState.NOT_BLOCKING; //the current state
//the parent entity to this tree
@@ -74,6 +74,31 @@ public class ClientBlockTree implements BehaviorTree {
});
}
+ /**
+ * (Initially) Automatically Generated
+ *
+ * Performs a state transition on a client state variable.
+ * Will be triggered when a server performs a state change.
+ *
+ * @param newState The new value of the state
+ */
+ public void transitionState(BlockState newState){
+ this.setState(newState);
+ switch(newState){
+ case WIND_UP: {
+ } break;
+ case BLOCKING: {
+ this.stateTransitionUtil.interrupt(BlockState.WIND_UP);
+ } break;
+ case COOLDOWN: {
+ this.stateTransitionUtil.interrupt(BlockState.BLOCKING);
+ } break;
+ case NOT_BLOCKING: {
+ this.stateTransitionUtil.interrupt(BlockState.COOLDOWN);
+ } break;
+ }
+ }
+
@Override
public void simulate(float deltaTime) {
switch(state){
diff --git a/src/main/java/electrosphere/entity/state/block/ServerBlockTree.java b/src/main/java/electrosphere/entity/state/block/ServerBlockTree.java
index b8792d79..9d9d95b4 100644
--- a/src/main/java/electrosphere/entity/state/block/ServerBlockTree.java
+++ b/src/main/java/electrosphere/entity/state/block/ServerBlockTree.java
@@ -26,7 +26,7 @@ import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree;
*/
public class ServerBlockTree implements BehaviorTree {
- @SyncedField
+ @SyncedField(serverSendTransitionPacket = true)
BlockState state = BlockState.NOT_BLOCKING; //the current state of the tree
//the parent entity to this tree
@@ -152,7 +152,7 @@ public class ServerBlockTree implements BehaviorTree {
public void setState(BlockState state){
this.state = state;
int value = ClientBlockTree.getBlockStateEnumAsShort(state);
- DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStateMessage(parent.getId(), BehaviorTreeIdEnums.BTREE_SERVERBLOCKTREE_ID, FieldIdEnums.TREE_SERVERBLOCKTREE_SYNCEDFIELD_STATE_ID, value));
+ DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructServerNotifyBTreeTransitionMessage(parent.getId(), BehaviorTreeIdEnums.BTREE_SERVERBLOCKTREE_ID, FieldIdEnums.TREE_SERVERBLOCKTREE_SYNCEDFIELD_STATE_ID, value));
}
/**
* (initially) Automatically generated
diff --git a/src/main/java/electrosphere/net/client/protocol/SynchronizationProtocol.java b/src/main/java/electrosphere/net/client/protocol/SynchronizationProtocol.java
index 875cd7f1..b97efd57 100644
--- a/src/main/java/electrosphere/net/client/protocol/SynchronizationProtocol.java
+++ b/src/main/java/electrosphere/net/client/protocol/SynchronizationProtocol.java
@@ -27,6 +27,7 @@ public class SynchronizationProtocol implements ClientProtocolTemplate= TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_SERVERNOTIFYBTREETRANSITION_SIZE){
+ return true;
+ } else {
+ return false;
+ }
case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_ATTACHTREE:
if(byteBuffer.getRemaining() >= TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_ATTACHTREE_SIZE){
return true;
@@ -340,6 +347,26 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal;
}
+ public static SynchronizationMessage parseServerNotifyBTreeTransitionMessage(CircularByteBuffer byteBuffer){
+ SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.SERVERNOTIFYBTREETRANSITION);
+ stripPacketHeader(byteBuffer);
+ rVal.setentityId(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
+ rVal.setbTreeId(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
+ rVal.setfieldId(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
+ rVal.setbTreeValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
+ return rVal;
+ }
+
+ public static SynchronizationMessage constructServerNotifyBTreeTransitionMessage(int entityId,int bTreeId,int fieldId,int bTreeValue){
+ SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.SERVERNOTIFYBTREETRANSITION);
+ rVal.setentityId(entityId);
+ rVal.setbTreeId(bTreeId);
+ rVal.setfieldId(fieldId);
+ rVal.setbTreeValue(bTreeValue);
+ rVal.serialize();
+ return rVal;
+ }
+
public static SynchronizationMessage parseAttachTreeMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.ATTACHTREE);
stripPacketHeader(byteBuffer);
@@ -570,6 +597,29 @@ public class SynchronizationMessage extends NetworkMessage {
rawBytes[10+i] = intValues[i];
}
break;
+ case SERVERNOTIFYBTREETRANSITION:
+ rawBytes = new byte[2+4+4+4+4];
+ //message header
+ rawBytes[0] = TypeBytes.MESSAGE_TYPE_SYNCHRONIZATION;
+ //entity messaage header
+ rawBytes[1] = TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_SERVERNOTIFYBTREETRANSITION;
+ intValues = ByteStreamUtils.serializeIntToBytes(entityId);
+ for(int i = 0; i < 4; i++){
+ rawBytes[2+i] = intValues[i];
+ }
+ intValues = ByteStreamUtils.serializeIntToBytes(bTreeId);
+ for(int i = 0; i < 4; i++){
+ rawBytes[6+i] = intValues[i];
+ }
+ intValues = ByteStreamUtils.serializeIntToBytes(fieldId);
+ for(int i = 0; i < 4; i++){
+ rawBytes[10+i] = intValues[i];
+ }
+ intValues = ByteStreamUtils.serializeIntToBytes(bTreeValue);
+ for(int i = 0; i < 4; i++){
+ rawBytes[14+i] = intValues[i];
+ }
+ break;
case ATTACHTREE:
rawBytes = new byte[2+4+4];
//message header
diff --git a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java
index d9243c79..4e3f70da 100644
--- a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java
+++ b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java
@@ -152,9 +152,10 @@ Message categories
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTFLOATSTATE = 4;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTDOUBLESTATE = 5;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_CLIENTREQUESTBTREEACTION = 6;
- public static final byte SYNCHRONIZATION_MESSAGE_TYPE_ATTACHTREE = 7;
- public static final byte SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE = 8;
- public static final byte SYNCHRONIZATION_MESSAGE_TYPE_LOADSCENE = 9;
+ public static final byte SYNCHRONIZATION_MESSAGE_TYPE_SERVERNOTIFYBTREETRANSITION = 7;
+ public static final byte SYNCHRONIZATION_MESSAGE_TYPE_ATTACHTREE = 8;
+ public static final byte SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE = 9;
+ public static final byte SYNCHRONIZATION_MESSAGE_TYPE_LOADSCENE = 10;
/*
Synchronization packet sizes
*/
@@ -164,6 +165,7 @@ Message categories
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTFLOATSTATE_SIZE = 18;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTDOUBLESTATE_SIZE = 22;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_CLIENTREQUESTBTREEACTION_SIZE = 14;
+ public static final byte SYNCHRONIZATION_MESSAGE_TYPE_SERVERNOTIFYBTREETRANSITION_SIZE = 18;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_ATTACHTREE_SIZE = 10;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE_SIZE = 10;
}
diff --git a/src/main/java/electrosphere/net/server/protocol/SynchronizationProtocol.java b/src/main/java/electrosphere/net/server/protocol/SynchronizationProtocol.java
index 0ff896f1..22a12359 100644
--- a/src/main/java/electrosphere/net/server/protocol/SynchronizationProtocol.java
+++ b/src/main/java/electrosphere/net/server/protocol/SynchronizationProtocol.java
@@ -24,6 +24,7 @@ public class SynchronizationProtocol implements ServerProtocolTemplate Automatically generated
+ *
+ * Transitions a behavior tree to a new state
+ *
+ * @param entity The entity
+ * @param bTreeId The id of the behavior tree
+ * @param message The raw synchronization message holding the update data
+ */
+ private void transitionBTree(Entity entity, int bTreeId, SynchronizationMessage message){
+ switch(bTreeId){
+ case BehaviorTreeIdEnums.BTREE_SERVERBLOCKTREE_ID: {
+ switch(message.getfieldId()){
+ case FieldIdEnums.TREE_SERVERBLOCKTREE_SYNCEDFIELD_STATE_ID:{
+ ClientBlockTree tree = ClientBlockTree.getClientBlockTree(entity);
+ tree.transitionState(ClientBlockTree.getBlockStateShortAsEnum((short)message.getbTreeValue()));
+ } break;
+ }
+ } break;
+
+ }
+ }
}
diff --git a/src/main/java/electrosphere/net/synchronization/ServerSynchronizationManager.java b/src/main/java/electrosphere/net/synchronization/ServerSynchronizationManager.java
index d04d1311..0251ca90 100644
--- a/src/main/java/electrosphere/net/synchronization/ServerSynchronizationManager.java
+++ b/src/main/java/electrosphere/net/synchronization/ServerSynchronizationManager.java
@@ -59,6 +59,7 @@ public class ServerSynchronizationManager {
case ATTACHTREE:
case DETATCHTREE:
case LOADSCENE:
+ case SERVERNOTIFYBTREETRANSITION:
//silently ignore
break;
}
diff --git a/src/main/java/electrosphere/net/synchronization/annotation/SyncedField.java b/src/main/java/electrosphere/net/synchronization/annotation/SyncedField.java
index 9c1601a5..b1e59c87 100644
--- a/src/main/java/electrosphere/net/synchronization/annotation/SyncedField.java
+++ b/src/main/java/electrosphere/net/synchronization/annotation/SyncedField.java
@@ -23,4 +23,8 @@ public @interface SyncedField {
//ie when idle state changes (to idle from not idle), tell the client the position of the entity when this update happened alongside the actual state update
public boolean updatePositionOnStateChange() default false;
+ //Instructs the server to send a state-transition packet instead of an immediate update packet
+ //The state transition packet will invoke the transitionBTree function on the client instead of immediately overwriting the state's value
+ public boolean serverSendTransitionPacket() default false;
+
}