connection code for client
This commit is contained in:
parent
7b809a2c60
commit
3738f802c5
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@ UserSettings
|
|||||||
*.csproj
|
*.csproj
|
||||||
*.sln
|
*.sln
|
||||||
*.DotSettings
|
*.DotSettings
|
||||||
|
*.user
|
||||||
.idea/
|
.idea/
|
||||||
# some random downloaded assets
|
# some random downloaded assets
|
||||||
Assets/imported/
|
Assets/imported/
|
||||||
|
|||||||
@ -27,7 +27,7 @@ namespace FastArena
|
|||||||
|
|
||||||
public void Kill()
|
public void Kill()
|
||||||
{
|
{
|
||||||
Destroy(this.gameObject);
|
Destroy(gameObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,37 +2,105 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using FastArena.Network.Packets;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
namespace FastArena
|
namespace FastArena.Network
|
||||||
{
|
{
|
||||||
|
public enum ClientStatus
|
||||||
|
{
|
||||||
|
Initialized, Connected, Disconnected
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ServerConnection
|
||||||
|
{
|
||||||
|
public IPAddress Ip;
|
||||||
|
public short PortFirst;
|
||||||
|
public Guid SessionId;
|
||||||
|
public byte[] ServerKey;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Client
|
public static class Client
|
||||||
{
|
{
|
||||||
private static TcpClient _tcp;
|
private static TcpClient _tcp;
|
||||||
private static UdpClient _udp;
|
private static UdpClient _udp;
|
||||||
|
private static byte[] _clientKey;
|
||||||
|
private static ServerConnection _serverConnection;
|
||||||
|
|
||||||
|
public static ClientStatus Status { get; private set; } = ClientStatus.Initialized;
|
||||||
|
|
||||||
public static void SendUserAction()
|
public static void SendUserAction()
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Connect(IPAddress ip, short portFirst)
|
public static void Connect(IPAddress ip, short portFirst)
|
||||||
{
|
{
|
||||||
_tcp = new TcpClient();
|
_tcp = new TcpClient();
|
||||||
_udp = new UdpClient();
|
_udp = new UdpClient();
|
||||||
|
_serverConnection = new ServerConnection()
|
||||||
|
{
|
||||||
|
Ip = ip, PortFirst = portFirst
|
||||||
|
};
|
||||||
|
_udp.Connect(ip, portFirst + 1);
|
||||||
_tcp.BeginConnect(ip, portFirst, TCPConnectCallback, null);
|
_tcp.BeginConnect(ip, portFirst, TCPConnectCallback, null);
|
||||||
_udp.BeginSend(StructBinaryConverter.GetBytes(new ConnectionRequestUDP()))
|
}
|
||||||
|
|
||||||
|
private static void ReadExactly(this TcpClient tcp, byte[] buffer, int offset, int exactSize, int timeoutMs = 5000)
|
||||||
|
{
|
||||||
|
int msPassed = 0;
|
||||||
|
while (tcp.Available < exactSize)
|
||||||
|
{
|
||||||
|
Thread.Sleep(1);
|
||||||
|
msPassed += 1;
|
||||||
|
if (msPassed > timeoutMs)
|
||||||
|
{
|
||||||
|
throw new TimeoutException(
|
||||||
|
$"couldn't get {exactSize} bytes from socket, {tcp.Available} avaliable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int readN = _tcp.GetStream().Read(buffer, offset, exactSize);
|
||||||
|
if (readN != exactSize)
|
||||||
|
throw new Exception($"expected to read {exactSize} bytes, got {readN}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TCPConnectCallback(IAsyncResult ar)
|
private static void TCPConnectCallback(IAsyncResult ar)
|
||||||
{
|
{
|
||||||
_tcp.EndConnect(ar);
|
_tcp.EndConnect(ar);
|
||||||
|
|
||||||
|
// receive first response from server
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
_tcp.ReadExactly(buffer, 0, Marshal.SizeOf<ConnectionResponseTCP>());
|
||||||
|
//TODO: implement decryption
|
||||||
|
var firstResponse = StructBinaryConverter.ReadStruct<ConnectionResponseTCP>(buffer);
|
||||||
|
firstResponse.ValidateResponseCode();
|
||||||
|
_serverConnection.SessionId = firstResponse.sessionId;
|
||||||
|
_serverConnection.ServerKey = firstResponse.serverKey;
|
||||||
|
|
||||||
|
// connect udp socket associated with current session
|
||||||
|
byte[] byteMessage = StructBinaryConverter.GetBytes(new ConnectionRequestUDP(_serverConnection.SessionId));
|
||||||
|
//TODO: implement encryption
|
||||||
|
_udp.Send(byteMessage, byteMessage.Length);
|
||||||
|
|
||||||
|
// receive second response from server
|
||||||
|
IPEndPoint remoteEndpoint = new IPEndPoint(IPAddress.None, 0);
|
||||||
|
byteMessage = _udp.Receive(ref remoteEndpoint);
|
||||||
|
//TODO: implement decryption
|
||||||
|
var secondResponse = StructBinaryConverter.ReadStruct<ConnectionResponseUDP>(byteMessage);
|
||||||
|
secondResponse.ValidateResponseCode();
|
||||||
|
|
||||||
|
//TODO: start tcp read loop
|
||||||
|
//TODO: start udp read loop
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<int, SyncTransform> _syncTransforms = new();
|
|
||||||
|
|
||||||
private static void CreateGameObject(int id, ref CreateObjectPacket packet)
|
private static readonly Dictionary<int, SyncTransform> _syncTransforms = new Dictionary<int, SyncTransform>();
|
||||||
|
|
||||||
|
private static void CreateGameObject(ref CreateObjectPacket packet)
|
||||||
{
|
{
|
||||||
var prefab = Resources.Load(packet.prefabPath, typeof(GameObject));
|
var prefab = Resources.Load(packet.prefabPath, typeof(GameObject));
|
||||||
var go = (GameObject)Object.Instantiate(prefab);
|
var go = (GameObject)Object.Instantiate(prefab);
|
||||||
@ -44,9 +112,9 @@ namespace FastArena
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateObjectTransform(int id, ref TransformUpdatePacket packet)
|
private static void UpdateObjectTransform(ref TransformUpdatePacket packet)
|
||||||
{
|
{
|
||||||
if(!_syncTransforms.TryGetValue(id, out var component))
|
if(!_syncTransforms.TryGetValue(packet.gameObjectId, out var component))
|
||||||
return;
|
return;
|
||||||
component.UpdateTransform(ref packet);
|
component.UpdateTransform(ref packet);
|
||||||
}
|
}
|
||||||
|
|||||||
17
Assets/Network/GameObjectId.cs
Normal file
17
Assets/Network/GameObjectId.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace FastArena.Network
|
||||||
|
{
|
||||||
|
public class GameObjectId : MonoBehaviour
|
||||||
|
{
|
||||||
|
public int Id { get; private set; } = -1;
|
||||||
|
|
||||||
|
internal void _SetId(int packetGameObjectId)
|
||||||
|
{
|
||||||
|
if (Id != -1)
|
||||||
|
throw new Exception($"GameObjectId is already set to {Id}");
|
||||||
|
Id = packetGameObjectId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Assets/Network/GameObjectId.cs.meta
Normal file
3
Assets/Network/GameObjectId.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a0274d4d086949a880c5e0b4a9e5cf20
|
||||||
|
timeCreated: 1752248822
|
||||||
@ -1,21 +1,75 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System;
|
||||||
using System.Security.Cryptography;
|
using System.Runtime.InteropServices;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace FastArena
|
namespace FastArena.Network.Packets
|
||||||
{
|
{
|
||||||
internal struct Hash256
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
internal readonly struct ConnectionRequestTCP
|
internal readonly struct ConnectionRequestTCP
|
||||||
{
|
{
|
||||||
internal readonly PacketHeader header;
|
internal readonly PacketHeader header;
|
||||||
private SHA256 _sha256;
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
|
||||||
|
internal readonly byte[] clientKey;
|
||||||
|
|
||||||
public ConnectionRequestTCP()
|
public ConnectionRequestTCP(byte[] clientKey)
|
||||||
{
|
{
|
||||||
header = new PacketHeader(PacketType.ConnectionRequestTCP);
|
header = new PacketHeader(PacketType.ConnectionRequestTCP);
|
||||||
|
this.clientKey = clientKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal readonly struct ConnectionResponseTCP
|
||||||
|
{
|
||||||
|
internal readonly PacketHeader header;
|
||||||
|
internal readonly StatusCode statusCode;
|
||||||
|
internal readonly Guid sessionId;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
|
||||||
|
internal readonly byte[] serverKey;
|
||||||
|
|
||||||
|
public ConnectionResponseTCP(StatusCode statusCode, Guid sessionId, byte[] serverKey)
|
||||||
|
{
|
||||||
|
header = new PacketHeader(PacketType.ConnectionResponseTCP);
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
this.serverKey = serverKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ValidateResponseCode()
|
||||||
|
{
|
||||||
|
if (statusCode != StatusCode.OK)
|
||||||
|
throw new StatusCodeException(statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal readonly struct ConnectionRequestUDP
|
||||||
|
{
|
||||||
|
internal readonly PacketHeader header;
|
||||||
|
internal readonly Guid sessionId;
|
||||||
|
|
||||||
|
public ConnectionRequestUDP(Guid sessionId)
|
||||||
|
{
|
||||||
|
header = new PacketHeader(PacketType.ConnectionRequestUDP);
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal readonly struct ConnectionResponseUDP
|
||||||
|
{
|
||||||
|
internal readonly PacketHeader header;
|
||||||
|
internal readonly StatusCode statusCode;
|
||||||
|
|
||||||
|
public ConnectionResponseUDP(StatusCode statusCode)
|
||||||
|
{
|
||||||
|
header = new PacketHeader(PacketType.ConnectionResponseUDP);
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ValidateResponseCode()
|
||||||
|
{
|
||||||
|
if (statusCode != StatusCode.OK)
|
||||||
|
throw new StatusCodeException(statusCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace FastArena;
|
namespace FastArena.Network.Packets
|
||||||
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
internal readonly struct CreateObjectPacket
|
internal readonly struct CreateObjectPacket
|
||||||
{
|
{
|
||||||
@ -16,3 +16,4 @@ internal readonly struct CreateObjectPacket
|
|||||||
prefabPath = _prefabPath;
|
prefabPath = _prefabPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace FastArena
|
namespace FastArena.Network.Packets
|
||||||
{
|
{
|
||||||
internal enum PacketType : ushort
|
internal enum PacketType : ushort
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
namespace FastArena;
|
namespace FastArena.Network.Packets
|
||||||
|
{
|
||||||
internal static class PacketParser
|
internal static class PacketParser
|
||||||
{
|
{
|
||||||
internal static PacketType ReadHeader(byte[] data)
|
internal static PacketType ReadHeader(byte[] data)
|
||||||
@ -13,3 +13,4 @@ internal static class PacketParser
|
|||||||
return StructBinaryConverter.ReadStruct<T>(data);
|
return StructBinaryConverter.ReadStruct<T>(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
46
Assets/Network/Packets/StructBinaryConverter.cs
Normal file
46
Assets/Network/Packets/StructBinaryConverter.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace FastArena.Network.Packets
|
||||||
|
{
|
||||||
|
public static class StructBinaryConverter
|
||||||
|
{
|
||||||
|
public static byte[] GetBytes<T>(T s) where T : struct
|
||||||
|
{
|
||||||
|
return GetBytes(ref s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] GetBytes<T>(ref T s) where T : struct
|
||||||
|
{
|
||||||
|
int size = Marshal.SizeOf<T>();
|
||||||
|
// TODO: implement array pool
|
||||||
|
byte[] buffer = new byte[size];
|
||||||
|
// tel GC to not move the buffer in memory
|
||||||
|
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Marshal.StructureToPtr(s, handle.AddrOfPinnedObject(), false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
handle.Free();
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T ReadStruct<T>(byte[] buffer) where T : struct
|
||||||
|
{
|
||||||
|
T s;
|
||||||
|
// tel GC to not move the buffer in memory
|
||||||
|
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
s = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
handle.Free();
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace FastArena;
|
namespace FastArena.Network.Packets
|
||||||
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
internal readonly struct TransformUpdatePacket
|
internal readonly struct TransformUpdatePacket
|
||||||
{
|
{
|
||||||
@ -19,3 +19,4 @@ internal readonly struct TransformUpdatePacket
|
|||||||
rotation = transform.rotation;
|
rotation = transform.rotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@ -2,9 +2,10 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using FastArena.Network.Packets;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace FastArena
|
namespace FastArena.Network
|
||||||
{
|
{
|
||||||
public class ServerConfig
|
public class ServerConfig
|
||||||
{
|
{
|
||||||
@ -33,7 +34,7 @@ namespace FastArena
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<ClientConnection> connections = new List<ClientConnection>();
|
public static readonly List<ClientConnection> Connections = new List<ClientConnection>();
|
||||||
|
|
||||||
public static void Start(ServerConfig config)
|
public static void Start(ServerConfig config)
|
||||||
{
|
{
|
||||||
@ -49,7 +50,7 @@ namespace FastArena
|
|||||||
{
|
{
|
||||||
var tcpClient = _tcp.EndAcceptTcpClient(ar);
|
var tcpClient = _tcp.EndAcceptTcpClient(ar);
|
||||||
var connection = new ClientConnection(tcpClient);
|
var connection = new ClientConnection(tcpClient);
|
||||||
connections.Add(connection);
|
Connections.Add(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UdpReceiveCallback(IAsyncResult ar)
|
private static void UdpReceiveCallback(IAsyncResult ar)
|
||||||
@ -83,7 +84,7 @@ namespace FastArena
|
|||||||
public static void SendTransformUpdate(int id, Transform transform)
|
public static void SendTransformUpdate(int id, Transform transform)
|
||||||
{
|
{
|
||||||
byte[] data = StructBinaryConverter.GetBytes(new TransformUpdatePacket(id, transform));
|
byte[] data = StructBinaryConverter.GetBytes(new TransformUpdatePacket(id, transform));
|
||||||
foreach(var p in connections)
|
foreach(var p in Connections)
|
||||||
{
|
{
|
||||||
UdpSendTo(data, p);
|
UdpSendTo(data, p);
|
||||||
}
|
}
|
||||||
|
|||||||
22
Assets/Network/StatusCode.cs
Normal file
22
Assets/Network/StatusCode.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FastArena.Network
|
||||||
|
{
|
||||||
|
public enum StatusCode
|
||||||
|
{
|
||||||
|
OK,
|
||||||
|
ServerError,
|
||||||
|
AccessNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StatusCodeException : Exception
|
||||||
|
{
|
||||||
|
public StatusCode Code;
|
||||||
|
|
||||||
|
public StatusCodeException(StatusCode code) :
|
||||||
|
base($"Status code {(int)code} ({code})")
|
||||||
|
{
|
||||||
|
Code = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Assets/Network/StatusCode.cs.meta
Normal file
3
Assets/Network/StatusCode.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6ef3bf843e27438cbaae508f73654ec6
|
||||||
|
timeCreated: 1752246290
|
||||||
@ -1,55 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace FastArena
|
|
||||||
{
|
|
||||||
public static class StructBinaryConverter
|
|
||||||
{
|
|
||||||
public static byte[] GetBytes<T>(T s) where T : unmanaged
|
|
||||||
{
|
|
||||||
return GetBytes(ref s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] GetBytes<T>(ref T s) where T : unmanaged
|
|
||||||
{
|
|
||||||
int size = Marshal.SizeOf(typeof(T));
|
|
||||||
byte[] buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(Marshal.SizeOf<T>());
|
|
||||||
|
|
||||||
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
|
||||||
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
fixed (T* s_ptr = &s)
|
|
||||||
{
|
|
||||||
Marshal.Copy((IntPtr)s_ptr, buffer, 0, size);
|
|
||||||
Marshal.StructureToPtr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(msg_struct, handle.AddrOfPinnedObject(), false);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
handle.Free();
|
|
||||||
ArrayPool<byte>.Shared.Return(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T ReadStruct<T>(byte[] buffer) where T : unmanaged
|
|
||||||
{
|
|
||||||
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
T s = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
handle.Free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +1,8 @@
|
|||||||
using System;
|
using FastArena.Network.Packets;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace FastArena
|
namespace FastArena.Network
|
||||||
{
|
{
|
||||||
public class GameObjectId : MonoBehaviour
|
|
||||||
{
|
|
||||||
public int Id { get; private set; } = -1;
|
|
||||||
|
|
||||||
internal void _SetId(int packetGameObjectId)
|
|
||||||
{
|
|
||||||
if (Id != -1)
|
|
||||||
throw new Exception($"GameObjectId is already set to {Id}");
|
|
||||||
Id = packetGameObjectId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Component fetching GameObject's Transform from the server every tick.
|
/// Component fetching GameObject's Transform from the server every tick.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"com.unity.ide.rider": "3.0.28",
|
"com.unity.ide.rider": "3.0.36",
|
||||||
"com.unity.ide.visualstudio": "2.0.22",
|
"com.unity.ide.visualstudio": "2.0.23",
|
||||||
"com.unity.ide.vscode": "1.2.5",
|
"com.unity.ide.vscode": "1.2.5",
|
||||||
"com.unity.textmeshpro": "2.1.6",
|
"com.unity.textmeshpro": "2.1.6",
|
||||||
"com.unity.ugui": "1.0.0",
|
"com.unity.ugui": "1.0.0",
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
"url": "https://packages.unity.com"
|
"url": "https://packages.unity.com"
|
||||||
},
|
},
|
||||||
"com.unity.ide.rider": {
|
"com.unity.ide.rider": {
|
||||||
"version": "3.0.28",
|
"version": "3.0.36",
|
||||||
"depth": 0,
|
"depth": 0,
|
||||||
"source": "registry",
|
"source": "registry",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -17,7 +17,7 @@
|
|||||||
"url": "https://packages.unity.com"
|
"url": "https://packages.unity.com"
|
||||||
},
|
},
|
||||||
"com.unity.ide.visualstudio": {
|
"com.unity.ide.visualstudio": {
|
||||||
"version": "2.0.22",
|
"version": "2.0.23",
|
||||||
"depth": 0,
|
"depth": 0,
|
||||||
"source": "registry",
|
"source": "registry",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user