From ca96e555b29132c0ce22a34040629d237f4c7ed5 Mon Sep 17 00:00:00 2001 From: Anton Franzluebbers Date: Fri, 7 Jan 2022 02:33:10 -0500 Subject: [PATCH] big reorganization to make NetworkObjects pass messages on to NetworkComponent, which do the actual syncing. Removed the need for a Player gameobject, added a ownershipLocked option for possible player objects, simplified instantiation process - but it doesn't seem to work all the time --- .../Dissonance Integration/VelCommsNetwork.cs | 10 +- .../VelNetDissonancePlayer.cs | 156 ++++++++++-------- .../VelNetUnity/1.0.0/Example/NetworkGUI.cs | 11 +- .../1.0.0/Example/PlayerController.cs | 127 ++++++-------- .../1.0.0/Example/PlayerPrefab.prefab | 30 ++-- .../Example/TestNetworkedGameObject.prefab | 26 ++- .../VelNetUnity/1.0.0/Example/VelNetMan.cs | 18 ++ .../1.0.0/Example/VelNetMan.cs.meta | 11 ++ .../VelNetUnity/1.0.0/Example/test.unity | 25 ++- .../VelNetUnity/Runtime/NetworkComponent.cs | 33 ++++ .../Runtime/NetworkComponent.cs.meta | 3 + .../VelNetUnity/Runtime/NetworkObject.cs | 47 +++++- .../VelNetUnity/Runtime/NetworkObject.cs.meta | 14 +- .../VelNetUnity/Runtime/NetworkPlayer.cs | 126 +++++--------- .../Runtime/NetworkSerializedObject.cs | 37 +++-- .../VelNetUnity/Runtime/Util/SyncTransform.cs | 8 +- .../{NetworkManager.cs => VelNetManager.cs} | 105 ++++++++---- ...kManager.cs.meta => VelNetManager.cs.meta} | 0 .../DissonanceIntegration/VelCommsNetwork.cs | 10 +- .../VelNetDissonancePlayer.cs | 156 ++++++++++-------- .../Samples~/Example/NetworkGUI.cs | 11 +- .../Samples~/Example/PlayerController.cs | 127 ++++++-------- .../Samples~/Example/PlayerPrefab.prefab | 30 ++-- .../Example/TestNetworkedGameObject.prefab | 26 ++- .../VelNetUnity/Samples~/Example/VelNetMan.cs | 18 ++ .../Samples~/Example/VelNetMan.cs.meta | 11 ++ .../VelNetUnity/Samples~/Example/test.unity | 25 ++- .../Packages/VelNetUnity/package.json | 2 +- .../ProjectSettings/TagManager.asset | 3 +- velnet_logo.xcf | Bin 0 -> 103589 bytes 30 files changed, 692 insertions(+), 514 deletions(-) create mode 100644 TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/VelNetMan.cs create mode 100644 TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/VelNetMan.cs.meta create mode 100644 TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkComponent.cs create mode 100644 TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkComponent.cs.meta rename TestVelGameServer/Packages/VelNetUnity/Runtime/{NetworkManager.cs => VelNetManager.cs} (78%) rename TestVelGameServer/Packages/VelNetUnity/Runtime/{NetworkManager.cs.meta => VelNetManager.cs.meta} (100%) create mode 100644 TestVelGameServer/Packages/VelNetUnity/Samples~/Example/VelNetMan.cs create mode 100644 TestVelGameServer/Packages/VelNetUnity/Samples~/Example/VelNetMan.cs.meta create mode 100644 velnet_logo.xcf diff --git a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Dissonance Integration/VelCommsNetwork.cs b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Dissonance Integration/VelCommsNetwork.cs index b1defa8..5633ee4 100644 --- a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Dissonance Integration/VelCommsNetwork.cs +++ b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Dissonance Integration/VelCommsNetwork.cs @@ -7,6 +7,10 @@ using UnityEngine.Serialization; namespace VelNetUnity { + /// + /// Added to the same object as DissonanceComms component. Only one in the scene. + /// + [RequireComponent(typeof(DissonanceComms))] [AddComponentMenu("VelNetUnity/Dissonance/VelNet Comms Network")] public class VelCommsNetwork : MonoBehaviour, ICommsNetwork { @@ -26,8 +30,8 @@ namespace VelNetUnity private ConnectionStatus _status = ConnectionStatus.Disconnected; private CodecSettings initSettings; public string dissonanceId; - [FormerlySerializedAs("comms")] public DissonanceComms dissonanceComms; - private NetworkManager manager; + [HideInInspector] public DissonanceComms dissonanceComms; + private VelNetManager manager; /// /// listen to this if you want to send voice @@ -40,7 +44,7 @@ namespace VelNetUnity { _status = ConnectionStatus.Connected; dissonanceComms = GetComponent(); - manager = NetworkManager.instance; + manager = VelNetManager.instance; } public void Initialize(string playerName, Rooms rooms, PlayerChannels playerChannels, RoomChannels roomChannels, CodecSettings codecSettings) diff --git a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Dissonance Integration/VelNetDissonancePlayer.cs b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Dissonance Integration/VelNetDissonancePlayer.cs index 9bf72c0..0ea2dba 100644 --- a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Dissonance Integration/VelNetDissonancePlayer.cs +++ b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Dissonance Integration/VelNetDissonancePlayer.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using Dissonance; using UnityEngine; @@ -12,7 +11,7 @@ namespace VelNetUnity /// This should be added to your player object /// [AddComponentMenu("VelNetUnity/Dissonance/VelNet Dissonance Player")] - public class VelNetDissonancePlayer : NetworkObject, IDissonancePlayer + public class VelNetDissonancePlayer : NetworkComponent, IDissonancePlayer { private VelCommsNetwork comms; private bool isSpeaking; @@ -25,20 +24,24 @@ namespace VelNetUnity public string PlayerId => dissonanceID; public Vector3 Position => transform.position; public Quaternion Rotation => transform.rotation; - public NetworkPlayerType Type => owner.isLocal ? NetworkPlayerType.Local : NetworkPlayerType.Remote; + public NetworkPlayerType Type => IsMine ? NetworkPlayerType.Local : NetworkPlayerType.Remote; public bool IsTracking => true; - public Vector3 targetPosition; - public Quaternion targetRotation; - private static readonly List allPlayers = new List(); public List closePlayers = new List(); [Tooltip("Maximum distance to transmit voice data. 0 to always send voice to all players.")] public float maxDistance; - // Start is called before the first frame update - private void Start() + private enum MessageType : byte + { + AudioData, + DissonanceId, + SpeakingState + } + + // This object should not be in the scene at the start. + private void Awake() { comms = FindObjectOfType(); if (comms == null) @@ -53,76 +56,37 @@ namespace VelNetUnity allPlayers.Add(this); } - if (owner.isLocal) + if (IsMine) { SetDissonanceID(comms.dissonanceId); comms.VoiceQueued += SendVoiceData; //we also need to know when other players join, so we can send the dissonance ID again - NetworkManager.instance.OnPlayerJoined += (player) => + VelNetManager.instance.OnPlayerJoined += (player) => { - byte[] b = Encoding.UTF8.GetBytes(dissonanceID); - owner.SendMessage(this, "d", b); + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.DissonanceId); + writer.Write(dissonanceID); + SendBytes(mem.ToArray()); }; - NetworkManager.instance.SetupMessageGroup("close", closePlayers.ToArray()); - } - } - - public override void HandleMessage(string identifier, byte[] message) - { - switch (identifier) - { - case "a": //audio data - { - if (isSpeaking) - { - comms.VoiceReceived(dissonanceID, message); - } - - break; - } - case "d": //dissonance id (player joined) - { - if (dissonanceID == "") // I don't have this yet - { - dissonanceID = Encoding.UTF8.GetString(message); - // tell the comms network that this player joined the channel - comms.SetPlayerJoined(dissonanceID); // tell dissonance - comms.dissonanceComms.TrackPlayerPosition(this); // tell dissonance to track the remote player - } - - break; - } - case "x": // speaking state - { - if (message[0] == 0) - { - comms.SetPlayerStoppedSpeaking(dissonanceID); - isSpeaking = false; - } - else - { - comms.SetPlayerStartedSpeaking(dissonanceID); - isSpeaking = true; - } - - break; - } + VelNetManager.instance.SetupMessageGroup("close", closePlayers.ToArray()); } } private void SendVoiceData(ArraySegment data) { // need to send it - if (owner == null || !owner.isLocal) return; + if (!IsMine) return; using MemoryStream mem = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.AudioData); writer.Write(BitConverter.GetBytes(lastAudioId++)); writer.Write(data.ToArray()); // send voice data unreliably - owner.SendGroupMessage(this, "close", "a", mem.ToArray(), false); + SendBytesToGroup("close", mem.ToArray(), false); } /// @@ -132,8 +96,12 @@ namespace VelNetUnity public void SetDissonanceID(string id) { dissonanceID = id; - byte[] b = Encoding.UTF8.GetBytes(dissonanceID); - owner.SendMessage(this, "d", b); + + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.DissonanceId); + writer.Write(dissonanceID); + SendBytes(mem.ToArray()); comms.dissonanceComms.TrackPlayerPosition(this); } @@ -151,8 +119,7 @@ namespace VelNetUnity // Update is called once per frame private void Update() { - if (owner == null) return; - if (!owner.isLocal) return; + if (!IsMine) return; // handle nearness cutoff if (maxDistance > 0) @@ -166,21 +133,21 @@ namespace VelNetUnity } float dist = Vector3.Distance(p.transform.position, transform.position); - if (dist < maxDistance && !closePlayers.Contains(p.owner.userid)) + if (dist < maxDistance && !closePlayers.Contains(p.Owner.userid)) { - closePlayers.Add(p.owner.userid); + closePlayers.Add(p.Owner.userid); closePlayerListChanged = true; } - else if (dist >= maxDistance && closePlayers.Contains(p.owner.userid)) + else if (dist >= maxDistance && closePlayers.Contains(p.Owner.userid)) { - closePlayers.Remove(p.owner.userid); + closePlayers.Remove(p.Owner.userid); closePlayerListChanged = true; } } if (closePlayerListChanged) { - NetworkManager.instance.SetupMessageGroup("close", closePlayers); + VelNetManager.instance.SetupMessageGroup("close", closePlayers); } } @@ -191,8 +158,12 @@ namespace VelNetUnity if (comms.dissonanceComms.FindPlayer(dissonanceID)?.IsSpeaking != isSpeaking) //unfortunately, there does not seem to be an event for this { isSpeaking = !isSpeaking; - byte[] toSend = { isSpeaking ? (byte)1 : (byte)0 }; - owner.SendMessage(this, "x", toSend); + + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.SpeakingState); + writer.Write(isSpeaking ? (byte)1 : (byte)0); + SendBytes(mem.ToArray()); if (!isSpeaking) { @@ -200,5 +171,52 @@ namespace VelNetUnity } } } + + public override void ReceiveBytes(byte[] message) + { + using MemoryStream mem = new MemoryStream(message); + using BinaryReader reader = new BinaryReader(mem); + + byte identifier = reader.ReadByte(); + switch (identifier) + { + case 0: //audio data + { + if (isSpeaking) + { + comms.VoiceReceived(dissonanceID, message.Skip(1).ToArray()); + } + + break; + } + case 1: //dissonance id (player joined) + { + if (dissonanceID == "") // I don't have this yet + { + dissonanceID = reader.ReadString(); + // tell the comms network that this player joined the channel + comms.SetPlayerJoined(dissonanceID); // tell dissonance + comms.dissonanceComms.TrackPlayerPosition(this); // tell dissonance to track the remote player + } + + break; + } + case 2: // speaking state + { + if (message[0] == 0) + { + comms.SetPlayerStoppedSpeaking(dissonanceID); + isSpeaking = false; + } + else + { + comms.SetPlayerStartedSpeaking(dissonanceID); + isSpeaking = true; + } + + break; + } + } + } } } \ No newline at end of file diff --git a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/NetworkGUI.cs b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/NetworkGUI.cs index b5ed7af..034466f 100644 --- a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/NetworkGUI.cs +++ b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/NetworkGUI.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using UnityEngine; +using UnityEngine.Serialization; using UnityEngine.UI; namespace VelNetUnity { public class NetworkGUI : MonoBehaviour { - public NetworkManager networkManager; + [FormerlySerializedAs("networkManager")] public VelNetManager velNetManager; public InputField userInput; public InputField sendInput; public InputField roomInput; @@ -19,7 +20,7 @@ namespace VelNetUnity { if (sendInput.text != "") { - networkManager.SendTo(NetworkManager.MessageType.OTHERS, sendInput.text); + velNetManager.SendTo(VelNetManager.MessageType.OTHERS, sendInput.text); } } @@ -27,7 +28,7 @@ namespace VelNetUnity { if (userInput.text != "") { - networkManager.Login(userInput.text, "nopass"); + velNetManager.Login(userInput.text, "nopass"); } } @@ -35,7 +36,7 @@ namespace VelNetUnity { if (roomInput.text != "") { - networkManager.Join(roomInput.text); + velNetManager.Join(roomInput.text); } } @@ -44,7 +45,7 @@ namespace VelNetUnity { comms = FindObjectOfType(); microphones.AddOptions(new List(Microphone.devices)); - networkManager.MessageReceived += (m) => + velNetManager.MessageReceived += (m) => { string s = m.type + ":" + m.sender + ":" + m.text; messageBuffer.Add(s); diff --git a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/PlayerController.cs b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/PlayerController.cs index 5f72100..aebafb5 100644 --- a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/PlayerController.cs +++ b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/PlayerController.cs @@ -1,16 +1,60 @@ -using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; namespace VelNetUnity { - public class PlayerController : NetworkObject + public class PlayerController : NetworkSerializedObject { public Vector3 targetPosition; public Quaternion targetRotation; - public byte[] GetSyncMessage() + + // Update is called once per frame + private void Update() + { + if (!IsMine) + { + transform.position = Vector3.Lerp(transform.position, targetPosition, .1f); + transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, .1f); + } + else + { + Vector3 movement = new Vector3(); + movement.x += Input.GetAxis("Horizontal"); + movement.y += Input.GetAxis("Vertical"); + movement.z = 0; + transform.Translate(movement * Time.deltaTime); + + if (Input.GetKeyDown(KeyCode.Space)) + { + VelNetManager.InstantiateNetworkObject("TestNetworkedGameObject"); + } + + if (Input.GetKeyDown(KeyCode.BackQuote)) + { + foreach (KeyValuePair kvp in VelNetManager.instance.objects) + { + Owner.TakeOwnership(kvp.Key); + } + } + + if (Input.GetKeyDown(KeyCode.Backspace)) + { + foreach (KeyValuePair kvp in VelNetManager.instance.objects) + { + // don't destroy player objects + if (!kvp.Value.ownershipLocked) + { + Owner.NetworkDestroy(kvp.Key); + } + } + } + } + } + + + protected override byte[] SendState() { using MemoryStream mem = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(mem); @@ -21,80 +65,13 @@ namespace VelNetUnity return mem.ToArray(); } - public override void HandleMessage(string identifier, byte[] message) + protected override void ReceiveState(byte[] message) { - switch (identifier) - { - case "s": // sync message - { - using MemoryStream mem = new MemoryStream(message); - using BinaryReader reader = new BinaryReader(mem); + using MemoryStream mem = new MemoryStream(message); + using BinaryReader reader = new BinaryReader(mem); - targetPosition = reader.ReadVector3(); - targetRotation = reader.ReadQuaternion(); - - break; - } - } - } - - // Start is called before the first frame update - private void Start() - { - // player controller shouldn't change ownership, so we can check here once - if (owner.isLocal) - { - StartCoroutine(SyncBehavior()); - } - } - - - private IEnumerator SyncBehavior() - { - while (true) - { - owner.SendMessage(this, "s", GetSyncMessage()); - yield return new WaitForSeconds(.1f); - } - } - - // Update is called once per frame - private void Update() - { - if (owner != null && !owner.isLocal) - { - transform.position = Vector3.Lerp(transform.position, targetPosition, .1f); - transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, .1f); - } - else if (owner != null && owner.isLocal) - { - Vector3 movement = new Vector3(); - movement.x += Input.GetAxis("Horizontal"); - movement.y += Input.GetAxis("Vertical"); - movement.z = 0; - transform.Translate(movement * Time.deltaTime); - - if (Input.GetKeyDown(KeyCode.Space)) - { - owner.NetworkInstantiate("TestNetworkedGameObject"); - } - - if (Input.GetKeyDown(KeyCode.BackQuote)) - { - foreach (KeyValuePair kvp in NetworkManager.instance.objects) - { - owner.TakeOwnership(kvp.Key); - } - } - - if (Input.GetKeyDown(KeyCode.Backspace)) - { - foreach (KeyValuePair kvp in NetworkManager.instance.objects) - { - owner.NetworkDestroy(kvp.Key); - } - } - } + targetPosition = reader.ReadVector3(); + targetRotation = reader.ReadQuaternion(); } } } \ No newline at end of file diff --git a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/PlayerPrefab.prefab b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/PlayerPrefab.prefab index d590dc3..e4b77af 100644 --- a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/PlayerPrefab.prefab +++ b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/PlayerPrefab.prefab @@ -12,7 +12,7 @@ GameObject: - component: {fileID: 8527011532923434593} - component: {fileID: 6854617867369839} - component: {fileID: 5845716565458182149} - - component: {fileID: 5713671764962751473} + - component: {fileID: 9102273340480352682} - component: {fileID: -4404668399269848200} - component: {fileID: 1181612843795795320} m_Layer: 0 @@ -98,7 +98,7 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 1, y: 1, z: 1} m_Center: {x: 0, y: 0, z: 0} ---- !u!114 &5713671764962751473 +--- !u!114 &9102273340480352682 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -107,14 +107,16 @@ MonoBehaviour: m_GameObject: {fileID: 6139051692386484099} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: d8d3b6de660834e3e898725928251405, type: 3} + m_Script: {fileID: 11500000, guid: 5515094c5c544b6b8ed7fd51a86548d4, type: 3} m_Name: m_EditorClassIdentifier: - userid: 0 - username: - room: - isLocal: 0 - lastObjectId: 0 + ownershipLocked: 1 + networkId: + prefabName: + isSceneObject: 0 + syncedComponents: + - {fileID: -4404668399269848200} + - {fileID: 1181612843795795320} --- !u!114 &-4404668399269848200 MonoBehaviour: m_ObjectHideFlags: 0 @@ -127,10 +129,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 89e3af759df774692a566a166b4bf69b, type: 3} m_Name: m_EditorClassIdentifier: - owner: {fileID: 5713671764962751473} - networkId: - prefabName: - isSceneObject: 0 + networkObject: {fileID: 9102273340480352682} + serializationRateHz: 30 targetPosition: {x: 0, y: 0, z: 0} targetRotation: {x: 0, y: 0, z: 0, w: 0} --- !u!114 &1181612843795795320 @@ -145,13 +145,9 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: c773e094326d413bb1bca7f91cbf7f8c, type: 3} m_Name: m_EditorClassIdentifier: - owner: {fileID: 5713671764962751473} - networkId: - prefabName: - isSceneObject: 0 + networkObject: {fileID: 9102273340480352682} dissonanceID: targetPosition: {x: 0, y: 0, z: 0} targetRotation: {x: 0, y: 0, z: 0, w: 0} - allPlayers: [] closePlayers: maxDistance: 0 diff --git a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/TestNetworkedGameObject.prefab b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/TestNetworkedGameObject.prefab index bac19d3..e08d947 100644 --- a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/TestNetworkedGameObject.prefab +++ b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/TestNetworkedGameObject.prefab @@ -9,10 +9,11 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 8565720275311462455} + - component: {fileID: 3951900052977689805} - component: {fileID: 8565720275311462452} m_Layer: 0 m_Name: TestNetworkedGameObject - m_TagString: Untagged + m_TagString: TestSphere m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 @@ -32,6 +33,24 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &3951900052977689805 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8565720275311462453} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5515094c5c544b6b8ed7fd51a86548d4, type: 3} + m_Name: + m_EditorClassIdentifier: + ownershipLocked: 0 + networkId: + prefabName: + isSceneObject: 0 + syncedComponents: + - {fileID: 8565720275311462452} --- !u!114 &8565720275311462452 MonoBehaviour: m_ObjectHideFlags: 0 @@ -44,10 +63,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 3f1f9b0bbd93a484a987c51f1107ebe5, type: 3} m_Name: m_EditorClassIdentifier: - owner: {fileID: 0} - networkId: + networkObject: {fileID: 3951900052977689805} + serializationRateHz: 30 targetPosition: {x: 0, y: 0, z: 0} targetRotation: {x: 0, y: 0, z: 0, w: 0} + smoothness: 0.1 --- !u!1 &8565720276181857625 GameObject: m_ObjectHideFlags: 0 diff --git a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/VelNetMan.cs b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/VelNetMan.cs new file mode 100644 index 0000000..00bf9e7 --- /dev/null +++ b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/VelNetMan.cs @@ -0,0 +1,18 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using VelNetUnity; + +public class VelNetMan : MonoBehaviour +{ + public GameObject playerPrefab; + + // Start is called before the first frame update + private void Start() + { + VelNetManager.instance.OnJoinedRoom += player => + { + VelNetManager.InstantiateNetworkObject(playerPrefab.name); + }; + } +} \ No newline at end of file diff --git a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/VelNetMan.cs.meta b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/VelNetMan.cs.meta new file mode 100644 index 0000000..225d370 --- /dev/null +++ b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/VelNetMan.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2fcf036844b060b47b23ad9a1e49eec2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/test.unity b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/test.unity index 2fcffdb..bda34bd 100644 --- a/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/test.unity +++ b/TestVelGameServer/Assets/Samples/VelNetUnity/1.0.0/Example/test.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641275, b: 0.5748172, a: 1} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -624,7 +624,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 7a7db5bc792cd471dbd8039664359eee, type: 3} m_Name: m_EditorClassIdentifier: - networkManager: {fileID: 1099803616} + velNetManager: {fileID: 1099803616} userInput: {fileID: 626742069} sendInput: {fileID: 945446555} roomInput: {fileID: 711524768} @@ -1787,6 +1787,7 @@ GameObject: m_Component: - component: {fileID: 1099803615} - component: {fileID: 1099803616} + - component: {fileID: 1099803613} m_Layer: 0 m_Name: NetworkManager m_TagString: Untagged @@ -1794,6 +1795,19 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!114 &1099803613 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1099803612} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2fcf036844b060b47b23ad9a1e49eec2, type: 3} + m_Name: + m_EditorClassIdentifier: + playerPrefab: {fileID: 6139051692386484099, guid: d4158ab9c4a204cdbba28d3273fc1fb3, type: 3} --- !u!4 &1099803615 Transform: m_ObjectHideFlags: 0 @@ -1825,9 +1839,9 @@ MonoBehaviour: udpConnected: 0 userid: -1 room: - playerPrefab: {fileID: 6139051692386484099, guid: d4158ab9c4a204cdbba28d3273fc1fb3, type: 3} prefabs: - - {fileID: 8565720275311462452, guid: 6e4a023f70e01405e8b249a4488fe319, type: 3} + - {fileID: 3951900052977689805, guid: 6e4a023f70e01405e8b249a4488fe319, type: 3} + - {fileID: 9102273340480352682, guid: d4158ab9c4a204cdbba28d3273fc1fb3, type: 3} sceneObjects: [] deletedSceneObjects: [] connected: 0 @@ -2281,8 +2295,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: dissonanceId: - comms: {fileID: 1434745021} - manager: {fileID: 1099803616} + dissonanceComms: {fileID: 1434745021} --- !u!4 &1434745020 Transform: m_ObjectHideFlags: 0 diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkComponent.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkComponent.cs new file mode 100644 index 0000000..0dead22 --- /dev/null +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkComponent.cs @@ -0,0 +1,33 @@ +using UnityEngine; + +namespace VelNetUnity +{ + public abstract class NetworkComponent : MonoBehaviour + { + public NetworkObject networkObject; + protected bool IsMine => networkObject != null && networkObject.owner != null && networkObject.owner.isLocal; + protected NetworkPlayer Owner => networkObject != null ? networkObject.owner : null; + + /// + /// call this in child classes to send a message to other people + /// + protected void SendBytes(byte[] message, bool reliable = true) + { + networkObject.SendBytes(this, message, reliable); + } + + /// + /// call this in child classes to send a message to other people + /// + protected void SendBytesToGroup(string group, byte[] message, bool reliable = true) + { + networkObject.SendBytesToGroup(this, group, message, reliable); + } + + // + /// + /// This is called by when messages are received for this component + /// + public abstract void ReceiveBytes(byte[] message); + } +} \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkComponent.cs.meta b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkComponent.cs.meta new file mode 100644 index 0000000..ba86217 --- /dev/null +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 52c3bc18f2284ca28a9d2095e77330d2 +timeCreated: 1641527089 \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs index ce0520a..e4139db 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace VelNetUnity @@ -5,20 +7,57 @@ namespace VelNetUnity /// /// This is a base class for all objects that a player can instantiated/owned /// - public abstract class NetworkObject : MonoBehaviour + public class NetworkObject : MonoBehaviour { - [Header("NetworkObject properties")] + [Header("NetworkObject properties")] public NetworkPlayer owner; - public bool IsMine => owner !=null && owner.isLocal; + public bool ownershipLocked; + public bool IsMine => owner != null && owner.isLocal; + /// /// This is forged from the combination of the creator's id (-1 in the case of a scene object) and an object id, so it's always unique for a room /// public string networkId; + /// /// This may be empty if it's not a prefab (scene object) /// public string prefabName; + public bool isSceneObject; - public abstract void HandleMessage(string identifier, byte[] message); + + public List syncedComponents; + + public void SendBytes(NetworkComponent component, byte[] message, bool reliable = true) + { + if (!IsMine) + { + Debug.LogError("Can't send message if owner is null or not local", this); + return; + } + + // send the message and an identifier for which component it belongs to + int index = syncedComponents.IndexOf(component); + owner.SendMessage(this, index.ToString(), message, reliable); + } + + public void SendBytesToGroup(NetworkComponent component, string group, byte[] message, bool reliable = true) + { + if (owner == null || !owner.isLocal) + { + Debug.LogError("Can't send message if owner is null or not local", this); + return; + } + + // send the message and an identifier for which component it belongs to + int index = syncedComponents.IndexOf(component); + owner.SendGroupMessage(this, group, index.ToString(), message, reliable); + } + + public void ReceiveBytes(string identifier, byte[] message) + { + // send the message to the right component + syncedComponents[int.Parse(identifier)].ReceiveBytes(message); + } } } \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs.meta b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs.meta index 6a1a6e9..29df2e6 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs.meta +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs.meta @@ -1,11 +1,3 @@ -fileFormatVersion: 2 -guid: dd77ffdf919cc444f863d7bf0cda29ea -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: +fileFormatVersion: 2 +guid: 5515094c5c544b6b8ed7fd51a86548d4 +timeCreated: 1641527700 \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkPlayer.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkPlayer.cs index b59d089..448c6b2 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkPlayer.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkPlayer.cs @@ -1,18 +1,13 @@ using System.Collections.Generic; -using UnityEngine; using System; - namespace VelNetUnity { /// - /// One of these will be instantiated for each player that joins the "room". + /// Represents a network player /// - [RequireComponent(typeof(NetworkObject))] - [AddComponentMenu("VelNetUnity/VelNet Network Player")] - public class NetworkPlayer : MonoBehaviour + public class NetworkPlayer { - private NetworkObject myObject; public int userid; public string username; @@ -20,7 +15,7 @@ namespace VelNetUnity public bool isLocal; - private NetworkManager manager; + private VelNetManager manager; /// /// For instantiation @@ -30,11 +25,10 @@ namespace VelNetUnity private bool isMaster; - private void Start() + + public NetworkPlayer() { - myObject = GetComponent(); - myObject.owner = this; - manager = NetworkManager.instance; + manager = VelNetManager.instance; manager.OnPlayerJoined += HandlePlayerJoined; } @@ -47,7 +41,7 @@ namespace VelNetUnity { if (kvp.Value.owner == this && kvp.Value.prefabName != "") { - manager.SendTo(NetworkManager.MessageType.OTHERS, "7," + kvp.Value.networkId + "," + kvp.Value.prefabName); + manager.SendTo(VelNetManager.MessageType.OTHERS, "7," + kvp.Value.networkId + "," + kvp.Value.prefabName); } } @@ -60,27 +54,20 @@ namespace VelNetUnity } - public void HandleMessage(NetworkManager.Message m) + public void HandleMessage(VelNetManager.Message m) { //these are generally things that come from the "owner" and should be enacted locally, where appropriate //we need to parse the message //types of messages string[] messages = m.text.Split(';'); //messages are split by ; - for (int i = 0; i < messages.Length; i++) + foreach (string s in messages) { //individual message parameters separated by comma - string[] sections = messages[i].Split(','); + string[] sections = s.Split(','); switch (sections[0]) { - case "1": //update my object's data - { - string identifier = sections[1]; - byte[] message = Convert.FromBase64String(sections[2]); - myObject.HandleMessage(identifier, message); - break; - } case "5": //sync update for an object I may own { string objectKey = sections[1]; @@ -91,7 +78,7 @@ namespace VelNetUnity { if (manager.objects[objectKey].owner == this) { - manager.objects[objectKey].HandleMessage(identifier, messageBytes); + manager.objects[objectKey].ReceiveBytes(identifier, messageBytes); } } @@ -108,7 +95,7 @@ namespace VelNetUnity break; } - case "7": //I'm trying to instantiate an object + case "7": // I'm trying to instantiate an object { string networkId = sections[1]; string prefabName = sections[2]; @@ -117,19 +104,11 @@ namespace VelNetUnity break; //we already have this one, ignore } - NetworkObject temp = manager.prefabs.Find((prefab) => prefab.name == prefabName); - if (temp != null) - { - NetworkObject instance = Instantiate(temp); - instance.networkId = networkId; - instance.prefabName = prefabName; - instance.owner = this; - manager.objects.Add(instance.networkId, instance); - } + VelNetManager.InstantiateNetworkObject(networkId, prefabName, this); break; } - case "8": //I'm trying to destroy a gameobject I own + case "8": // I'm trying to destroy a gameobject I own { string networkId = sections[1]; @@ -158,71 +137,50 @@ namespace VelNetUnity public void SendGroupMessage(NetworkObject obj, string group, string identifier, byte[] data, bool reliable = true) { - if (obj == myObject) - { - manager.SendToGroup(group, "1," + identifier + "," + Convert.ToBase64String(data), reliable); - } - else - { - manager.SendToGroup(group, "5," + obj.networkId + "," + identifier + "," + Convert.ToBase64String(data), reliable); - } + manager.SendToGroup(group, "5," + obj.networkId + "," + identifier + "," + Convert.ToBase64String(data), reliable); } public void SendMessage(NetworkObject obj, string identifier, byte[] data, bool reliable = true) { - if (obj == myObject) - { - manager.SendTo(NetworkManager.MessageType.OTHERS, "1," + identifier + "," + Convert.ToBase64String(data), reliable); - } - else - { - manager.SendTo(NetworkManager.MessageType.OTHERS, "5," + obj.networkId + "," + identifier + "," + Convert.ToBase64String(data), reliable); - } - } - - public NetworkObject NetworkInstantiate(string prefabName) - { - if (!isLocal) - { - return null; //must be the local player to call instantiate - } - - string networkId = userid + "-" + lastObjectId++; - - - NetworkObject temp = manager.prefabs.Find((prefab) => prefab.name == prefabName); - if (temp != null) - { - NetworkObject instance = Instantiate(temp); - instance.networkId = networkId; - instance.prefabName = prefabName; - instance.owner = this; - manager.objects.Add(instance.networkId, instance); - - manager.SendTo(NetworkManager.MessageType.OTHERS, "7," + networkId + "," + prefabName); //only sent to others, as I already instantiated this. Nice that it happens immediately. - return instance; - } - - return null; + manager.SendTo(VelNetManager.MessageType.OTHERS, "5," + obj.networkId + "," + identifier + "," + Convert.ToBase64String(data), reliable); } + /// + /// TODO could move this to a static method in VelNetManager + /// public void NetworkDestroy(string networkId) { - if (!manager.objects.ContainsKey(networkId) || manager.objects[networkId].owner != this || !isLocal) return; //must be the local owner of the object to destroy it - manager.SendTo(NetworkManager.MessageType.ALL_ORDERED, "8," + networkId); //send to all, which will make me delete as well + // must be the local owner of the object to destroy it + if (!manager.objects.ContainsKey(networkId) || manager.objects[networkId].owner != this || !isLocal) return; + + // send to all, which will make me delete as well + manager.SendTo(VelNetManager.MessageType.ALL_ORDERED, "8," + networkId); } - public void TakeOwnership(string networkId) + /// + /// TODO could move this to a static method in VelNetManager + /// + /// True if successful, False if failed to transfer ownership + public bool TakeOwnership(string networkId) { - if (!manager.objects.ContainsKey(networkId) || !isLocal) return; //must exist and be the the local player + // must exist and be the the local player + if (!manager.objects.ContainsKey(networkId) || !isLocal) return false; - manager.objects[networkId].owner = this; //immediately successful - manager.SendTo(NetworkManager.MessageType.ALL_ORDERED, "6," + networkId); //must be ordered, so that ownership transfers are not confused. Also sent to all players, so that multiple simultaneous requests will result in the same outcome. + // if the ownership is locked, fail + if (manager.objects[networkId].ownershipLocked) return false; + + // immediately successful + manager.objects[networkId].owner = this; + + // must be ordered, so that ownership transfers are not confused. Also sent to all players, so that multiple simultaneous requests will result in the same outcome. + manager.SendTo(VelNetManager.MessageType.ALL_ORDERED, "6," + networkId); + + return true; } public void SendSceneUpdate() { - manager.SendTo(NetworkManager.MessageType.OTHERS, "9," + string.Join(",", manager.deletedSceneObjects)); + manager.SendTo(VelNetManager.MessageType.OTHERS, "9," + string.Join(",", manager.deletedSceneObjects)); } } } \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkSerializedObject.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkSerializedObject.cs index 18ac263..162ced2 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkSerializedObject.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkSerializedObject.cs @@ -1,39 +1,40 @@ -using System; -using System.Timers; +using System.Collections; using UnityEngine; using UnityEngine.Serialization; namespace VelNetUnity { - public abstract class NetworkSerializedObject : NetworkObject + public abstract class NetworkSerializedObject : NetworkComponent { - [FormerlySerializedAs("updateRateHz")] [Tooltip("Send rate of this object")] public float serializationRateHz = 30; + [FormerlySerializedAs("updateRateHz")] [Tooltip("Send rate of this object")] + public float serializationRateHz = 30; private void Start() { - Timer timer = new Timer(); - timer.Interval = serializationRateHz; - timer.Elapsed += SendMessageUpdate; + StartCoroutine(SendMessageUpdate()); } - private void SendMessageUpdate(object sender, ElapsedEventArgs e) + private IEnumerator SendMessageUpdate() { - if (owner != null && owner.isLocal) + while (true) { - owner.SendMessage(this, "s", SendState()); + if (IsMine) + { + SendBytes(SendState()); + } + + yield return new WaitForSeconds(1 / serializationRateHz); } + // ReSharper disable once IteratorNeverReturns + } + + public override void ReceiveBytes(byte[] message) + { + ReceiveState(message); } protected abstract byte[] SendState(); - public override void HandleMessage(string identifier, byte[] message) - { - if (identifier == "s") - { - ReceiveState(message); - } - } - protected abstract void ReceiveState(byte[] message); } } \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/SyncTransform.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/SyncTransform.cs index fe8cbc4..5cb2b50 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/SyncTransform.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/SyncTransform.cs @@ -1,4 +1,3 @@ -using System.Collections; using System.IO; using UnityEngine; @@ -13,6 +12,7 @@ namespace VelNetUnity { public Vector3 targetPosition; public Quaternion targetRotation; + public float smoothness = .1f; protected override byte[] SendState() { @@ -36,9 +36,9 @@ namespace VelNetUnity private void Update() { - if (owner == null || owner.isLocal) return; - transform.position = Vector3.Lerp(transform.position, targetPosition, .1f); - transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, .1f); + if (IsMine) return; + transform.position = Vector3.Lerp(transform.position, targetPosition, 1 / smoothness / serializationRateHz); + transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 1 / smoothness / serializationRateHz); } } } \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkManager.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs similarity index 78% rename from TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkManager.cs rename to TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs index 65bb271..140d231 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkManager.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading; @@ -8,8 +9,8 @@ using System.Net; namespace VelNetUnity { - [AddComponentMenu("VelNetUnity/VelNet Network Manager")] - public class NetworkManager : MonoBehaviour + [AddComponentMenu("VelNetUnity/VelNet Manager")] + public class VelNetManager : MonoBehaviour { public enum MessageType { @@ -22,9 +23,7 @@ namespace VelNetUnity public string host; public int port; - public static NetworkManager instance; - - #region private members + public static VelNetManager instance; private TcpClient socketConnection; private Socket udpSocket; @@ -36,8 +35,7 @@ namespace VelNetUnity public string room; private int messagesReceived = 0; - public GameObject playerPrefab; - public Dictionary players = new Dictionary(); + public readonly Dictionary players = new Dictionary(); public Action OnJoinedRoom; public Action OnPlayerJoined; @@ -48,8 +46,9 @@ namespace VelNetUnity public List deletedSceneObjects = new List(); public readonly Dictionary objects = new Dictionary(); //maintains a list of all known objects on the server (ones that have ids) private NetworkPlayer masterPlayer; + public static NetworkPlayer LocalPlayer => instance.players.Where(p => p.Value.isLocal).Select(p=>p.Value).FirstOrDefault(); + - #endregion // Use this for initialization public class Message @@ -67,6 +66,7 @@ namespace VelNetUnity { Debug.LogError("Multiple NetworkManagers detected! Bad!", this); } + instance = this; } @@ -106,85 +106,92 @@ namespace VelNetUnity if (m.type == 2) { - //if this message is for me, that means I joined a new room... + // if this message is for me, that means I joined a new room... if (userid == m.sender) { - foreach (KeyValuePair kvp in players) - { - Destroy(kvp.Value.gameObject); - } + // TODO delete all old objects when joining a new room players.Clear(); //we clear the list, but will recreate as we get messages from people in our room if (m.text != "") { - NetworkPlayer player = Instantiate(playerPrefab).GetComponent(); + NetworkPlayer player = new NetworkPlayer + { + isLocal = true, + userid = m.sender, + room = m.text + }; - player.isLocal = true; - player.userid = m.sender; players.Add(userid, player); - player.room = m.text; OnJoinedRoom?.Invoke(player); } } - else //not for me, a player is joining or leaving + else // not for me, a player is joining or leaving { NetworkPlayer me = players[userid]; if (me.room != m.text) { - //we got a left message, kill it - //change ownership of all objects to master - + // we got a left message, kill it + // change ownership of all objects to master foreach (KeyValuePair kvp in objects) { - if (kvp.Value.owner == players[m.sender]) //the owner is the player that left + if (kvp.Value.owner == players[m.sender]) // the owner is the player that left { - if (me.isLocal && me == masterPlayer) //I'm the local master player, so can take ownership immediately + // if this object has locked ownership, delete it + if (kvp.Value.ownershipLocked) + { + // TODO this may check for ownership in the future. We don't need ownership here + DeleteNetworkObject(kvp.Value.networkId); + } + // I'm the local master player, so can take ownership immediately + else if (me.isLocal && me == masterPlayer) { me.TakeOwnership(kvp.Key); } - else if (players[m.sender] == masterPlayer) //the master player left, so everyone should set the owner null (we should get a new master shortly) + // the master player left, so everyone should set the owner null (we should get a new master shortly) + else if (players[m.sender] == masterPlayer) { kvp.Value.owner = null; } } } - Destroy(players[m.sender].gameObject); players.Remove(m.sender); } else { - //we got a join mesage, create it - NetworkPlayer player = Instantiate(playerPrefab).GetComponent(); - player.isLocal = false; - player.room = m.text; - player.userid = m.sender; + // we got a join message, create it + NetworkPlayer player = new NetworkPlayer + { + isLocal = false, + room = m.text, + userid = m.sender + }; players.Add(m.sender, player); OnPlayerJoined?.Invoke(player); } } } - if (m.type == 3) //generic message + if (m.type == 3) // generic message { players[m.sender]?.HandleMessage(m); } - if (m.type == 4) //change master player (this should only happen when the first player joins or if the master player leaves) + if (m.type == 4) // change master player (this should only happen when the first player joins or if the master player leaves) { if (masterPlayer == null) { masterPlayer = players[m.sender]; - //no master player yet, add the scene objects + // no master player yet, add the scene objects for (int i = 0; i < sceneObjects.Length; i++) { sceneObjects[i].networkId = -1 + "-" + i; sceneObjects[i].owner = masterPlayer; - sceneObjects[i].isSceneObject = true; //needed for special handling when deleted + sceneObjects[i].isSceneObject = true; // needed for special handling when deleted objects.Add(sceneObjects[i].networkId, sceneObjects[i]); } } @@ -195,7 +202,7 @@ namespace VelNetUnity masterPlayer.SetAsMasterPlayer(); - //master player should take over any objects that do not have an owner + // master player should take over any objects that do not have an owner foreach (KeyValuePair kvp in objects) { @@ -515,6 +522,34 @@ namespace VelNetUnity { SendNetworkMessage($"5:{groupName}:{string.Join(":", userIds)}"); } + + + public static void InstantiateNetworkObject(string prefabName) + { + NetworkPlayer localPlayer = LocalPlayer; + NetworkObject prefab = instance.prefabs.Find(p => p.name == prefabName); + if (prefab == null) + { + Debug.LogError("Couldn't find a prefab with that name: " + prefabName); + return; + } + NetworkObject newObject = Instantiate(prefab); + newObject.networkId = localPlayer.userid + "-" + localPlayer.lastObjectId++;; + newObject.prefabName = prefabName; + newObject.owner = localPlayer; + instance.objects.Add(newObject.networkId, newObject); + } + + public static void InstantiateNetworkObject(string networkId, string prefabName, NetworkPlayer owner) + { + NetworkObject prefab = instance.prefabs.Find(p => p.name == prefabName); + if (prefab == null) return; + NetworkObject newObject = Instantiate(prefab); + newObject.networkId = networkId; + newObject.prefabName = prefabName; + newObject.owner = owner; + instance.objects.Add(newObject.networkId, newObject); + } public void DeleteNetworkObject(string networkId) { diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkManager.cs.meta b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs.meta similarity index 100% rename from TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkManager.cs.meta rename to TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs.meta diff --git a/TestVelGameServer/Packages/VelNetUnity/Samples~/DissonanceIntegration/VelCommsNetwork.cs b/TestVelGameServer/Packages/VelNetUnity/Samples~/DissonanceIntegration/VelCommsNetwork.cs index b1defa8..5633ee4 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Samples~/DissonanceIntegration/VelCommsNetwork.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Samples~/DissonanceIntegration/VelCommsNetwork.cs @@ -7,6 +7,10 @@ using UnityEngine.Serialization; namespace VelNetUnity { + /// + /// Added to the same object as DissonanceComms component. Only one in the scene. + /// + [RequireComponent(typeof(DissonanceComms))] [AddComponentMenu("VelNetUnity/Dissonance/VelNet Comms Network")] public class VelCommsNetwork : MonoBehaviour, ICommsNetwork { @@ -26,8 +30,8 @@ namespace VelNetUnity private ConnectionStatus _status = ConnectionStatus.Disconnected; private CodecSettings initSettings; public string dissonanceId; - [FormerlySerializedAs("comms")] public DissonanceComms dissonanceComms; - private NetworkManager manager; + [HideInInspector] public DissonanceComms dissonanceComms; + private VelNetManager manager; /// /// listen to this if you want to send voice @@ -40,7 +44,7 @@ namespace VelNetUnity { _status = ConnectionStatus.Connected; dissonanceComms = GetComponent(); - manager = NetworkManager.instance; + manager = VelNetManager.instance; } public void Initialize(string playerName, Rooms rooms, PlayerChannels playerChannels, RoomChannels roomChannels, CodecSettings codecSettings) diff --git a/TestVelGameServer/Packages/VelNetUnity/Samples~/DissonanceIntegration/VelNetDissonancePlayer.cs b/TestVelGameServer/Packages/VelNetUnity/Samples~/DissonanceIntegration/VelNetDissonancePlayer.cs index 9bf72c0..0ea2dba 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Samples~/DissonanceIntegration/VelNetDissonancePlayer.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Samples~/DissonanceIntegration/VelNetDissonancePlayer.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using Dissonance; using UnityEngine; @@ -12,7 +11,7 @@ namespace VelNetUnity /// This should be added to your player object /// [AddComponentMenu("VelNetUnity/Dissonance/VelNet Dissonance Player")] - public class VelNetDissonancePlayer : NetworkObject, IDissonancePlayer + public class VelNetDissonancePlayer : NetworkComponent, IDissonancePlayer { private VelCommsNetwork comms; private bool isSpeaking; @@ -25,20 +24,24 @@ namespace VelNetUnity public string PlayerId => dissonanceID; public Vector3 Position => transform.position; public Quaternion Rotation => transform.rotation; - public NetworkPlayerType Type => owner.isLocal ? NetworkPlayerType.Local : NetworkPlayerType.Remote; + public NetworkPlayerType Type => IsMine ? NetworkPlayerType.Local : NetworkPlayerType.Remote; public bool IsTracking => true; - public Vector3 targetPosition; - public Quaternion targetRotation; - private static readonly List allPlayers = new List(); public List closePlayers = new List(); [Tooltip("Maximum distance to transmit voice data. 0 to always send voice to all players.")] public float maxDistance; - // Start is called before the first frame update - private void Start() + private enum MessageType : byte + { + AudioData, + DissonanceId, + SpeakingState + } + + // This object should not be in the scene at the start. + private void Awake() { comms = FindObjectOfType(); if (comms == null) @@ -53,76 +56,37 @@ namespace VelNetUnity allPlayers.Add(this); } - if (owner.isLocal) + if (IsMine) { SetDissonanceID(comms.dissonanceId); comms.VoiceQueued += SendVoiceData; //we also need to know when other players join, so we can send the dissonance ID again - NetworkManager.instance.OnPlayerJoined += (player) => + VelNetManager.instance.OnPlayerJoined += (player) => { - byte[] b = Encoding.UTF8.GetBytes(dissonanceID); - owner.SendMessage(this, "d", b); + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.DissonanceId); + writer.Write(dissonanceID); + SendBytes(mem.ToArray()); }; - NetworkManager.instance.SetupMessageGroup("close", closePlayers.ToArray()); - } - } - - public override void HandleMessage(string identifier, byte[] message) - { - switch (identifier) - { - case "a": //audio data - { - if (isSpeaking) - { - comms.VoiceReceived(dissonanceID, message); - } - - break; - } - case "d": //dissonance id (player joined) - { - if (dissonanceID == "") // I don't have this yet - { - dissonanceID = Encoding.UTF8.GetString(message); - // tell the comms network that this player joined the channel - comms.SetPlayerJoined(dissonanceID); // tell dissonance - comms.dissonanceComms.TrackPlayerPosition(this); // tell dissonance to track the remote player - } - - break; - } - case "x": // speaking state - { - if (message[0] == 0) - { - comms.SetPlayerStoppedSpeaking(dissonanceID); - isSpeaking = false; - } - else - { - comms.SetPlayerStartedSpeaking(dissonanceID); - isSpeaking = true; - } - - break; - } + VelNetManager.instance.SetupMessageGroup("close", closePlayers.ToArray()); } } private void SendVoiceData(ArraySegment data) { // need to send it - if (owner == null || !owner.isLocal) return; + if (!IsMine) return; using MemoryStream mem = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.AudioData); writer.Write(BitConverter.GetBytes(lastAudioId++)); writer.Write(data.ToArray()); // send voice data unreliably - owner.SendGroupMessage(this, "close", "a", mem.ToArray(), false); + SendBytesToGroup("close", mem.ToArray(), false); } /// @@ -132,8 +96,12 @@ namespace VelNetUnity public void SetDissonanceID(string id) { dissonanceID = id; - byte[] b = Encoding.UTF8.GetBytes(dissonanceID); - owner.SendMessage(this, "d", b); + + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.DissonanceId); + writer.Write(dissonanceID); + SendBytes(mem.ToArray()); comms.dissonanceComms.TrackPlayerPosition(this); } @@ -151,8 +119,7 @@ namespace VelNetUnity // Update is called once per frame private void Update() { - if (owner == null) return; - if (!owner.isLocal) return; + if (!IsMine) return; // handle nearness cutoff if (maxDistance > 0) @@ -166,21 +133,21 @@ namespace VelNetUnity } float dist = Vector3.Distance(p.transform.position, transform.position); - if (dist < maxDistance && !closePlayers.Contains(p.owner.userid)) + if (dist < maxDistance && !closePlayers.Contains(p.Owner.userid)) { - closePlayers.Add(p.owner.userid); + closePlayers.Add(p.Owner.userid); closePlayerListChanged = true; } - else if (dist >= maxDistance && closePlayers.Contains(p.owner.userid)) + else if (dist >= maxDistance && closePlayers.Contains(p.Owner.userid)) { - closePlayers.Remove(p.owner.userid); + closePlayers.Remove(p.Owner.userid); closePlayerListChanged = true; } } if (closePlayerListChanged) { - NetworkManager.instance.SetupMessageGroup("close", closePlayers); + VelNetManager.instance.SetupMessageGroup("close", closePlayers); } } @@ -191,8 +158,12 @@ namespace VelNetUnity if (comms.dissonanceComms.FindPlayer(dissonanceID)?.IsSpeaking != isSpeaking) //unfortunately, there does not seem to be an event for this { isSpeaking = !isSpeaking; - byte[] toSend = { isSpeaking ? (byte)1 : (byte)0 }; - owner.SendMessage(this, "x", toSend); + + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.SpeakingState); + writer.Write(isSpeaking ? (byte)1 : (byte)0); + SendBytes(mem.ToArray()); if (!isSpeaking) { @@ -200,5 +171,52 @@ namespace VelNetUnity } } } + + public override void ReceiveBytes(byte[] message) + { + using MemoryStream mem = new MemoryStream(message); + using BinaryReader reader = new BinaryReader(mem); + + byte identifier = reader.ReadByte(); + switch (identifier) + { + case 0: //audio data + { + if (isSpeaking) + { + comms.VoiceReceived(dissonanceID, message.Skip(1).ToArray()); + } + + break; + } + case 1: //dissonance id (player joined) + { + if (dissonanceID == "") // I don't have this yet + { + dissonanceID = reader.ReadString(); + // tell the comms network that this player joined the channel + comms.SetPlayerJoined(dissonanceID); // tell dissonance + comms.dissonanceComms.TrackPlayerPosition(this); // tell dissonance to track the remote player + } + + break; + } + case 2: // speaking state + { + if (message[0] == 0) + { + comms.SetPlayerStoppedSpeaking(dissonanceID); + isSpeaking = false; + } + else + { + comms.SetPlayerStartedSpeaking(dissonanceID); + isSpeaking = true; + } + + break; + } + } + } } } \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/NetworkGUI.cs b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/NetworkGUI.cs index b5ed7af..034466f 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/NetworkGUI.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/NetworkGUI.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using UnityEngine; +using UnityEngine.Serialization; using UnityEngine.UI; namespace VelNetUnity { public class NetworkGUI : MonoBehaviour { - public NetworkManager networkManager; + [FormerlySerializedAs("networkManager")] public VelNetManager velNetManager; public InputField userInput; public InputField sendInput; public InputField roomInput; @@ -19,7 +20,7 @@ namespace VelNetUnity { if (sendInput.text != "") { - networkManager.SendTo(NetworkManager.MessageType.OTHERS, sendInput.text); + velNetManager.SendTo(VelNetManager.MessageType.OTHERS, sendInput.text); } } @@ -27,7 +28,7 @@ namespace VelNetUnity { if (userInput.text != "") { - networkManager.Login(userInput.text, "nopass"); + velNetManager.Login(userInput.text, "nopass"); } } @@ -35,7 +36,7 @@ namespace VelNetUnity { if (roomInput.text != "") { - networkManager.Join(roomInput.text); + velNetManager.Join(roomInput.text); } } @@ -44,7 +45,7 @@ namespace VelNetUnity { comms = FindObjectOfType(); microphones.AddOptions(new List(Microphone.devices)); - networkManager.MessageReceived += (m) => + velNetManager.MessageReceived += (m) => { string s = m.type + ":" + m.sender + ":" + m.text; messageBuffer.Add(s); diff --git a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/PlayerController.cs b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/PlayerController.cs index 5f72100..aebafb5 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/PlayerController.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/PlayerController.cs @@ -1,16 +1,60 @@ -using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; namespace VelNetUnity { - public class PlayerController : NetworkObject + public class PlayerController : NetworkSerializedObject { public Vector3 targetPosition; public Quaternion targetRotation; - public byte[] GetSyncMessage() + + // Update is called once per frame + private void Update() + { + if (!IsMine) + { + transform.position = Vector3.Lerp(transform.position, targetPosition, .1f); + transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, .1f); + } + else + { + Vector3 movement = new Vector3(); + movement.x += Input.GetAxis("Horizontal"); + movement.y += Input.GetAxis("Vertical"); + movement.z = 0; + transform.Translate(movement * Time.deltaTime); + + if (Input.GetKeyDown(KeyCode.Space)) + { + VelNetManager.InstantiateNetworkObject("TestNetworkedGameObject"); + } + + if (Input.GetKeyDown(KeyCode.BackQuote)) + { + foreach (KeyValuePair kvp in VelNetManager.instance.objects) + { + Owner.TakeOwnership(kvp.Key); + } + } + + if (Input.GetKeyDown(KeyCode.Backspace)) + { + foreach (KeyValuePair kvp in VelNetManager.instance.objects) + { + // don't destroy player objects + if (!kvp.Value.ownershipLocked) + { + Owner.NetworkDestroy(kvp.Key); + } + } + } + } + } + + + protected override byte[] SendState() { using MemoryStream mem = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(mem); @@ -21,80 +65,13 @@ namespace VelNetUnity return mem.ToArray(); } - public override void HandleMessage(string identifier, byte[] message) + protected override void ReceiveState(byte[] message) { - switch (identifier) - { - case "s": // sync message - { - using MemoryStream mem = new MemoryStream(message); - using BinaryReader reader = new BinaryReader(mem); + using MemoryStream mem = new MemoryStream(message); + using BinaryReader reader = new BinaryReader(mem); - targetPosition = reader.ReadVector3(); - targetRotation = reader.ReadQuaternion(); - - break; - } - } - } - - // Start is called before the first frame update - private void Start() - { - // player controller shouldn't change ownership, so we can check here once - if (owner.isLocal) - { - StartCoroutine(SyncBehavior()); - } - } - - - private IEnumerator SyncBehavior() - { - while (true) - { - owner.SendMessage(this, "s", GetSyncMessage()); - yield return new WaitForSeconds(.1f); - } - } - - // Update is called once per frame - private void Update() - { - if (owner != null && !owner.isLocal) - { - transform.position = Vector3.Lerp(transform.position, targetPosition, .1f); - transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, .1f); - } - else if (owner != null && owner.isLocal) - { - Vector3 movement = new Vector3(); - movement.x += Input.GetAxis("Horizontal"); - movement.y += Input.GetAxis("Vertical"); - movement.z = 0; - transform.Translate(movement * Time.deltaTime); - - if (Input.GetKeyDown(KeyCode.Space)) - { - owner.NetworkInstantiate("TestNetworkedGameObject"); - } - - if (Input.GetKeyDown(KeyCode.BackQuote)) - { - foreach (KeyValuePair kvp in NetworkManager.instance.objects) - { - owner.TakeOwnership(kvp.Key); - } - } - - if (Input.GetKeyDown(KeyCode.Backspace)) - { - foreach (KeyValuePair kvp in NetworkManager.instance.objects) - { - owner.NetworkDestroy(kvp.Key); - } - } - } + targetPosition = reader.ReadVector3(); + targetRotation = reader.ReadQuaternion(); } } } \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/PlayerPrefab.prefab b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/PlayerPrefab.prefab index d590dc3..e4b77af 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/PlayerPrefab.prefab +++ b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/PlayerPrefab.prefab @@ -12,7 +12,7 @@ GameObject: - component: {fileID: 8527011532923434593} - component: {fileID: 6854617867369839} - component: {fileID: 5845716565458182149} - - component: {fileID: 5713671764962751473} + - component: {fileID: 9102273340480352682} - component: {fileID: -4404668399269848200} - component: {fileID: 1181612843795795320} m_Layer: 0 @@ -98,7 +98,7 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 1, y: 1, z: 1} m_Center: {x: 0, y: 0, z: 0} ---- !u!114 &5713671764962751473 +--- !u!114 &9102273340480352682 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -107,14 +107,16 @@ MonoBehaviour: m_GameObject: {fileID: 6139051692386484099} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: d8d3b6de660834e3e898725928251405, type: 3} + m_Script: {fileID: 11500000, guid: 5515094c5c544b6b8ed7fd51a86548d4, type: 3} m_Name: m_EditorClassIdentifier: - userid: 0 - username: - room: - isLocal: 0 - lastObjectId: 0 + ownershipLocked: 1 + networkId: + prefabName: + isSceneObject: 0 + syncedComponents: + - {fileID: -4404668399269848200} + - {fileID: 1181612843795795320} --- !u!114 &-4404668399269848200 MonoBehaviour: m_ObjectHideFlags: 0 @@ -127,10 +129,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 89e3af759df774692a566a166b4bf69b, type: 3} m_Name: m_EditorClassIdentifier: - owner: {fileID: 5713671764962751473} - networkId: - prefabName: - isSceneObject: 0 + networkObject: {fileID: 9102273340480352682} + serializationRateHz: 30 targetPosition: {x: 0, y: 0, z: 0} targetRotation: {x: 0, y: 0, z: 0, w: 0} --- !u!114 &1181612843795795320 @@ -145,13 +145,9 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: c773e094326d413bb1bca7f91cbf7f8c, type: 3} m_Name: m_EditorClassIdentifier: - owner: {fileID: 5713671764962751473} - networkId: - prefabName: - isSceneObject: 0 + networkObject: {fileID: 9102273340480352682} dissonanceID: targetPosition: {x: 0, y: 0, z: 0} targetRotation: {x: 0, y: 0, z: 0, w: 0} - allPlayers: [] closePlayers: maxDistance: 0 diff --git a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/TestNetworkedGameObject.prefab b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/TestNetworkedGameObject.prefab index bac19d3..e08d947 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/TestNetworkedGameObject.prefab +++ b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/TestNetworkedGameObject.prefab @@ -9,10 +9,11 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 8565720275311462455} + - component: {fileID: 3951900052977689805} - component: {fileID: 8565720275311462452} m_Layer: 0 m_Name: TestNetworkedGameObject - m_TagString: Untagged + m_TagString: TestSphere m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 @@ -32,6 +33,24 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &3951900052977689805 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8565720275311462453} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5515094c5c544b6b8ed7fd51a86548d4, type: 3} + m_Name: + m_EditorClassIdentifier: + ownershipLocked: 0 + networkId: + prefabName: + isSceneObject: 0 + syncedComponents: + - {fileID: 8565720275311462452} --- !u!114 &8565720275311462452 MonoBehaviour: m_ObjectHideFlags: 0 @@ -44,10 +63,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 3f1f9b0bbd93a484a987c51f1107ebe5, type: 3} m_Name: m_EditorClassIdentifier: - owner: {fileID: 0} - networkId: + networkObject: {fileID: 3951900052977689805} + serializationRateHz: 30 targetPosition: {x: 0, y: 0, z: 0} targetRotation: {x: 0, y: 0, z: 0, w: 0} + smoothness: 0.1 --- !u!1 &8565720276181857625 GameObject: m_ObjectHideFlags: 0 diff --git a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/VelNetMan.cs b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/VelNetMan.cs new file mode 100644 index 0000000..00bf9e7 --- /dev/null +++ b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/VelNetMan.cs @@ -0,0 +1,18 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using VelNetUnity; + +public class VelNetMan : MonoBehaviour +{ + public GameObject playerPrefab; + + // Start is called before the first frame update + private void Start() + { + VelNetManager.instance.OnJoinedRoom += player => + { + VelNetManager.InstantiateNetworkObject(playerPrefab.name); + }; + } +} \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/VelNetMan.cs.meta b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/VelNetMan.cs.meta new file mode 100644 index 0000000..225d370 --- /dev/null +++ b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/VelNetMan.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2fcf036844b060b47b23ad9a1e49eec2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/test.unity b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/test.unity index 2fcffdb..bda34bd 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/test.unity +++ b/TestVelGameServer/Packages/VelNetUnity/Samples~/Example/test.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641275, b: 0.5748172, a: 1} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -624,7 +624,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 7a7db5bc792cd471dbd8039664359eee, type: 3} m_Name: m_EditorClassIdentifier: - networkManager: {fileID: 1099803616} + velNetManager: {fileID: 1099803616} userInput: {fileID: 626742069} sendInput: {fileID: 945446555} roomInput: {fileID: 711524768} @@ -1787,6 +1787,7 @@ GameObject: m_Component: - component: {fileID: 1099803615} - component: {fileID: 1099803616} + - component: {fileID: 1099803613} m_Layer: 0 m_Name: NetworkManager m_TagString: Untagged @@ -1794,6 +1795,19 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!114 &1099803613 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1099803612} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2fcf036844b060b47b23ad9a1e49eec2, type: 3} + m_Name: + m_EditorClassIdentifier: + playerPrefab: {fileID: 6139051692386484099, guid: d4158ab9c4a204cdbba28d3273fc1fb3, type: 3} --- !u!4 &1099803615 Transform: m_ObjectHideFlags: 0 @@ -1825,9 +1839,9 @@ MonoBehaviour: udpConnected: 0 userid: -1 room: - playerPrefab: {fileID: 6139051692386484099, guid: d4158ab9c4a204cdbba28d3273fc1fb3, type: 3} prefabs: - - {fileID: 8565720275311462452, guid: 6e4a023f70e01405e8b249a4488fe319, type: 3} + - {fileID: 3951900052977689805, guid: 6e4a023f70e01405e8b249a4488fe319, type: 3} + - {fileID: 9102273340480352682, guid: d4158ab9c4a204cdbba28d3273fc1fb3, type: 3} sceneObjects: [] deletedSceneObjects: [] connected: 0 @@ -2281,8 +2295,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: dissonanceId: - comms: {fileID: 1434745021} - manager: {fileID: 1099803616} + dissonanceComms: {fileID: 1434745021} --- !u!4 &1434745020 Transform: m_ObjectHideFlags: 0 diff --git a/TestVelGameServer/Packages/VelNetUnity/package.json b/TestVelGameServer/Packages/VelNetUnity/package.json index 50f6464..fcdbec8 100644 --- a/TestVelGameServer/Packages/VelNetUnity/package.json +++ b/TestVelGameServer/Packages/VelNetUnity/package.json @@ -1,7 +1,7 @@ { "name": "edu.uga.engr.vel.velnetunity", "displayName": "VelNetUnity", - "version": "1.0.2", + "version": "1.0.3", "unity": "2019.1", "description": "A custom networking library for Unity.", "keywords": [ diff --git a/TestVelGameServer/ProjectSettings/TagManager.asset b/TestVelGameServer/ProjectSettings/TagManager.asset index 1c92a78..d6a26c3 100644 --- a/TestVelGameServer/ProjectSettings/TagManager.asset +++ b/TestVelGameServer/ProjectSettings/TagManager.asset @@ -3,7 +3,8 @@ --- !u!78 &1 TagManager: serializedVersion: 2 - tags: [] + tags: + - TestSphere layers: - Default - TransparentFX diff --git a/velnet_logo.xcf b/velnet_logo.xcf new file mode 100644 index 0000000000000000000000000000000000000000..ca47f8db30e8854226b76817c01ae725c78d5b7b GIT binary patch literal 103589 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