diff --git a/Runtime/NetworkObject.cs b/Runtime/NetworkObject.cs
index 6bcb3aa..46d1c5b 100644
--- a/Runtime/NetworkObject.cs
+++ b/Runtime/NetworkObject.cs
@@ -31,6 +31,7 @@ namespace VelNet
/// This way objects can be spawned in a static location
///
internal bool instantiatedWithTransform = false;
+
internal Vector3 initialPosition;
internal Quaternion initialRotation;
diff --git a/Runtime/Util/NetworkSerializedObject.cs b/Runtime/Util/NetworkSerializedObject.cs
index 2614c5e..5bb2183 100644
--- a/Runtime/Util/NetworkSerializedObject.cs
+++ b/Runtime/Util/NetworkSerializedObject.cs
@@ -4,6 +4,7 @@ using UnityEngine;
namespace VelNet
{
+ [Obsolete("Use SyncState instead.")]
public abstract class NetworkSerializedObject : NetworkComponent, IPackState
{
[Tooltip("Send rate of this object. This caps out at the framerate of the game.")]
diff --git a/Runtime/Util/NetworkSerializedObjectStream.cs b/Runtime/Util/NetworkSerializedObjectStream.cs
index f87a7f9..f372f6d 100644
--- a/Runtime/Util/NetworkSerializedObjectStream.cs
+++ b/Runtime/Util/NetworkSerializedObjectStream.cs
@@ -1,17 +1,20 @@
using System;
using System.Collections;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
using UnityEngine;
namespace VelNet
{
+ [Obsolete("Use SyncState instead")]
public abstract class NetworkSerializedObjectStream : NetworkComponent, IPackState
{
[Tooltip("Send rate of this object. This caps out at the framerate of the game.")]
public float serializationRateHz = 30;
///
- /// If the data hasn't changed, only sends updates across the network at 1Hz
+ /// If the data hasn't changed, only sends updates across the network at 0.5Hz
///
public bool hybridOnChangeCompression = true;
@@ -19,7 +22,6 @@ namespace VelNet
private double lastSendTime;
private const double slowSendInterval = 2;
-
protected virtual void Awake()
{
StartCoroutine(SendMessageUpdate());
diff --git a/Runtime/Util/SyncRigidbody.cs b/Runtime/Util/SyncRigidbody.cs
index 083ea1b..a7b4e26 100644
--- a/Runtime/Util/SyncRigidbody.cs
+++ b/Runtime/Util/SyncRigidbody.cs
@@ -10,7 +10,7 @@ namespace VelNet
///
[AddComponentMenu("VelNet/VelNet Sync Rigidbody")]
[RequireComponent(typeof(Rigidbody))]
- public class SyncRigidbody : NetworkSerializedObjectStream
+ public class SyncRigidbody : SyncState
{
public bool useLocalTransform;
public float minPosDelta = .01f;
diff --git a/Runtime/Util/SyncState.cs b/Runtime/Util/SyncState.cs
new file mode 100644
index 0000000..3a494c8
--- /dev/null
+++ b/Runtime/Util/SyncState.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections;
+using System.IO;
+using UnityEngine;
+
+namespace VelNet
+{
+ public abstract class SyncState : NetworkComponent, IPackState
+ {
+ [Tooltip("Send rate of this object. This caps out at the framerate of the game.")]
+ public float serializationRateHz = 30;
+
+ ///
+ /// If the data hasn't changed, only sends updates across the network at 0.5Hz
+ ///
+ public bool hybridOnChangeCompression = true;
+
+ private byte[] lastSentBytes;
+ private double lastSendTime;
+ private const double slowSendInterval = 2;
+
+ private MemoryStream writerMemory;
+ private BinaryWriter writer;
+ private MemoryStream readerMemory;
+ private BinaryReader reader;
+
+ protected virtual void Awake()
+ {
+ writerMemory = new MemoryStream();
+ writer = new BinaryWriter(writerMemory);
+ readerMemory = new MemoryStream();
+ reader = new BinaryReader(readerMemory);
+
+ StartCoroutine(SendMessageUpdate());
+ }
+
+ private IEnumerator SendMessageUpdate()
+ {
+ while (true)
+ {
+ try
+ {
+ if (IsMine && enabled)
+ {
+ byte[] newBytes = PackState();
+
+ if (hybridOnChangeCompression)
+ {
+ if (Time.timeAsDouble - lastSendTime > slowSendInterval ||
+ !BinaryWriterExtensions.BytesSame(lastSentBytes, newBytes))
+ {
+ SendBytes(newBytes);
+ lastSendTime = Time.timeAsDouble;
+ }
+ }
+ else
+ {
+ SendBytes(newBytes);
+ lastSendTime = Time.timeAsDouble;
+ }
+
+ lastSentBytes = newBytes;
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+
+ yield return new WaitForSeconds(1f / serializationRateHz);
+ }
+ // ReSharper disable once IteratorNeverReturns
+ }
+
+ public override void ReceiveBytes(byte[] message)
+ {
+ UnpackState(message);
+ }
+
+ protected abstract void SendState(BinaryWriter binaryWriter);
+
+ protected abstract void ReceiveState(BinaryReader binaryReader);
+
+ public byte[] PackState()
+ {
+ writerMemory.Position = 0;
+ writerMemory.SetLength(0);
+ SendState(writer);
+ return writerMemory.ToArray();
+ }
+
+ public void UnpackState(byte[] state)
+ {
+ readerMemory.Position = 0;
+ readerMemory.SetLength(0);
+ readerMemory.Write(state, 0, state.Length);
+ readerMemory.Position = 0;
+ ReceiveState(reader);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Util/SyncState.cs.meta b/Runtime/Util/SyncState.cs.meta
new file mode 100644
index 0000000..1762749
--- /dev/null
+++ b/Runtime/Util/SyncState.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 91bac26baa2fadb4d86aa2ff05c94a2a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Util/SyncTransform.cs b/Runtime/Util/SyncTransform.cs
index 29be722..984a233 100644
--- a/Runtime/Util/SyncTransform.cs
+++ b/Runtime/Util/SyncTransform.cs
@@ -7,7 +7,7 @@ namespace VelNet
/// A simple class that will sync the position and rotation of a network object
///
[AddComponentMenu("VelNet/VelNet Sync Transform")]
- public class SyncTransform : NetworkSerializedObjectStream
+ public class SyncTransform : SyncState
{
[Space] public bool position = true;
public bool rotation = true;
diff --git a/Runtime/Util/UndoGroup.cs b/Runtime/Util/UndoGroup.cs
new file mode 100644
index 0000000..a59d0ba
--- /dev/null
+++ b/Runtime/Util/UndoGroup.cs
@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+#if UNITY_EDITOR
+using UnityEditor;
+#endif
+
+namespace VelNet
+{
+ public class UndoGroup : MonoBehaviour
+ {
+ ///
+ /// This cannot be changed at runtime.
+ ///
+ public List objects;
+
+ private readonly List undoBuffer = new List();
+ public int maxUndoSteps = 50;
+
+
+ ///
+ /// Reset to the last UndoState. This only takes ownership if the IPackState component is also a NetworkComponent
+ ///
+ public void Undo()
+ {
+ byte[][] lastStates = undoBuffer.LastOrDefault();
+ if (lastStates != null)
+ {
+ for (int i = 0; i < objects.Count; i++)
+ {
+ objects[i].networkObject.TakeOwnership();
+ objects[i].UnpackState(lastStates[i]);
+ }
+ }
+ }
+
+ public void SaveUndoState()
+ {
+ byte[][] states = new byte[objects.Count][];
+ for (int i = 0; i < objects.Count; i++)
+ {
+ states[i] = objects[i].PackState();
+ }
+
+ undoBuffer.Add(states);
+
+ while (undoBuffer.Count > maxUndoSteps)
+ {
+ undoBuffer.RemoveAt(0);
+ }
+ }
+
+ public int UndoHistoryLength()
+ {
+ return undoBuffer.Count;
+ }
+ }
+
+#if UNITY_EDITOR
+ [CustomEditor(typeof(UndoGroup))]
+ public class UndoGroupEditor : Editor
+ {
+ public override void OnInspectorGUI()
+ {
+ UndoGroup t = target as UndoGroup;
+
+ EditorGUILayout.Space();
+
+ if (t == null) return;
+
+ EditorGUILayout.HelpBox("Undo Group. Use SaveUndoState() to make checkpoints.", MessageType.Info);
+
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUILayout.TextField("Undo Length: ", t.UndoHistoryLength().ToString("N0"));
+ EditorGUI.EndDisabledGroup();
+ EditorGUILayout.Space();
+
+ if (EditorApplication.isPlaying && GUILayout.Button("Save undo checkpoint now."))
+ {
+ t.SaveUndoState();
+ }
+
+ if (GUILayout.Button("Find all undoable components in children."))
+ {
+ SyncState[] components = t.GetComponentsInChildren();
+ SerializedObject so = new SerializedObject(t);
+ SerializedProperty prop = so.FindProperty("objects");
+ prop.ClearArray();
+ foreach (SyncState comp in components)
+ {
+ prop.InsertArrayElementAtIndex(0);
+ prop.GetArrayElementAtIndex(0).objectReferenceValue = comp;
+ }
+
+ so.ApplyModifiedProperties();
+ }
+
+ EditorGUILayout.Space();
+
+ DrawDefaultInspector();
+ }
+ }
+#endif
+}
\ No newline at end of file
diff --git a/Runtime/Util/UndoGroup.cs.meta b/Runtime/Util/UndoGroup.cs.meta
new file mode 100644
index 0000000..d91ff0b
--- /dev/null
+++ b/Runtime/Util/UndoGroup.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3ab6cea605a37144a87766745c28fcc4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Samples/DissonanceExample/Scripts/PlayerController.cs b/Samples/DissonanceExample/Scripts/PlayerController.cs
index 50d7b24..94516a1 100644
--- a/Samples/DissonanceExample/Scripts/PlayerController.cs
+++ b/Samples/DissonanceExample/Scripts/PlayerController.cs
@@ -6,7 +6,7 @@ using Random = UnityEngine.Random;
namespace VelNet
{
- public class PlayerController : NetworkSerializedObjectStream
+ public class PlayerController : SyncState
{
private Renderer rend;
public Color color;
diff --git a/Samples/DissonanceExample/Scripts/SyncedTextbox.cs b/Samples/DissonanceExample/Scripts/SyncedTextbox.cs
index af83c6d..c8b264b 100644
--- a/Samples/DissonanceExample/Scripts/SyncedTextbox.cs
+++ b/Samples/DissonanceExample/Scripts/SyncedTextbox.cs
@@ -2,7 +2,7 @@ using System.IO;
using UnityEngine.UI;
using VelNet;
-public class SyncedTextbox : NetworkSerializedObjectStream
+public class SyncedTextbox : SyncState
{
public InputField text;
diff --git a/Samples/FullExample/Prefabs/PlayerPrefab.prefab b/Samples/FullExample/Prefabs/PlayerPrefab.prefab
index fbc90dc..914aad7 100644
--- a/Samples/FullExample/Prefabs/PlayerPrefab.prefab
+++ b/Samples/FullExample/Prefabs/PlayerPrefab.prefab
@@ -92,9 +92,17 @@ BoxCollider:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 857495161650682534}
m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
m_IsTrigger: 0
+ m_ProvidesContacts: 0
m_Enabled: 1
- serializedVersion: 2
+ serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &2597866068570990601
@@ -189,9 +197,17 @@ BoxCollider:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2597866068570990601}
m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
m_IsTrigger: 0
+ m_ProvidesContacts: 0
m_Enabled: 1
- serializedVersion: 2
+ serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &6139051692386484099
@@ -294,9 +310,17 @@ BoxCollider:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6139051692386484099}
m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
m_IsTrigger: 0
+ m_ProvidesContacts: 0
m_Enabled: 1
- serializedVersion: 2
+ serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &9102273340480352682
@@ -357,6 +381,7 @@ MonoBehaviour:
serializationRateHz: 30
hybridOnChangeCompression: 1
color: {r: 0, g: 0, b: 0, a: 0}
+ spawnableObj: {fileID: 419466048389313174, guid: c0c7ee35b5be203468523c819c9da422, type: 3}
--- !u!114 &1674689795532972108
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -562,8 +587,16 @@ BoxCollider:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7360746642267561319}
m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
m_IsTrigger: 0
+ m_ProvidesContacts: 0
m_Enabled: 1
- serializedVersion: 2
+ serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
diff --git a/Samples/FullExample/Scenes/FullExample.unity b/Samples/FullExample/Scenes/FullExample.unity
index 296fc31..25ba695 100644
--- a/Samples/FullExample/Scenes/FullExample.unity
+++ b/Samples/FullExample/Scenes/FullExample.unity
@@ -104,7 +104,7 @@ NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
- serializedVersion: 2
+ serializedVersion: 3
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
@@ -117,7 +117,7 @@ NavMeshSettings:
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
- accuratePlacement: 0
+ buildHeightMesh: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
@@ -585,7 +585,9 @@ Canvas:
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
+ m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
+ m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
@@ -1727,9 +1729,17 @@ Camera:
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
+ m_Iso: 200
+ m_ShutterSpeed: 0.005
+ m_Aperture: 16
+ m_FocusDistance: 10
+ m_FocalLength: 50
+ m_BladeCount: 5
+ m_Curvature: {x: 2, y: 11}
+ m_BarrelClipping: 0.25
+ m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
- m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
@@ -2589,7 +2599,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 233344de094f11341bdb834d564708dc, type: 3}
m_Name:
m_EditorClassIdentifier:
- host: vn.ugavel.com
+ host: velnet-demo.ugavel.com
port: 5000
udpConnected: 0
userid: -1
diff --git a/Samples/FullExample/Scripts/PlayerController.cs b/Samples/FullExample/Scripts/PlayerController.cs
index 530065d..6be3180 100644
--- a/Samples/FullExample/Scripts/PlayerController.cs
+++ b/Samples/FullExample/Scripts/PlayerController.cs
@@ -6,7 +6,7 @@ using Random = UnityEngine.Random;
namespace VelNet
{
- public class PlayerController : NetworkSerializedObjectStream
+ public class PlayerController : SyncState
{
private Renderer rend;
public Color color;
diff --git a/Samples/FullExample/Scripts/SyncedCustomObj.cs b/Samples/FullExample/Scripts/SyncedCustomObj.cs
index c9c43bd..2fb5052 100644
--- a/Samples/FullExample/Scripts/SyncedCustomObj.cs
+++ b/Samples/FullExample/Scripts/SyncedCustomObj.cs
@@ -4,7 +4,7 @@ using System.IO;
using UnityEngine;
using VelNet;
-public class SyncedCustomObj : NetworkSerializedObjectStream
+public class SyncedCustomObj : SyncState
{
private Renderer rend;
private Rigidbody rb;
diff --git a/Samples/FullExample/Scripts/SyncedTextbox.cs b/Samples/FullExample/Scripts/SyncedTextbox.cs
index af83c6d..c8b264b 100644
--- a/Samples/FullExample/Scripts/SyncedTextbox.cs
+++ b/Samples/FullExample/Scripts/SyncedTextbox.cs
@@ -2,7 +2,7 @@ using System.IO;
using UnityEngine.UI;
using VelNet;
-public class SyncedTextbox : NetworkSerializedObjectStream
+public class SyncedTextbox : SyncState
{
public InputField text;
diff --git a/package.json b/package.json
index 9df5034..4923be5 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "edu.uga.engr.vel.velnet",
"displayName": "VelNet",
- "version": "1.1.9",
+ "version": "1.2.0",
"unity": "2019.1",
"description": "A custom networking library for Unity.",
"keywords": [