Repository: RevenantX/NetGameExample Branch: master Commit: a3d06604ef8f Files: 169 Total size: 470.1 KB Directory structure: gitextract_i90u_vd7/ ├── .gitignore ├── .vscode/ │ └── settings.json ├── Assets/ │ ├── Code/ │ │ ├── Client/ │ │ │ ├── ClientLogic.cs │ │ │ ├── ClientLogic.cs.meta │ │ │ ├── ClientPlayer.cs │ │ │ ├── ClientPlayer.cs.meta │ │ │ ├── ClientPlayerManager.cs │ │ │ ├── ClientPlayerManager.cs.meta │ │ │ ├── ClientPlayerView.cs │ │ │ ├── ClientPlayerView.cs.meta │ │ │ ├── IPlayerView.cs │ │ │ ├── IPlayerView.cs.meta │ │ │ ├── RemotePlayer.cs │ │ │ ├── RemotePlayer.cs.meta │ │ │ ├── RemotePlayerView.cs │ │ │ ├── RemotePlayerView.cs.meta │ │ │ ├── ShootEffect.cs │ │ │ ├── ShootEffect.cs.meta │ │ │ ├── UiController.cs │ │ │ └── UiController.cs.meta │ │ ├── Client.meta │ │ ├── Server/ │ │ │ ├── AntilagSystem.cs │ │ │ ├── AntilagSystem.cs.meta │ │ │ ├── ServerLogic.cs │ │ │ ├── ServerLogic.cs.meta │ │ │ ├── ServerPlayer.cs │ │ │ ├── ServerPlayer.cs.meta │ │ │ ├── ServerPlayerManager.cs │ │ │ └── ServerPlayerManager.cs.meta │ │ ├── Server.meta │ │ ├── Shared/ │ │ │ ├── BasePlayer.cs │ │ │ ├── BasePlayer.cs.meta │ │ │ ├── BasePlayerManager.cs │ │ │ ├── BasePlayerManager.cs.meta │ │ │ ├── Collisions.cs │ │ │ ├── Collisions.cs.meta │ │ │ ├── Extensions.cs │ │ │ ├── Extensions.cs.meta │ │ │ ├── GamePackets.cs │ │ │ ├── GamePackets.cs.meta │ │ │ ├── GamePool.cs │ │ │ ├── GamePool.cs.meta │ │ │ ├── GameTimer.cs │ │ │ ├── GameTimer.cs.meta │ │ │ ├── LiteRingBuffer.cs │ │ │ ├── LiteRingBuffer.cs.meta │ │ │ ├── LogicTimer.cs │ │ │ ├── LogicTimer.cs.meta │ │ │ ├── NetworkGeneral.cs │ │ │ └── NetworkGeneral.cs.meta │ │ └── Shared.meta │ ├── Code.meta │ ├── Materials/ │ │ ├── ShootEffectMat.mat │ │ └── ShootEffectMat.mat.meta │ ├── Materials.meta │ ├── Plugins/ │ │ ├── LiteNetLib/ │ │ │ ├── BaseChannel.cs │ │ │ ├── BaseChannel.cs.meta │ │ │ ├── ConnectionRequest.cs │ │ │ ├── ConnectionRequest.cs.meta │ │ │ ├── INetEventListener.cs │ │ │ ├── INetEventListener.cs.meta │ │ │ ├── Layers/ │ │ │ │ ├── Crc32cLayer.cs │ │ │ │ ├── Crc32cLayer.cs.meta │ │ │ │ ├── PacketLayerBase.cs │ │ │ │ ├── PacketLayerBase.cs.meta │ │ │ │ ├── XorEncryptLayer.cs │ │ │ │ └── XorEncryptLayer.cs.meta │ │ │ ├── Layers.meta │ │ │ ├── NatPunchModule.cs │ │ │ ├── NatPunchModule.cs.meta │ │ │ ├── NetConstants.cs │ │ │ ├── NetConstants.cs.meta │ │ │ ├── NetDebug.cs │ │ │ ├── NetDebug.cs.meta │ │ │ ├── NetManager.cs │ │ │ ├── NetManager.cs.meta │ │ │ ├── NetPacket.cs │ │ │ ├── NetPacket.cs.meta │ │ │ ├── NetPacketPool.cs │ │ │ ├── NetPacketPool.cs.meta │ │ │ ├── NetPeer.cs │ │ │ ├── NetPeer.cs.meta │ │ │ ├── NetSocket.cs │ │ │ ├── NetSocket.cs.meta │ │ │ ├── NetStatistics.cs │ │ │ ├── NetStatistics.cs.meta │ │ │ ├── NetUtils.cs │ │ │ ├── NetUtils.cs.meta │ │ │ ├── ReliableChannel.cs │ │ │ ├── ReliableChannel.cs.meta │ │ │ ├── SequencedChannel.cs │ │ │ ├── SequencedChannel.cs.meta │ │ │ ├── Utils/ │ │ │ │ ├── CRC32C.cs │ │ │ │ ├── CRC32C.cs.meta │ │ │ │ ├── FastBitConverter.cs │ │ │ │ ├── FastBitConverter.cs.meta │ │ │ │ ├── INetSerializable.cs │ │ │ │ ├── INetSerializable.cs.meta │ │ │ │ ├── NetDataReader.cs │ │ │ │ ├── NetDataReader.cs.meta │ │ │ │ ├── NetDataWriter.cs │ │ │ │ ├── NetDataWriter.cs.meta │ │ │ │ ├── NetPacketProcessor.cs │ │ │ │ ├── NetPacketProcessor.cs.meta │ │ │ │ ├── NetSerializer.cs │ │ │ │ ├── NetSerializer.cs.meta │ │ │ │ ├── NtpPacket.cs │ │ │ │ ├── NtpPacket.cs.meta │ │ │ │ ├── NtpRequest.cs │ │ │ │ └── NtpRequest.cs.meta │ │ │ └── Utils.meta │ │ └── LiteNetLib.meta │ ├── Plugins.meta │ ├── Prefabs/ │ │ ├── ClientPlayerPrefab.prefab │ │ ├── ClientPlayerPrefab.prefab.meta │ │ ├── RemotePlayerPrefab.prefab │ │ ├── RemotePlayerPrefab.prefab.meta │ │ ├── ShootEffect.prefab │ │ └── ShootEffect.prefab.meta │ ├── Prefabs.meta │ ├── Scenes/ │ │ ├── SampleScene.unity │ │ └── SampleScene.unity.meta │ ├── Scenes.meta │ ├── Sounds/ │ │ ├── death1.ogg │ │ ├── death1.ogg.meta │ │ ├── death2.ogg │ │ ├── death2.ogg.meta │ │ ├── death3.ogg │ │ ├── death3.ogg.meta │ │ ├── hit1.ogg │ │ ├── hit1.ogg.meta │ │ ├── hit2.ogg │ │ ├── hit2.ogg.meta │ │ ├── hit3.ogg │ │ ├── hit3.ogg.meta │ │ ├── shoot1.ogg │ │ ├── shoot1.ogg.meta │ │ ├── shoot2.ogg │ │ ├── shoot2.ogg.meta │ │ ├── shoot3.ogg │ │ └── shoot3.ogg.meta │ └── Sounds.meta ├── LICENSE ├── Packages/ │ ├── manifest.json │ └── packages-lock.json ├── ProjectSettings/ │ ├── AudioManager.asset │ ├── ClusterInputManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── MemorySettings.asset │ ├── NavMeshAreas.asset │ ├── NetworkManager.asset │ ├── PackageManagerSettings.asset │ ├── Physics2DSettings.asset │ ├── PresetManager.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── UnityConnectSettings.asset │ ├── VFXManager.asset │ ├── VersionControlSettings.asset │ ├── XRSettings.asset │ └── boot.config └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # This .gitignore file should be placed at the root of your Unity project directory # # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore # /[Ll]ibrary/ /[Tt]emp/ /[Oo]bj/ /[Bb]uild/ /[Bb]uilds/ /[Ll]ogs/ /[Uu]ser[Ss]ettings/ # MemoryCaptures can get excessive in size. # They also could contain extremely sensitive data /[Mm]emoryCaptures/ # Recordings can get excessive in size /[Rr]ecordings/ # Uncomment this line if you wish to ignore the asset store tools plugin # /[Aa]ssets/AssetStoreTools* # Autogenerated Jetbrains Rider plugin /[Aa]ssets/Plugins/Editor/JetBrains* # Visual Studio cache directory .vs/ # JetBrains Rider cache directory .idea/ # Gradle cache directory .gradle/ # Autogenerated VS/MD/Consulo solution and project files ExportedObj/ .consulo/ *.csproj *.unityproj *.sln *.suo *.tmp *.user *.userprefs *.pidb *.booproj *.svd *.pdb *.mdb *.opendb *.VC.db # Unity3D generated meta files *.pidb.meta *.pdb.meta *.mdb.meta # Unity3D generated file on crash reports sysinfo.txt # Builds *.apk *.aab *.unitypackage *.app # Crashlytics generated file crashlytics-build.properties # Packed Addressables /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* # Temporary auto-generated Android Assets /[Aa]ssets/[Ss]treamingAssets/aa.meta /[Aa]ssets/[Ss]treamingAssets/aa/* ================================================ FILE: .vscode/settings.json ================================================ { "files.exclude": { "**/.DS_Store":true, "**/.git":true, "**/.gitignore":true, "**/.gitmodules":true, "**/*.booproj":true, "**/*.pidb":true, "**/*.suo":true, "**/*.user":true, "**/*.userprefs":true, "**/*.unityproj":true, "**/*.dll":true, "**/*.exe":true, "**/*.pdf":true, "**/*.mid":true, "**/*.midi":true, "**/*.wav":true, "**/*.gif":true, "**/*.ico":true, "**/*.jpg":true, "**/*.jpeg":true, "**/*.png":true, "**/*.psd":true, "**/*.tga":true, "**/*.tif":true, "**/*.tiff":true, "**/*.3ds":true, "**/*.3DS":true, "**/*.fbx":true, "**/*.FBX":true, "**/*.lxo":true, "**/*.LXO":true, "**/*.ma":true, "**/*.MA":true, "**/*.obj":true, "**/*.OBJ":true, "**/*.asset":true, "**/*.cubemap":true, "**/*.flare":true, "**/*.mat":true, "**/*.meta":true, "**/*.prefab":true, "**/*.unity":true, "build/":true, "Build/":true, "Library/":true, "library/":true, "obj/":true, "Obj/":true, "ProjectSettings/":true, "temp/":true, "Temp/":true } } ================================================ FILE: Assets/Code/Client/ClientLogic.cs ================================================ using System; using System.Net; using System.Net.Sockets; using Code.Shared; using LiteNetLib; using LiteNetLib.Utils; using UnityEngine; using UnityEngine.UI; using Random = System.Random; namespace Code.Client { public class ClientLogic : MonoBehaviour, INetEventListener { [SerializeField] private ClientPlayerView _clientPlayerViewPrefab; [SerializeField] private RemotePlayerView _remotePlayerViewPrefab; [SerializeField] private Text _debugText; [SerializeField] private ShootEffect _shootEffectPrefab; private Action _onDisconnected; private GamePool _shootsPool; private NetManager _netManager; private NetDataWriter _writer; private NetPacketProcessor _packetProcessor; private string _userName; private ServerState _cachedServerState; private ShootPacket _cachedShootData; private ushort _lastServerTick; private NetPeer _server; private ClientPlayerManager _playerManager; private int _ping; public static LogicTimer LogicTimer { get; private set; } private ShootEffect ShootEffectContructor() { var eff = Instantiate(_shootEffectPrefab); eff.Init(e => _shootsPool.Put(e)); return eff; } private void Awake() { DontDestroyOnLoad(gameObject); Random r = new Random(); _cachedServerState = new ServerState(); _cachedShootData = new ShootPacket(); _userName = Environment.MachineName + " " + r.Next(100000); LogicTimer = new LogicTimer(OnLogicUpdate); _writer = new NetDataWriter(); _playerManager = new ClientPlayerManager(this); _shootsPool = new GamePool(ShootEffectContructor, 100); _packetProcessor = new NetPacketProcessor(); _packetProcessor.RegisterNestedType((w, v) => w.Put(v), reader => reader.GetVector2()); _packetProcessor.RegisterNestedType(); _packetProcessor.SubscribeReusable(OnPlayerJoined); _packetProcessor.SubscribeReusable(OnJoinAccept); _packetProcessor.SubscribeReusable(OnPlayerLeaved); _netManager = new NetManager(this) { AutoRecycle = true, IPv6Enabled = IPv6Mode.Disabled }; _netManager.Start(); } private void OnLogicUpdate() { _playerManager.LogicUpdate(); } private void Update() { _netManager.PollEvents(); LogicTimer.Update(); if (_playerManager.OurPlayer != null) _debugText.text = string.Format( $"LastServerTick: {_lastServerTick}\n" + $"StoredCommands: {_playerManager.OurPlayer.StoredCommands}\n" + $"Ping: {_ping}"); else _debugText.text = "Disconnected"; } private void OnDestroy() { _netManager.Stop(); } private void OnPlayerJoined(PlayerJoinedPacket packet) { Debug.Log($"[C] Player joined: {packet.UserName}"); var remotePlayer = new RemotePlayer(_playerManager, packet.UserName, packet); var view = RemotePlayerView.Create(_remotePlayerViewPrefab, remotePlayer); _playerManager.AddPlayer(remotePlayer, view); } private void OnServerState() { //skip duplicate or old because we received that packet unreliably if (NetworkGeneral.SeqDiff(_cachedServerState.Tick, _lastServerTick) <= 0) return; _lastServerTick = _cachedServerState.Tick; _playerManager.ApplyServerState(ref _cachedServerState); } private void OnShoot() { var p = _playerManager.GetById(_cachedShootData.FromPlayer); if (p == null || p == _playerManager.OurPlayer) return; SpawnShoot(p.Position, _cachedShootData.Hit); } public void SpawnShoot(Vector2 from, Vector2 to) { var eff = _shootsPool.Get(); eff.Spawn(from, to); } private void OnPlayerLeaved(PlayerLeavedPacket packet) { var player = _playerManager.RemovePlayer(packet.Id); if(player != null) Debug.Log($"[C] Player leaved: {player.Name}"); } private void OnJoinAccept(JoinAcceptPacket packet) { Debug.Log("[C] Join accept. Received player id: " + packet.Id); _lastServerTick = packet.ServerTick; var clientPlayer = new ClientPlayer(this, _playerManager, _userName, packet.Id); var view = ClientPlayerView.Create(_clientPlayerViewPrefab, clientPlayer); _playerManager.AddClientPlayer(clientPlayer, view); } public void SendPacketSerializable(PacketType type, T packet, DeliveryMethod deliveryMethod) where T : INetSerializable { if (_server == null) return; _writer.Reset(); _writer.Put((byte)type); packet.Serialize(_writer); _server.Send(_writer, deliveryMethod); } public void SendPacket(T packet, DeliveryMethod deliveryMethod) where T : class, new() { if (_server == null) return; _writer.Reset(); _writer.Put((byte) PacketType.Serialized); _packetProcessor.Write(_writer, packet); _server.Send(_writer, deliveryMethod); } void INetEventListener.OnPeerConnected(NetPeer peer) { Debug.Log("[C] Connected to server: " + peer.EndPoint); _server = peer; SendPacket(new JoinPacket {UserName = _userName}, DeliveryMethod.ReliableOrdered); LogicTimer.Start(); } void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { _playerManager.Clear(); _server = null; LogicTimer.Stop(); Debug.Log("[C] Disconnected from server: " + disconnectInfo.Reason); if (_onDisconnected != null) { _onDisconnected(disconnectInfo); _onDisconnected = null; } } void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketError) { Debug.Log("[C] NetworkError: " + socketError); } void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) { byte packetType = reader.GetByte(); if (packetType >= NetworkGeneral.PacketTypesCount) return; PacketType pt = (PacketType) packetType; switch (pt) { case PacketType.Spawn: break; case PacketType.ServerState: _cachedServerState.Deserialize(reader); OnServerState(); break; case PacketType.Serialized: _packetProcessor.ReadAllPackets(reader); break; case PacketType.Shoot: _cachedShootData.Deserialize(reader); OnShoot(); break; default: Debug.Log("Unhandled packet: " + pt); break; } } void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { } void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) { _ping = latency; } void INetEventListener.OnConnectionRequest(ConnectionRequest request) { request.Reject(); } public void Connect(string ip, Action onDisconnected) { _onDisconnected = onDisconnected; _netManager.Connect(ip, 10515, "ExampleGame"); } } } ================================================ FILE: Assets/Code/Client/ClientLogic.cs.meta ================================================ fileFormatVersion: 2 guid: 4a77c76b7885899499706477e4e0eeb4 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Code/Client/ClientPlayer.cs ================================================ using Code.Shared; using LiteNetLib; using UnityEngine; namespace Code.Client { public class ClientPlayer : BasePlayer { private PlayerInputPacket _nextCommand; private readonly ClientLogic _clientLogic; private readonly ClientPlayerManager _playerManager; private readonly LiteRingBuffer _predictionPlayerStates; private ServerState _lastServerState; private const int MaxStoredCommands = 60; private bool _firstStateReceived; private int _updateCount; public Vector2 LastPosition { get; private set; } public float LastRotation { get; private set; } public int StoredCommands => _predictionPlayerStates.Count; public ClientPlayer(ClientLogic clientLogic, ClientPlayerManager manager, string name, byte id) : base(manager, name, id) { _playerManager = manager; _predictionPlayerStates = new LiteRingBuffer(MaxStoredCommands); _clientLogic = clientLogic; } public void ReceiveServerState(ServerState serverState, PlayerState ourState) { if (!_firstStateReceived) { if (serverState.LastProcessedCommand == 0) return; _firstStateReceived = true; } if (serverState.Tick == _lastServerState.Tick || serverState.LastProcessedCommand == _lastServerState.LastProcessedCommand) return; _lastServerState = serverState; //sync _position = ourState.Position; _rotation = ourState.Rotation; if (_predictionPlayerStates.Count == 0) return; ushort lastProcessedCommand = serverState.LastProcessedCommand; int diff = NetworkGeneral.SeqDiff(lastProcessedCommand,_predictionPlayerStates.First.Id); //apply prediction if (diff >= 0 && diff < _predictionPlayerStates.Count) { //Debug.Log($"[OK] SP: {serverState.LastProcessedCommand}, OUR: {_predictionPlayerStates.First.Id}, DF:{diff}"); _predictionPlayerStates.RemoveFromStart(diff+1); foreach (var state in _predictionPlayerStates) ApplyInput(state, LogicTimer.FixedDelta); } else if(diff >= _predictionPlayerStates.Count) { Debug.Log($"[C] Player input lag st: {_predictionPlayerStates.First.Id} ls:{lastProcessedCommand} df:{diff}"); //lag _predictionPlayerStates.FastClear(); _nextCommand.Id = lastProcessedCommand; } else { Debug.Log($"[ERR] SP: {serverState.LastProcessedCommand}, OUR: {_predictionPlayerStates.First.Id}, DF:{diff}, STORED: {StoredCommands}"); } } public override void Spawn(Vector2 position) { base.Spawn(position); } public void SetInput(Vector2 velocity, float rotation, bool fire) { _nextCommand.Keys = 0; if(fire) _nextCommand.Keys |= MovementKeys.Fire; if (velocity.x < -0.5f) _nextCommand.Keys |= MovementKeys.Left; if (velocity.x > 0.5f) _nextCommand.Keys |= MovementKeys.Right; if (velocity.y < -0.5f) _nextCommand.Keys |= MovementKeys.Up; if (velocity.y > 0.5f) _nextCommand.Keys |= MovementKeys.Down; _nextCommand.Rotation = rotation; } public override void Update(float delta) { LastPosition = _position; LastRotation = _rotation; _nextCommand.Id = (ushort)((_nextCommand.Id + 1) % NetworkGeneral.MaxGameSequence); _nextCommand.ServerTick = _lastServerState.Tick; ApplyInput(_nextCommand, delta); if (_predictionPlayerStates.IsFull) { _nextCommand.Id = (ushort)(_lastServerState.LastProcessedCommand+1); _predictionPlayerStates.FastClear(); } _predictionPlayerStates.Add(_nextCommand); _updateCount++; if (_updateCount == 3) { _updateCount = 0; foreach (var t in _predictionPlayerStates) _clientLogic.SendPacketSerializable(PacketType.Movement, t, DeliveryMethod.Unreliable); } base.Update(delta); } } } ================================================ FILE: Assets/Code/Client/ClientPlayer.cs.meta ================================================ fileFormatVersion: 2 guid: aff1c62a30b048c9afcb6b247d10acc6 timeCreated: 1552515488 ================================================ FILE: Assets/Code/Client/ClientPlayerManager.cs ================================================ using System.Collections.Generic; using Code.Shared; using UnityEngine; namespace Code.Client { public struct PlayerHandler { public readonly BasePlayer Player; public readonly IPlayerView View; public PlayerHandler(BasePlayer player, IPlayerView view) { Player = player; View = view; } public void Update(float delta) { Player.Update(delta); } } public class ClientPlayerManager : BasePlayerManager { private readonly Dictionary _players; private readonly ClientLogic _clientLogic; private ClientPlayer _clientPlayer; public ClientPlayer OurPlayer => _clientPlayer; public override int Count => _players.Count; public ClientPlayerManager(ClientLogic clientLogic) { _clientLogic = clientLogic; _players = new Dictionary(); } public override IEnumerator GetEnumerator() { foreach (var ph in _players) yield return ph.Value.Player; } public void ApplyServerState(ref ServerState serverState) { for (int i = 0; i < serverState.PlayerStatesCount; i++) { var state = serverState.PlayerStates[i]; if(!_players.TryGetValue(state.Id, out var handler)) return; if (handler.Player == _clientPlayer) { _clientPlayer.ReceiveServerState(serverState, state); } else { var rp = (RemotePlayer)handler.Player; rp.OnPlayerState(state); } } } public override void OnShoot(BasePlayer from, Vector2 to, BasePlayer hit) { if(from == _clientPlayer) _clientLogic.SpawnShoot(from.Position, to); } public BasePlayer GetById(byte id) { return _players.TryGetValue(id, out var ph) ? ph.Player : null; } public BasePlayer RemovePlayer(byte id) { if (_players.TryGetValue(id, out var handler)) { _players.Remove(id); handler.View.Destroy(); } return handler.Player; } public override void LogicUpdate() { foreach (var kv in _players) kv.Value.Update(LogicTimer.FixedDelta); } public void AddClientPlayer(ClientPlayer player, ClientPlayerView view) { _clientPlayer = player; _players.Add(player.Id, new PlayerHandler(player, view)); } public void AddPlayer(RemotePlayer player, RemotePlayerView view) { _players.Add(player.Id, new PlayerHandler(player, view)); } public void Clear() { foreach (var p in _players.Values) p.View.Destroy(); _players.Clear(); } } } ================================================ FILE: Assets/Code/Client/ClientPlayerManager.cs.meta ================================================ fileFormatVersion: 2 guid: b654f7e3a7ba40b0860b89829339a4b5 timeCreated: 1553432137 ================================================ FILE: Assets/Code/Client/ClientPlayerView.cs ================================================ using UnityEngine; namespace Code.Client { public class ClientPlayerView : MonoBehaviour, IPlayerView { [SerializeField] private TextMesh _name; private ClientPlayer _player; private Camera _mainCamera; public static ClientPlayerView Create(ClientPlayerView prefab, ClientPlayer player) { Quaternion rot = Quaternion.Euler(0f, player.Rotation, 0f); var obj = Instantiate(prefab, player.Position, rot); obj._player = player; obj._name.text = player.Name; obj._mainCamera = Camera.main; return obj; } private void Update() { var vert = Input.GetAxis("Vertical"); var horz = Input.GetAxis("Horizontal"); var fire = Input.GetAxis("Fire1"); Vector2 velocty = new Vector2(horz, vert); Vector2 mousePos = _mainCamera.ScreenToWorldPoint(Input.mousePosition); Vector2 dir = mousePos - _player.Position; float rotation = Mathf.Atan2(dir.y, dir.x); _player.SetInput(velocty, rotation, fire > 0f); float lerpT = ClientLogic.LogicTimer.LerpAlpha; transform.position = Vector2.Lerp(_player.LastPosition, _player.Position, lerpT); float angle = Mathf.Lerp(_player.LastRotation, _player.Rotation, lerpT); transform.rotation = Quaternion.Euler(0f, 0f, angle * Mathf.Rad2Deg ); } public void Destroy() { Destroy(gameObject); } } } ================================================ FILE: Assets/Code/Client/ClientPlayerView.cs.meta ================================================ fileFormatVersion: 2 guid: 34c60df172a14e9ba8eeb93c4b0789c8 timeCreated: 1552515543 ================================================ FILE: Assets/Code/Client/IPlayerView.cs ================================================ namespace Code.Client { public interface IPlayerView { void Destroy(); } } ================================================ FILE: Assets/Code/Client/IPlayerView.cs.meta ================================================ fileFormatVersion: 2 guid: 4be69af384c84c7892b27a41f0d59f23 timeCreated: 1553427365 ================================================ FILE: Assets/Code/Client/RemotePlayer.cs ================================================ using Code.Shared; using UnityEngine; namespace Code.Client { public class RemotePlayer : BasePlayer { private readonly LiteRingBuffer _buffer = new LiteRingBuffer(30); private float _receivedTime; private float _timer; private const float BufferTime = 0.1f; //100 milliseconds public RemotePlayer(ClientPlayerManager manager, string name, PlayerJoinedPacket pjPacket) : base(manager, name, pjPacket.InitialPlayerState.Id) { _position = pjPacket.InitialPlayerState.Position; _health = pjPacket.Health; _rotation = pjPacket.InitialPlayerState.Rotation; _buffer.Add(pjPacket.InitialPlayerState); } public override void Spawn(Vector2 position) { _buffer.FastClear(); base.Spawn(position); } public void UpdatePosition(float delta) { if (_receivedTime < BufferTime || _buffer.Count < 2) return; var dataA = _buffer[0]; var dataB = _buffer[1]; float lerpTime = NetworkGeneral.SeqDiff(dataB.Tick, dataA.Tick)*LogicTimer.FixedDelta; float t = _timer / lerpTime; _position = Vector2.Lerp(dataA.Position, dataB.Position, t); _rotation = Mathf.Lerp(dataA.Rotation, dataB.Rotation, t); _timer += delta; if (_timer > lerpTime) { _receivedTime -= lerpTime; _buffer.RemoveFromStart(1); _timer -= lerpTime; } } public void OnPlayerState(PlayerState state) { //old command int diff = NetworkGeneral.SeqDiff(state.Tick, _buffer.Last.Tick); if (diff <= 0) return; _receivedTime += diff * LogicTimer.FixedDelta; if (_buffer.IsFull) { Debug.LogWarning("[C] Remote: Something happened"); //Lag? _receivedTime = 0f; _buffer.FastClear(); } _buffer.Add(state); } } } ================================================ FILE: Assets/Code/Client/RemotePlayer.cs.meta ================================================ fileFormatVersion: 2 guid: 0a420c02346543379e092fc538932775 timeCreated: 1552515495 ================================================ FILE: Assets/Code/Client/RemotePlayerView.cs ================================================ using UnityEngine; namespace Code.Client { public class RemotePlayerView : MonoBehaviour, IPlayerView { private RemotePlayer _player; public static RemotePlayerView Create(RemotePlayerView prefab, RemotePlayer player) { Quaternion rot = Quaternion.Euler(0f, player.Rotation, 0f); var obj = Instantiate(prefab, player.Position, rot); obj._player = player; return obj; } private void Update() { _player.UpdatePosition(Time.deltaTime); transform.position = _player.Position; transform.rotation = Quaternion.Euler(0f, 0f, _player.Rotation * Mathf.Rad2Deg ); } public void Destroy() { Destroy(gameObject); } } } ================================================ FILE: Assets/Code/Client/RemotePlayerView.cs.meta ================================================ fileFormatVersion: 2 guid: 79ae69e7e2264cea80924512389f864d timeCreated: 1552862368 ================================================ FILE: Assets/Code/Client/ShootEffect.cs ================================================ using System; using Code.Shared; using UnityEngine; public class ShootEffect : MonoBehaviour { [SerializeField] private LineRenderer _trailRenderer; [SerializeField] private AudioSource _source; [SerializeField] private AudioSource _target; [SerializeField] private AudioClip[] _shootClips; [SerializeField] private AudioClip[] _hitClips; private GameTimer _aliveTimer = new GameTimer(0.3f); private Action _onDeathCallback; private Vector3[] _positions = new Vector3[2]; public void Init(Action onDeathCallback) { _onDeathCallback = onDeathCallback; gameObject.SetActive(false); } public void Spawn(Vector2 from, Vector2 to) { _source.transform.position = from; _target.transform.position = to; _trailRenderer.transform.position = from; _positions[0] = from; _positions[1] = to; _trailRenderer.SetPositions(_positions); gameObject.SetActive(true); _source.PlayOneShot(_shootClips.GetRandomElement()); _target.PlayOneShot(_hitClips.GetRandomElement()); } private void OnDeath() { _onDeathCallback(this); gameObject.SetActive(false); } private void Update() { _aliveTimer.Update(Time.deltaTime, OnDeath); float t1 = _aliveTimer.Time / _aliveTimer.MaxTime; float t2 = _aliveTimer.Time / (_aliveTimer.MaxTime * 2f); Color a = new Color(1f, 1f, 0f, 1f); Color b = new Color(1f, 1f, 0f, 0f); _trailRenderer.startColor = Color.Lerp(a, b, t1); _trailRenderer.endColor = Color.Lerp(a, b, t2); } } ================================================ FILE: Assets/Code/Client/ShootEffect.cs.meta ================================================ fileFormatVersion: 2 guid: 2b4d80dfb54119b48b368fd5b60c6ab6 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Code/Client/UiController.cs ================================================ using Code.Server; using LiteNetLib; using UnityEngine; using UnityEngine.UI; namespace Code.Client { public class UiController : MonoBehaviour { [SerializeField] private GameObject _uiObject; [SerializeField] private ClientLogic _clientLogic; [SerializeField] private ServerLogic _serverLogic; [SerializeField] private InputField _ipField; [SerializeField] private Text _disconnectInfoField; private void Awake() { _ipField.text = NetUtils.GetLocalIp(LocalAddrType.IPv4); } public void OnHostClick() { _serverLogic.StartServer(); _uiObject.SetActive(false); _clientLogic.Connect("localhost", OnDisconnected); } private void OnDisconnected(DisconnectInfo info) { _uiObject.SetActive(true); _disconnectInfoField.text = info.Reason.ToString(); } public void OnConnectClick() { _uiObject.SetActive(false); _clientLogic.Connect(_ipField.text, OnDisconnected); } } } ================================================ FILE: Assets/Code/Client/UiController.cs.meta ================================================ fileFormatVersion: 2 guid: 637a36ad5bdc47f47a5e5c0de52df2fc MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Code/Client.meta ================================================ fileFormatVersion: 2 guid: 154eed4c1661601469c7de37e38b933a folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Code/Server/AntilagSystem.cs ================================================ using System.Collections.Generic; using UnityEngine; namespace Code.Server { public struct StateInfo { public Vector2 Position; } public class AntilagSystem { private readonly Dictionary[] _storedPositions; private readonly Dictionary _savedStates; private int _currentArrayPos; private ushort _lastTick; private readonly int _maxTicks; public AntilagSystem(int maxTicks, int maxPlayers) { int dictSize = (maxPlayers + 1)*3; _maxTicks = maxTicks; _storedPositions = new Dictionary[maxTicks]; _savedStates = new Dictionary(dictSize); for (int i = 0; i < _storedPositions.Length; i++) { _storedPositions[i] = new Dictionary(dictSize); } } private Dictionary GetStates(ushort tick) { if (tick < _lastTick - _maxTicks || _lastTick < _maxTicks) return null; return _storedPositions[(_currentArrayPos - _lastTick + tick - 1) % _maxTicks]; } public void StorePositions(ushort serverTick, ServerPlayer[] players) { var currentDict = _storedPositions[_currentArrayPos]; currentDict.Clear(); foreach (var p in players) { if (!p.IsAlive) continue; StateInfo si = new StateInfo { Position = p.Position }; currentDict.Add(p.AssociatedPeer.Id, si); } _lastTick = serverTick; _currentArrayPos = (_currentArrayPos + 1) % _maxTicks; } public bool TryApplyAntilag(ServerPlayer[] players, ushort tick, int exceptId) { var antilagStates = GetStates(tick); if (antilagStates == null) return false; _savedStates.Clear(); foreach (var p in players) { int id = p.AssociatedPeer.Id; if (id == exceptId) continue; //Save current states StateInfo state = new StateInfo { Position = p.Position }; //Console.WriteLine("Save state {0} = {1} {2}", id, state.Position, state.Pose); _savedStates[id] = state; //Apply antilag StateInfo antilagState; if (antilagStates.TryGetValue(id, out antilagState)) { //serverController.Player.ChangeState(antilagState.Position, antilagState.Pose, true); } } return true; } public void RevertAntilag(ServerPlayer[] players) { //Revert states foreach (var p in players) { StateInfo state; if (_savedStates.TryGetValue(p.AssociatedPeer.Id, out state)) { //Console.WriteLine("Load state {0} = {1} {2}", serverController.ServerId, state.Position, state.Pose); //p.ChangeState(state.Position, state.Pose, true); } } } } } ================================================ FILE: Assets/Code/Server/AntilagSystem.cs.meta ================================================ fileFormatVersion: 2 guid: 601c7fe5599c477ea58e6af0a0d7e91c timeCreated: 1552862682 ================================================ FILE: Assets/Code/Server/ServerLogic.cs ================================================ using System.Net; using System.Net.Sockets; using Code.Shared; using LiteNetLib; using LiteNetLib.Utils; using UnityEngine; namespace Code.Server { public class ServerLogic : MonoBehaviour, INetEventListener { private NetManager _netManager; private NetPacketProcessor _packetProcessor; public const int MaxPlayers = 64; private LogicTimer _logicTimer; private readonly NetDataWriter _cachedWriter = new NetDataWriter(); private ushort _serverTick; private ServerPlayerManager _playerManager; private PlayerInputPacket _cachedCommand = new PlayerInputPacket(); private ServerState _serverState; public ushort Tick => _serverTick; public void StartServer() { if (_netManager.IsRunning) return; _netManager.Start(10515); _logicTimer.Start(); } private void Awake() { DontDestroyOnLoad(gameObject); _logicTimer = new LogicTimer(OnLogicUpdate); _packetProcessor = new NetPacketProcessor(); _playerManager = new ServerPlayerManager(this); //register auto serializable vector2 _packetProcessor.RegisterNestedType((w, v) => w.Put(v), r => r.GetVector2()); //register auto serializable PlayerState _packetProcessor.RegisterNestedType(); _packetProcessor.SubscribeReusable(OnJoinReceived); _netManager = new NetManager(this) { AutoRecycle = true }; } private void OnDestroy() { _netManager.Stop(); _logicTimer.Stop(); } private void OnLogicUpdate() { _serverTick = (ushort)((_serverTick + 1) % NetworkGeneral.MaxGameSequence); _playerManager.LogicUpdate(); if (_serverTick % 2 == 0) { _serverState.Tick = _serverTick; _serverState.PlayerStates = _playerManager.PlayerStates; int pCount = _playerManager.Count; foreach(ServerPlayer p in _playerManager) { int statesMax = p.AssociatedPeer.GetMaxSinglePacketSize(DeliveryMethod.Unreliable) - ServerState.HeaderSize; statesMax /= PlayerState.Size; for (int s = 0; s < (pCount-1)/statesMax + 1; s++) { //TODO: divide _serverState.LastProcessedCommand = p.LastProcessedCommandId; _serverState.PlayerStatesCount = pCount; _serverState.StartState = s * statesMax; p.AssociatedPeer.Send(WriteSerializable(PacketType.ServerState, _serverState), DeliveryMethod.Unreliable); } } } } private void Update() { _netManager.PollEvents(); _logicTimer.Update(); } private NetDataWriter WriteSerializable(PacketType type, T packet) where T : struct, INetSerializable { _cachedWriter.Reset(); _cachedWriter.Put((byte) type); packet.Serialize(_cachedWriter); return _cachedWriter; } private NetDataWriter WritePacket(T packet) where T : class, new() { _cachedWriter.Reset(); _cachedWriter.Put((byte) PacketType.Serialized); _packetProcessor.Write(_cachedWriter, packet); return _cachedWriter; } private void OnJoinReceived(JoinPacket joinPacket, NetPeer peer) { Debug.Log("[S] Join packet received: " + joinPacket.UserName); var player = new ServerPlayer(_playerManager, joinPacket.UserName, peer); _playerManager.AddPlayer(player); player.Spawn(new Vector2(Random.Range(-2f, 2f), Random.Range(-2f, 2f))); //Send join accept var ja = new JoinAcceptPacket { Id = player.Id, ServerTick = _serverTick }; peer.Send(WritePacket(ja), DeliveryMethod.ReliableOrdered); //Send to old players info about new player var pj = new PlayerJoinedPacket { UserName = joinPacket.UserName, NewPlayer = true, InitialPlayerState = player.NetworkState, ServerTick = _serverTick }; _netManager.SendToAll(WritePacket(pj), DeliveryMethod.ReliableOrdered, peer); //Send to new player info about old players pj.NewPlayer = false; foreach(ServerPlayer otherPlayer in _playerManager) { if(otherPlayer == player) continue; pj.UserName = otherPlayer.Name; pj.InitialPlayerState = otherPlayer.NetworkState; peer.Send(WritePacket(pj), DeliveryMethod.ReliableOrdered); } } private void OnInputReceived(NetPacketReader reader, NetPeer peer) { if (peer.Tag == null) return; _cachedCommand.Deserialize(reader); var player = (ServerPlayer) peer.Tag; bool antilagApplied = _playerManager.EnableAntilag(player); player.ApplyInput(_cachedCommand, LogicTimer.FixedDelta); if(antilagApplied) _playerManager.DisableAntilag(); } public void SendShoot(ref ShootPacket sp) { _netManager.SendToAll(WriteSerializable(PacketType.Shoot, sp), DeliveryMethod.ReliableUnordered); } void INetEventListener.OnPeerConnected(NetPeer peer) { Debug.Log("[S] Player connected: " + peer.EndPoint); } void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { Debug.Log("[S] Player disconnected: " + disconnectInfo.Reason); if (peer.Tag != null) { byte playerId = (byte)peer.Id; if (_playerManager.RemovePlayer(playerId)) { var plp = new PlayerLeavedPacket { Id = (byte)peer.Id }; _netManager.SendToAll(WritePacket(plp), DeliveryMethod.ReliableOrdered); } } } void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketError) { Debug.Log("[S] NetworkError: " + socketError); } void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) { byte packetType = reader.GetByte(); if (packetType >= NetworkGeneral.PacketTypesCount) return; PacketType pt = (PacketType) packetType; switch (pt) { case PacketType.Movement: OnInputReceived(reader, peer); break; case PacketType.Serialized: _packetProcessor.ReadAllPackets(reader, peer); break; default: Debug.Log("Unhandled packet: " + pt); break; } } void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { } void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) { if (peer.Tag != null) { var p = (ServerPlayer) peer.Tag; p.Ping = latency; } } void INetEventListener.OnConnectionRequest(ConnectionRequest request) { request.AcceptIfKey("ExampleGame"); } } } ================================================ FILE: Assets/Code/Server/ServerLogic.cs.meta ================================================ fileFormatVersion: 2 guid: 1e51c98f0b54146429c775c426760c8a MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Code/Server/ServerPlayer.cs ================================================ using Code.Shared; using LiteNetLib; using UnityEngine; namespace Code.Server { public class ServerPlayer : BasePlayer { private readonly ServerPlayerManager _playerManager; public readonly NetPeer AssociatedPeer; public PlayerState NetworkState; public ushort LastProcessedCommandId { get; private set; } public ServerPlayer(ServerPlayerManager playerManager, string name, NetPeer peer) : base(playerManager, name, (byte)peer.Id) { _playerManager = playerManager; peer.Tag = this; AssociatedPeer = peer; NetworkState = new PlayerState {Id = (byte) peer.Id}; } public override void ApplyInput(PlayerInputPacket command, float delta) { if (NetworkGeneral.SeqDiff(command.Id, LastProcessedCommandId) <= 0) return; LastProcessedCommandId = command.Id; base.ApplyInput(command, delta); } public override void Update(float delta) { base.Update(delta); NetworkState.Position = _position; NetworkState.Rotation = _rotation; NetworkState.Tick = LastProcessedCommandId; //Draw cross as server player const float sz = 0.1f; Debug.DrawLine( new Vector2(Position.x - sz, Position.y ), new Vector2(Position.x + sz, Position.y ), Color.white); Debug.DrawLine( new Vector2(Position.x, Position.y - sz ), new Vector2(Position.x, Position.y + sz ), Color.white); } } } ================================================ FILE: Assets/Code/Server/ServerPlayer.cs.meta ================================================ fileFormatVersion: 2 guid: 8ad17e029d4b43eeaedf4a29939994e3 timeCreated: 1552515641 ================================================ FILE: Assets/Code/Server/ServerPlayerManager.cs ================================================ using System.Collections.Generic; using Code.Shared; using LiteNetLib; using UnityEngine; namespace Code.Server { public class ServerPlayerManager : BasePlayerManager { private readonly ServerLogic _serverLogic; private readonly ServerPlayer[] _players; private readonly AntilagSystem _antilagSystem; public readonly PlayerState[] PlayerStates; private int _playersCount; public override int Count => _playersCount; public ServerPlayerManager(ServerLogic serverLogic) { _serverLogic = serverLogic; _antilagSystem = new AntilagSystem(60, ServerLogic.MaxPlayers); _players = new ServerPlayer[ServerLogic.MaxPlayers]; PlayerStates = new PlayerState[ServerLogic.MaxPlayers]; } public bool EnableAntilag(ServerPlayer forPlayer) { return _antilagSystem.TryApplyAntilag(_players, _serverLogic.Tick, forPlayer.AssociatedPeer.Id); } public void DisableAntilag() { _antilagSystem.RevertAntilag(_players); } public override IEnumerator GetEnumerator() { int i = 0; while (i < _playersCount) { yield return _players[i]; i++; } } public override void OnShoot(BasePlayer from, Vector2 to, BasePlayer hit) { var serverPlayer = (ServerPlayer) from; ShootPacket sp = new ShootPacket { FromPlayer = serverPlayer.Id, CommandId = serverPlayer.LastProcessedCommandId, ServerTick = _serverLogic.Tick, Hit = to }; _serverLogic.SendShoot(ref sp); } public void AddPlayer(ServerPlayer player) { for (int i = 0; i < _playersCount; i++) { if (_players[i].Id == player.Id) { _players[i] = player; return; } } _players[_playersCount] = player; _playersCount++; } public override void LogicUpdate() { for (int i = 0; i < _playersCount; i++) { var p = _players[i]; p.Update(LogicTimer.FixedDelta); PlayerStates[i] = p.NetworkState; } } public bool RemovePlayer(byte playerId) { for (int i = 0; i < _playersCount; i++) { if (_players[i].Id == playerId) { _playersCount--; _players[i] = _players[_playersCount]; _players[_playersCount] = null; return true; } } return false; } } } ================================================ FILE: Assets/Code/Server/ServerPlayerManager.cs.meta ================================================ fileFormatVersion: 2 guid: a4f821c063aa411fa470499231c8553a timeCreated: 1553431094 ================================================ FILE: Assets/Code/Server.meta ================================================ fileFormatVersion: 2 guid: 26e8c150dc35a224e965b8abec2e0cfc folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Code/Shared/BasePlayer.cs ================================================ using UnityEngine; namespace Code.Shared { public abstract class BasePlayer { public readonly string Name; private float _speed = 3f; private GameTimer _shootTimer = new GameTimer(0.2f); private BasePlayerManager _playerManager; protected Vector2 _position; protected float _rotation; protected byte _health; public const float Radius = 0.5f; public bool IsAlive => _health > 0; public byte Health => _health; public Vector2 Position => _position; public float Rotation => _rotation; public readonly byte Id; public int Ping; protected BasePlayer(BasePlayerManager playerManager, string name, byte id) { Id = id; Name = name; _playerManager = playerManager; } public virtual void Spawn(Vector2 position) { _position = position; _rotation = 0; _health = 100; } private void Shoot() { const float MaxLength = 20f; Vector2 dir = new Vector2(Mathf.Cos(_rotation), Mathf.Sin(_rotation)); var player = _playerManager.CastToPlayer(_position, dir, MaxLength, this); Vector2 target = _position + dir * (player != null ? Vector2.Distance(_position, player._position) : MaxLength); _playerManager.OnShoot(this, target, player); } public virtual void ApplyInput(PlayerInputPacket command, float delta) { Vector2 velocity = Vector2.zero; if ((command.Keys & MovementKeys.Up) != 0) velocity.y = -1f; if ((command.Keys & MovementKeys.Down) != 0) velocity.y = 1f; if ((command.Keys & MovementKeys.Left) != 0) velocity.x = -1f; if ((command.Keys & MovementKeys.Right) != 0) velocity.x = 1f; _position += velocity.normalized * _speed * delta; _rotation = command.Rotation; if ((command.Keys & MovementKeys.Fire) != 0) { if (_shootTimer.IsTimeElapsed) { _shootTimer.Reset(); Shoot(); } } } public virtual void Update(float delta) { _shootTimer.UpdateAsCooldown(delta); } } } ================================================ FILE: Assets/Code/Shared/BasePlayer.cs.meta ================================================ fileFormatVersion: 2 guid: 0b3166ab83ce8bc42995be041437971b MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Code/Shared/BasePlayerManager.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Code.Shared { public abstract class BasePlayerManager : IEnumerable { public abstract IEnumerator GetEnumerator(); public abstract int Count { get; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public BasePlayer CastToPlayer(Vector2 from, Vector2 dir, float length, BasePlayer exclude) { BasePlayer result = null; Vector2 target = from + dir * length; foreach(var p in this) { if(p == exclude) continue; if (Collisions.CheckIntersection(from.x, from.y, target.x, target.y, p)) { //TODO: check near if(result == null) result = p; } } return result; } public abstract void LogicUpdate(); public abstract void OnShoot(BasePlayer from, Vector2 to, BasePlayer hit); } } ================================================ FILE: Assets/Code/Shared/BasePlayerManager.cs.meta ================================================ fileFormatVersion: 2 guid: 464641a9645748548b39dcb0afed1fd4 timeCreated: 1553430803 ================================================ FILE: Assets/Code/Shared/Collisions.cs ================================================ namespace Code.Shared { public static class Collisions { public static bool CheckIntersection(float x1, float y1, float x2, float y2, BasePlayer player) { float cx = player.Position.x; float cy = player.Position.y; float distX = x2-x1; float distY = y2-y1; float lineLenSqr = distX * distX + distY * distY; float dot = ( (cx-x1)*distX + (cy-y1)*distY ) / lineLenSqr; float closestX = x1 + dot * distX; float closestY = y1 + dot * distY; float dcx1 = closestX - x1; float dcy1 = closestY - y1; float dcx2 = closestX - x2; float dcy2 = closestY - y2; float distToLineSqr1 = dcx1 * dcx1 + dcy1 * dcy1; float distToLineSqr2 = dcx2 * dcx2 + dcy2 * dcy2; if (distToLineSqr1 > lineLenSqr || distToLineSqr2 > lineLenSqr) return false; distX = closestX - cx; distY = closestY - cy; return distX*distX + distY*distY <= BasePlayer.Radius * BasePlayer.Radius; } } } ================================================ FILE: Assets/Code/Shared/Collisions.cs.meta ================================================ fileFormatVersion: 2 guid: 22917542b6524d9085c4f7323d79e532 timeCreated: 1553428656 ================================================ FILE: Assets/Code/Shared/Extensions.cs ================================================ using LiteNetLib.Utils; using UnityEngine; namespace Code.Shared { public static class Extensions { public static void Put(this NetDataWriter writer, Vector2 vector) { writer.Put(vector.x); writer.Put(vector.y); } public static Vector2 GetVector2(this NetDataReader reader) { Vector2 v; v.x = reader.GetFloat(); v.y = reader.GetFloat(); return v; } public static T GetRandomElement(this T[] array) { return array[Random.Range(0, array.Length)]; } } } ================================================ FILE: Assets/Code/Shared/Extensions.cs.meta ================================================ fileFormatVersion: 2 guid: 774af25bfc0f472287bc2812673fa9d0 timeCreated: 1552513976 ================================================ FILE: Assets/Code/Shared/GamePackets.cs ================================================ using System; using LiteNetLib.Utils; using UnityEngine; namespace Code.Shared { public enum PacketType : byte { Movement, Spawn, ServerState, Serialized, Shoot } //Auto serializable packets public class JoinPacket { public string UserName { get; set; } } public class JoinAcceptPacket { public byte Id { get; set; } public ushort ServerTick { get; set; } } public class PlayerJoinedPacket { public string UserName { get; set; } public bool NewPlayer { get; set; } public byte Health { get; set; } public ushort ServerTick { get; set; } public PlayerState InitialPlayerState { get; set; } } public class PlayerLeavedPacket { public byte Id { get; set; } } //Manual serializable packets public struct SpawnPacket : INetSerializable { public long PlayerId; public Vector2 Position; public void Serialize(NetDataWriter writer) { writer.Put(PlayerId); writer.Put(Position); } public void Deserialize(NetDataReader reader) { PlayerId = reader.GetLong(); Position = reader.GetVector2(); } } [Flags] public enum MovementKeys : byte { Left = 1 << 1, Right = 1 << 2, Up = 1 << 3, Down = 1 << 4, Fire = 1 << 5 } public struct ShootPacket : INetSerializable { public byte FromPlayer; public ushort CommandId; public Vector2 Hit; public ushort ServerTick; public void Serialize(NetDataWriter writer) { writer.Put(FromPlayer); writer.Put(CommandId); writer.Put(Hit); writer.Put(ServerTick); } public void Deserialize(NetDataReader reader) { FromPlayer = reader.GetByte(); CommandId = reader.GetUShort(); Hit = reader.GetVector2(); ServerTick = reader.GetUShort(); } } public struct PlayerInputPacket : INetSerializable { public ushort Id; public MovementKeys Keys; public float Rotation; public ushort ServerTick; public void Serialize(NetDataWriter writer) { writer.Put(Id); writer.Put((byte)Keys); writer.Put(Rotation); writer.Put(ServerTick); } public void Deserialize(NetDataReader reader) { Id = reader.GetUShort(); Keys = (MovementKeys)reader.GetByte(); Rotation = reader.GetFloat(); ServerTick = reader.GetUShort(); } } public struct PlayerState : INetSerializable { public byte Id; public Vector2 Position; public float Rotation; public ushort Tick; public const int Size = 1 + 8 + 4 + 2; public void Serialize(NetDataWriter writer) { writer.Put(Id); writer.Put(Position); writer.Put(Rotation); writer.Put(Tick); } public void Deserialize(NetDataReader reader) { Id = reader.GetByte(); Position = reader.GetVector2(); Rotation = reader.GetFloat(); Tick = reader.GetUShort(); } } public struct ServerState : INetSerializable { public ushort Tick; public ushort LastProcessedCommand; public int PlayerStatesCount; public int StartState; //server only public PlayerState[] PlayerStates; //tick public const int HeaderSize = sizeof(ushort)*2; public void Serialize(NetDataWriter writer) { writer.Put(Tick); writer.Put(LastProcessedCommand); for (int i = 0; i < PlayerStatesCount; i++) PlayerStates[StartState + i].Serialize(writer); } public void Deserialize(NetDataReader reader) { Tick = reader.GetUShort(); LastProcessedCommand = reader.GetUShort(); PlayerStatesCount = reader.AvailableBytes / PlayerState.Size; if (PlayerStates == null || PlayerStates.Length < PlayerStatesCount) PlayerStates = new PlayerState[PlayerStatesCount]; for (int i = 0; i < PlayerStatesCount; i++) PlayerStates[i].Deserialize(reader); } } } ================================================ FILE: Assets/Code/Shared/GamePackets.cs.meta ================================================ fileFormatVersion: 2 guid: ea1f70e104cd418e8d5e6dc52b99ffb5 timeCreated: 1552514144 ================================================ FILE: Assets/Code/Shared/GamePool.cs ================================================ using System; namespace Code.Shared { public class GamePool where T : class { private readonly T[] _pool; private readonly Func _creator; private int _count; public GamePool(Func creator) : this(creator, 8) { } public GamePool(Func creator, int capacity) { _pool = new T[capacity]; _creator = creator; } public T Get() { if (_count > 0) { _count--; var result = _pool[_count]; _pool[_count] = default; return result; } return _creator(); } public void Put(T gameObject) { _pool[_count] = gameObject; _count++; } } } ================================================ FILE: Assets/Code/Shared/GamePool.cs.meta ================================================ fileFormatVersion: 2 guid: 26c97d2cfdfd4ba48e7b065fd13e9c6b timeCreated: 1553454288 ================================================ FILE: Assets/Code/Shared/GameTimer.cs ================================================ using System; namespace Code.Shared { public struct GameTimer { private float _maxTime; private float _time; public bool IsTimeElapsed => _time >= _maxTime; public float Time => _time; public float MaxTime { get => _maxTime; set => _maxTime = value; } public GameTimer(float maxTime) { _maxTime = maxTime; _time = 0f; } public void Reset() { _time = 0f; } public void UpdateAsCooldown(float delta) { _time += delta; } public void Update(float delta, Action onUpdate) { _time += delta; while (_time >= _maxTime) { _time -= _maxTime; onUpdate(); } } } } ================================================ FILE: Assets/Code/Shared/GameTimer.cs.meta ================================================ fileFormatVersion: 2 guid: d0c4fc1f8e504c7f910bd163300866e8 timeCreated: 1553426365 ================================================ FILE: Assets/Code/Shared/LiteRingBuffer.cs ================================================ using System; using System.Collections; using System.Collections.Generic; namespace Code.Shared { public class LiteRingBuffer : IEnumerable { private readonly T[] _elements; private int _start; private int _end; private int _count; private readonly int _capacity; public T this[int i] => _elements[(_start + i) % _capacity]; public LiteRingBuffer(int count) { _elements = new T[count]; _capacity = count; } public void Add(T element) { if(_count == _capacity) throw new ArgumentException(); _elements[_end] = element; _end = (_end + 1) % _capacity; _count++; } public void FastClear() { _start = 0; _end = 0; _count = 0; } public int Count => _count; public T First => _elements[_start]; public T Last => _elements[(_start+_count-1)%_capacity]; public bool IsFull => _count == _capacity; public void RemoveFromStart(int count) { if(count > _capacity || count > _count) throw new ArgumentException(); _start = (_start + count) % _capacity; _count -= count; } public IEnumerator GetEnumerator() { int counter = _start; while (counter != _end) { yield return _elements[counter]; counter = (counter + 1) % _capacity; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: Assets/Code/Shared/LiteRingBuffer.cs.meta ================================================ fileFormatVersion: 2 guid: de5c9b10e72c4672ad210ea9f2a7ccad timeCreated: 1553348923 ================================================ FILE: Assets/Code/Shared/LogicTimer.cs ================================================ using System; using System.Diagnostics; namespace Code.Shared { public class LogicTimer { public const float FramesPerSecond = 30.0f; public const float FixedDelta = 1.0f / FramesPerSecond; private double _accumulator; private long _lastTime; private readonly Stopwatch _stopwatch; private readonly Action _action; public float LerpAlpha => (float)_accumulator/FixedDelta; public LogicTimer(Action action) { _stopwatch = new Stopwatch(); _action = action; } public void Start() { _lastTime = 0; _accumulator = 0.0; _stopwatch.Restart(); } public void Stop() { _stopwatch.Stop(); } public void Update() { long elapsedTicks = _stopwatch.ElapsedTicks; _accumulator += (double)(elapsedTicks - _lastTime)/Stopwatch.Frequency; _lastTime = elapsedTicks; while (_accumulator >= FixedDelta) { _action(); _accumulator -= FixedDelta; } } } } ================================================ FILE: Assets/Code/Shared/LogicTimer.cs.meta ================================================ fileFormatVersion: 2 guid: c2cf8177f8ed49a4badb018a3dc7c710 timeCreated: 1552521012 ================================================ FILE: Assets/Code/Shared/NetworkGeneral.cs ================================================ using System; namespace Code.Shared { public static class NetworkGeneral { public const int ProtocolId = 1; public static readonly int PacketTypesCount = Enum.GetValues(typeof(PacketType)).Length; public const int MaxGameSequence = 1024; public const int HalfMaxGameSequence = MaxGameSequence / 2; public static int SeqDiff(int a, int b) { return Diff(a, b, HalfMaxGameSequence); } public static int Diff(int a, int b, int halfMax) { return (a - b + halfMax*3) % (halfMax*2) - halfMax; } } } ================================================ FILE: Assets/Code/Shared/NetworkGeneral.cs.meta ================================================ fileFormatVersion: 2 guid: b8a3ef5ee59f4b75ae44ec2e14551cc7 timeCreated: 1552514562 ================================================ FILE: Assets/Code/Shared.meta ================================================ fileFormatVersion: 2 guid: 5b5e02f68e288b14ba1ca61ba84a6e1b folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Code.meta ================================================ fileFormatVersion: 2 guid: 0d17f5c4499ee5c4498aa660551e4658 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Materials/ShootEffectMat.mat ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!21 &2100000 Material: serializedVersion: 6 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: ShootEffectMat m_Shader: {fileID: 211, guid: 0000000000000000f000000000000000, type: 0} m_ShaderKeywords: _ALPHABLEND_ON _COLORCOLOR_ON m_LightmapFlags: 0 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: 3000 stringTagMap: RenderType: Transparent disabledShaderPasses: - ALWAYS m_SavedProperties: serializedVersion: 3 m_TexEnvs: - _AlphaTex: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _BumpMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _DetailAlbedoMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _DetailMask: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _DetailNormalMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _EmissionMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _MainTex: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _MetallicGlossMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _OcclusionMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _ParallaxMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} m_Floats: - PixelSnap: 0 - _BlendOp: 0 - _BumpScale: 1 - _CameraFadingEnabled: 0 - _CameraFarFadeDistance: 2 - _CameraNearFadeDistance: 1 - _ColorMode: 4 - _Cull: 2 - _Cutoff: 0.5 - _DetailNormalMapScale: 1 - _DistortionBlend: 0.5 - _DistortionEnabled: 0 - _DistortionStrength: 1 - _DistortionStrengthScaled: 0 - _DstBlend: 1 - _EmissionEnabled: 0 - _EnableExternalAlpha: 0 - _FlipbookMode: 0 - _GlossMapScale: 1 - _Glossiness: 0.5 - _GlossyReflections: 1 - _LightingEnabled: 0 - _Metallic: 0 - _Mode: 4 - _OcclusionStrength: 1 - _Parallax: 0.02 - _SmoothnessTextureChannel: 0 - _SoftParticlesEnabled: 0 - _SoftParticlesFarFadeDistance: 1 - _SoftParticlesNearFadeDistance: 0 - _SpecularHighlights: 1 - _SrcBlend: 5 - _UVSec: 0 - _ZWrite: 0 m_Colors: - _CameraFadeParams: {r: 0, g: Infinity, b: 0, a: 0} - _Color: {r: 1, g: 0.9798179, b: 0, a: 1} - _ColorAddSubDiff: {r: 0, g: 0, b: 0, a: 0} - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - _Flip: {r: 1, g: 1, b: 1, a: 1} - _RendererColor: {r: 1, g: 1, b: 1, a: 1} - _SoftParticleFadeParams: {r: 0, g: 0, b: 0, a: 0} ================================================ FILE: Assets/Materials/ShootEffectMat.mat.meta ================================================ fileFormatVersion: 2 guid: 1d13efa0bfe6559499d9e8d00237ef50 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Materials.meta ================================================ fileFormatVersion: 2 guid: e409e904533a1fc46928119b0f513138 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/BaseChannel.cs ================================================ using System.Collections.Generic; namespace LiteNetLib { internal abstract class BaseChannel { public BaseChannel Next; protected readonly NetPeer Peer; protected readonly Queue OutgoingQueue; protected BaseChannel(NetPeer peer) { Peer = peer; OutgoingQueue = new Queue(64); } public int PacketsInQueue { get { return OutgoingQueue.Count; } } public void AddToQueue(NetPacket packet) { lock (OutgoingQueue) OutgoingQueue.Enqueue(packet); } public abstract void SendNextPackets(); public abstract bool ProcessPacket(NetPacket packet); } } ================================================ FILE: Assets/Plugins/LiteNetLib/BaseChannel.cs.meta ================================================ fileFormatVersion: 2 guid: 507b99235e7d3fe4387f5cbbc92f03ba MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/ConnectionRequest.cs ================================================ using System.Net; using System.Threading; using LiteNetLib.Utils; namespace LiteNetLib { internal enum ConnectionRequestResult { None, Accept, Reject, RejectForce } public class ConnectionRequest { private readonly NetManager _listener; private int _used; public readonly NetDataReader Data; internal ConnectionRequestResult Result { get; private set; } internal long ConnectionTime; internal byte ConnectionNumber; public readonly IPEndPoint RemoteEndPoint; private bool TryActivate() { return Interlocked.CompareExchange(ref _used, 1, 0) == 0; } internal void UpdateRequest(NetConnectRequestPacket connRequest) { if (connRequest.ConnectionTime >= ConnectionTime) { ConnectionTime = connRequest.ConnectionTime; ConnectionNumber = connRequest.ConnectionNumber; } } internal ConnectionRequest( long connectionId, byte connectionNumber, NetDataReader netDataReader, IPEndPoint endPoint, NetManager listener) { ConnectionTime = connectionId; ConnectionNumber = connectionNumber; RemoteEndPoint = endPoint; Data = netDataReader; _listener = listener; } public NetPeer AcceptIfKey(string key) { if (!TryActivate()) return null; try { if (Data.GetString() == key) Result = ConnectionRequestResult.Accept; } catch { NetDebug.WriteError("[AC] Invalid incoming data"); } if (Result == ConnectionRequestResult.Accept) return _listener.OnConnectionSolved(this, null, 0, 0); Result = ConnectionRequestResult.Reject; _listener.OnConnectionSolved(this, null, 0, 0); return null; } /// /// Accept connection and get new NetPeer as result /// /// Connected NetPeer public NetPeer Accept() { if (!TryActivate()) return null; Result = ConnectionRequestResult.Accept; return _listener.OnConnectionSolved(this, null, 0, 0); } public void Reject(byte[] rejectData, int start, int length, bool force) { if (!TryActivate()) return; Result = force ? ConnectionRequestResult.RejectForce : ConnectionRequestResult.Reject; _listener.OnConnectionSolved(this, rejectData, start, length); } public void Reject(byte[] rejectData, int start, int length) { Reject(rejectData, start, length, false); } public void RejectForce(byte[] rejectData, int start, int length) { Reject(rejectData, start, length, true); } public void RejectForce() { Reject(null, 0, 0, true); } public void RejectForce(byte[] rejectData) { Reject(rejectData, 0, rejectData.Length, true); } public void RejectForce(NetDataWriter rejectData) { Reject(rejectData.Data, 0, rejectData.Length, true); } public void Reject() { Reject(null, 0, 0, false); } public void Reject(byte[] rejectData) { Reject(rejectData, 0, rejectData.Length, false); } public void Reject(NetDataWriter rejectData) { Reject(rejectData.Data, 0, rejectData.Length, false); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/ConnectionRequest.cs.meta ================================================ fileFormatVersion: 2 guid: c7f36a00f0b4511498133b6c3c893427 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/INetEventListener.cs ================================================ using System.Net; using System.Net.Sockets; namespace LiteNetLib { /// /// Type of message that you receive in OnNetworkReceiveUnconnected event /// public enum UnconnectedMessageType { BasicMessage, Broadcast } /// /// Disconnect reason that you receive in OnPeerDisconnected event /// public enum DisconnectReason { ConnectionFailed, Timeout, HostUnreachable, NetworkUnreachable, RemoteConnectionClose, DisconnectPeerCalled, ConnectionRejected, InvalidProtocol, UnknownHost, Reconnect, PeerToPeerConnection } /// /// Additional information about disconnection /// public struct DisconnectInfo { /// /// Additional info why peer disconnected /// public DisconnectReason Reason; /// /// Error code (if reason is SocketSendError or SocketReceiveError) /// public SocketError SocketErrorCode; /// /// Additional data that can be accessed (only if reason is RemoteConnectionClose) /// public NetPacketReader AdditionalData; } public interface INetEventListener { /// /// New remote peer connected to host, or client connected to remote host /// /// Connected peer object void OnPeerConnected(NetPeer peer); /// /// Peer disconnected /// /// disconnected peer /// additional info about reason, errorCode or data received with disconnect message void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo); /// /// Network error (on send or receive) /// /// From endPoint (can be null) /// Socket error void OnNetworkError(IPEndPoint endPoint, SocketError socketError); /// /// Received some data /// /// From peer /// DataReader containing all received data /// Type of received packet void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod); /// /// Received unconnected message /// /// From address (IP and Port) /// Message data /// Message type (simple, discovery request or response) void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType); /// /// Latency information updated /// /// Peer with updated latency /// latency value in milliseconds void OnNetworkLatencyUpdate(NetPeer peer, int latency); /// /// On peer connection requested /// /// Request information (EndPoint, internal id, additional data) void OnConnectionRequest(ConnectionRequest request); } public interface IDeliveryEventListener { /// /// On reliable message delivered /// /// /// void OnMessageDelivered(NetPeer peer, object userData); } public class EventBasedNetListener : INetEventListener, IDeliveryEventListener { public delegate void OnPeerConnected(NetPeer peer); public delegate void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo); public delegate void OnNetworkError(IPEndPoint endPoint, SocketError socketError); public delegate void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod); public delegate void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType); public delegate void OnNetworkLatencyUpdate(NetPeer peer, int latency); public delegate void OnConnectionRequest(ConnectionRequest request); public delegate void OnDeliveryEvent(NetPeer peer, object userData); public event OnPeerConnected PeerConnectedEvent; public event OnPeerDisconnected PeerDisconnectedEvent; public event OnNetworkError NetworkErrorEvent; public event OnNetworkReceive NetworkReceiveEvent; public event OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent; public event OnNetworkLatencyUpdate NetworkLatencyUpdateEvent; public event OnConnectionRequest ConnectionRequestEvent; public event OnDeliveryEvent DeliveryEvent; public void ClearPeerConnectedEvent() { PeerConnectedEvent = null; } public void ClearPeerDisconnectedEvent() { PeerDisconnectedEvent = null; } public void ClearNetworkErrorEvent() { NetworkErrorEvent = null; } public void ClearNetworkReceiveEvent() { NetworkReceiveEvent = null; } public void ClearNetworkReceiveUnconnectedEvent() { NetworkReceiveUnconnectedEvent = null; } public void ClearNetworkLatencyUpdateEvent() { NetworkLatencyUpdateEvent = null; } public void ClearConnectionRequestEvent() { ConnectionRequestEvent = null; } public void ClearDeliveryEvent() { DeliveryEvent = null; } void INetEventListener.OnPeerConnected(NetPeer peer) { if (PeerConnectedEvent != null) PeerConnectedEvent(peer); } void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { if (PeerDisconnectedEvent != null) PeerDisconnectedEvent(peer, disconnectInfo); } void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) { if (NetworkErrorEvent != null) NetworkErrorEvent(endPoint, socketErrorCode); } void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) { if (NetworkReceiveEvent != null) NetworkReceiveEvent(peer, reader, deliveryMethod); } void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { if (NetworkReceiveUnconnectedEvent != null) NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType); } void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) { if (NetworkLatencyUpdateEvent != null) NetworkLatencyUpdateEvent(peer, latency); } void INetEventListener.OnConnectionRequest(ConnectionRequest request) { if (ConnectionRequestEvent != null) ConnectionRequestEvent(request); } void IDeliveryEventListener.OnMessageDelivered(NetPeer peer, object userData) { if (DeliveryEvent != null) DeliveryEvent(peer, userData); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/INetEventListener.cs.meta ================================================ fileFormatVersion: 2 guid: 1ad7743bbac7af64e930fcae94fd1a7d MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Layers/Crc32cLayer.cs ================================================ using LiteNetLib.Utils; using System; using System.Net; namespace LiteNetLib.Layers { public sealed class Crc32cLayer : PacketLayerBase { public Crc32cLayer() : base(CRC32C.ChecksumSize) { } public override void ProcessInboundPacket(IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length) { if (length < NetConstants.HeaderSize + CRC32C.ChecksumSize) { NetDebug.WriteError("[NM] DataReceived size: bad!"); return; } int checksumPoint = length - CRC32C.ChecksumSize; if (CRC32C.Compute(data, offset, checksumPoint) != BitConverter.ToUInt32(data, checksumPoint)) { NetDebug.Write("[NM] DataReceived checksum: bad!"); return; } length -= CRC32C.ChecksumSize; } public override void ProcessOutBoundPacket(IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length) { FastBitConverter.GetBytes(data, length, CRC32C.Compute(data, offset, length)); length += CRC32C.ChecksumSize; } } } ================================================ FILE: Assets/Plugins/LiteNetLib/Layers/Crc32cLayer.cs.meta ================================================ fileFormatVersion: 2 guid: 85b0c9ac1a5bf284082c22f13da241e4 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Layers/PacketLayerBase.cs ================================================ using System.Net; namespace LiteNetLib.Layers { public abstract class PacketLayerBase { public readonly int ExtraPacketSizeForLayer; protected PacketLayerBase(int extraPacketSizeForLayer) { ExtraPacketSizeForLayer = extraPacketSizeForLayer; } public abstract void ProcessInboundPacket(IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length); public abstract void ProcessOutBoundPacket(IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length); } } ================================================ FILE: Assets/Plugins/LiteNetLib/Layers/PacketLayerBase.cs.meta ================================================ fileFormatVersion: 2 guid: bf2e5160bd776544693c1c289882a126 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Layers/XorEncryptLayer.cs ================================================ using System; using System.Net; using System.Text; namespace LiteNetLib.Layers { public class XorEncryptLayer : PacketLayerBase { private byte[] _byteKey; public XorEncryptLayer() : base(0) { } public XorEncryptLayer(byte[] key) : this() { SetKey(key); } public XorEncryptLayer(string key) : this() { SetKey(key); } public void SetKey(string key) { _byteKey = Encoding.UTF8.GetBytes(key); } public void SetKey(byte[] key) { if (_byteKey == null || _byteKey.Length != key.Length) _byteKey = new byte[key.Length]; Buffer.BlockCopy(key, 0, _byteKey, 0, key.Length); } public override void ProcessInboundPacket(IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length) { if (_byteKey == null) return; var cur = offset; for (var i = 0; i < length; i++, cur++) { data[cur] = (byte)(data[cur] ^ _byteKey[i % _byteKey.Length]); } } public override void ProcessOutBoundPacket(IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length) { if (_byteKey == null) return; var cur = offset; for (var i = 0; i < length; i++, cur++) { data[cur] = (byte)(data[cur] ^ _byteKey[i % _byteKey.Length]); } } } } ================================================ FILE: Assets/Plugins/LiteNetLib/Layers/XorEncryptLayer.cs.meta ================================================ fileFormatVersion: 2 guid: 10422bff1ab7eb04894100d55d899764 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Layers.meta ================================================ fileFormatVersion: 2 guid: 370c4c298d0daf74781d2b0aa5420b81 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NatPunchModule.cs ================================================ using System.Collections.Generic; using System.Net; using System.Net.Sockets; using LiteNetLib.Utils; namespace LiteNetLib { public enum NatAddressType { Internal, External } public interface INatPunchListener { void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); } public class EventBasedNatPunchListener : INatPunchListener { public delegate void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); public delegate void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); public event OnNatIntroductionRequest NatIntroductionRequest; public event OnNatIntroductionSuccess NatIntroductionSuccess; void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token) { if(NatIntroductionRequest != null) NatIntroductionRequest(localEndPoint, remoteEndPoint, token); } void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token) { if (NatIntroductionSuccess != null) NatIntroductionSuccess(targetEndPoint, type, token); } } /// /// Module for UDP NAT Hole punching operations. Can be accessed from NetManager /// public sealed class NatPunchModule { struct RequestEventData { public IPEndPoint LocalEndPoint; public IPEndPoint RemoteEndPoint; public string Token; } struct SuccessEventData { public IPEndPoint TargetEndPoint; public NatAddressType Type; public string Token; } class NatIntroduceRequestPacket { public IPEndPoint Internal { get; set; } public string Token { get; set; } } class NatIntroduceResponsePacket { public IPEndPoint Internal { get; set; } public IPEndPoint External { get; set; } public string Token { get; set; } } class NatPunchPacket { public string Token { get; set; } public bool IsExternal { get; set; } } private readonly NetSocket _socket; private readonly Queue _requestEvents = new Queue(); private readonly Queue _successEvents = new Queue(); private readonly NetDataReader _cacheReader = new NetDataReader(); private readonly NetDataWriter _cacheWriter = new NetDataWriter(); private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(MaxTokenLength); private INatPunchListener _natPunchListener; public const int MaxTokenLength = 256; internal NatPunchModule(NetSocket socket) { _socket = socket; _netPacketProcessor.SubscribeReusable(OnNatIntroductionResponse); _netPacketProcessor.SubscribeReusable(OnNatIntroductionRequest); _netPacketProcessor.SubscribeReusable(OnNatPunch); } internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet) { lock (_cacheReader) { _cacheReader.SetSource(packet.RawData, NetConstants.HeaderSize, packet.Size); _netPacketProcessor.ReadAllPackets(_cacheReader, senderEndPoint); } } public void Init(INatPunchListener listener) { _natPunchListener = listener; } private void Send(T packet, IPEndPoint target) where T : class, new() { SocketError errorCode = 0; _cacheWriter.Reset(); _cacheWriter.Put((byte)PacketProperty.NatMessage); _netPacketProcessor.Write(_cacheWriter, packet); _socket.SendTo(_cacheWriter.Data, 0, _cacheWriter.Length, target, ref errorCode); } public void NatIntroduce( IPEndPoint hostInternal, IPEndPoint hostExternal, IPEndPoint clientInternal, IPEndPoint clientExternal, string additionalInfo) { var req = new NatIntroduceResponsePacket { Token = additionalInfo }; //First packet (server) send to client req.Internal = hostInternal; req.External = hostExternal; Send(req, clientExternal); //Second packet (client) send to server req.Internal = clientInternal; req.External = clientExternal; Send(req, hostExternal); } public void PollEvents() { if (_natPunchListener == null || (_successEvents.Count == 0 && _requestEvents.Count == 0)) return; lock (_successEvents) { while (_successEvents.Count > 0) { var evt = _successEvents.Dequeue(); _natPunchListener.OnNatIntroductionSuccess( evt.TargetEndPoint, evt.Type, evt.Token); } } lock (_requestEvents) { while (_requestEvents.Count > 0) { var evt = _requestEvents.Dequeue(); _natPunchListener.OnNatIntroductionRequest(evt.LocalEndPoint, evt.RemoteEndPoint, evt.Token); } } } public void SendNatIntroduceRequest(string host, int port, string additionalInfo) { SendNatIntroduceRequest(NetUtils.MakeEndPoint(host, port), additionalInfo); } public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo) { //prepare outgoing data string networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv4); if (string.IsNullOrEmpty(networkIp)) { networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv6); } Send( new NatIntroduceRequestPacket { Internal = NetUtils.MakeEndPoint(networkIp, _socket.LocalPort), Token = additionalInfo }, masterServerEndPoint); } //We got request and must introduce private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint) { lock (_requestEvents) { _requestEvents.Enqueue(new RequestEventData { LocalEndPoint = req.Internal, RemoteEndPoint = senderEndPoint, Token = req.Token }); } } //We got introduce and must punch private void OnNatIntroductionResponse(NatIntroduceResponsePacket req) { NetDebug.Write(NetLogLevel.Trace, "[NAT] introduction received"); // send internal punch var punchPacket = new NatPunchPacket {Token = req.Token}; Send(punchPacket, req.Internal); NetDebug.Write(NetLogLevel.Trace, "[NAT] internal punch sent to " + req.Internal); // hack for some routers SocketError errorCode = 0; _socket.Ttl = 2; _socket.SendTo(new[] { (byte)PacketProperty.Empty }, 0, 1, req.External, ref errorCode); // send external punch _socket.Ttl = NetConstants.SocketTTL; punchPacket.IsExternal = true; Send(punchPacket, req.External); NetDebug.Write(NetLogLevel.Trace, "[NAT] external punch sent to " + req.External); } //We got punch and can connect private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint) { //Read info NetDebug.Write(NetLogLevel.Trace, "[NAT] punch received from {0} - additional info: {1}", senderEndPoint, req.Token); //Release punch success to client; enabling him to Connect() to Sender if token is ok lock (_successEvents) { _successEvents.Enqueue(new SuccessEventData { TargetEndPoint = senderEndPoint, Type = req.IsExternal ? NatAddressType.External : NatAddressType.Internal, Token = req.Token }); } } } } ================================================ FILE: Assets/Plugins/LiteNetLib/NatPunchModule.cs.meta ================================================ fileFormatVersion: 2 guid: be2d3965b2909b74a842c90154448111 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NetConstants.cs ================================================ namespace LiteNetLib { /// /// Sending method type /// public enum DeliveryMethod : byte { /// /// Unreliable. Packets can be dropped, can be duplicated, can arrive without order. /// Unreliable = 4, /// /// Reliable. Packets won't be dropped, won't be duplicated, can arrive without order. /// ReliableUnordered = 0, /// /// Unreliable. Packets can be dropped, won't be duplicated, will arrive in order. /// Sequenced = 1, /// /// Reliable and ordered. Packets won't be dropped, won't be duplicated, will arrive in order. /// ReliableOrdered = 2, /// /// Reliable only last packet. Packets can be dropped (except the last one), won't be duplicated, will arrive in order. /// ReliableSequenced = 3 } /// /// Network constants. Can be tuned from sources for your purposes. /// public static class NetConstants { //can be tuned public const int DefaultWindowSize = 64; public const int SocketBufferSize = 1024 * 1024; //1mb public const int SocketTTL = 255; public const int HeaderSize = 1; public const int ChanneledHeaderSize = 4; public const int FragmentHeaderSize = 6; public const int FragmentedHeaderTotalSize = ChanneledHeaderSize + FragmentHeaderSize; public const ushort MaxSequence = 32768; public const ushort HalfMaxSequence = MaxSequence / 2; //protocol internal const int ProtocolId = 11; internal const int MaxUdpHeaderSize = 68; internal static readonly int[] PossibleMtu = { 576 - MaxUdpHeaderSize, //minimal 1232 - MaxUdpHeaderSize, 1460 - MaxUdpHeaderSize, //google cloud 1472 - MaxUdpHeaderSize, //VPN 1492 - MaxUdpHeaderSize, //Ethernet with LLC and SNAP, PPPoE (RFC 1042) 1500 - MaxUdpHeaderSize //Ethernet II (RFC 1191) }; internal static readonly int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1]; //peer specific public const byte MaxConnectionNumber = 4; public const int PacketPoolSize = 1000; } } ================================================ FILE: Assets/Plugins/LiteNetLib/NetConstants.cs.meta ================================================ fileFormatVersion: 2 guid: 53626ac03d7feb74b9b39561430c8c97 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NetDebug.cs ================================================ using System; using System.Diagnostics; namespace LiteNetLib { public class InvalidPacketException : ArgumentException { public InvalidPacketException(string message) : base(message) { } } public class TooBigPacketException : InvalidPacketException { public TooBigPacketException(string message) : base(message) { } } public enum NetLogLevel { Warning, Error, Trace, Info } /// /// Interface to implement for your own logger /// public interface INetLogger { void WriteNet(NetLogLevel level, string str, params object[] args); } /// /// Static class for defining your own LiteNetLib logger instead of Console.WriteLine /// or Debug.Log if compiled with UNITY flag /// public static class NetDebug { public static INetLogger Logger = null; private static readonly object DebugLogLock = new object(); private static void WriteLogic(NetLogLevel logLevel, string str, params object[] args) { lock (DebugLogLock) { if (Logger == null) { #if UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER UnityEngine.Debug.Log(string.Format(str, args)); #else Console.WriteLine(str, args); #endif } else { Logger.WriteNet(logLevel, str, args); } } } [Conditional("DEBUG_MESSAGES")] internal static void Write(string str, params object[] args) { WriteLogic(NetLogLevel.Trace, str, args); } [Conditional("DEBUG_MESSAGES")] internal static void Write(NetLogLevel level, string str, params object[] args) { WriteLogic(level, str, args); } [Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")] internal static void WriteForce(string str, params object[] args) { WriteLogic(NetLogLevel.Trace, str, args); } [Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")] internal static void WriteForce(NetLogLevel level, string str, params object[] args) { WriteLogic(level, str, args); } internal static void WriteError(string str, params object[] args) { WriteLogic(NetLogLevel.Error, str, args); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/NetDebug.cs.meta ================================================ fileFormatVersion: 2 guid: 03c9260e78a794a4b8839e0054f491bd MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NetManager.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Threading; using LiteNetLib.Layers; using LiteNetLib.Utils; namespace LiteNetLib { public enum IPv6Mode { Disabled, SeparateSocket, DualMode } public sealed class NetPacketReader : NetDataReader { private NetPacket _packet; private readonly NetManager _manager; private readonly NetEvent _evt; internal NetPacketReader(NetManager manager, NetEvent evt) { _manager = manager; _evt = evt; } internal void SetSource(NetPacket packet, int headerSize) { if (packet == null) return; _packet = packet; SetSource(packet.RawData, headerSize, packet.Size); } internal void RecycleInternal() { Clear(); if (_packet != null) _manager.NetPacketPool.Recycle(_packet); _packet = null; _manager.RecycleEvent(_evt); } public void Recycle() { if(_manager.AutoRecycle) throw new Exception("Recycle called with AutoRecycle enabled"); RecycleInternal(); } } internal sealed class NetEvent { public NetEvent Next; public enum EType { Connect, Disconnect, Receive, ReceiveUnconnected, Error, ConnectionLatencyUpdated, Broadcast, ConnectionRequest, MessageDelivered } public EType Type; public NetPeer Peer; public IPEndPoint RemoteEndPoint; public object UserData; public int Latency; public SocketError ErrorCode; public DisconnectReason DisconnectReason; public ConnectionRequest ConnectionRequest; public DeliveryMethod DeliveryMethod; public readonly NetPacketReader DataReader; public NetEvent(NetManager manager) { DataReader = new NetPacketReader(manager, this); } } /// /// Main class for all network operations. Can be used as client and/or server. /// public class NetManager : INetSocketListener, IEnumerable { private class IPEndPointComparer : IEqualityComparer { public bool Equals(IPEndPoint x, IPEndPoint y) { return x.Address.Equals(y.Address) && x.Port == y.Port; } public int GetHashCode(IPEndPoint obj) { return obj.GetHashCode(); } } public struct NetPeerEnumerator : IEnumerator { private readonly NetPeer _initialPeer; private NetPeer _p; public NetPeerEnumerator(NetPeer p) { _initialPeer = p; _p = null; } public void Dispose() { } public bool MoveNext() { _p = _p == null ? _initialPeer : _p.NextPeer; return _p != null; } public void Reset() { throw new NotSupportedException(); } public NetPeer Current { get { return _p; } } object IEnumerator.Current { get { return _p; } } } #if DEBUG private struct IncomingData { public byte[] Data; public IPEndPoint EndPoint; public DateTime TimeWhenGet; } private readonly List _pingSimulationList = new List(); private readonly Random _randomGenerator = new Random(); private const int MinLatencyThreshold = 5; #endif private readonly NetSocket _socket; private Thread _logicThread; private readonly AutoResetEvent _updateTriggerEvent = new AutoResetEvent(true); private readonly Queue _netEventsQueue; private NetEvent _netEventPoolHead; private readonly INetEventListener _netEventListener; private readonly IDeliveryEventListener _deliveryEventListener; private readonly Dictionary _peersDict; private readonly Dictionary _requestsDict; private readonly ReaderWriterLockSlim _peersLock; private volatile NetPeer _headPeer; private volatile int _connectedPeersCount; private readonly List _connectedPeerListCache; private NetPeer[] _peersArray; private readonly PacketLayerBase _extraPacketLayer; private int _lastPeerId; private readonly Queue _peerIds; private byte _channelsCount = 1; internal readonly NetPacketPool NetPacketPool; //config section /// /// Enable messages receiving without connection. (with SendUnconnectedMessage method) /// public bool UnconnectedMessagesEnabled = false; /// /// Enable nat punch messages /// public bool NatPunchEnabled = false; /// /// Library logic update and send period in milliseconds /// public int UpdateTime = 15; /// /// Interval for latency detection and checking connection /// public int PingInterval = 1000; /// /// If NetManager doesn't receive any packet from remote peer during this time then connection will be closed /// (including library internal keepalive packets) /// public int DisconnectTimeout = 5000; /// /// Simulate packet loss by dropping random amount of packets. (Works only in DEBUG mode) /// public bool SimulatePacketLoss = false; /// /// Simulate latency by holding packets for random time. (Works only in DEBUG mode) /// public bool SimulateLatency = false; /// /// Chance of packet loss when simulation enabled. value in percents (1 - 100). /// public int SimulationPacketLossChance = 10; /// /// Minimum simulated latency /// public int SimulationMinLatency = 30; /// /// Maximum simulated latency /// public int SimulationMaxLatency = 100; /// /// Events automatically will be called without PollEvents method from another thread /// public bool UnsyncedEvents = false; /// /// If true - receive event will be called from "receive" thread immediately otherwise on PollEvents call /// public bool UnsyncedReceiveEvent = false; /// /// If true - delivery event will be called from "receive" thread immediately otherwise on PollEvents call /// public bool UnsyncedDeliveryEvent = false; /// /// Allows receive broadcast packets /// public bool BroadcastReceiveEnabled = false; /// /// Delay between initial connection attempts /// public int ReconnectDelay = 500; /// /// Maximum connection attempts before client stops and call disconnect event. /// public int MaxConnectAttempts = 10; /// /// Enables socket option "ReuseAddress" for specific purposes /// public bool ReuseAddress = false; /// /// Statistics of all connections /// public readonly NetStatistics Statistics; /// /// Toggles the collection of network statistics for the instance and all known peers /// public bool EnableStatistics = false; /// /// NatPunchModule for NAT hole punching operations /// public readonly NatPunchModule NatPunchModule; /// /// Returns true if socket listening and update thread is running /// public bool IsRunning { get { return _socket.IsRunning; } } /// /// Local EndPoint (host and port) /// public int LocalPort { get { return _socket.LocalPort; } } /// /// Automatically recycle NetPacketReader after OnReceive event /// public bool AutoRecycle; /// /// IPv6 support /// public IPv6Mode IPv6Enabled = IPv6Mode.SeparateSocket; /// /// First peer. Useful for Client mode /// public NetPeer FirstPeer { get { return _headPeer; } } /// /// QoS channel count per message type (value must be between 1 and 64 channels) /// public byte ChannelsCount { get { return _channelsCount; } set { if (value < 1 || value > 64) throw new ArgumentException("Channels count must be between 1 and 64"); _channelsCount = value; } } /// /// Returns connected peers list (with internal cached list) /// public List ConnectedPeerList { get { GetPeersNonAlloc(_connectedPeerListCache, ConnectionState.Connected); return _connectedPeerListCache; } } /// /// Gets peer by peer id /// /// id of peer /// Peer if peer with id exist, otherwise null public NetPeer GetPeerById(int id) { return _peersArray[id]; } /// /// Returns connected peers count /// public int ConnectedPeersCount { get { return _connectedPeersCount; } } public int ExtraPacketSizeForLayer { get { return _extraPacketLayer != null ? _extraPacketLayer.ExtraPacketSizeForLayer : 0; } } private bool TryGetPeer(IPEndPoint endPoint, out NetPeer peer) { _peersLock.EnterReadLock(); bool result = _peersDict.TryGetValue(endPoint, out peer); _peersLock.ExitReadLock(); return result; } private void AddPeer(NetPeer peer) { _peersLock.EnterWriteLock(); if (_headPeer != null) { peer.NextPeer = _headPeer; _headPeer.PrevPeer = peer; } _headPeer = peer; _peersDict.Add(peer.EndPoint, peer); if (peer.Id >= _peersArray.Length) { int newSize = _peersArray.Length * 2; while (peer.Id >= newSize) newSize *= 2; Array.Resize(ref _peersArray, newSize); } _peersArray[peer.Id] = peer; _peersLock.ExitWriteLock(); } private void RemovePeer(NetPeer peer) { _peersLock.EnterWriteLock(); RemovePeerInternal(peer); _peersLock.ExitWriteLock(); } private void RemovePeerInternal(NetPeer peer) { if (!_peersDict.Remove(peer.EndPoint)) return; if (peer == _headPeer) _headPeer = peer.NextPeer; if (peer.PrevPeer != null) peer.PrevPeer.NextPeer = peer.NextPeer; if (peer.NextPeer != null) peer.NextPeer.PrevPeer = peer.PrevPeer; peer.PrevPeer = null; _peersArray[peer.Id] = null; lock (_peerIds) _peerIds.Enqueue(peer.Id); } /// /// NetManager constructor /// /// Network events listener (also can implement IDeliveryEventListener) /// Extra processing of packages, like CRC checksum or encryption. All connected NetManagers must have same layer. public NetManager(INetEventListener listener, PacketLayerBase extraPacketLayer = null) { _socket = new NetSocket(this); _netEventListener = listener; _deliveryEventListener = listener as IDeliveryEventListener; _netEventsQueue = new Queue(); NetPacketPool = new NetPacketPool(); NatPunchModule = new NatPunchModule(_socket); Statistics = new NetStatistics(); _connectedPeerListCache = new List(); _peersDict = new Dictionary(new IPEndPointComparer()); _requestsDict = new Dictionary(new IPEndPointComparer()); _peersLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); _peerIds = new Queue(); _peersArray = new NetPeer[32]; _extraPacketLayer = extraPacketLayer; } internal void ConnectionLatencyUpdated(NetPeer fromPeer, int latency) { CreateEvent(NetEvent.EType.ConnectionLatencyUpdated, fromPeer, latency: latency); } internal void MessageDelivered(NetPeer fromPeer, object userData) { if(_deliveryEventListener != null) CreateEvent(NetEvent.EType.MessageDelivered, fromPeer, userData: userData); } internal int SendRawAndRecycle(NetPacket packet, IPEndPoint remoteEndPoint) { var result = SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint); NetPacketPool.Recycle(packet); return result; } internal int SendRaw(NetPacket packet, IPEndPoint remoteEndPoint) { return SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint); } internal int SendRaw(byte[] message, int start, int length, IPEndPoint remoteEndPoint) { if (!_socket.IsRunning) return 0; SocketError errorCode = 0; int result; if (_extraPacketLayer != null) { var expandedPacket = NetPacketPool.GetPacket(length + _extraPacketLayer.ExtraPacketSizeForLayer); Buffer.BlockCopy(message, start, expandedPacket.RawData, 0, length); int newStart = 0; _extraPacketLayer.ProcessOutBoundPacket(remoteEndPoint, ref expandedPacket.RawData, ref newStart, ref length); result = _socket.SendTo(expandedPacket.RawData, newStart, length, remoteEndPoint, ref errorCode); NetPacketPool.Recycle(expandedPacket); } else { result = _socket.SendTo(message, start, length, remoteEndPoint, ref errorCode); } NetPeer fromPeer; switch (errorCode) { case SocketError.MessageSize: NetDebug.Write(NetLogLevel.Trace, "[SRD] 10040, datalen: {0}", length); return -1; case SocketError.HostUnreachable: if (TryGetPeer(remoteEndPoint, out fromPeer)) DisconnectPeerForce(fromPeer, DisconnectReason.HostUnreachable, errorCode, null); CreateEvent(NetEvent.EType.Error, remoteEndPoint: remoteEndPoint, errorCode: errorCode); return -1; case SocketError.NetworkUnreachable: if (TryGetPeer(remoteEndPoint, out fromPeer)) DisconnectPeerForce(fromPeer, DisconnectReason.NetworkUnreachable, errorCode, null); CreateEvent(NetEvent.EType.Error, remoteEndPoint: remoteEndPoint, errorCode: errorCode); return -1; } if (result <= 0) return 0; if (EnableStatistics) { Statistics.IncrementPacketsSent(); Statistics.AddBytesSent(length); } return result; } internal void DisconnectPeerForce(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, NetPacket eventData) { DisconnectPeer(peer, reason, socketErrorCode, true, null, 0, 0, eventData); } private void DisconnectPeer( NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, bool force, byte[] data, int start, int count, NetPacket eventData) { var shutdownResult = peer.Shutdown(data, start, count, force); if (shutdownResult == ShutdownResult.None) return; if(shutdownResult == ShutdownResult.WasConnected) Interlocked.Decrement(ref _connectedPeersCount); Thread.MemoryBarrier(); CreateEvent( NetEvent.EType.Disconnect, peer, errorCode: socketErrorCode, disconnectReason: reason, readerSource: eventData); } private void CreateEvent( NetEvent.EType type, NetPeer peer = null, IPEndPoint remoteEndPoint = null, SocketError errorCode = 0, int latency = 0, DisconnectReason disconnectReason = DisconnectReason.ConnectionFailed, ConnectionRequest connectionRequest = null, DeliveryMethod deliveryMethod = DeliveryMethod.Unreliable, NetPacket readerSource = null, object userData = null) { NetEvent evt; bool unsyncEvent = UnsyncedEvents; if (type == NetEvent.EType.Connect) Interlocked.Increment(ref _connectedPeersCount); else if (type == NetEvent.EType.MessageDelivered) unsyncEvent = UnsyncedDeliveryEvent; do { evt = _netEventPoolHead; if (evt == null) { evt = new NetEvent(this); break; } } while (evt != Interlocked.CompareExchange(ref _netEventPoolHead, evt.Next, evt)); evt.Type = type; evt.DataReader.SetSource(readerSource, readerSource == null ? 0 : readerSource.GetHeaderSize()); evt.Peer = peer; evt.RemoteEndPoint = remoteEndPoint; evt.Latency = latency; evt.ErrorCode = errorCode; evt.DisconnectReason = disconnectReason; evt.ConnectionRequest = connectionRequest; evt.DeliveryMethod = deliveryMethod; evt.UserData = userData; if (unsyncEvent) { ProcessEvent(evt); } else { lock (_netEventsQueue) _netEventsQueue.Enqueue(evt); } } private void ProcessEvent(NetEvent evt) { NetDebug.Write("[NM] Processing event: " + evt.Type); bool emptyData = evt.DataReader.IsNull; switch (evt.Type) { case NetEvent.EType.Connect: _netEventListener.OnPeerConnected(evt.Peer); break; case NetEvent.EType.Disconnect: var info = new DisconnectInfo { Reason = evt.DisconnectReason, AdditionalData = evt.DataReader, SocketErrorCode = evt.ErrorCode }; _netEventListener.OnPeerDisconnected(evt.Peer, info); break; case NetEvent.EType.Receive: _netEventListener.OnNetworkReceive(evt.Peer, evt.DataReader, evt.DeliveryMethod); break; case NetEvent.EType.ReceiveUnconnected: _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.BasicMessage); break; case NetEvent.EType.Broadcast: _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.Broadcast); break; case NetEvent.EType.Error: _netEventListener.OnNetworkError(evt.RemoteEndPoint, evt.ErrorCode); break; case NetEvent.EType.ConnectionLatencyUpdated: _netEventListener.OnNetworkLatencyUpdate(evt.Peer, evt.Latency); break; case NetEvent.EType.ConnectionRequest: _netEventListener.OnConnectionRequest(evt.ConnectionRequest); break; case NetEvent.EType.MessageDelivered: _deliveryEventListener.OnMessageDelivered(evt.Peer, evt.UserData); break; } //Recycle if not message if (emptyData) RecycleEvent(evt); else if (AutoRecycle) evt.DataReader.RecycleInternal(); } internal void RecycleEvent(NetEvent evt) { evt.Peer = null; evt.ErrorCode = 0; evt.RemoteEndPoint = null; evt.ConnectionRequest = null; do { evt.Next = _netEventPoolHead; } while (evt.Next != Interlocked.CompareExchange(ref _netEventPoolHead, evt, evt.Next)); } //Update function private void UpdateLogic() { var peersToRemove = new List(); var stopwatch = new Stopwatch(); stopwatch.Start(); while (_socket.IsRunning) { #if DEBUG if (SimulateLatency) { var time = DateTime.UtcNow; lock (_pingSimulationList) { for (int i = 0; i < _pingSimulationList.Count; i++) { var incomingData = _pingSimulationList[i]; if (incomingData.TimeWhenGet <= time) { DataReceived(incomingData.Data, incomingData.Data.Length, incomingData.EndPoint); _pingSimulationList.RemoveAt(i); i--; } } } } #endif int elapsed = (int)stopwatch.ElapsedMilliseconds; elapsed = elapsed <= 0 ? 1 : elapsed; stopwatch.Reset(); stopwatch.Start(); for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > DisconnectTimeout) { peersToRemove.Add(netPeer); } else { netPeer.Update(elapsed); } } if (peersToRemove.Count > 0) { _peersLock.EnterWriteLock(); for (int i = 0; i < peersToRemove.Count; i++) RemovePeerInternal(peersToRemove[i]); _peersLock.ExitWriteLock(); peersToRemove.Clear(); } int sleepTime = UpdateTime - (int)stopwatch.ElapsedMilliseconds; if (sleepTime > 0) _updateTriggerEvent.WaitOne(sleepTime); } stopwatch.Stop(); } void INetSocketListener.OnMessageReceived(byte[] data, int length, SocketError errorCode, IPEndPoint remoteEndPoint) { if (errorCode != 0) { CreateEvent(NetEvent.EType.Error, errorCode: errorCode); NetDebug.WriteError("[NM] Receive error: {0}", errorCode); return; } #if DEBUG if (SimulatePacketLoss && _randomGenerator.NextDouble() * 100 < SimulationPacketLossChance) { //drop packet return; } if (SimulateLatency) { int latency = _randomGenerator.Next(SimulationMinLatency, SimulationMaxLatency); if (latency > MinLatencyThreshold) { byte[] holdedData = new byte[length]; Buffer.BlockCopy(data, 0, holdedData, 0, length); lock (_pingSimulationList) { _pingSimulationList.Add(new IncomingData { Data = holdedData, EndPoint = remoteEndPoint, TimeWhenGet = DateTime.UtcNow.AddMilliseconds(latency) }); } //hold packet return; } } #endif try { //ProcessEvents DataReceived(data, length, remoteEndPoint); } catch(Exception e) { //protects socket receive thread NetDebug.WriteError("[NM] SocketReceiveThread error: " + e ); } } internal NetPeer OnConnectionSolved(ConnectionRequest request, byte[] rejectData, int start, int length) { NetPeer netPeer = null; if (request.Result == ConnectionRequestResult.RejectForce) { NetDebug.Write(NetLogLevel.Trace, "[NM] Peer connect reject force."); if (rejectData != null && length > 0) { var shutdownPacket = NetPacketPool.GetWithProperty(PacketProperty.Disconnect, length); shutdownPacket.ConnectionNumber = request.ConnectionNumber; FastBitConverter.GetBytes(shutdownPacket.RawData, 1, request.ConnectionTime); if (shutdownPacket.Size >= NetConstants.PossibleMtu[0]) NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU!"); else Buffer.BlockCopy(rejectData, start, shutdownPacket.RawData, 9, length); SendRawAndRecycle(shutdownPacket, request.RemoteEndPoint); } } else { _peersLock.EnterUpgradeableReadLock(); if (_peersDict.TryGetValue(request.RemoteEndPoint, out netPeer)) { //already have peer _peersLock.ExitUpgradeableReadLock(); } else if (request.Result == ConnectionRequestResult.Reject) { netPeer = new NetPeer(this, request.RemoteEndPoint, GetNextPeerId()); netPeer.Reject(request.ConnectionTime, request.ConnectionNumber, rejectData, start, length); AddPeer(netPeer); _peersLock.ExitUpgradeableReadLock(); NetDebug.Write(NetLogLevel.Trace, "[NM] Peer connect reject."); } else //Accept { netPeer = new NetPeer(this, request.RemoteEndPoint, GetNextPeerId(), request.ConnectionTime, request.ConnectionNumber); AddPeer(netPeer); _peersLock.ExitUpgradeableReadLock(); CreateEvent(NetEvent.EType.Connect, netPeer); NetDebug.Write(NetLogLevel.Trace, "[NM] Received peer connection Id: {0}, EP: {1}", netPeer.ConnectTime, netPeer.EndPoint); } } lock(_requestsDict) _requestsDict.Remove(request.RemoteEndPoint); return netPeer; } private int GetNextPeerId() { lock (_peerIds) return _peerIds.Count == 0 ? _lastPeerId++ : _peerIds.Dequeue(); } private void ProcessConnectRequest( IPEndPoint remoteEndPoint, NetPeer netPeer, NetConnectRequestPacket connRequest) { byte connectionNumber = connRequest.ConnectionNumber; ConnectionRequest req; //if we have peer if (netPeer != null) { var processResult = netPeer.ProcessConnectRequest(connRequest); NetDebug.Write("ConnectRequest LastId: {0}, NewId: {1}, EP: {2}, Result: {3}", netPeer.ConnectTime, connRequest.ConnectionTime, remoteEndPoint, processResult); switch (processResult) { case ConnectRequestResult.Reconnection: DisconnectPeerForce(netPeer, DisconnectReason.Reconnect, 0, null); RemovePeer(netPeer); //go to new connection break; case ConnectRequestResult.NewConnection: RemovePeer(netPeer); //go to new connection break; case ConnectRequestResult.P2PLose: DisconnectPeerForce(netPeer, DisconnectReason.PeerToPeerConnection, 0, null); RemovePeer(netPeer); //go to new connection break; default: //no operations needed return; } //ConnectRequestResult.NewConnection //Set next connection number if(processResult != ConnectRequestResult.P2PLose) connectionNumber = (byte)((netPeer.ConnectionNum + 1) % NetConstants.MaxConnectionNumber); //To reconnect peer } else { NetDebug.Write("ConnectRequest Id: {0}, EP: {1}", connRequest.ConnectionTime, remoteEndPoint); } lock (_requestsDict) { if (_requestsDict.TryGetValue(remoteEndPoint, out req)) { req.UpdateRequest(connRequest); return; } req = new ConnectionRequest( connRequest.ConnectionTime, connectionNumber, connRequest.Data, remoteEndPoint, this); _requestsDict.Add(remoteEndPoint, req); } NetDebug.Write("[NM] Creating request event: " + connRequest.ConnectionTime); CreateEvent(NetEvent.EType.ConnectionRequest, connectionRequest: req); } private void DataReceived(byte[] reusableBuffer, int count, IPEndPoint remoteEndPoint) { if (EnableStatistics) { Statistics.IncrementPacketsReceived(); Statistics.AddBytesReceived(count); } int start = 0; if (_extraPacketLayer != null) { _extraPacketLayer.ProcessInboundPacket(remoteEndPoint, ref reusableBuffer, ref start, ref count); if (count == 0) return; } //empty packet if (reusableBuffer[start] == (byte) PacketProperty.Empty) return; //Try read packet NetPacket packet = NetPacketPool.GetPacket(count); if (!packet.FromBytes(reusableBuffer, start, count)) { NetPacketPool.Recycle(packet); NetDebug.WriteError("[NM] DataReceived: bad!"); return; } switch (packet.Property) { //special case connect request case PacketProperty.ConnectRequest: if (NetConnectRequestPacket.GetProtocolId(packet) != NetConstants.ProtocolId) { SendRawAndRecycle(NetPacketPool.GetWithProperty(PacketProperty.InvalidProtocol), remoteEndPoint); return; } break; //unconnected messages case PacketProperty.Broadcast: if (!BroadcastReceiveEnabled) return; CreateEvent(NetEvent.EType.Broadcast, remoteEndPoint: remoteEndPoint, readerSource: packet); return; case PacketProperty.UnconnectedMessage: if (!UnconnectedMessagesEnabled) return; CreateEvent(NetEvent.EType.ReceiveUnconnected, remoteEndPoint: remoteEndPoint, readerSource: packet); return; case PacketProperty.NatMessage: if (NatPunchEnabled) NatPunchModule.ProcessMessage(remoteEndPoint, packet); return; } //Check normal packets NetPeer netPeer; _peersLock.EnterReadLock(); bool peerFound = _peersDict.TryGetValue(remoteEndPoint, out netPeer); _peersLock.ExitReadLock(); switch (packet.Property) { case PacketProperty.ConnectRequest: var connRequest = NetConnectRequestPacket.FromData(packet); if (connRequest != null) ProcessConnectRequest(remoteEndPoint, netPeer, connRequest); break; case PacketProperty.PeerNotFound: if (peerFound) { if (netPeer.ConnectionState != ConnectionState.Connected) return; if (packet.Size == 1) { //first reply var p = NetPacketPool.GetWithProperty(PacketProperty.PeerNotFound, 9); p.RawData[1] = 0; FastBitConverter.GetBytes(p.RawData, 2, netPeer.ConnectTime); SendRawAndRecycle(p, remoteEndPoint); NetDebug.Write("PeerNotFound sending connectTime: {0}", netPeer.ConnectTime); } else if (packet.Size == 10 && packet.RawData[1] == 1 && BitConverter.ToInt64(packet.RawData, 2) == netPeer.ConnectTime) { //second reply NetDebug.Write("PeerNotFound received our connectTime: {0}", netPeer.ConnectTime); DisconnectPeerForce(netPeer, DisconnectReason.RemoteConnectionClose, 0, null); } } else if (packet.Size == 10 && packet.RawData[1] == 0) { //send reply back packet.RawData[1] = 1; SendRawAndRecycle(packet, remoteEndPoint); } break; case PacketProperty.InvalidProtocol: if (peerFound && netPeer.ConnectionState == ConnectionState.Outgoing) DisconnectPeerForce(netPeer, DisconnectReason.InvalidProtocol, 0, null); break; case PacketProperty.Disconnect: if (peerFound) { var disconnectResult = netPeer.ProcessDisconnect(packet); if (disconnectResult == DisconnectResult.None) { NetPacketPool.Recycle(packet); return; } DisconnectPeerForce( netPeer, disconnectResult == DisconnectResult.Disconnect ? DisconnectReason.RemoteConnectionClose : DisconnectReason.ConnectionRejected, 0, packet); } else { NetPacketPool.Recycle(packet); } //Send shutdown SendRawAndRecycle(NetPacketPool.GetWithProperty(PacketProperty.ShutdownOk), remoteEndPoint); break; case PacketProperty.ConnectAccept: if (!peerFound) return; var connAccept = NetConnectAcceptPacket.FromData(packet); if (connAccept != null && netPeer.ProcessConnectAccept(connAccept)) CreateEvent(NetEvent.EType.Connect, netPeer); break; default: if(peerFound) netPeer.ProcessPacket(packet); else SendRawAndRecycle(NetPacketPool.GetWithProperty(PacketProperty.PeerNotFound), remoteEndPoint); break; } } internal void CreateReceiveEvent(NetPacket packet, DeliveryMethod method, int headerSize, NetPeer fromPeer) { NetEvent evt; do { evt = _netEventPoolHead; if (evt == null) { evt = new NetEvent(this); break; } } while (evt != Interlocked.CompareExchange(ref _netEventPoolHead, evt.Next, evt)); evt.Type = NetEvent.EType.Receive; evt.DataReader.SetSource(packet, headerSize); evt.Peer = fromPeer; evt.DeliveryMethod = method; if (UnsyncedEvents || UnsyncedReceiveEvent) { ProcessEvent(evt); } else { lock (_netEventsQueue) _netEventsQueue.Enqueue(evt); } } /// /// Send data to all connected peers (channel - 0) /// /// DataWriter with data /// Send options (reliable, unreliable, etc.) public void SendToAll(NetDataWriter writer, DeliveryMethod options) { SendToAll(writer.Data, 0, writer.Length, options); } /// /// Send data to all connected peers (channel - 0) /// /// Data /// Send options (reliable, unreliable, etc.) public void SendToAll(byte[] data, DeliveryMethod options) { SendToAll(data, 0, data.Length, options); } /// /// Send data to all connected peers (channel - 0) /// /// Data /// Start of data /// Length of data /// Send options (reliable, unreliable, etc.) public void SendToAll(byte[] data, int start, int length, DeliveryMethod options) { SendToAll(data, start, length, 0, options); } /// /// Send data to all connected peers /// /// DataWriter with data /// Number of channel (from 0 to channelsCount - 1) /// Send options (reliable, unreliable, etc.) public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options) { SendToAll(writer.Data, 0, writer.Length, channelNumber, options); } /// /// Send data to all connected peers /// /// Data /// Number of channel (from 0 to channelsCount - 1) /// Send options (reliable, unreliable, etc.) public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options) { SendToAll(data, 0, data.Length, channelNumber, options); } /// /// Send data to all connected peers /// /// Data /// Start of data /// Length of data /// Number of channel (from 0 to channelsCount - 1) /// Send options (reliable, unreliable, etc.) public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options) { try { _peersLock.EnterReadLock(); for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) netPeer.Send(data, start, length, channelNumber, options); } finally { _peersLock.ExitReadLock(); } } /// /// Send data to all connected peers (channel - 0) /// /// DataWriter with data /// Send options (reliable, unreliable, etc.) /// Excluded peer public void SendToAll(NetDataWriter writer, DeliveryMethod options, NetPeer excludePeer) { SendToAll(writer.Data, 0, writer.Length, 0, options, excludePeer); } /// /// Send data to all connected peers (channel - 0) /// /// Data /// Send options (reliable, unreliable, etc.) /// Excluded peer public void SendToAll(byte[] data, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, 0, data.Length, 0, options, excludePeer); } /// /// Send data to all connected peers (channel - 0) /// /// Data /// Start of data /// Length of data /// Send options (reliable, unreliable, etc.) /// Excluded peer public void SendToAll(byte[] data, int start, int length, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, start, length, 0, options, excludePeer); } /// /// Send data to all connected peers /// /// DataWriter with data /// Number of channel (from 0 to channelsCount - 1) /// Send options (reliable, unreliable, etc.) /// Excluded peer public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { SendToAll(writer.Data, 0, writer.Length, channelNumber, options, excludePeer); } /// /// Send data to all connected peers /// /// Data /// Number of channel (from 0 to channelsCount - 1) /// Send options (reliable, unreliable, etc.) /// Excluded peer public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, 0, data.Length, channelNumber, options, excludePeer); } /// /// Send data to all connected peers /// /// Data /// Start of data /// Length of data /// Number of channel (from 0 to channelsCount - 1) /// Send options (reliable, unreliable, etc.) /// Excluded peer public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { try { _peersLock.EnterReadLock(); for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if (netPeer != excludePeer) netPeer.Send(data, start, length, channelNumber, options); } } finally { _peersLock.ExitReadLock(); } } /// /// Start logic thread and listening on available port /// public bool Start() { return Start(0); } /// /// Start logic thread and listening on selected port /// /// bind to specific ipv4 address /// bind to specific ipv6 address /// port to listen public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port) { if (!_socket.Bind(addressIPv4, addressIPv6, port, ReuseAddress, IPv6Enabled)) return false; _logicThread = new Thread(UpdateLogic) { Name = "LogicThread", IsBackground = true }; _logicThread.Start(); return true; } /// /// Start logic thread and listening on selected port /// /// bind to specific ipv4 address /// bind to specific ipv6 address /// port to listen public bool Start(string addressIPv4, string addressIPv6, int port) { IPAddress ipv4 = NetUtils.ResolveAddress(addressIPv4); IPAddress ipv6 = NetUtils.ResolveAddress(addressIPv6); return Start(ipv4, ipv6, port); } /// /// Start logic thread and listening on selected port /// /// port to listen public bool Start(int port) { return Start(IPAddress.Any, IPAddress.IPv6Any, port); } /// /// Send message without connection /// /// Raw data /// Packet destination /// Operation result public bool SendUnconnectedMessage(byte[] message, IPEndPoint remoteEndPoint) { return SendUnconnectedMessage(message, 0, message.Length, remoteEndPoint); } /// /// Send message without connection /// /// Data serializer /// Packet destination /// Operation result public bool SendUnconnectedMessage(NetDataWriter writer, IPEndPoint remoteEndPoint) { return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint); } /// /// Send message without connection /// /// Raw data /// data start /// data length /// Packet destination /// Operation result public bool SendUnconnectedMessage(byte[] message, int start, int length, IPEndPoint remoteEndPoint) { //No need for CRC here, SendRaw does that NetPacket packet = NetPacketPool.GetWithData(PacketProperty.UnconnectedMessage, message, start, length); return SendRawAndRecycle(packet, remoteEndPoint) > 0; } public bool SendBroadcast(NetDataWriter writer, int port) { return SendBroadcast(writer.Data, 0, writer.Length, port); } public bool SendBroadcast(byte[] data, int port) { return SendBroadcast(data, 0, data.Length, port); } public bool SendBroadcast(byte[] data, int start, int length, int port) { NetPacket packet; if (_extraPacketLayer != null) { var headerSize = NetPacket.GetHeaderSize(PacketProperty.Broadcast); packet = NetPacketPool.GetPacket(headerSize + length + _extraPacketLayer.ExtraPacketSizeForLayer); packet.Property = PacketProperty.Broadcast; Buffer.BlockCopy(data, start, packet.RawData, headerSize, length); var checksumComputeStart = 0; int preCrcLength = length + headerSize; _extraPacketLayer.ProcessOutBoundPacket(null, ref packet.RawData, ref checksumComputeStart, ref preCrcLength); } else { packet = NetPacketPool.GetWithData(PacketProperty.Broadcast, data, start, length); } bool result = _socket.SendBroadcast(packet.RawData, 0, packet.Size, port); NetPacketPool.Recycle(packet); return result; } /// /// Triggers update and send logic immediately (works asynchronously) /// public void TriggerUpdate() { _updateTriggerEvent.Set(); } /// /// Receive all pending events. Call this in game update code /// public void PollEvents() { if (UnsyncedEvents) return; int eventsCount; lock (_netEventsQueue) eventsCount = _netEventsQueue.Count; for(int i = 0; i < eventsCount; i++) { NetEvent evt; lock (_netEventsQueue) evt = _netEventsQueue.Dequeue(); ProcessEvent(evt); } } /// /// Connect to remote host /// /// Server IP or hostname /// Server Port /// Connection key /// New NetPeer if new connection, Old NetPeer if already connected, null peer if there is ConnectionRequest awaiting /// Manager is not running. Call public NetPeer Connect(string address, int port, string key) { return Connect(address, port, NetDataWriter.FromString(key)); } /// /// Connect to remote host /// /// Server IP or hostname /// Server Port /// Additional data for remote peer /// New NetPeer if new connection, Old NetPeer if already connected, null peer if there is ConnectionRequest awaiting /// Manager is not running. Call public NetPeer Connect(string address, int port, NetDataWriter connectionData) { IPEndPoint ep; try { ep = NetUtils.MakeEndPoint(address, port); } catch { CreateEvent(NetEvent.EType.Disconnect, disconnectReason: DisconnectReason.UnknownHost); return null; } return Connect(ep, connectionData); } /// /// Connect to remote host /// /// Server end point (ip and port) /// Connection key /// New NetPeer if new connection, Old NetPeer if already connected, null peer if there is ConnectionRequest awaiting /// Manager is not running. Call public NetPeer Connect(IPEndPoint target, string key) { return Connect(target, NetDataWriter.FromString(key)); } /// /// Connect to remote host /// /// Server end point (ip and port) /// Additional data for remote peer /// New NetPeer if new connection, Old NetPeer if already connected, null peer if there is ConnectionRequest awaiting /// Manager is not running. Call public NetPeer Connect(IPEndPoint target, NetDataWriter connectionData) { if (!_socket.IsRunning) throw new InvalidOperationException("Client is not running"); NetPeer peer; byte connectionNumber = 0; lock(_requestsDict) { if (_requestsDict.ContainsKey(target)) return null; } _peersLock.EnterUpgradeableReadLock(); if (_peersDict.TryGetValue(target, out peer)) { switch (peer.ConnectionState) { //just return already connected peer case ConnectionState.Connected: case ConnectionState.Outgoing: _peersLock.ExitUpgradeableReadLock(); return peer; } //else reconnect connectionNumber = (byte)((peer.ConnectionNum + 1) % NetConstants.MaxConnectionNumber); RemovePeer(peer); } //Create reliable connection //And send connection request peer = new NetPeer(this, target, GetNextPeerId(), connectionNumber, connectionData); AddPeer(peer); _peersLock.ExitUpgradeableReadLock(); return peer; } /// /// Force closes connection and stop all threads. /// public void Stop() { Stop(true); } /// /// Force closes connection and stop all threads. /// /// Send disconnect messages public void Stop(bool sendDisconnectMessages) { if (!_socket.IsRunning) return; NetDebug.Write("[NM] Stop"); //Send last disconnect for(var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) netPeer.Shutdown(null, 0, 0, !sendDisconnectMessages); //Stop _socket.Close(false); _updateTriggerEvent.Set(); _logicThread.Join(); _logicThread = null; //clear peers _peersLock.EnterWriteLock(); _headPeer = null; _peersDict.Clear(); _peersArray = new NetPeer[32]; _peersLock.ExitWriteLock(); lock(_peerIds) _peerIds.Clear(); #if DEBUG lock (_pingSimulationList) _pingSimulationList.Clear(); #endif _connectedPeersCount = 0; lock(_netEventsQueue) _netEventsQueue.Clear(); } /// /// Return peers count with connection state /// /// peer connection state (you can use as bit flags) /// peers count public int GetPeersCount(ConnectionState peerState) { int count = 0; _peersLock.EnterReadLock(); for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if ((netPeer.ConnectionState & peerState) != 0) count++; } _peersLock.ExitReadLock(); return count; } /// /// Get copy of peers (without allocations) /// /// List that will contain result /// State of peers public void GetPeersNonAlloc(List peers, ConnectionState peerState) { peers.Clear(); _peersLock.EnterReadLock(); for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if ((netPeer.ConnectionState & peerState) != 0) peers.Add(netPeer); } _peersLock.ExitReadLock(); } /// /// Disconnect all peers without any additional data /// public void DisconnectAll() { DisconnectAll(null, 0, 0); } /// /// Disconnect all peers with shutdown message /// /// Data to send (must be less or equal MTU) /// Data start /// Data count public void DisconnectAll(byte[] data, int start, int count) { //Send disconnect packets _peersLock.EnterReadLock(); for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { DisconnectPeer( netPeer, DisconnectReason.DisconnectPeerCalled, 0, false, data, start, count, null); } _peersLock.ExitReadLock(); } /// /// Immediately disconnect peer from server without additional data /// /// peer to disconnect public void DisconnectPeerForce(NetPeer peer) { DisconnectPeerForce(peer, DisconnectReason.DisconnectPeerCalled, 0, null); } /// /// Disconnect peer from server /// /// peer to disconnect public void DisconnectPeer(NetPeer peer) { DisconnectPeer(peer, null, 0, 0); } /// /// Disconnect peer from server and send additional data (Size must be less or equal MTU - 8) /// /// peer to disconnect /// additional data public void DisconnectPeer(NetPeer peer, byte[] data) { DisconnectPeer(peer, data, 0, data.Length); } /// /// Disconnect peer from server and send additional data (Size must be less or equal MTU - 8) /// /// peer to disconnect /// additional data public void DisconnectPeer(NetPeer peer, NetDataWriter writer) { DisconnectPeer(peer, writer.Data, 0, writer.Length); } /// /// Disconnect peer from server and send additional data (Size must be less or equal MTU - 8) /// /// peer to disconnect /// additional data /// data start /// data length public void DisconnectPeer(NetPeer peer, byte[] data, int start, int count) { DisconnectPeer( peer, DisconnectReason.DisconnectPeerCalled, 0, false, data, start, count, null); } public NetPeerEnumerator GetEnumerator() { return new NetPeerEnumerator(_headPeer); } IEnumerator IEnumerable.GetEnumerator() { return new NetPeerEnumerator(_headPeer); } IEnumerator IEnumerable.GetEnumerator() { return new NetPeerEnumerator(_headPeer); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/NetManager.cs.meta ================================================ fileFormatVersion: 2 guid: 58e96e53edef5bc4ea934b50d4d4c8ba MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NetPacket.cs ================================================ using System; using System.Net; using LiteNetLib.Utils; namespace LiteNetLib { internal enum PacketProperty : byte { Unreliable, Channeled, Ack, Ping, Pong, ConnectRequest, ConnectAccept, Disconnect, UnconnectedMessage, MtuCheck, MtuOk, Broadcast, Merged, ShutdownOk, PeerNotFound, InvalidProtocol, NatMessage, Empty } internal sealed class NetPacket { private static readonly int LastProperty = Enum.GetValues(typeof(PacketProperty)).Length; private static readonly int[] HeaderSizes; static NetPacket() { HeaderSizes = new int[LastProperty+1]; for (int i = 0; i < HeaderSizes.Length; i++) { switch ((PacketProperty)i) { case PacketProperty.Channeled: case PacketProperty.Ack: HeaderSizes[i] = NetConstants.ChanneledHeaderSize; break; case PacketProperty.Ping: HeaderSizes[i] = NetConstants.HeaderSize + 2; break; case PacketProperty.ConnectRequest: HeaderSizes[i] = NetConnectRequestPacket.HeaderSize; break; case PacketProperty.ConnectAccept: HeaderSizes[i] = NetConnectAcceptPacket.Size; break; case PacketProperty.Disconnect: HeaderSizes[i] = NetConstants.HeaderSize + 8; break; case PacketProperty.Pong: HeaderSizes[i] = NetConstants.HeaderSize + 10; break; default: HeaderSizes[i] = NetConstants.HeaderSize; break; } } } //Header public PacketProperty Property { get { return (PacketProperty)(RawData[0] & 0x1F); } set { RawData[0] = (byte)((RawData[0] & 0xE0) | (byte)value); } } public byte ConnectionNumber { get { return (byte)((RawData[0] & 0x60) >> 5); } set { RawData[0] = (byte) ((RawData[0] & 0x9F) | (value << 5)); } } public ushort Sequence { get { return BitConverter.ToUInt16(RawData, 1); } set { FastBitConverter.GetBytes(RawData, 1, value); } } public bool IsFragmented { get { return (RawData[0] & 0x80) != 0; } } public void MarkFragmented() { RawData[0] |= 0x80; //set first bit } public byte ChannelId { get { return RawData[3]; } set { RawData[3] = value; } } public ushort FragmentId { get { return BitConverter.ToUInt16(RawData, 4); } set { FastBitConverter.GetBytes(RawData, 4, value); } } public ushort FragmentPart { get { return BitConverter.ToUInt16(RawData, 6); } set { FastBitConverter.GetBytes(RawData, 6, value); } } public ushort FragmentsTotal { get { return BitConverter.ToUInt16(RawData, 8); } set { FastBitConverter.GetBytes(RawData, 8, value); } } //Data public byte[] RawData; public int Size; //Delivery public object UserData; //Pool node public NetPacket Next; public NetPacket(int size) { RawData = new byte[size]; Size = size; } public NetPacket(PacketProperty property, int size) { size += GetHeaderSize(property); RawData = new byte[size]; Property = property; Size = size; } public static int GetHeaderSize(PacketProperty property) { return HeaderSizes[(int)property]; } public int GetHeaderSize() { return HeaderSizes[RawData[0] & 0x1F]; } //Packet constructor from byte array public bool FromBytes(byte[] data, int start, int packetSize) { //Reading property byte property = (byte)(data[start] & 0x1F); bool fragmented = (data[start] & 0x80) != 0; int headerSize = HeaderSizes[property]; if (property > LastProperty || packetSize < headerSize || (fragmented && packetSize < headerSize + NetConstants.FragmentHeaderSize) || data.Length < start + packetSize) { return false; } Buffer.BlockCopy(data, start, RawData, 0, packetSize); Size = (ushort)packetSize; return true; } } internal sealed class NetConnectRequestPacket { public const int HeaderSize = 14; public readonly long ConnectionTime; public readonly byte ConnectionNumber; public readonly byte[] TargetAddress; public readonly NetDataReader Data; private NetConnectRequestPacket(long connectionTime, byte connectionNumber, byte[] targetAddress, NetDataReader data) { ConnectionTime = connectionTime; ConnectionNumber = connectionNumber; TargetAddress = targetAddress; Data = data; } public static int GetProtocolId(NetPacket packet) { return BitConverter.ToInt32(packet.RawData, 1); } public static NetConnectRequestPacket FromData(NetPacket packet) { if (packet.ConnectionNumber >= NetConstants.MaxConnectionNumber) return null; //Getting new id for peer long connectionId = BitConverter.ToInt64(packet.RawData, 5); //Get target address int addrSize = packet.RawData[13]; if (addrSize != 16 && addrSize != 28) return null; byte[] addressBytes = new byte[addrSize]; Buffer.BlockCopy(packet.RawData, 14, addressBytes, 0, addrSize); // Read data and create request var reader = new NetDataReader(null, 0, 0); if (packet.Size > HeaderSize+addrSize) reader.SetSource(packet.RawData, HeaderSize + addrSize, packet.Size); return new NetConnectRequestPacket(connectionId, packet.ConnectionNumber, addressBytes, reader); } public static NetPacket Make(NetDataWriter connectData, SocketAddress addressBytes, long connectId) { //Make initial packet var packet = new NetPacket(PacketProperty.ConnectRequest, connectData.Length+addressBytes.Size); //Add data FastBitConverter.GetBytes(packet.RawData, 1, NetConstants.ProtocolId); FastBitConverter.GetBytes(packet.RawData, 5, connectId); packet.RawData[13] = (byte)addressBytes.Size; for (int i = 0; i < addressBytes.Size; i++) packet.RawData[14+i] = addressBytes[i]; Buffer.BlockCopy(connectData.Data, 0, packet.RawData, 14+addressBytes.Size, connectData.Length); return packet; } } internal sealed class NetConnectAcceptPacket { public const int Size = 11; public readonly long ConnectionId; public readonly byte ConnectionNumber; public readonly bool IsReusedPeer; private NetConnectAcceptPacket(long connectionId, byte connectionNumber, bool isReusedPeer) { ConnectionId = connectionId; ConnectionNumber = connectionNumber; IsReusedPeer = isReusedPeer; } public static NetConnectAcceptPacket FromData(NetPacket packet) { if (packet.Size > Size) return null; long connectionId = BitConverter.ToInt64(packet.RawData, 1); //check connect num byte connectionNumber = packet.RawData[9]; if (connectionNumber >= NetConstants.MaxConnectionNumber) return null; //check reused flag byte isReused = packet.RawData[10]; if (isReused > 1) return null; return new NetConnectAcceptPacket(connectionId, connectionNumber, isReused == 1); } public static NetPacket Make(long connectId, byte connectNum, bool reusedPeer) { var packet = new NetPacket(PacketProperty.ConnectAccept, 0); FastBitConverter.GetBytes(packet.RawData, 1, connectId); packet.RawData[9] = connectNum; packet.RawData[10] = (byte)(reusedPeer ? 1 : 0); return packet; } } } ================================================ FILE: Assets/Plugins/LiteNetLib/NetPacket.cs.meta ================================================ fileFormatVersion: 2 guid: 7522936b42c769445b2f5d3d2558f5ed MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NetPacketPool.cs ================================================ using System; using System.Threading; namespace LiteNetLib { internal sealed class NetPacketPool { private NetPacket _head; private int _count; public NetPacket GetWithData(PacketProperty property, byte[] data, int start, int length) { int headerSize = NetPacket.GetHeaderSize(property); NetPacket packet = GetPacket(length + headerSize); packet.Property = property; Buffer.BlockCopy(data, start, packet.RawData, headerSize, length); return packet; } //Get packet with size public NetPacket GetWithProperty(PacketProperty property, int size) { NetPacket packet = GetPacket(size + NetPacket.GetHeaderSize(property)); packet.Property = property; return packet; } public NetPacket GetWithProperty(PacketProperty property) { NetPacket packet = GetPacket(NetPacket.GetHeaderSize(property)); packet.Property = property; return packet; } public NetPacket GetPacket(int size) { if (size > NetConstants.MaxPacketSize) return new NetPacket(size); NetPacket packet; do { packet = _head; if (packet == null) return new NetPacket(size); } while (packet != Interlocked.CompareExchange(ref _head, packet.Next, packet)); _count--; packet.Size = size; if (packet.RawData.Length < size) packet.RawData = new byte[size]; return packet; } public void Recycle(NetPacket packet) { if (packet.RawData.Length > NetConstants.MaxPacketSize || _count >= NetConstants.PacketPoolSize) { //Don't pool big packets. Save memory return; } _count++; //Clean fragmented flag packet.RawData[0] = 0; do { packet.Next = _head; } while (packet.Next != Interlocked.CompareExchange(ref _head, packet, packet.Next)); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/NetPacketPool.cs.meta ================================================ fileFormatVersion: 2 guid: 779e0db7afc7c6e4eb88b849e7c3eced MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NetPeer.cs ================================================ #if DEBUG #define STATS_ENABLED #endif using System; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Threading; using LiteNetLib.Utils; namespace LiteNetLib { /// /// Peer connection state /// [Flags] public enum ConnectionState : byte { Outgoing = 1 << 1, Connected = 1 << 2, ShutdownRequested = 1 << 3, Disconnected = 1 << 4, Any = Outgoing | Connected | ShutdownRequested } internal enum ConnectRequestResult { None, P2PLose, //when peer connecting Reconnection, //when peer was connected NewConnection //when peer was disconnected } internal enum DisconnectResult { None, Reject, Disconnect } internal enum ShutdownResult { None, Success, WasConnected } /// /// Network peer. Main purpose is sending messages to specific peer. /// public class NetPeer { //Ping and RTT private int _rtt; private int _avgRtt; private int _rttCount; private double _resendDelay = 27.0; private int _pingSendTimer; private int _rttResetTimer; private readonly Stopwatch _pingTimer = new Stopwatch(); private int _timeSinceLastPacket; private long _remoteDelta; //Common private readonly NetPacketPool _packetPool; private readonly object _shutdownLock = new object(); internal volatile NetPeer NextPeer; internal NetPeer PrevPeer; internal byte ConnectionNum { get { return _connectNum; } private set { _connectNum = value; _mergeData.ConnectionNumber = value; _pingPacket.ConnectionNumber = value; _pongPacket.ConnectionNumber = value; } } //Channels private readonly Queue _unreliableChannel; private readonly BaseChannel[] _channels; private BaseChannel _headChannel; //MTU private int _mtu; private int _mtuIdx; private bool _finishMtu; private int _mtuCheckTimer; private int _mtuCheckAttempts; private const int MtuCheckDelay = 1000; private const int MaxMtuCheckAttempts = 4; private readonly object _mtuMutex = new object(); //Fragment private class IncomingFragments { public NetPacket[] Fragments; public int ReceivedCount; public int TotalSize; public byte ChannelId; } private int _fragmentId; private readonly Dictionary _holdedFragments; private readonly Dictionary _deliveredFragments; //Merging private readonly NetPacket _mergeData; private int _mergePos; private int _mergeCount; //Connection private int _connectAttempts; private int _connectTimer; private long _connectTime; private byte _connectNum; private ConnectionState _connectionState; private NetPacket _shutdownPacket; private const int ShutdownDelay = 300; private int _shutdownTimer; private readonly NetPacket _pingPacket; private readonly NetPacket _pongPacket; private readonly NetPacket _connectRequestPacket; private readonly NetPacket _connectAcceptPacket; /// /// Peer ip address and port /// public readonly IPEndPoint EndPoint; /// /// Peer parent NetManager /// public readonly NetManager NetManager; /// /// Current connection state /// public ConnectionState ConnectionState { get { return _connectionState; } } /// /// Connection time for internal purposes /// internal long ConnectTime { get { return _connectTime; } } /// /// Peer id can be used as key in your dictionary of peers /// public readonly int Id; /// /// Current ping in milliseconds /// public int Ping { get { return _avgRtt/2; } } /// /// Current MTU - Maximum Transfer Unit ( maximum udp packet size without fragmentation ) /// public int Mtu { get { return _mtu; } } /// /// Delta with remote time in ticks (not accurate) /// positive - remote time > our time /// public long RemoteTimeDelta { get { return _remoteDelta; } } /// /// Remote UTC time (not accurate) /// public DateTime RemoteUtcTime { get { return new DateTime(DateTime.UtcNow.Ticks + _remoteDelta); } } /// /// Time since last packet received (including internal library packets) /// public int TimeSinceLastPacket { get { return _timeSinceLastPacket; } } internal double ResendDelay { get { return _resendDelay; } } /// /// Application defined object containing data about the connection /// public object Tag; /// /// Statistics of peer connection /// public readonly NetStatistics Statistics; //incoming connection constructor internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id) { Id = id; Statistics = new NetStatistics(); _packetPool = netManager.NetPacketPool; NetManager = netManager; SetMtu(0); EndPoint = remoteEndPoint; _connectionState = ConnectionState.Connected; _mergeData = new NetPacket(PacketProperty.Merged, NetConstants.MaxPacketSize); _pongPacket = new NetPacket(PacketProperty.Pong, 0); _pingPacket = new NetPacket(PacketProperty.Ping, 0) {Sequence = 1}; _unreliableChannel = new Queue(64); _headChannel = null; _holdedFragments = new Dictionary(); _deliveredFragments = new Dictionary(); _channels = new BaseChannel[netManager.ChannelsCount * 4]; } private void SetMtu(int mtuIdx) { _mtu = NetConstants.PossibleMtu[mtuIdx] - NetManager.ExtraPacketSizeForLayer; } /// /// Returns packets count in queue for reliable channel /// /// number of channel 0-63 /// type of channel ReliableOrdered or ReliableUnordered /// packets count in channel queue public int GetPacketsCountInReliableQueue(byte channelNumber, bool ordered) { int idx = channelNumber * 4 + (byte) (ordered ? DeliveryMethod.ReliableOrdered : DeliveryMethod.ReliableUnordered); var channel = _channels[idx]; return channel != null ? ((ReliableChannel)channel).PacketsInQueue : 0; } private BaseChannel CreateChannel(byte idx) { BaseChannel newChannel = _channels[idx]; if (newChannel != null) return newChannel; switch ((DeliveryMethod)(idx % 4)) { case DeliveryMethod.ReliableUnordered: newChannel = new ReliableChannel(this, false, idx); break; case DeliveryMethod.Sequenced: newChannel = new SequencedChannel(this, false, idx); break; case DeliveryMethod.ReliableOrdered: newChannel = new ReliableChannel(this, true, idx); break; case DeliveryMethod.ReliableSequenced: newChannel = new SequencedChannel(this, true, idx); break; } BaseChannel prevChannel = Interlocked.CompareExchange(ref _channels[idx], newChannel, null); if (prevChannel != null) return prevChannel; BaseChannel headChannel; do { headChannel = _headChannel; newChannel.Next = headChannel; } while (Interlocked.CompareExchange(ref _headChannel, newChannel, headChannel) != headChannel); return newChannel; } //"Connect to" constructor internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id, byte connectNum, NetDataWriter connectData) : this(netManager, remoteEndPoint, id) { _connectTime = DateTime.UtcNow.Ticks; _connectionState = ConnectionState.Outgoing; ConnectionNum = connectNum; //Make initial packet _connectRequestPacket = NetConnectRequestPacket.Make(connectData, remoteEndPoint.Serialize(), _connectTime); _connectRequestPacket.ConnectionNumber = connectNum; //Send request NetManager.SendRaw(_connectRequestPacket, EndPoint); NetDebug.Write(NetLogLevel.Trace, "[CC] ConnectId: {0}, ConnectNum: {1}", _connectTime, connectNum); } //"Accept" incoming constructor internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id, long connectId, byte connectNum) : this(netManager, remoteEndPoint, id) { _connectTime = connectId; _connectionState = ConnectionState.Connected; ConnectionNum = connectNum; //Make initial packet _connectAcceptPacket = NetConnectAcceptPacket.Make(_connectTime, connectNum, false); //Send NetManager.SendRaw(_connectAcceptPacket, EndPoint); NetDebug.Write(NetLogLevel.Trace, "[CC] ConnectId: {0}", _connectTime); } //Reject internal void Reject(long connectionId, byte connectionNumber, byte[] data, int start, int length) { _connectTime = connectionId; _connectNum = connectionNumber; Shutdown(data, start, length, false); } internal bool ProcessConnectAccept(NetConnectAcceptPacket packet) { if (_connectionState != ConnectionState.Outgoing) return false; //check connection id if (packet.ConnectionId != _connectTime) { NetDebug.Write(NetLogLevel.Trace, "[NC] Invalid connectId: {0}", _connectTime); return false; } //check connect num ConnectionNum = packet.ConnectionNumber; NetDebug.Write(NetLogLevel.Trace, "[NC] Received connection accept"); Interlocked.Exchange(ref _timeSinceLastPacket, 0); _connectionState = ConnectionState.Connected; return true; } /// /// Gets maximum size of packet that will be not fragmented. /// /// Type of packet that you want send /// size in bytes public int GetMaxSinglePacketSize(DeliveryMethod options) { return _mtu - NetPacket.GetHeaderSize(options == DeliveryMethod.Unreliable ? PacketProperty.Unreliable : PacketProperty.Channeled); } /// /// Send data to peer with delivery event called /// /// Data /// Number of channel (from 0 to channelsCount - 1) /// Delivery method (reliable, unreliable, etc.) /// User data that will be received in DeliveryEvent /// /// If you trying to send unreliable packet type /// public void SendWithDeliveryEvent(byte[] data, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); SendInternal(data, 0, data.Length, channelNumber, deliveryMethod, userData); } /// /// Send data to peer with delivery event called /// /// Data /// Start of data /// Length of data /// Number of channel (from 0 to channelsCount - 1) /// Delivery method (reliable, unreliable, etc.) /// User data that will be received in DeliveryEvent /// /// If you trying to send unreliable packet type /// public void SendWithDeliveryEvent(byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); SendInternal(data, start, length, channelNumber, deliveryMethod, userData); } /// /// Send data to peer with delivery event called /// /// Data /// Number of channel (from 0 to channelsCount - 1) /// Delivery method (reliable, unreliable, etc.) /// User data that will be received in DeliveryEvent /// /// If you trying to send unreliable packet type /// public void SendWithDeliveryEvent(NetDataWriter dataWriter, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); SendInternal(dataWriter.Data, 0, dataWriter.Length, channelNumber, deliveryMethod, userData); } /// /// Send data to peer (channel - 0) /// /// Data /// Send options (reliable, unreliable, etc.) /// /// If size exceeds maximum limit: /// MTU - headerSize bytes for Unreliable /// Fragment count exceeded ushort.MaxValue /// public void Send(byte[] data, DeliveryMethod deliveryMethod) { SendInternal(data, 0, data.Length, 0, deliveryMethod, null); } /// /// Send data to peer (channel - 0) /// /// DataWriter with data /// Send options (reliable, unreliable, etc.) /// /// If size exceeds maximum limit: /// MTU - headerSize bytes for Unreliable /// Fragment count exceeded ushort.MaxValue /// public void Send(NetDataWriter dataWriter, DeliveryMethod deliveryMethod) { SendInternal(dataWriter.Data, 0, dataWriter.Length, 0, deliveryMethod, null); } /// /// Send data to peer (channel - 0) /// /// Data /// Start of data /// Length of data /// Send options (reliable, unreliable, etc.) /// /// If size exceeds maximum limit: /// MTU - headerSize bytes for Unreliable /// Fragment count exceeded ushort.MaxValue /// public void Send(byte[] data, int start, int length, DeliveryMethod options) { SendInternal(data, start, length, 0, options, null); } /// /// Send data to peer /// /// Data /// Number of channel (from 0 to channelsCount - 1) /// Send options (reliable, unreliable, etc.) /// /// If size exceeds maximum limit: /// MTU - headerSize bytes for Unreliable /// Fragment count exceeded ushort.MaxValue /// public void Send(byte[] data, byte channelNumber, DeliveryMethod deliveryMethod) { SendInternal(data, 0, data.Length, channelNumber, deliveryMethod, null); } /// /// Send data to peer /// /// DataWriter with data /// Number of channel (from 0 to channelsCount - 1) /// Send options (reliable, unreliable, etc.) /// /// If size exceeds maximum limit: /// MTU - headerSize bytes for Unreliable /// Fragment count exceeded ushort.MaxValue /// public void Send(NetDataWriter dataWriter, byte channelNumber, DeliveryMethod deliveryMethod) { SendInternal(dataWriter.Data, 0, dataWriter.Length, channelNumber, deliveryMethod, null); } /// /// Send data to peer /// /// Data /// Start of data /// Length of data /// Number of channel (from 0 to channelsCount - 1) /// Delivery method (reliable, unreliable, etc.) /// /// If size exceeds maximum limit: /// MTU - headerSize bytes for Unreliable /// Fragment count exceeded ushort.MaxValue /// public void Send(byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod) { SendInternal(data, start, length, channelNumber, deliveryMethod, null); } private void SendInternal( byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (_connectionState != ConnectionState.Connected || channelNumber >= _channels.Length) return; //Select channel PacketProperty property; BaseChannel channel = null; if (deliveryMethod == DeliveryMethod.Unreliable) { property = PacketProperty.Unreliable; } else { property = PacketProperty.Channeled; channel = CreateChannel((byte)(channelNumber*4 + (byte)deliveryMethod)); } //Prepare NetDebug.Write("[RS]Packet: " + property); //Check fragmentation int headerSize = NetPacket.GetHeaderSize(property); //Save mtu for multithread int mtu = _mtu; if (length + headerSize > mtu) { //if cannot be fragmented if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) throw new TooBigPacketException("Unreliable packet size exceeded maximum of " + (mtu - headerSize) + " bytes"); int packetFullSize = mtu - headerSize; int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize; int totalPackets = length / packetDataSize + (length % packetDataSize == 0 ? 0 : 1); NetDebug.Write("FragmentSend:\n" + " MTU: {0}\n" + " headerSize: {1}\n" + " packetFullSize: {2}\n" + " packetDataSize: {3}\n" + " totalPackets: {4}", mtu, headerSize, packetFullSize, packetDataSize, totalPackets); if (totalPackets > ushort.MaxValue) throw new TooBigPacketException("Data was split in " + totalPackets + " fragments, which exceeds " + ushort.MaxValue); ushort currentFragmentId = (ushort)Interlocked.Increment(ref _fragmentId); for(ushort partIdx = 0; partIdx < totalPackets; partIdx++) { int sendLength = length > packetDataSize ? packetDataSize : length; NetPacket p = _packetPool.GetPacket(headerSize + sendLength + NetConstants.FragmentHeaderSize); p.Property = property; p.UserData = userData; p.FragmentId = currentFragmentId; p.FragmentPart = partIdx; p.FragmentsTotal = (ushort)totalPackets; p.MarkFragmented(); Buffer.BlockCopy(data, partIdx * packetDataSize, p.RawData, NetConstants.FragmentedHeaderTotalSize, sendLength); channel.AddToQueue(p); length -= sendLength; } return; } //Else just send NetPacket packet = _packetPool.GetPacket(headerSize + length); packet.Property = property; Buffer.BlockCopy(data, start, packet.RawData, headerSize, length); packet.UserData = userData; if (channel == null) //unreliable { lock(_unreliableChannel) _unreliableChannel.Enqueue(packet); } else { channel.AddToQueue(packet); } } public void Disconnect(byte[] data) { NetManager.DisconnectPeer(this, data); } public void Disconnect(NetDataWriter writer) { NetManager.DisconnectPeer(this, writer); } public void Disconnect(byte[] data, int start, int count) { NetManager.DisconnectPeer(this, data, start, count); } public void Disconnect() { NetManager.DisconnectPeer(this); } internal DisconnectResult ProcessDisconnect(NetPacket packet) { if ((_connectionState == ConnectionState.Connected || _connectionState == ConnectionState.Outgoing) && packet.Size >= 9 && BitConverter.ToInt64(packet.RawData, 1) == _connectTime && packet.ConnectionNumber == _connectNum) { return _connectionState == ConnectionState.Connected ? DisconnectResult.Disconnect : DisconnectResult.Reject; } return DisconnectResult.None; } internal ShutdownResult Shutdown(byte[] data, int start, int length, bool force) { lock (_shutdownLock) { //trying to shutdown already disconnected if (_connectionState == ConnectionState.Disconnected || _connectionState == ConnectionState.ShutdownRequested) { return ShutdownResult.None; } var result = _connectionState == ConnectionState.Connected ? ShutdownResult.WasConnected : ShutdownResult.Success; //don't send anything if (force) { _connectionState = ConnectionState.Disconnected; return result; } //reset time for reconnect protection Interlocked.Exchange(ref _timeSinceLastPacket, 0); //send shutdown packet _shutdownPacket = new NetPacket(PacketProperty.Disconnect, length) {ConnectionNumber = _connectNum}; FastBitConverter.GetBytes(_shutdownPacket.RawData, 1, _connectTime); if (_shutdownPacket.Size >= _mtu) { //Drop additional data NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU - 8!"); } else if (data != null && length > 0) { Buffer.BlockCopy(data, start, _shutdownPacket.RawData, 9, length); } _connectionState = ConnectionState.ShutdownRequested; NetDebug.Write("[Peer] Send disconnect"); NetManager.SendRaw(_shutdownPacket, EndPoint); return result; } } private void UpdateRoundTripTime(int roundTripTime) { _rtt += roundTripTime; _rttCount++; _avgRtt = _rtt/_rttCount; _resendDelay = 25.0 + _avgRtt * 2.1; // 25 ms + double rtt } internal void AddReliablePacket(DeliveryMethod method, NetPacket p) { if (p.IsFragmented) { NetDebug.Write("Fragment. Id: {0}, Part: {1}, Total: {2}", p.FragmentId, p.FragmentPart, p.FragmentsTotal); //Get needed array from dictionary ushort packetFragId = p.FragmentId; IncomingFragments incomingFragments; if (!_holdedFragments.TryGetValue(packetFragId, out incomingFragments)) { incomingFragments = new IncomingFragments { Fragments = new NetPacket[p.FragmentsTotal], ChannelId = p.ChannelId }; _holdedFragments.Add(packetFragId, incomingFragments); } //Cache var fragments = incomingFragments.Fragments; //Error check if (p.FragmentPart >= fragments.Length || fragments[p.FragmentPart] != null || p.ChannelId != incomingFragments.ChannelId) { _packetPool.Recycle(p); NetDebug.WriteError("Invalid fragment packet"); return; } //Fill array fragments[p.FragmentPart] = p; //Increase received fragments count incomingFragments.ReceivedCount++; //Increase total size incomingFragments.TotalSize += p.Size - NetConstants.FragmentedHeaderTotalSize; //Check for finish if (incomingFragments.ReceivedCount != fragments.Length) return; //just simple packet NetPacket resultingPacket = _packetPool.GetPacket(incomingFragments.TotalSize); int firstFragmentSize = fragments[0].Size - NetConstants.FragmentedHeaderTotalSize; for (int i = 0; i < incomingFragments.ReceivedCount; i++) { var fragment = fragments[i]; //Create resulting big packet Buffer.BlockCopy( fragment.RawData, NetConstants.FragmentedHeaderTotalSize, resultingPacket.RawData, firstFragmentSize * i, fragment.Size - NetConstants.FragmentedHeaderTotalSize); //Free memory _packetPool.Recycle(fragment); } Array.Clear(fragments, 0, incomingFragments.ReceivedCount); //Send to process NetManager.CreateReceiveEvent(resultingPacket, method, 0, this); //Clear memory _holdedFragments.Remove(packetFragId); } else //Just simple packet { NetManager.CreateReceiveEvent(p, method, NetConstants.ChanneledHeaderSize, this); } } private void ProcessMtuPacket(NetPacket packet) { //header + int if (packet.Size < NetConstants.PossibleMtu[0]) return; //first stage check (mtu check and mtu ok) int receivedMtu = BitConverter.ToInt32(packet.RawData, 1); int endMtuCheck = BitConverter.ToInt32(packet.RawData, packet.Size - 4); if (receivedMtu != packet.Size || receivedMtu != endMtuCheck || receivedMtu > NetConstants.MaxPacketSize) { NetDebug.WriteError("[MTU] Broken packet. RMTU {0}, EMTU {1}, PSIZE {2}", receivedMtu, endMtuCheck, packet.Size); return; } if (packet.Property == PacketProperty.MtuCheck) { _mtuCheckAttempts = 0; NetDebug.Write("[MTU] check. send back: " + receivedMtu); packet.Property = PacketProperty.MtuOk; NetManager.SendRawAndRecycle(packet, EndPoint); } else if(receivedMtu > _mtu && !_finishMtu) //MtuOk { //invalid packet if (receivedMtu != NetConstants.PossibleMtu[_mtuIdx + 1]) return; lock (_mtuMutex) { _mtuIdx++; SetMtu(_mtuIdx); } //if maxed - finish. if (_mtuIdx == NetConstants.PossibleMtu.Length - 1) _finishMtu = true; NetDebug.Write("[MTU] ok. Increase to: " + _mtu); } } private void UpdateMtuLogic(int deltaTime) { if (_finishMtu) return; _mtuCheckTimer += deltaTime; if (_mtuCheckTimer < MtuCheckDelay) return; _mtuCheckTimer = 0; _mtuCheckAttempts++; if (_mtuCheckAttempts >= MaxMtuCheckAttempts) { _finishMtu = true; return; } lock (_mtuMutex) { if (_mtuIdx >= NetConstants.PossibleMtu.Length - 1) return; //Send increased packet int newMtu = NetConstants.PossibleMtu[_mtuIdx + 1]; var p = _packetPool.GetPacket(newMtu); p.Property = PacketProperty.MtuCheck; FastBitConverter.GetBytes(p.RawData, 1, newMtu); //place into start FastBitConverter.GetBytes(p.RawData, p.Size - 4, newMtu);//and end of packet //Must check result for MTU fix if (NetManager.SendRawAndRecycle(p, EndPoint) <= 0) _finishMtu = true; } } internal ConnectRequestResult ProcessConnectRequest(NetConnectRequestPacket connRequest) { //current or new request switch (_connectionState) { //P2P case case ConnectionState.Outgoing: //fast check if (connRequest.ConnectionTime < _connectTime) { return ConnectRequestResult.P2PLose; } //slow rare case check else if (connRequest.ConnectionTime == _connectTime) { var remoteBytes = EndPoint.Serialize(); var localBytes = connRequest.TargetAddress; for (int i = remoteBytes.Size-1; i >= 0; i--) { byte rb = remoteBytes[i]; if (rb == localBytes[i]) continue; if (rb < localBytes[i]) return ConnectRequestResult.P2PLose; } } break; case ConnectionState.Connected: //Old connect request if (connRequest.ConnectionTime == _connectTime) { //just reply accept NetManager.SendRaw(_connectAcceptPacket, EndPoint); } //New connect request else if (connRequest.ConnectionTime > _connectTime) { return ConnectRequestResult.Reconnection; } break; case ConnectionState.Disconnected: case ConnectionState.ShutdownRequested: if (connRequest.ConnectionTime >= _connectTime) return ConnectRequestResult.NewConnection; break; } return ConnectRequestResult.None; } //Process incoming packet internal void ProcessPacket(NetPacket packet) { //not initialized if (_connectionState == ConnectionState.Outgoing || _connectionState == ConnectionState.Disconnected) { _packetPool.Recycle(packet); return; } if (packet.Property == PacketProperty.ShutdownOk) { if (_connectionState == ConnectionState.ShutdownRequested) _connectionState = ConnectionState.Disconnected; _packetPool.Recycle(packet); return; } if (packet.ConnectionNumber != _connectNum) { NetDebug.Write(NetLogLevel.Trace, "[RR]Old packet"); _packetPool.Recycle(packet); return; } Interlocked.Exchange(ref _timeSinceLastPacket, 0); NetDebug.Write("[RR]PacketProperty: {0}", packet.Property); switch (packet.Property) { case PacketProperty.Merged: int pos = NetConstants.HeaderSize; while (pos < packet.Size) { ushort size = BitConverter.ToUInt16(packet.RawData, pos); pos += 2; NetPacket mergedPacket = _packetPool.GetPacket(size); if (!mergedPacket.FromBytes(packet.RawData, pos, size)) { _packetPool.Recycle(packet); break; } pos += size; ProcessPacket(mergedPacket); } break; //If we get ping, send pong case PacketProperty.Ping: if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pongPacket.Sequence) > 0) { NetDebug.Write("[PP]Ping receive, send pong"); FastBitConverter.GetBytes(_pongPacket.RawData, 3, DateTime.UtcNow.Ticks); _pongPacket.Sequence = packet.Sequence; NetManager.SendRaw(_pongPacket, EndPoint); } _packetPool.Recycle(packet); break; //If we get pong, calculate ping time and rtt case PacketProperty.Pong: if (packet.Sequence == _pingPacket.Sequence) { _pingTimer.Stop(); int elapsedMs = (int)_pingTimer.ElapsedMilliseconds; _remoteDelta = BitConverter.ToInt64(packet.RawData, 3) + (elapsedMs * TimeSpan.TicksPerMillisecond ) / 2 - DateTime.UtcNow.Ticks; UpdateRoundTripTime(elapsedMs); NetManager.ConnectionLatencyUpdated(this, elapsedMs / 2); NetDebug.Write("[PP]Ping: {0} - {1} - {2}", packet.Sequence, elapsedMs, _remoteDelta); } _packetPool.Recycle(packet); break; case PacketProperty.Ack: case PacketProperty.Channeled: if (packet.ChannelId > _channels.Length) { _packetPool.Recycle(packet); break; } var channel = _channels[packet.ChannelId] ?? (packet.Property == PacketProperty.Ack ? null : CreateChannel(packet.ChannelId)); if (channel != null) { if (!channel.ProcessPacket(packet)) _packetPool.Recycle(packet); } break; //Simple packet without acks case PacketProperty.Unreliable: NetManager.CreateReceiveEvent(packet, DeliveryMethod.Unreliable, NetConstants.HeaderSize, this); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; default: NetDebug.WriteError("Error! Unexpected packet type: " + packet.Property); break; } } private void SendMerged() { if (_mergeCount == 0) return; int bytesSent; if (_mergeCount > 1) { NetDebug.Write("[P]Send merged: " + _mergePos + ", count: " + _mergeCount); bytesSent = NetManager.SendRaw(_mergeData.RawData, 0, NetConstants.HeaderSize + _mergePos, EndPoint); } else { //Send without length information and merging bytesSent = NetManager.SendRaw(_mergeData.RawData, NetConstants.HeaderSize + 2, _mergePos - 2, EndPoint); } if (NetManager.EnableStatistics) { Statistics.IncrementPacketsSent(); Statistics.AddBytesSent(bytesSent); } _mergePos = 0; _mergeCount = 0; } internal void SendUserData(NetPacket packet) { packet.ConnectionNumber = _connectNum; int mergedPacketSize = NetConstants.HeaderSize + packet.Size + 2; const int sizeTreshold = 20; if (mergedPacketSize + sizeTreshold >= _mtu) { NetDebug.Write(NetLogLevel.Trace, "[P]SendingPacket: " + packet.Property); int bytesSent = NetManager.SendRaw(packet, EndPoint); if (NetManager.EnableStatistics) { Statistics.IncrementPacketsSent(); Statistics.AddBytesSent(bytesSent); } return; } if (_mergePos + mergedPacketSize > _mtu) SendMerged(); FastBitConverter.GetBytes(_mergeData.RawData, _mergePos + NetConstants.HeaderSize, (ushort)packet.Size); Buffer.BlockCopy(packet.RawData, 0, _mergeData.RawData, _mergePos + NetConstants.HeaderSize + 2, packet.Size); _mergePos += packet.Size + 2; _mergeCount++; //DebugWriteForce("Merged: " + _mergePos + "/" + (_mtu - 2) + ", count: " + _mergeCount); } internal void Update(int deltaTime) { Interlocked.Add(ref _timeSinceLastPacket, deltaTime); switch (_connectionState) { case ConnectionState.Connected: if (_timeSinceLastPacket > NetManager.DisconnectTimeout) { NetDebug.Write( "[UPDATE] Disconnect by timeout: {0} > {1}", _timeSinceLastPacket, NetManager.DisconnectTimeout); NetManager.DisconnectPeerForce(this, DisconnectReason.Timeout, 0, null); return; } break; case ConnectionState.ShutdownRequested: if (_timeSinceLastPacket > NetManager.DisconnectTimeout) { _connectionState = ConnectionState.Disconnected; } else { _shutdownTimer += deltaTime; if (_shutdownTimer >= ShutdownDelay) { _shutdownTimer = 0; NetManager.SendRaw(_shutdownPacket, EndPoint); } } return; case ConnectionState.Outgoing: _connectTimer += deltaTime; if (_connectTimer > NetManager.ReconnectDelay) { _connectTimer = 0; _connectAttempts++; if (_connectAttempts > NetManager.MaxConnectAttempts) { NetManager.DisconnectPeerForce(this, DisconnectReason.ConnectionFailed, 0, null); return; } //else send connect again NetManager.SendRaw(_connectRequestPacket, EndPoint); } return; case ConnectionState.Disconnected: return; } //Send ping _pingSendTimer += deltaTime; if (_pingSendTimer >= NetManager.PingInterval) { NetDebug.Write("[PP] Send ping..."); //reset timer _pingSendTimer = 0; //send ping _pingPacket.Sequence++; //ping timeout if (_pingTimer.IsRunning) UpdateRoundTripTime((int)_pingTimer.ElapsedMilliseconds); _pingTimer.Reset(); _pingTimer.Start(); NetManager.SendRaw(_pingPacket, EndPoint); } //RTT - round trip time _rttResetTimer += deltaTime; if (_rttResetTimer >= NetManager.PingInterval * 3) { _rttResetTimer = 0; _rtt = _avgRtt; _rttCount = 1; } UpdateMtuLogic(deltaTime); //Pending send BaseChannel currentChannel = _headChannel; while (currentChannel != null) { currentChannel.SendNextPackets(); currentChannel = currentChannel.Next; } lock (_unreliableChannel) { while (_unreliableChannel.Count > 0) { NetPacket packet = _unreliableChannel.Dequeue(); SendUserData(packet); NetManager.NetPacketPool.Recycle(packet); } } SendMerged(); } //For reliable channel internal void RecycleAndDeliver(NetPacket packet) { if (packet.UserData != null) { if (packet.IsFragmented) { ushort fragCount; _deliveredFragments.TryGetValue(packet.FragmentId, out fragCount); fragCount++; if (fragCount == packet.FragmentsTotal) { NetManager.MessageDelivered(this, packet.UserData); _deliveredFragments.Remove(packet.FragmentId); } else { _deliveredFragments[packet.FragmentId] = fragCount; } } else { NetManager.MessageDelivered(this, packet.UserData); } packet.UserData = null; } _packetPool.Recycle(packet); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/NetPeer.cs.meta ================================================ fileFormatVersion: 2 guid: 05dcc73648ef4304f91c2a22996d3652 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NetSocket.cs ================================================ #if UNITY_5_3_OR_NEWER #define UNITY #if UNITY_IOS && !UNITY_EDITOR using UnityEngine; #endif #endif #if NETSTANDARD || NETCOREAPP using System.Runtime.InteropServices; #endif using System; using System.Net; using System.Net.Sockets; using System.Threading; namespace LiteNetLib { #if UNITY_IOS && !UNITY_EDITOR public class UnitySocketFix : MonoBehaviour { internal IPAddress BindAddrIPv4; internal IPAddress BindAddrIPv6; internal bool Reuse; internal IPv6Mode IPv6; internal int Port; internal bool Paused; internal NetSocket Socket; private void Update() { if (Socket == null) Destroy(gameObject); } private void OnApplicationPause(bool pause) { if (Socket == null) return; if (pause) { Paused = true; Socket.Close(true); } else if (Paused) { if (!Socket.Bind(BindAddrIPv4, BindAddrIPv6, Port, Reuse, IPv6)) { NetDebug.WriteError("[S] Cannot restore connection \"{0}\",\"{1}\" port {2}", BindAddrIPv4, BindAddrIPv6, Port); Socket.OnErrorRestore(); } } } } #endif internal interface INetSocketListener { void OnMessageReceived(byte[] data, int length, SocketError errorCode, IPEndPoint remoteEndPoint); } internal sealed class NetSocket { public const int ReceivePollingTime = 500000; //0.5 second private Socket _udpSocketv4; private Socket _udpSocketv6; private Thread _threadv4; private Thread _threadv6; private readonly INetSocketListener _listener; private const int SioUdpConnreset = -1744830452; //SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12 private static readonly IPAddress MulticastAddressV6 = IPAddress.Parse("ff02::1"); internal static readonly bool IPv6Support; #if UNITY_IOS && !UNITY_EDITOR private UnitySocketFix _unitySocketFix; public void OnErrorRestore() { Close(false); _listener.OnMessageReceived(null, 0, SocketError.NotConnected, new IPEndPoint(0,0)); } #endif public int LocalPort { get; private set; } public volatile bool IsRunning; public short Ttl { get { #if UNITY_SWITCH return 0; #else if (_udpSocketv4.AddressFamily == AddressFamily.InterNetworkV6) return (short)_udpSocketv4.GetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.HopLimit); return _udpSocketv4.Ttl; #endif } set { #if !UNITY_SWITCH if (_udpSocketv4.AddressFamily == AddressFamily.InterNetworkV6) _udpSocketv4.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.HopLimit, value); else _udpSocketv4.Ttl = value; #endif } } static NetSocket() { #if DISABLE_IPV6 || (!UNITY_EDITOR && ENABLE_IL2CPP && !UNITY_2018_3_OR_NEWER) IPv6Support = false; #elif !UNITY_2019_1_OR_NEWER && !UNITY_2018_4_OR_NEWER && (!UNITY_EDITOR && ENABLE_IL2CPP && UNITY_2018_3_OR_NEWER) string version = UnityEngine.Application.unityVersion; IPv6Support = Socket.OSSupportsIPv6 && int.Parse(version.Remove(version.IndexOf('f')).Split('.')[2]) >= 6; #elif UNITY_2018_2_OR_NEWER IPv6Support = Socket.OSSupportsIPv6; #elif UNITY #pragma warning disable 618 IPv6Support = Socket.SupportsIPv6; #pragma warning restore 618 #else IPv6Support = Socket.OSSupportsIPv6; #endif } public NetSocket(INetSocketListener listener) { _listener = listener; } private bool IsActive() { #if UNITY_IOS && !UNITY_EDITOR var unitySocketFix = _unitySocketFix; //save for multithread if (unitySocketFix != null && unitySocketFix.Paused) return false; #endif return IsRunning; } private void ReceiveLogic(object state) { Socket socket = (Socket)state; EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0); byte[] receiveBuffer = new byte[NetConstants.MaxPacketSize]; while (IsActive()) { int result; //Reading data try { if (socket.Available == 0 && !socket.Poll(ReceivePollingTime, SelectMode.SelectRead)) continue; result = socket.ReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref bufferEndPoint); } catch (SocketException ex) { switch (ex.SocketErrorCode) { #if UNITY_IOS && !UNITY_EDITOR case SocketError.NotConnected: #endif case SocketError.Interrupted: case SocketError.NotSocket: return; case SocketError.ConnectionReset: case SocketError.MessageSize: case SocketError.TimedOut: NetDebug.Write(NetLogLevel.Trace, "[R]Ignored error: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString()); break; default: NetDebug.WriteError("[R]Error code: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString()); _listener.OnMessageReceived(null, 0, ex.SocketErrorCode, (IPEndPoint)bufferEndPoint); break; } continue; } catch (ObjectDisposedException) { return; } //All ok! NetDebug.Write(NetLogLevel.Trace, "[R]Received data from {0}, result: {1}", bufferEndPoint.ToString(), result); _listener.OnMessageReceived(receiveBuffer, result, 0, (IPEndPoint)bufferEndPoint); } } public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool reuseAddress, IPv6Mode ipv6Mode) { if (IsActive()) return false; bool dualMode = ipv6Mode == IPv6Mode.DualMode && IPv6Support; _udpSocketv4 = new Socket( dualMode ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); if (!BindSocket(_udpSocketv4, new IPEndPoint(dualMode ? addressIPv6 : addressIPv4, port), reuseAddress, ipv6Mode)) return false; LocalPort = ((IPEndPoint) _udpSocketv4.LocalEndPoint).Port; #if UNITY_IOS && !UNITY_EDITOR if (_unitySocketFix == null) { var unityFixObj = new GameObject("LiteNetLib_UnitySocketFix"); GameObject.DontDestroyOnLoad(unityFixObj); _unitySocketFix = unityFixObj.AddComponent(); _unitySocketFix.Socket = this; _unitySocketFix.BindAddrIPv4 = addressIPv4; _unitySocketFix.BindAddrIPv6 = addressIPv6; _unitySocketFix.Reuse = reuseAddress; _unitySocketFix.Port = LocalPort; _unitySocketFix.IPv6 = ipv6Mode; } else { _unitySocketFix.Paused = false; } #endif if (dualMode) _udpSocketv6 = _udpSocketv4; IsRunning = true; _threadv4 = new Thread(ReceiveLogic) { Name = "SocketThreadv4(" + LocalPort + ")", IsBackground = true }; _threadv4.Start(_udpSocketv4); //Check IPv6 support if (!IPv6Support || ipv6Mode != IPv6Mode.SeparateSocket) return true; _udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); //Use one port for two sockets if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort), reuseAddress, ipv6Mode)) { _threadv6 = new Thread(ReceiveLogic) { Name = "SocketThreadv6(" + LocalPort + ")", IsBackground = true }; _threadv6.Start(_udpSocketv6); } return true; } private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress, IPv6Mode ipv6Mode) { //Setup socket socket.ReceiveTimeout = 500; socket.SendTimeout = 500; socket.ReceiveBufferSize = NetConstants.SocketBufferSize; socket.SendBufferSize = NetConstants.SocketBufferSize; #if !UNITY || UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN #if NETSTANDARD || NETCOREAPP if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) #endif try { socket.IOControl(SioUdpConnreset, new byte[] { 0 }, null); } catch { //ignored } #endif try { socket.ExclusiveAddressUse = !reuseAddress; socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress); } catch { //Unity with IL2CPP throws an exception here, it doesn't matter in most cases so just ignore it } if (socket.AddressFamily == AddressFamily.InterNetwork) { Ttl = NetConstants.SocketTTL; #if NETSTANDARD || NETCOREAPP if(!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) #endif try { socket.DontFragment = true; } catch (SocketException e) { NetDebug.WriteError("[B]DontFragment error: {0}", e.SocketErrorCode); } try { socket.EnableBroadcast = true; } catch (SocketException e) { NetDebug.WriteError("[B]Broadcast error: {0}", e.SocketErrorCode); } } else //IPv6 specific { if (ipv6Mode == IPv6Mode.DualMode) { try { //Disable IPv6 only mode socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false); } catch(Exception e) { NetDebug.WriteError("[B]Bind exception (dualmode setting): {0}", e.ToString()); } } } //Bind try { socket.Bind(ep); NetDebug.Write(NetLogLevel.Trace, "[B]Successfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port); //join multicast if (socket.AddressFamily == AddressFamily.InterNetworkV6) { try { #if !UNITY socket.SetSocketOption( SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(MulticastAddressV6)); #endif } catch (Exception) { // Unity3d throws exception - ignored } } } catch (SocketException bindException) { switch (bindException.SocketErrorCode) { //IPv6 bind fix case SocketError.AddressAlreadyInUse: if (socket.AddressFamily == AddressFamily.InterNetworkV6 && ipv6Mode != IPv6Mode.DualMode) { try { //Set IPv6Only socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, true); socket.Bind(ep); } #if UNITY_2018_3_OR_NEWER catch (SocketException ex) { //because its fixed in 2018_3 NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", ex.ToString(), ex.SocketErrorCode); #else catch(SocketException) { #endif return false; } return true; } break; //hack for iOS (Unity3D) case SocketError.AddressFamilyNotSupported: return true; } NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", bindException.ToString(), bindException.SocketErrorCode); return false; } return true; } public bool SendBroadcast(byte[] data, int offset, int size, int port) { if (!IsActive()) return false; bool broadcastSuccess = false; bool multicastSuccess = false; try { broadcastSuccess = _udpSocketv4.SendTo( data, offset, size, SocketFlags.None, new IPEndPoint(IPAddress.Broadcast, port)) > 0; if (_udpSocketv6 != null) { multicastSuccess = _udpSocketv6.SendTo( data, offset, size, SocketFlags.None, new IPEndPoint(MulticastAddressV6, port)) > 0; } } catch (Exception ex) { NetDebug.WriteError("[S][MCAST]" + ex); return broadcastSuccess; } return broadcastSuccess || multicastSuccess; } public int SendTo(byte[] data, int offset, int size, IPEndPoint remoteEndPoint, ref SocketError errorCode) { if (!IsActive()) return 0; try { var socket = _udpSocketv4; if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support) socket = _udpSocketv6; int result = socket.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint); NetDebug.Write(NetLogLevel.Trace, "[S]Send packet to {0}, result: {1}", remoteEndPoint, result); return result; } catch (SocketException ex) { switch (ex.SocketErrorCode) { case SocketError.NoBufferSpaceAvailable: case SocketError.Interrupted: return 0; case SocketError.MessageSize: //do nothing break; default: NetDebug.WriteError("[S]" + ex); break; } errorCode = ex.SocketErrorCode; return -1; } catch (Exception ex) { NetDebug.WriteError("[S]" + ex); return -1; } } public void Close(bool suspend) { if (!suspend) { IsRunning = false; #if UNITY_IOS && !UNITY_EDITOR _unitySocketFix.Socket = null; _unitySocketFix = null; #endif } //cleanup dual mode if (_udpSocketv4 == _udpSocketv6) _udpSocketv6 = null; if (_udpSocketv4 != null) _udpSocketv4.Close(); if (_udpSocketv6 != null) _udpSocketv6.Close(); _udpSocketv4 = null; _udpSocketv6 = null; if (_threadv4 != null && _threadv4 != Thread.CurrentThread) _threadv4.Join(); if (_threadv6 != null && _threadv6 != Thread.CurrentThread) _threadv6.Join(); _threadv4 = null; _threadv6 = null; } } } ================================================ FILE: Assets/Plugins/LiteNetLib/NetSocket.cs.meta ================================================ fileFormatVersion: 2 guid: bbe4f2df734c49848b0f4b3f3167bb38 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NetStatistics.cs ================================================ using System.Threading; namespace LiteNetLib { public sealed class NetStatistics { private long _packetsSent; private long _packetsReceived; private long _bytesSent; private long _bytesReceived; private long _packetLoss; public long PacketsSent { get { return Interlocked.Read(ref _packetsSent); } } public long PacketsReceived { get { return Interlocked.Read(ref _packetsReceived); } } public long BytesSent { get { return Interlocked.Read(ref _bytesSent); } } public long BytesReceived { get { return Interlocked.Read(ref _bytesReceived); } } public long PacketLoss { get { return Interlocked.Read(ref _packetLoss); } } public long PacketLossPercent { get { long sent = PacketsSent, loss = PacketLoss; return sent == 0 ? 0 : loss * 100 / sent; } } public void Reset() { Interlocked.Exchange(ref _packetsSent, 0); Interlocked.Exchange(ref _packetsReceived, 0); Interlocked.Exchange(ref _bytesSent, 0); Interlocked.Exchange(ref _bytesReceived, 0); Interlocked.Exchange(ref _packetLoss, 0); } public void IncrementPacketsSent() { Interlocked.Increment(ref _packetsSent); } public void IncrementPacketsReceived() { Interlocked.Increment(ref _packetsReceived); } public void AddBytesSent(long bytesSent) { Interlocked.Add(ref _bytesSent, bytesSent); } public void AddBytesReceived(long bytesReceived) { Interlocked.Add(ref _bytesReceived, bytesReceived); } public void IncrementPacketLoss() { Interlocked.Increment(ref _packetLoss); } public void AddPacketLoss(long packetLoss) { Interlocked.Add(ref _packetLoss, packetLoss); } public override string ToString() { return string.Format( "BytesReceived: {0}\nPacketsReceived: {1}\nBytesSent: {2}\nPacketsSent: {3}\nPacketLoss: {4}\nPacketLossPercent: {5}\n", BytesReceived, PacketsReceived, BytesSent, PacketsSent, PacketLoss, PacketLossPercent); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/NetStatistics.cs.meta ================================================ fileFormatVersion: 2 guid: 2c91a08025fc3144ea528aa15dabe0e5 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/NetUtils.cs ================================================ using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Net.NetworkInformation; namespace LiteNetLib { /// /// Address type that you want to receive from NetUtils.GetLocalIp method /// [Flags] public enum LocalAddrType { IPv4 = 1, IPv6 = 2, All = IPv4 | IPv6 } /// /// Some specific network utilities /// public static class NetUtils { public static IPEndPoint MakeEndPoint(string hostStr, int port) { return new IPEndPoint(ResolveAddress(hostStr), port); } public static IPAddress ResolveAddress(string hostStr) { if(hostStr == "localhost") return IPAddress.Loopback; IPAddress ipAddress; if (!IPAddress.TryParse(hostStr, out ipAddress)) { if (NetSocket.IPv6Support) ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetworkV6); if (ipAddress == null) ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetwork); } if (ipAddress == null) throw new ArgumentException("Invalid address: " + hostStr); return ipAddress; } private static IPAddress ResolveAddress(string hostStr, AddressFamily addressFamily) { IPAddress[] addresses = ResolveAddresses(hostStr); foreach (IPAddress ip in addresses) { if (ip.AddressFamily == addressFamily) { return ip; } } return null; } private static IPAddress[] ResolveAddresses(string hostStr) { #if NETSTANDARD || NETCOREAPP var hostTask = Dns.GetHostEntryAsync(hostStr); hostTask.GetAwaiter().GetResult(); var host = hostTask.Result; #else var host = Dns.GetHostEntry(hostStr); #endif return host.AddressList; } /// /// Get all local ip addresses /// /// type of address (IPv4, IPv6 or both) /// List with all local ip addresses public static List GetLocalIpList(LocalAddrType addrType) { List targetList = new List(); GetLocalIpList(targetList, addrType); return targetList; } /// /// Get all local ip addresses (non alloc version) /// /// result list /// type of address (IPv4, IPv6 or both) public static void GetLocalIpList(IList targetList, LocalAddrType addrType) { bool ipv4 = (addrType & LocalAddrType.IPv4) == LocalAddrType.IPv4; bool ipv6 = (addrType & LocalAddrType.IPv6) == LocalAddrType.IPv6; try { foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) { //Skip loopback and disabled network interfaces if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.OperationalStatus != OperationalStatus.Up) continue; var ipProps = ni.GetIPProperties(); //Skip address without gateway if (ipProps.GatewayAddresses.Count == 0) continue; foreach (UnicastIPAddressInformation ip in ipProps.UnicastAddresses) { var address = ip.Address; if ((ipv4 && address.AddressFamily == AddressFamily.InterNetwork) || (ipv6 && address.AddressFamily == AddressFamily.InterNetworkV6)) targetList.Add(address.ToString()); } } } catch { //ignored } //Fallback mode (unity android) if (targetList.Count == 0) { IPAddress[] addresses = ResolveAddresses(Dns.GetHostName()); foreach (IPAddress ip in addresses) { if((ipv4 && ip.AddressFamily == AddressFamily.InterNetwork) || (ipv6 && ip.AddressFamily == AddressFamily.InterNetworkV6)) targetList.Add(ip.ToString()); } } if (targetList.Count == 0) { if(ipv4) targetList.Add("127.0.0.1"); if(ipv6) targetList.Add("::1"); } } private static readonly List IpList = new List(); /// /// Get first detected local ip address /// /// type of address (IPv4, IPv6 or both) /// IP address if available. Else - string.Empty public static string GetLocalIp(LocalAddrType addrType) { lock (IpList) { IpList.Clear(); GetLocalIpList(IpList, addrType); return IpList.Count == 0 ? string.Empty : IpList[0]; } } // =========================================== // Internal and debug log related stuff // =========================================== internal static void PrintInterfaceInfos() { NetDebug.WriteForce(NetLogLevel.Info, "IPv6Support: {0}", NetSocket.IPv6Support); try { foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) { foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses) { if (ip.Address.AddressFamily == AddressFamily.InterNetwork || ip.Address.AddressFamily == AddressFamily.InterNetworkV6) { NetDebug.WriteForce( NetLogLevel.Info, "Interface: {0}, Type: {1}, Ip: {2}, OpStatus: {3}", ni.Name, ni.NetworkInterfaceType.ToString(), ip.Address.ToString(), ni.OperationalStatus.ToString()); } } } } catch (Exception e) { NetDebug.WriteForce(NetLogLevel.Info, "Error while getting interface infos: {0}", e.ToString()); } } internal static int RelativeSequenceNumber(int number, int expected) { return (number - expected + NetConstants.MaxSequence + NetConstants.HalfMaxSequence) % NetConstants.MaxSequence - NetConstants.HalfMaxSequence; } } } ================================================ FILE: Assets/Plugins/LiteNetLib/NetUtils.cs.meta ================================================ fileFormatVersion: 2 guid: f348c24015ad55140beb9171b51c7540 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/ReliableChannel.cs ================================================ using System; namespace LiteNetLib { internal sealed class ReliableChannel : BaseChannel { private struct PendingPacket { private NetPacket _packet; private long _timeStamp; private bool _isSent; public override string ToString() { return _packet == null ? "Empty" : _packet.Sequence.ToString(); } public void Init(NetPacket packet) { _packet = packet; _isSent = false; } public void TrySend(long currentTime, NetPeer peer) { if (_packet == null) return; if (_isSent) //check send time { double resendDelay = peer.ResendDelay * TimeSpan.TicksPerMillisecond; double packetHoldTime = currentTime - _timeStamp; if (packetHoldTime < resendDelay) return; NetDebug.Write("[RC]Resend: {0} > {1}", (int)packetHoldTime, resendDelay); } _timeStamp = currentTime; _isSent = true; peer.SendUserData(_packet); } public bool Clear(NetPeer peer) { if (_packet != null) { peer.RecycleAndDeliver(_packet); _packet = null; return true; } return false; } } private readonly NetPacket _outgoingAcks; //for send acks private readonly PendingPacket[] _pendingPackets; //for unacked packets and duplicates private readonly NetPacket[] _receivedPackets; //for order private readonly bool[] _earlyReceived; //for unordered private int _localSeqence; private int _remoteSequence; private int _localWindowStart; private int _remoteWindowStart; private bool _mustSendAcks; private readonly DeliveryMethod _deliveryMethod; private readonly bool _ordered; private readonly int _windowSize; private const int BitsInByte = 8; private readonly byte _id; public ReliableChannel(NetPeer peer, bool ordered, byte id) : base(peer) { _id = id; _windowSize = NetConstants.DefaultWindowSize; _ordered = ordered; _pendingPackets = new PendingPacket[_windowSize]; for (int i = 0; i < _pendingPackets.Length; i++) _pendingPackets[i] = new PendingPacket(); if (_ordered) { _deliveryMethod = DeliveryMethod.ReliableOrdered; _receivedPackets = new NetPacket[_windowSize]; } else { _deliveryMethod = DeliveryMethod.ReliableUnordered; _earlyReceived = new bool[_windowSize]; } _localWindowStart = 0; _localSeqence = 0; _remoteSequence = 0; _remoteWindowStart = 0; _outgoingAcks = new NetPacket(PacketProperty.Ack, (_windowSize - 1) / BitsInByte + 2) {ChannelId = id}; } //ProcessAck in packet private void ProcessAck(NetPacket packet) { if (packet.Size != _outgoingAcks.Size) { NetDebug.Write("[PA]Invalid acks packet size"); return; } ushort ackWindowStart = packet.Sequence; int windowRel = NetUtils.RelativeSequenceNumber(_localWindowStart, ackWindowStart); if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0) { NetDebug.Write("[PA]Bad window start"); return; } //check relevance if (windowRel >= _windowSize) { NetDebug.Write("[PA]Old acks"); return; } byte[] acksData = packet.RawData; lock (_pendingPackets) { for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) { int rel = NetUtils.RelativeSequenceNumber(pendingSeq, ackWindowStart); if (rel >= _windowSize) { NetDebug.Write("[PA]REL: " + rel); break; } int pendingIdx = pendingSeq % _windowSize; int currentByte = NetConstants.ChanneledHeaderSize + pendingIdx / BitsInByte; int currentBit = pendingIdx % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { if (Peer.NetManager.EnableStatistics) { Peer.Statistics.IncrementPacketLoss(); Peer.NetManager.Statistics.IncrementPacketLoss(); } //Skip false ack NetDebug.Write("[PA]False ack: {0}", pendingSeq); continue; } if (pendingSeq == _localWindowStart) { //Move window _localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; } //clear packet if (_pendingPackets[pendingIdx].Clear(Peer)) NetDebug.Write("[PA]Removing reliableInOrder ack: {0} - true", pendingSeq); } } } public override void SendNextPackets() { if (_mustSendAcks) { _mustSendAcks = false; NetDebug.Write("[RR]SendAcks"); lock(_outgoingAcks) Peer.SendUserData(_outgoingAcks); } long currentTime = DateTime.UtcNow.Ticks; lock (_pendingPackets) { //get packets from queue lock (OutgoingQueue) { while (OutgoingQueue.Count > 0) { int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart); if (relate >= _windowSize) break; var netPacket = OutgoingQueue.Dequeue(); netPacket.Sequence = (ushort) _localSeqence; netPacket.ChannelId = _id; _pendingPackets[_localSeqence % _windowSize].Init(netPacket); _localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence; } } //send for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) _pendingPackets[pendingSeq % _windowSize].TrySend(currentTime, Peer); } } //Process incoming packet public override bool ProcessPacket(NetPacket packet) { if (packet.Property == PacketProperty.Ack) { ProcessAck(packet); return false; } int seq = packet.Sequence; if (seq >= NetConstants.MaxSequence) { NetDebug.Write("[RR]Bad sequence"); return false; } int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart); int relateSeq = NetUtils.RelativeSequenceNumber(seq, _remoteSequence); if (relateSeq > _windowSize) { NetDebug.Write("[RR]Bad sequence"); return false; } //Drop bad packets if (relate < 0) { //Too old packet doesn't ack NetDebug.Write("[RR]ReliableInOrder too old"); return false; } if (relate >= _windowSize * 2) { //Some very new packet NetDebug.Write("[RR]ReliableInOrder too new"); return false; } //If very new - move window int ackIdx; int ackByte; int ackBit; lock (_outgoingAcks) { if (relate >= _windowSize) { //New window position int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence; _outgoingAcks.Sequence = (ushort) newWindowStart; //Clean old data while (_remoteWindowStart != newWindowStart) { ackIdx = _remoteWindowStart % _windowSize; ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte; ackBit = ackIdx % BitsInByte; _outgoingAcks.RawData[ackByte] &= (byte) ~(1 << ackBit); _remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence; } } //Final stage - process valid packet //trigger acks send _mustSendAcks = true; ackIdx = seq % _windowSize; ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte; ackBit = ackIdx % BitsInByte; if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0) { NetDebug.Write("[RR]ReliableInOrder duplicate"); return false; } //save ack _outgoingAcks.RawData[ackByte] |= (byte) (1 << ackBit); } //detailed check if (seq == _remoteSequence) { NetDebug.Write("[RR]ReliableInOrder packet succes"); Peer.AddReliablePacket(_deliveryMethod, packet); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; if (_ordered) { NetPacket p; while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null) { //process holden packet _receivedPackets[_remoteSequence % _windowSize] = null; Peer.AddReliablePacket(_deliveryMethod, p); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } else { while (_earlyReceived[_remoteSequence % _windowSize]) { //process early packet _earlyReceived[_remoteSequence % _windowSize] = false; _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } return true; } //holden packet if (_ordered) { _receivedPackets[ackIdx] = packet; } else { _earlyReceived[ackIdx] = true; Peer.AddReliablePacket(_deliveryMethod, packet); } return true; } } } ================================================ FILE: Assets/Plugins/LiteNetLib/ReliableChannel.cs.meta ================================================ fileFormatVersion: 2 guid: eee991fc4aaa1f3418cf4ac524154427 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/SequencedChannel.cs ================================================ using System; namespace LiteNetLib { internal sealed class SequencedChannel : BaseChannel { private int _localSequence; private ushort _remoteSequence; private readonly bool _reliable; private NetPacket _lastPacket; private readonly NetPacket _ackPacket; private bool _mustSendAck; private readonly byte _id; private long _lastPacketSendTime; public SequencedChannel(NetPeer peer, bool reliable, byte id) : base(peer) { _id = id; _reliable = reliable; if (_reliable) _ackPacket = new NetPacket(PacketProperty.Ack, 0) {ChannelId = id}; } public override void SendNextPackets() { if (_reliable && OutgoingQueue.Count == 0) { long currentTime = DateTime.UtcNow.Ticks; long packetHoldTime = currentTime - _lastPacketSendTime; if (packetHoldTime < Peer.ResendDelay * TimeSpan.TicksPerMillisecond) return; var packet = _lastPacket; if (packet != null) { _lastPacketSendTime = currentTime; Peer.SendUserData(packet); } } else { lock (OutgoingQueue) { while (OutgoingQueue.Count > 0) { NetPacket packet = OutgoingQueue.Dequeue(); _localSequence = (_localSequence + 1) % NetConstants.MaxSequence; packet.Sequence = (ushort)_localSequence; packet.ChannelId = _id; Peer.SendUserData(packet); if (_reliable && OutgoingQueue.Count == 0) { _lastPacketSendTime = DateTime.UtcNow.Ticks; _lastPacket = packet; } else { Peer.NetManager.NetPacketPool.Recycle(packet); } } } } if (_reliable && _mustSendAck) { _mustSendAck = false; _ackPacket.Sequence = _remoteSequence; Peer.SendUserData(_ackPacket); } } public override bool ProcessPacket(NetPacket packet) { if (packet.IsFragmented) return false; if (packet.Property == PacketProperty.Ack) { if (_reliable && _lastPacket != null && packet.Sequence == _lastPacket.Sequence) _lastPacket = null; return false; } int relative = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence); bool packetProcessed = false; if (packet.Sequence < NetConstants.MaxSequence && relative > 0) { if (Peer.NetManager.EnableStatistics) { Peer.Statistics.AddPacketLoss(relative - 1); Peer.NetManager.Statistics.AddPacketLoss(relative - 1); } _remoteSequence = packet.Sequence; Peer.NetManager.CreateReceiveEvent( packet, _reliable ? DeliveryMethod.ReliableSequenced : DeliveryMethod.Sequenced, NetConstants.ChanneledHeaderSize, Peer); packetProcessed = true; } _mustSendAck = true; return packetProcessed; } } } ================================================ FILE: Assets/Plugins/LiteNetLib/SequencedChannel.cs.meta ================================================ fileFormatVersion: 2 guid: 3ee3332c6d2734f4980328c17ae0a71d MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/CRC32C.cs ================================================ #if NETCOREAPP3_0 using System; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; #endif namespace LiteNetLib.Utils { //Implementation from Crc32.NET public static class CRC32C { public const int ChecksumSize = 4; private const uint Poly = 0x82F63B78u; private static readonly uint[] Table; static CRC32C() { #if NETCOREAPP3_0 if(Sse42.IsSupported) return; #endif Table = new uint[16 * 256]; for (uint i = 0; i < 256; i++) { uint res = i; for (int t = 0; t < 16; t++) { for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? Poly ^ (res >> 1) : (res >> 1); Table[t * 256 + i] = res; } } } /// /// Compute CRC32C for data /// /// input data /// offset /// length /// CRC32C checksum public static uint Compute(byte[] input, int offset, int length) { uint crcLocal = uint.MaxValue; #if NETCOREAPP3_0 if (Sse42.IsSupported) { var data = new ReadOnlySpan(input, offset, length); int processed = 0; if (Sse42.X64.IsSupported && data.Length > sizeof(ulong)) { processed = data.Length / sizeof(ulong) * sizeof(ulong); var ulongs = MemoryMarshal.Cast(data.Slice(0, processed)); ulong crclong = crcLocal; for (int i = 0; i < ulongs.Length; i++) { crclong = Sse42.X64.Crc32(crclong, ulongs[i]); } crcLocal = (uint)crclong; } else if (data.Length > sizeof(uint)) { processed = data.Length / sizeof(uint) * sizeof(uint); var uints = MemoryMarshal.Cast(data.Slice(0, processed)); for (int i = 0; i < uints.Length; i++) { crcLocal = Sse42.Crc32(crcLocal, uints[i]); } } for (int i = processed; i < data.Length; i++) { crcLocal = Sse42.Crc32(crcLocal, data[i]); } return crcLocal ^ uint.MaxValue; } #endif while (length >= 16) { var a = Table[(3 * 256) + input[offset + 12]] ^ Table[(2 * 256) + input[offset + 13]] ^ Table[(1 * 256) + input[offset + 14]] ^ Table[(0 * 256) + input[offset + 15]]; var b = Table[(7 * 256) + input[offset + 8]] ^ Table[(6 * 256) + input[offset + 9]] ^ Table[(5 * 256) + input[offset + 10]] ^ Table[(4 * 256) + input[offset + 11]]; var c = Table[(11 * 256) + input[offset + 4]] ^ Table[(10 * 256) + input[offset + 5]] ^ Table[(9 * 256) + input[offset + 6]] ^ Table[(8 * 256) + input[offset + 7]]; var d = Table[(15 * 256) + ((byte)crcLocal ^ input[offset])] ^ Table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])] ^ Table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])] ^ Table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])]; crcLocal = d ^ c ^ b ^ a; offset += 16; length -= 16; } while (--length >= 0) crcLocal = Table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8; return crcLocal ^ uint.MaxValue; } } } ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/CRC32C.cs.meta ================================================ fileFormatVersion: 2 guid: 082cd999e5284844d82fd3f7ee0a9ede MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/FastBitConverter.cs ================================================ using System.Runtime.InteropServices; namespace LiteNetLib.Utils { public static class FastBitConverter { [StructLayout(LayoutKind.Explicit)] private struct ConverterHelperDouble { [FieldOffset(0)] public ulong Along; [FieldOffset(0)] public double Adouble; } [StructLayout(LayoutKind.Explicit)] private struct ConverterHelperFloat { [FieldOffset(0)] public int Aint; [FieldOffset(0)] public float Afloat; } private static void WriteLittleEndian(byte[] buffer, int offset, ulong data) { #if BIGENDIAN buffer[offset + 7] = (byte)(data); buffer[offset + 6] = (byte)(data >> 8); buffer[offset + 5] = (byte)(data >> 16); buffer[offset + 4] = (byte)(data >> 24); buffer[offset + 3] = (byte)(data >> 32); buffer[offset + 2] = (byte)(data >> 40); buffer[offset + 1] = (byte)(data >> 48); buffer[offset ] = (byte)(data >> 56); #else buffer[offset] = (byte)(data); buffer[offset + 1] = (byte)(data >> 8); buffer[offset + 2] = (byte)(data >> 16); buffer[offset + 3] = (byte)(data >> 24); buffer[offset + 4] = (byte)(data >> 32); buffer[offset + 5] = (byte)(data >> 40); buffer[offset + 6] = (byte)(data >> 48); buffer[offset + 7] = (byte)(data >> 56); #endif } private static void WriteLittleEndian(byte[] buffer, int offset, int data) { #if BIGENDIAN buffer[offset + 3] = (byte)(data); buffer[offset + 2] = (byte)(data >> 8); buffer[offset + 1] = (byte)(data >> 16); buffer[offset ] = (byte)(data >> 24); #else buffer[offset] = (byte)(data); buffer[offset + 1] = (byte)(data >> 8); buffer[offset + 2] = (byte)(data >> 16); buffer[offset + 3] = (byte)(data >> 24); #endif } public static void WriteLittleEndian(byte[] buffer, int offset, short data) { #if BIGENDIAN buffer[offset + 1] = (byte)(data); buffer[offset ] = (byte)(data >> 8); #else buffer[offset] = (byte)(data); buffer[offset + 1] = (byte)(data >> 8); #endif } public static void GetBytes(byte[] bytes, int startIndex, double value) { ConverterHelperDouble ch = new ConverterHelperDouble { Adouble = value }; WriteLittleEndian(bytes, startIndex, ch.Along); } public static void GetBytes(byte[] bytes, int startIndex, float value) { ConverterHelperFloat ch = new ConverterHelperFloat { Afloat = value }; WriteLittleEndian(bytes, startIndex, ch.Aint); } public static void GetBytes(byte[] bytes, int startIndex, short value) { WriteLittleEndian(bytes, startIndex, value); } public static void GetBytes(byte[] bytes, int startIndex, ushort value) { WriteLittleEndian(bytes, startIndex, (short)value); } public static void GetBytes(byte[] bytes, int startIndex, int value) { WriteLittleEndian(bytes, startIndex, value); } public static void GetBytes(byte[] bytes, int startIndex, uint value) { WriteLittleEndian(bytes, startIndex, (int)value); } public static void GetBytes(byte[] bytes, int startIndex, long value) { WriteLittleEndian(bytes, startIndex, (ulong)value); } public static void GetBytes(byte[] bytes, int startIndex, ulong value) { WriteLittleEndian(bytes, startIndex, value); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/FastBitConverter.cs.meta ================================================ fileFormatVersion: 2 guid: 95d17deeb9f33bf4abfb7ad06ebcc80b MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/INetSerializable.cs ================================================ namespace LiteNetLib.Utils { public interface INetSerializable { void Serialize(NetDataWriter writer); void Deserialize(NetDataReader reader); } } ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/INetSerializable.cs.meta ================================================ fileFormatVersion: 2 guid: 51a5959e04e00a549bb5d9e37d9d6053 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NetDataReader.cs ================================================ using System; using System.Net; using System.Text; namespace LiteNetLib.Utils { public class NetDataReader { protected byte[] _data; protected int _position; protected int _dataSize; private int _offset; public byte[] RawData { get { return _data; } } public int RawDataSize { get { return _dataSize; } } public int UserDataOffset { get { return _offset; } } public int UserDataSize { get { return _dataSize - _offset; } } public bool IsNull { get { return _data == null; } } public int Position { get { return _position; } } public bool EndOfData { get { return _position == _dataSize; } } public int AvailableBytes { get { return _dataSize - _position; } } public void SkipBytes(int count) { _position += count; } public void SetSource(NetDataWriter dataWriter) { _data = dataWriter.Data; _position = 0; _offset = 0; _dataSize = dataWriter.Length; } public void SetSource(byte[] source) { _data = source; _position = 0; _offset = 0; _dataSize = source.Length; } public void SetSource(byte[] source, int offset) { _data = source; _position = offset; _offset = offset; _dataSize = source.Length; } public void SetSource(byte[] source, int offset, int maxSize) { _data = source; _position = offset; _offset = offset; _dataSize = maxSize; } public NetDataReader() { } public NetDataReader(NetDataWriter writer) { SetSource(writer); } public NetDataReader(byte[] source) { SetSource(source); } public NetDataReader(byte[] source, int offset) { SetSource(source, offset); } public NetDataReader(byte[] source, int offset, int maxSize) { SetSource(source, offset, maxSize); } #region GetMethods public IPEndPoint GetNetEndPoint() { string host = GetString(1000); int port = GetInt(); return NetUtils.MakeEndPoint(host, port); } public byte GetByte() { byte res = _data[_position]; _position += 1; return res; } public sbyte GetSByte() { var b = (sbyte)_data[_position]; _position++; return b; } public bool[] GetBoolArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new bool[size]; Buffer.BlockCopy(_data, _position, arr, 0, size); _position += size; return arr; } public ushort[] GetUShortArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new ushort[size]; Buffer.BlockCopy(_data, _position, arr, 0, size * 2); _position += size * 2; return arr; } public short[] GetShortArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new short[size]; Buffer.BlockCopy(_data, _position, arr, 0, size * 2); _position += size * 2; return arr; } public long[] GetLongArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new long[size]; Buffer.BlockCopy(_data, _position, arr, 0, size * 8); _position += size * 8; return arr; } public ulong[] GetULongArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new ulong[size]; Buffer.BlockCopy(_data, _position, arr, 0, size * 8); _position += size * 8; return arr; } public int[] GetIntArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new int[size]; Buffer.BlockCopy(_data, _position, arr, 0, size * 4); _position += size * 4; return arr; } public uint[] GetUIntArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new uint[size]; Buffer.BlockCopy(_data, _position, arr, 0, size * 4); _position += size * 4; return arr; } public float[] GetFloatArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new float[size]; Buffer.BlockCopy(_data, _position, arr, 0, size * 4); _position += size * 4; return arr; } public double[] GetDoubleArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new double[size]; Buffer.BlockCopy(_data, _position, arr, 0, size * 8); _position += size * 8; return arr; } public string[] GetStringArray() { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new string[size]; for (int i = 0; i < size; i++) { arr[i] = GetString(); } return arr; } public string[] GetStringArray(int maxStringLength) { ushort size = BitConverter.ToUInt16(_data, _position); _position += 2; var arr = new string[size]; for (int i = 0; i < size; i++) { arr[i] = GetString(maxStringLength); } return arr; } public bool GetBool() { bool res = _data[_position] > 0; _position += 1; return res; } public char GetChar() { char result = BitConverter.ToChar(_data, _position); _position += 2; return result; } public ushort GetUShort() { ushort result = BitConverter.ToUInt16(_data, _position); _position += 2; return result; } public short GetShort() { short result = BitConverter.ToInt16(_data, _position); _position += 2; return result; } public long GetLong() { long result = BitConverter.ToInt64(_data, _position); _position += 8; return result; } public ulong GetULong() { ulong result = BitConverter.ToUInt64(_data, _position); _position += 8; return result; } public int GetInt() { int result = BitConverter.ToInt32(_data, _position); _position += 4; return result; } public uint GetUInt() { uint result = BitConverter.ToUInt32(_data, _position); _position += 4; return result; } public float GetFloat() { float result = BitConverter.ToSingle(_data, _position); _position += 4; return result; } public double GetDouble() { double result = BitConverter.ToDouble(_data, _position); _position += 8; return result; } public string GetString(int maxLength) { int bytesCount = GetInt(); if (bytesCount <= 0 || bytesCount > maxLength*2) { return string.Empty; } int charCount = Encoding.UTF8.GetCharCount(_data, _position, bytesCount); if (charCount > maxLength) { return string.Empty; } string result = Encoding.UTF8.GetString(_data, _position, bytesCount); _position += bytesCount; return result; } public string GetString() { int bytesCount = GetInt(); if (bytesCount <= 0) { return string.Empty; } string result = Encoding.UTF8.GetString(_data, _position, bytesCount); _position += bytesCount; return result; } public ArraySegment GetRemainingBytesSegment() { ArraySegment segment = new ArraySegment(_data, _position, AvailableBytes); _position = _data.Length; return segment; } public T Get() where T : INetSerializable, new() { var obj = new T(); obj.Deserialize(this); return obj; } public byte[] GetRemainingBytes() { byte[] outgoingData = new byte[AvailableBytes]; Buffer.BlockCopy(_data, _position, outgoingData, 0, AvailableBytes); _position = _data.Length; return outgoingData; } public void GetBytes(byte[] destination, int start, int count) { Buffer.BlockCopy(_data, _position, destination, start, count); _position += count; } public void GetBytes(byte[] destination, int count) { Buffer.BlockCopy(_data, _position, destination, 0, count); _position += count; } public sbyte[] GetSBytesWithLength() { int length = GetInt(); sbyte[] outgoingData = new sbyte[length]; Buffer.BlockCopy(_data, _position, outgoingData, 0, length); _position += length; return outgoingData; } public byte[] GetBytesWithLength() { int length = GetInt(); byte[] outgoingData = new byte[length]; Buffer.BlockCopy(_data, _position, outgoingData, 0, length); _position += length; return outgoingData; } #endregion #region PeekMethods public byte PeekByte() { return _data[_position]; } public sbyte PeekSByte() { return (sbyte)_data[_position]; } public bool PeekBool() { return _data[_position] > 0; } public char PeekChar() { return BitConverter.ToChar(_data, _position); } public ushort PeekUShort() { return BitConverter.ToUInt16(_data, _position); } public short PeekShort() { return BitConverter.ToInt16(_data, _position); } public long PeekLong() { return BitConverter.ToInt64(_data, _position); } public ulong PeekULong() { return BitConverter.ToUInt64(_data, _position); } public int PeekInt() { return BitConverter.ToInt32(_data, _position); } public uint PeekUInt() { return BitConverter.ToUInt32(_data, _position); } public float PeekFloat() { return BitConverter.ToSingle(_data, _position); } public double PeekDouble() { return BitConverter.ToDouble(_data, _position); } public string PeekString(int maxLength) { int bytesCount = BitConverter.ToInt32(_data, _position); if (bytesCount <= 0 || bytesCount > maxLength * 2) { return string.Empty; } int charCount = Encoding.UTF8.GetCharCount(_data, _position + 4, bytesCount); if (charCount > maxLength) { return string.Empty; } string result = Encoding.UTF8.GetString(_data, _position + 4, bytesCount); return result; } public string PeekString() { int bytesCount = BitConverter.ToInt32(_data, _position); if (bytesCount <= 0) { return string.Empty; } string result = Encoding.UTF8.GetString(_data, _position + 4, bytesCount); return result; } #endregion #region TryGetMethods public bool TryGetByte(out byte result) { if (AvailableBytes >= 1) { result = GetByte(); return true; } result = 0; return false; } public bool TryGetSByte(out sbyte result) { if (AvailableBytes >= 1) { result = GetSByte(); return true; } result = 0; return false; } public bool TryGetBool(out bool result) { if (AvailableBytes >= 1) { result = GetBool(); return true; } result = false; return false; } public bool TryGetChar(out char result) { if (AvailableBytes >= 2) { result = GetChar(); return true; } result = '\0'; return false; } public bool TryGetShort(out short result) { if (AvailableBytes >= 2) { result = GetShort(); return true; } result = 0; return false; } public bool TryGetUShort(out ushort result) { if (AvailableBytes >= 2) { result = GetUShort(); return true; } result = 0; return false; } public bool TryGetInt(out int result) { if (AvailableBytes >= 4) { result = GetInt(); return true; } result = 0; return false; } public bool TryGetUInt(out uint result) { if (AvailableBytes >= 4) { result = GetUInt(); return true; } result = 0; return false; } public bool TryGetLong(out long result) { if (AvailableBytes >= 8) { result = GetLong(); return true; } result = 0; return false; } public bool TryGetULong(out ulong result) { if (AvailableBytes >= 8) { result = GetULong(); return true; } result = 0; return false; } public bool TryGetFloat(out float result) { if (AvailableBytes >= 4) { result = GetFloat(); return true; } result = 0; return false; } public bool TryGetDouble(out double result) { if (AvailableBytes >= 8) { result = GetDouble(); return true; } result = 0; return false; } public bool TryGetString(out string result) { if (AvailableBytes >= 4) { var bytesCount = PeekInt(); if (AvailableBytes >= bytesCount + 4) { result = GetString(); return true; } } result = null; return false; } public bool TryGetStringArray(out string[] result) { ushort size; if (!TryGetUShort(out size)) { result = null; return false; } result = new string[size]; for (int i = 0; i < size; i++) { if (!TryGetString(out result[i])) { result = null; return false; } } return true; } public bool TryGetBytesWithLength(out byte[] result) { if (AvailableBytes >= 4) { var length = PeekInt(); if (length >= 0 && AvailableBytes >= length + 4) { result = GetBytesWithLength(); return true; } } result = null; return false; } #endregion public void Clear() { _position = 0; _dataSize = 0; _data = null; } } } ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NetDataReader.cs.meta ================================================ fileFormatVersion: 2 guid: a46d434c453198440bcb27e10f692391 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NetDataWriter.cs ================================================ using System; using System.Net; using System.Text; namespace LiteNetLib.Utils { public class NetDataWriter { protected byte[] _data; protected int _position; private const int InitialSize = 64; private readonly bool _autoResize; public int Capacity { get { return _data.Length; } } public NetDataWriter() : this(true, InitialSize) { } public NetDataWriter(bool autoResize) : this(autoResize, InitialSize) { } public NetDataWriter(bool autoResize, int initialSize) { _data = new byte[initialSize]; _autoResize = autoResize; } /// /// Creates NetDataWriter from existing ByteArray /// /// Source byte array /// Copy array to new location or use existing public static NetDataWriter FromBytes(byte[] bytes, bool copy) { if (copy) { var netDataWriter = new NetDataWriter(true, bytes.Length); netDataWriter.Put(bytes); return netDataWriter; } return new NetDataWriter(true, 0) {_data = bytes}; } /// /// Creates NetDataWriter from existing ByteArray (always copied data) /// /// Source byte array /// Offset of array /// Length of array public static NetDataWriter FromBytes(byte[] bytes, int offset, int length) { var netDataWriter = new NetDataWriter(true, bytes.Length); netDataWriter.Put(bytes, offset, length); return netDataWriter; } public static NetDataWriter FromString(string value) { var netDataWriter = new NetDataWriter(); netDataWriter.Put(value); return netDataWriter; } public void ResizeIfNeed(int newSize) { int len = _data.Length; if (len < newSize) { while (len < newSize) len *= 2; Array.Resize(ref _data, len); } } public void Reset(int size) { ResizeIfNeed(size); _position = 0; } public void Reset() { _position = 0; } public byte[] CopyData() { byte[] resultData = new byte[_position]; Buffer.BlockCopy(_data, 0, resultData, 0, _position); return resultData; } public byte[] Data { get { return _data; } } public int Length { get { return _position; } } public void Put(float value) { if (_autoResize) ResizeIfNeed(_position + 4); FastBitConverter.GetBytes(_data, _position, value); _position += 4; } public void Put(double value) { if (_autoResize) ResizeIfNeed(_position + 8); FastBitConverter.GetBytes(_data, _position, value); _position += 8; } public void Put(long value) { if (_autoResize) ResizeIfNeed(_position + 8); FastBitConverter.GetBytes(_data, _position, value); _position += 8; } public void Put(ulong value) { if (_autoResize) ResizeIfNeed(_position + 8); FastBitConverter.GetBytes(_data, _position, value); _position += 8; } public void Put(int value) { if (_autoResize) ResizeIfNeed(_position + 4); FastBitConverter.GetBytes(_data, _position, value); _position += 4; } public void Put(uint value) { if (_autoResize) ResizeIfNeed(_position + 4); FastBitConverter.GetBytes(_data, _position, value); _position += 4; } public void Put(char value) { if (_autoResize) ResizeIfNeed(_position + 2); FastBitConverter.GetBytes(_data, _position, value); _position += 2; } public void Put(ushort value) { if (_autoResize) ResizeIfNeed(_position + 2); FastBitConverter.GetBytes(_data, _position, value); _position += 2; } public void Put(short value) { if (_autoResize) ResizeIfNeed(_position + 2); FastBitConverter.GetBytes(_data, _position, value); _position += 2; } public void Put(sbyte value) { if (_autoResize) ResizeIfNeed(_position + 1); _data[_position] = (byte)value; _position++; } public void Put(byte value) { if (_autoResize) ResizeIfNeed(_position + 1); _data[_position] = value; _position++; } public void Put(byte[] data, int offset, int length) { if (_autoResize) ResizeIfNeed(_position + length); Buffer.BlockCopy(data, offset, _data, _position, length); _position += length; } public void Put(byte[] data) { if (_autoResize) ResizeIfNeed(_position + data.Length); Buffer.BlockCopy(data, 0, _data, _position, data.Length); _position += data.Length; } public void PutSBytesWithLength(sbyte[] data, int offset, int length) { if (_autoResize) ResizeIfNeed(_position + length + 4); FastBitConverter.GetBytes(_data, _position, length); Buffer.BlockCopy(data, offset, _data, _position + 4, length); _position += length + 4; } public void PutSBytesWithLength(sbyte[] data) { if (_autoResize) ResizeIfNeed(_position + data.Length + 4); FastBitConverter.GetBytes(_data, _position, data.Length); Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length); _position += data.Length + 4; } public void PutBytesWithLength(byte[] data, int offset, int length) { if (_autoResize) ResizeIfNeed(_position + length + 4); FastBitConverter.GetBytes(_data, _position, length); Buffer.BlockCopy(data, offset, _data, _position + 4, length); _position += length + 4; } public void PutBytesWithLength(byte[] data) { if (_autoResize) ResizeIfNeed(_position + data.Length + 4); FastBitConverter.GetBytes(_data, _position, data.Length); Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length); _position += data.Length + 4; } public void Put(bool value) { if (_autoResize) ResizeIfNeed(_position + 1); _data[_position] = (byte)(value ? 1 : 0); _position++; } private void PutArray(Array arr, int sz) { ushort length = arr == null ? (ushort) 0 : (ushort)arr.Length; sz *= length; if (_autoResize) ResizeIfNeed(_position + sz + 2); FastBitConverter.GetBytes(_data, _position, length); if (arr != null) Buffer.BlockCopy(arr, 0, _data, _position + 2, sz); _position += sz + 2; } public void PutArray(float[] value) { PutArray(value, 4); } public void PutArray(double[] value) { PutArray(value, 8); } public void PutArray(long[] value) { PutArray(value, 8); } public void PutArray(ulong[] value) { PutArray(value, 8); } public void PutArray(int[] value) { PutArray(value, 4); } public void PutArray(uint[] value) { PutArray(value, 4); } public void PutArray(ushort[] value) { PutArray(value, 2); } public void PutArray(short[] value) { PutArray(value, 2); } public void PutArray(bool[] value) { PutArray(value, 1); } public void PutArray(string[] value) { ushort len = value == null ? (ushort)0 : (ushort)value.Length; Put(len); for (int i = 0; i < len; i++) Put(value[i]); } public void PutArray(string[] value, int maxLength) { ushort len = value == null ? (ushort)0 : (ushort)value.Length; Put(len); for (int i = 0; i < len; i++) Put(value[i], maxLength); } public void Put(IPEndPoint endPoint) { Put(endPoint.Address.ToString()); Put(endPoint.Port); } public void Put(string value) { if (string.IsNullOrEmpty(value)) { Put(0); return; } //put bytes count int bytesCount = Encoding.UTF8.GetByteCount(value); if (_autoResize) ResizeIfNeed(_position + bytesCount + 4); Put(bytesCount); //put string Encoding.UTF8.GetBytes(value, 0, value.Length, _data, _position); _position += bytesCount; } public void Put(string value, int maxLength) { if (string.IsNullOrEmpty(value)) { Put(0); return; } int length = value.Length > maxLength ? maxLength : value.Length; //calculate max count int bytesCount = Encoding.UTF8.GetByteCount(value); if (_autoResize) ResizeIfNeed(_position + bytesCount + 4); //put bytes count Put(bytesCount); //put string Encoding.UTF8.GetBytes(value, 0, length, _data, _position); _position += bytesCount; } public void Put(T obj) where T : INetSerializable { obj.Serialize(this); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NetDataWriter.cs.meta ================================================ fileFormatVersion: 2 guid: c0ffde081f520024ab3d374372df31ff MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NetPacketProcessor.cs ================================================ using System; using System.Collections.Generic; namespace LiteNetLib.Utils { public class NetPacketProcessor { private static class HashCache { public static bool Initialized; public static ulong Id; } protected delegate void SubscribeDelegate(NetDataReader reader, object userData); private readonly NetSerializer _netSerializer; private readonly Dictionary _callbacks = new Dictionary(); private readonly NetDataWriter _netDataWriter = new NetDataWriter(); public NetPacketProcessor() { _netSerializer = new NetSerializer(); } public NetPacketProcessor(int maxStringLength) { _netSerializer = new NetSerializer(maxStringLength); } //FNV-1 64 bit hash protected virtual ulong GetHash() { if(HashCache.Initialized) return HashCache.Id; ulong hash = 14695981039346656037UL; //offset string typeName = typeof(T).FullName; for (var i = 0; i < typeName.Length; i++) { hash = hash ^ typeName[i]; hash *= 1099511628211UL; //prime } HashCache.Initialized = true; HashCache.Id = hash; return hash; } protected virtual SubscribeDelegate GetCallbackFromData(NetDataReader reader) { var hash = reader.GetULong(); SubscribeDelegate action; if (!_callbacks.TryGetValue(hash, out action)) { throw new ParseException("Undefined packet in NetDataReader"); } return action; } protected virtual void WriteHash(NetDataWriter writer) { writer.Put(GetHash()); } /// /// Register nested property type /// /// INetSerializable structure public void RegisterNestedType() where T : struct, INetSerializable { _netSerializer.RegisterNestedType(); } /// /// Register nested property type /// /// /// public void RegisterNestedType(Action writeDelegate, Func readDelegate) { _netSerializer.RegisterNestedType(writeDelegate, readDelegate); } /// /// Register nested property type /// /// INetSerializable class public void RegisterNestedType(Func constructor) where T : class, INetSerializable { _netSerializer.RegisterNestedType(constructor); } /// /// Reads all available data from NetDataReader and calls OnReceive delegates /// /// NetDataReader with packets data public void ReadAllPackets(NetDataReader reader) { while (reader.AvailableBytes > 0) ReadPacket(reader); } /// /// Reads all available data from NetDataReader and calls OnReceive delegates /// /// NetDataReader with packets data /// Argument that passed to OnReceivedEvent /// Malformed packet public void ReadAllPackets(NetDataReader reader, object userData) { while (reader.AvailableBytes > 0) ReadPacket(reader, userData); } /// /// Reads one packet from NetDataReader and calls OnReceive delegate /// /// NetDataReader with packet /// Malformed packet public void ReadPacket(NetDataReader reader) { ReadPacket(reader, null); } public void Send(NetPeer peer, T packet, DeliveryMethod options) where T : class, new() { _netDataWriter.Reset(); Write(_netDataWriter, packet); peer.Send(_netDataWriter, options); } public void SendNetSerializable(NetPeer peer, T packet, DeliveryMethod options) where T : INetSerializable { _netDataWriter.Reset(); WriteNetSerializable(_netDataWriter, packet); peer.Send(_netDataWriter, options); } public void Send(NetManager manager, T packet, DeliveryMethod options) where T : class, new() { _netDataWriter.Reset(); Write(_netDataWriter, packet); manager.SendToAll(_netDataWriter, options); } public void SendNetSerializable(NetManager manager, T packet, DeliveryMethod options) where T : INetSerializable { _netDataWriter.Reset(); WriteNetSerializable(_netDataWriter, packet); manager.SendToAll(_netDataWriter, options); } public void Write(NetDataWriter writer, T packet) where T : class, new() { WriteHash(writer); _netSerializer.Serialize(writer, packet); } public void WriteNetSerializable(NetDataWriter writer, T packet) where T : INetSerializable { WriteHash(writer); packet.Serialize(writer); } public byte[] Write(T packet) where T : class, new() { _netDataWriter.Reset(); WriteHash(_netDataWriter); _netSerializer.Serialize(_netDataWriter, packet); return _netDataWriter.CopyData(); } public byte[] WriteNetSerializable(T packet) where T : INetSerializable { _netDataWriter.Reset(); WriteHash(_netDataWriter); packet.Serialize(_netDataWriter); return _netDataWriter.CopyData(); } /// /// Reads one packet from NetDataReader and calls OnReceive delegate /// /// NetDataReader with packet /// Argument that passed to OnReceivedEvent /// Malformed packet public void ReadPacket(NetDataReader reader, object userData) { GetCallbackFromData(reader)(reader, userData); } /// /// Register and subscribe to packet receive event /// /// event that will be called when packet deserialized with ReadPacket method /// Method that constructs packet instead of slow Activator.CreateInstance /// 's fields are not supported, or it has no fields public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new() { _netSerializer.Register(); _callbacks[GetHash()] = (reader, userData) => { var reference = packetConstructor(); _netSerializer.Deserialize(reader, reference); onReceive(reference); }; } /// /// Register and subscribe to packet receive event (with userData) /// /// event that will be called when packet deserialized with ReadPacket method /// Method that constructs packet instead of slow Activator.CreateInstance /// 's fields are not supported, or it has no fields public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new() { _netSerializer.Register(); _callbacks[GetHash()] = (reader, userData) => { var reference = packetConstructor(); _netSerializer.Deserialize(reader, reference); onReceive(reference, (TUserData)userData); }; } /// /// Register and subscribe to packet receive event /// This method will overwrite last received packet class on receive (less garbage) /// /// event that will be called when packet deserialized with ReadPacket method /// 's fields are not supported, or it has no fields public void SubscribeReusable(Action onReceive) where T : class, new() { _netSerializer.Register(); var reference = new T(); _callbacks[GetHash()] = (reader, userData) => { _netSerializer.Deserialize(reader, reference); onReceive(reference); }; } /// /// Register and subscribe to packet receive event /// This method will overwrite last received packet class on receive (less garbage) /// /// event that will be called when packet deserialized with ReadPacket method /// 's fields are not supported, or it has no fields public void SubscribeReusable(Action onReceive) where T : class, new() { _netSerializer.Register(); var reference = new T(); _callbacks[GetHash()] = (reader, userData) => { _netSerializer.Deserialize(reader, reference); onReceive(reference, (TUserData)userData); }; } public void SubscribeNetSerializable( Action onReceive, Func packetConstructor) where T : INetSerializable { _callbacks[GetHash()] = (reader, userData) => { var pkt = packetConstructor(); pkt.Deserialize(reader); onReceive(pkt, (TUserData)userData); }; } public void SubscribeNetSerializable( Action onReceive, Func packetConstructor) where T : INetSerializable { _callbacks[GetHash()] = (reader, userData) => { var pkt = packetConstructor(); pkt.Deserialize(reader); onReceive(pkt); }; } public void SubscribeNetSerializable( Action onReceive) where T : INetSerializable, new() { var reference = new T(); _callbacks[GetHash()] = (reader, userData) => { reference.Deserialize(reader); onReceive(reference, (TUserData)userData); }; } public void SubscribeNetSerializable( Action onReceive) where T : INetSerializable, new() { var reference = new T(); _callbacks[GetHash()] = (reader, userData) => { reference.Deserialize(reader); onReceive(reference); }; } /// /// Remove any subscriptions by type /// /// Packet type /// true if remove is success public bool RemoveSubscription() { return _callbacks.Remove(GetHash()); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NetPacketProcessor.cs.meta ================================================ fileFormatVersion: 2 guid: b42e8b07a748cfa49b39546f3d747abe MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NetSerializer.cs ================================================ using System; using System.Reflection; using System.Collections.Generic; using System.Net; namespace LiteNetLib.Utils { public class InvalidTypeException : ArgumentException { public InvalidTypeException(string message) : base(message) { } } public class ParseException : Exception { public ParseException(string message) : base(message) { } } public class NetSerializer { private enum CallType { Basic, Array, List } private abstract class FastCall { public CallType Type; public virtual void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) { Type = type; } public abstract void Read(T inf, NetDataReader r); public abstract void Write(T inf, NetDataWriter w); public abstract void ReadArray(T inf, NetDataReader r); public abstract void WriteArray(T inf, NetDataWriter w); public abstract void ReadList(T inf, NetDataReader r); public abstract void WriteList(T inf, NetDataWriter w); } private abstract class FastCallSpecific : FastCall { protected Func Getter; protected Action Setter; protected Func GetterArr; protected Action SetterArr; protected Func> GetterList; protected Action> SetterList; public override void ReadArray(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); } public override void WriteArray(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); } public override void ReadList(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); } public override void WriteList(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); } protected TProperty[] ReadArrayHelper(TClass inf, NetDataReader r) { ushort count = r.GetUShort(); var arr = GetterArr(inf); arr = arr == null || arr.Length != count ? new TProperty[count] : arr; SetterArr(inf, arr); return arr; } protected TProperty[] WriteArrayHelper(TClass inf, NetDataWriter w) { var arr = GetterArr(inf); w.Put((ushort)arr.Length); return arr; } protected List ReadListHelper(TClass inf, NetDataReader r, out int len) { len = r.GetUShort(); var list = GetterList(inf); if (list == null) { list = new List(len); SetterList(inf, list); } return list; } protected List WriteListHelper(TClass inf, NetDataWriter w, out int len) { var list = GetterList(inf); if (list == null) { len = 0; w.Put(0); return null; } len = list.Count; w.Put((ushort)len); return list; } public override void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) { base.Init(getMethod, setMethod, type); switch (type) { case CallType.Array: GetterArr = (Func)Delegate.CreateDelegate(typeof(Func), getMethod); SetterArr = (Action)Delegate.CreateDelegate(typeof(Action), setMethod); break; case CallType.List: GetterList = (Func>)Delegate.CreateDelegate(typeof(Func>), getMethod); SetterList = (Action>)Delegate.CreateDelegate(typeof(Action>), setMethod); break; default: Getter = (Func)Delegate.CreateDelegate(typeof(Func), getMethod); Setter = (Action)Delegate.CreateDelegate(typeof(Action), setMethod); break; } } } private abstract class FastCallSpecificAuto : FastCallSpecific { protected abstract void ElementRead(NetDataReader r, out TProperty prop); protected abstract void ElementWrite(NetDataWriter w, ref TProperty prop); public override void Read(TClass inf, NetDataReader r) { TProperty elem; ElementRead(r, out elem); Setter(inf, elem); } public override void Write(TClass inf, NetDataWriter w) { var elem = Getter(inf); ElementWrite(w, ref elem); } public override void ReadArray(TClass inf, NetDataReader r) { var arr = ReadArrayHelper(inf, r); for (int i = 0; i < arr.Length; i++) ElementRead(r, out arr[i]); } public override void WriteArray(TClass inf, NetDataWriter w) { var arr = WriteArrayHelper(inf, w); for (int i = 0; i < arr.Length; i++) ElementWrite(w, ref arr[i]); } } private sealed class FastCallStatic : FastCallSpecific { private readonly Action _writer; private readonly Func _reader; public FastCallStatic(Action write, Func read) { _writer = write; _reader = read; } public override void Read(TClass inf, NetDataReader r) { Setter(inf, _reader(r)); } public override void Write(TClass inf, NetDataWriter w) { _writer(w, Getter(inf)); } public override void ReadList(TClass inf, NetDataReader r) { int len; var list = ReadListHelper(inf, r, out len); int listCount = list.Count; for (int i = 0; i < len; i++) { if (i < listCount) list[i] = _reader(r); else list.Add(_reader(r)); } if (len < listCount) list.RemoveRange(len, listCount - len); } public override void WriteList(TClass inf, NetDataWriter w) { int len; var list = WriteListHelper(inf, w, out len); for (int i = 0; i < len; i++) _writer(w, list[i]); } public override void ReadArray(TClass inf, NetDataReader r) { var arr = ReadArrayHelper(inf, r); int len = arr.Length; for (int i = 0; i < len; i++) arr[i] = _reader(r); } public override void WriteArray(TClass inf, NetDataWriter w) { var arr = WriteArrayHelper(inf, w); int len = arr.Length; for (int i = 0; i < len; i++) _writer(w, arr[i]); } } private sealed class FastCallStruct : FastCallSpecific where TProperty : struct, INetSerializable { private TProperty _p; public override void Read(TClass inf, NetDataReader r) { _p.Deserialize(r); Setter(inf, _p); } public override void Write(TClass inf, NetDataWriter w) { _p = Getter(inf); _p.Serialize(w); } public override void ReadList(TClass inf, NetDataReader r) { int len; var list = ReadListHelper(inf, r, out len); int listCount = list.Count; for (int i = 0; i < len; i++) { var itm = default(TProperty); itm.Deserialize(r); if(i < listCount) list[i] = itm; else list.Add(itm); } if (len < listCount) list.RemoveRange(len, listCount - len); } public override void WriteList(TClass inf, NetDataWriter w) { int len; var list = WriteListHelper(inf, w, out len); for (int i = 0; i < len; i++) list[i].Serialize(w); } public override void ReadArray(TClass inf, NetDataReader r) { var arr = ReadArrayHelper(inf, r); int len = arr.Length; for (int i = 0; i < len; i++) arr[i].Deserialize(r); } public override void WriteArray(TClass inf, NetDataWriter w) { var arr = WriteArrayHelper(inf, w); int len = arr.Length; for (int i = 0; i < len; i++) arr[i].Serialize(w); } } private sealed class FastCallClass : FastCallSpecific where TProperty : class, INetSerializable { private readonly Func _constructor; public FastCallClass(Func constructor) { _constructor = constructor; } public override void Read(TClass inf, NetDataReader r) { var p = _constructor(); p.Deserialize(r); Setter(inf, p); } public override void Write(TClass inf, NetDataWriter w) { var p = Getter(inf); if(p != null) p.Serialize(w); } public override void ReadList(TClass inf, NetDataReader r) { int len; var list = ReadListHelper(inf, r, out len); int listCount = list.Count; for (int i = 0; i < len; i++) { if (i < listCount) { list[i].Deserialize(r); } else { var itm = _constructor(); itm.Deserialize(r); list.Add(itm); } } if (len < listCount) list.RemoveRange(len, listCount - len); } public override void WriteList(TClass inf, NetDataWriter w) { int len; var list = WriteListHelper(inf, w, out len); for (int i = 0; i < len; i++) list[i].Serialize(w); } public override void ReadArray(TClass inf, NetDataReader r) { var arr = ReadArrayHelper(inf, r); int len = arr.Length; for (int i = 0; i < len; i++) { arr[i] = _constructor(); arr[i].Deserialize(r); } } public override void WriteArray(TClass inf, NetDataWriter w) { var arr = WriteArrayHelper(inf, w); int len = arr.Length; for (int i = 0; i < len; i++) arr[i].Serialize(w); } } private class IntSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetInt()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetIntArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class UIntSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUInt()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUIntArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class ShortSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetShort()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetShortArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class UShortSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUShort()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUShortArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class LongSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetLong()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetLongArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class ULongSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetULong()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetULongArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class ByteSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetByte()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBytesWithLength()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutBytesWithLength(GetterArr(inf)); } } private class SByteSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetSByte()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetSBytesWithLength()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutSBytesWithLength(GetterArr(inf)); } } private class FloatSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetFloat()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetFloatArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class DoubleSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetDouble()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetDoubleArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class BoolSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetBool()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBoolArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class CharSerializer : FastCallSpecificAuto { protected override void ElementWrite(NetDataWriter w, ref char prop) { w.Put(prop); } protected override void ElementRead(NetDataReader r, out char prop) { prop = r.GetChar(); } } private class IPEndPointSerializer : FastCallSpecificAuto { protected override void ElementWrite(NetDataWriter w, ref IPEndPoint prop) { w.Put(prop); } protected override void ElementRead(NetDataReader r, out IPEndPoint prop) { prop = r.GetNetEndPoint(); } } private class StringSerializer : FastCallSpecific { private readonly int _maxLength; public StringSerializer(int maxLength) { _maxLength = maxLength > 0 ? maxLength : short.MaxValue; } public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetString(_maxLength)); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf), _maxLength); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetStringArray(_maxLength)); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf), _maxLength); } } private class EnumByteSerializer : FastCall { protected readonly PropertyInfo Property; protected readonly Type PropertyType; public EnumByteSerializer(PropertyInfo property, Type propertyType) { Property = property; PropertyType = propertyType; } public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetByte()), null); } public override void Write(T inf, NetDataWriter w) { w.Put((byte)Property.GetValue(inf, null)); } public override void ReadArray(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: Enum[]"); } public override void WriteArray(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: Enum[]"); } public override void ReadList(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List"); } public override void WriteList(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List"); } } private class EnumIntSerializer : EnumByteSerializer { public EnumIntSerializer(PropertyInfo property, Type propertyType) : base(property, propertyType) { } public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetInt()), null); } public override void Write(T inf, NetDataWriter w) { w.Put((int)Property.GetValue(inf, null)); } } private sealed class ClassInfo { public static ClassInfo Instance; private readonly FastCall[] _serializers; private readonly int _membersCount; public ClassInfo(List> serializers) { _membersCount = serializers.Count; _serializers = serializers.ToArray(); } public void Write(T obj, NetDataWriter writer) { for (int i = 0; i < _membersCount; i++) { var s = _serializers[i]; if (s.Type == CallType.Basic) s.Write(obj, writer); else if (s.Type == CallType.Array) s.WriteArray(obj, writer); else s.WriteList(obj, writer); } } public void Read(T obj, NetDataReader reader) { for (int i = 0; i < _membersCount; i++) { var s = _serializers[i]; if (s.Type == CallType.Basic) s.Read(obj, reader); else if(s.Type == CallType.Array) s.ReadArray(obj, reader); else s.ReadList(obj, reader); } } } private abstract class CustomType { public abstract FastCall Get(); } private sealed class CustomTypeStruct : CustomType where TProperty : struct, INetSerializable { public override FastCall Get() { return new FastCallStruct(); } } private sealed class CustomTypeClass : CustomType where TProperty : class, INetSerializable { private readonly Func _constructor; public CustomTypeClass(Func constructor) { _constructor = constructor; } public override FastCall Get() { return new FastCallClass(_constructor); } } private sealed class CustomTypeStatic : CustomType { private readonly Action _writer; private readonly Func _reader; public CustomTypeStatic(Action writer, Func reader) { _writer = writer; _reader = reader; } public override FastCall Get() { return new FastCallStatic(_writer, _reader); } } /// /// Register custom property type /// /// INetSerializable structure public void RegisterNestedType() where T : struct, INetSerializable { _registeredTypes.Add(typeof(T), new CustomTypeStruct()); } /// /// Register custom property type /// /// INetSerializable class public void RegisterNestedType(Func constructor) where T : class, INetSerializable { _registeredTypes.Add(typeof(T), new CustomTypeClass(constructor)); } /// /// Register custom property type /// /// Any packet /// custom type writer /// custom type reader public void RegisterNestedType(Action writer, Func reader) { _registeredTypes.Add(typeof(T), new CustomTypeStatic(writer, reader)); } private NetDataWriter _writer; private readonly int _maxStringLength; private readonly Dictionary _registeredTypes = new Dictionary(); public NetSerializer() : this(0) { } public NetSerializer(int maxStringLength) { _maxStringLength = maxStringLength; } private ClassInfo RegisterInternal() { if (ClassInfo.Instance != null) return ClassInfo.Instance; Type t = typeof(T); var props = t.GetProperties( BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty); var serializers = new List>(); for (int i = 0; i < props.Length; i++) { var property = props[i]; var propertyType = property.PropertyType; var elementType = propertyType.IsArray ? propertyType.GetElementType() : propertyType; var callType = propertyType.IsArray ? CallType.Array : CallType.Basic; if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>)) { elementType = propertyType.GetGenericArguments()[0]; callType = CallType.List; } var getMethod = property.GetGetMethod(); var setMethod = property.GetSetMethod(); if (getMethod == null || setMethod == null) continue; FastCall serialzer = null; if (propertyType.IsEnum) { var underlyingType = Enum.GetUnderlyingType(propertyType); if (underlyingType == typeof(byte)) serialzer = new EnumByteSerializer(property, propertyType); else if (underlyingType == typeof(int)) serialzer = new EnumIntSerializer(property, propertyType); else throw new InvalidTypeException("Not supported enum underlying type: " + underlyingType.Name); } else if (elementType == typeof(string)) serialzer = new StringSerializer(_maxStringLength); else if (elementType == typeof(bool)) serialzer = new BoolSerializer(); else if (elementType == typeof(byte)) serialzer = new ByteSerializer(); else if (elementType == typeof(sbyte)) serialzer = new SByteSerializer(); else if (elementType == typeof(short)) serialzer = new ShortSerializer(); else if (elementType == typeof(ushort)) serialzer = new UShortSerializer(); else if (elementType == typeof(int)) serialzer = new IntSerializer(); else if (elementType == typeof(uint)) serialzer = new UIntSerializer(); else if (elementType == typeof(long)) serialzer = new LongSerializer(); else if (elementType == typeof(ulong)) serialzer = new ULongSerializer(); else if (elementType == typeof(float)) serialzer = new FloatSerializer(); else if (elementType == typeof(double)) serialzer = new DoubleSerializer(); else if (elementType == typeof(char)) serialzer = new CharSerializer(); else if (elementType == typeof(IPEndPoint)) serialzer = new IPEndPointSerializer(); else { CustomType customType; _registeredTypes.TryGetValue(elementType, out customType); if (customType != null) serialzer = customType.Get(); } if (serialzer != null) { serialzer.Init(getMethod, setMethod, callType); serializers.Add(serialzer); } else { throw new InvalidTypeException("Unknown property type: " + propertyType.FullName); } } ClassInfo.Instance = new ClassInfo(serializers); return ClassInfo.Instance; } /// 's fields are not supported, or it has no fields public void Register() { RegisterInternal(); } /// /// Reads packet with known type /// /// NetDataReader with packet /// Returns packet if packet in reader is matched type /// 's fields are not supported, or it has no fields public T Deserialize(NetDataReader reader) where T : class, new() { var info = RegisterInternal(); var result = new T(); try { info.Read(result, reader); } catch { return null; } return result; } /// /// Reads packet with known type (non alloc variant) /// /// NetDataReader with packet /// Deserialization target /// Returns true if packet in reader is matched type /// 's fields are not supported, or it has no fields public bool Deserialize(NetDataReader reader, T target) where T : class, new() { var info = RegisterInternal(); try { info.Read(target, reader); } catch { return false; } return true; } /// /// Serialize object to NetDataWriter (fast) /// /// Serialization target NetDataWriter /// Object to serialize /// 's fields are not supported, or it has no fields public void Serialize(NetDataWriter writer, T obj) where T : class, new() { RegisterInternal().Write(obj, writer); } /// /// Serialize object to byte array /// /// Object to serialize /// byte array with serialized data public byte[] Serialize(T obj) where T : class, new() { if (_writer == null) _writer = new NetDataWriter(); _writer.Reset(); Serialize(_writer, obj); return _writer.CopyData(); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NetSerializer.cs.meta ================================================ fileFormatVersion: 2 guid: 2369079a03e234a459eac1fb0d7f9ffa MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NtpPacket.cs ================================================ using System; namespace LiteNetLib.Utils { /// /// Represents RFC4330 SNTP packet used for communication to and from a network time server. /// /// /// /// Most applications should just use the property. /// /// /// The same data structure represents both request and reply packets. /// Request and reply differ in which properties are set and to what values. /// /// /// The only real property is . /// All other properties read from and write to the underlying byte array /// with the exception of , /// which is not part of the packet on network and it is instead set locally after receiving the packet. /// /// /// Copied from GuerrillaNtp project /// with permission from Robert Vazan (@robertvazan) under MIT license, see https://github.com/RevenantX/LiteNetLib/pull/236 /// /// public class NtpPacket { private static readonly DateTime Epoch = new DateTime(1900, 1, 1); /// /// Gets RFC4330-encoded SNTP packet. /// /// /// Byte array containing RFC4330-encoded SNTP packet. It is at least 48 bytes long. /// /// /// This is the only real property. All other properties except /// read from or write to this byte array. /// public byte[] Bytes { get; private set; } /// /// Gets the leap second indicator. /// /// /// Leap second warning, if any. Special value /// indicates unsynchronized server clock. /// Default is . /// /// /// Only servers fill in this property. Clients can consult this property for possible leap second warning. /// public NtpLeapIndicator LeapIndicator { get { return (NtpLeapIndicator)((Bytes[0] & 0xC0) >> 6); } } /// /// Gets or sets protocol version number. /// /// /// SNTP protocol version. Default is 4, which is the latest version at the time of this writing. /// /// /// In request packets, clients should leave this property at default value 4. /// Servers usually reply with the same protocol version. /// public int VersionNumber { get { return (Bytes[0] & 0x38) >> 3; } private set { Bytes[0] = (byte)((Bytes[0] & ~0x38) | value << 3); } } /// /// Gets or sets SNTP packet mode, i.e. whether this is client or server packet. /// /// /// SNTP packet mode. Default is in newly created packets. /// Server reply should have this property set to . /// public NtpMode Mode { get { return (NtpMode)(Bytes[0] & 0x07); } private set { Bytes[0] = (byte)((Bytes[0] & ~0x07) | (int)value); } } /// /// Gets server's distance from the reference clock. /// /// /// /// Distance from the reference clock. This property is set only in server reply packets. /// Servers connected directly to reference clock hardware set this property to 1. /// Statum number is incremented by 1 on every hop down the NTP server hierarchy. /// /// /// Special value 0 indicates that this packet is a Kiss-o'-Death message /// with kiss code stored in . /// /// public int Stratum { get { return Bytes[1]; } } /// /// Gets server's preferred polling interval. /// /// /// Polling interval in log2 seconds, e.g. 4 stands for 16s and 17 means 131,072s. /// public int Poll { get { return Bytes[2]; } } /// /// Gets the precision of server clock. /// /// /// Clock precision in log2 seconds, e.g. -20 for microsecond precision. /// public int Precision { get { return (sbyte)Bytes[3]; } } /// /// Gets the total round-trip delay from the server to the reference clock. /// /// /// Round-trip delay to the reference clock. Normally a positive value smaller than one second. /// public TimeSpan RootDelay { get { return GetTimeSpan32(4); } } /// /// Gets the estimated error in time reported by the server. /// /// /// Estimated error in time reported by the server. Normally a positive value smaller than one second. /// public TimeSpan RootDispersion { get { return GetTimeSpan32(8); } } /// /// Gets the ID of the time source used by the server or Kiss-o'-Death code sent by the server. /// /// /// /// ID of server's time source or Kiss-o'-Death code. /// Purpose of this property depends on value of property. /// /// /// Stratum 1 servers write here one of several special values that describe the kind of hardware clock they use. /// /// /// Stratum 2 and lower servers set this property to IPv4 address of their upstream server. /// If upstream server has IPv6 address, the address is hashed, because it doesn't fit in this property. /// /// /// When server sets to special value 0, /// this property contains so called kiss code that instructs the client to stop querying the server. /// /// public uint ReferenceId { get { return GetUInt32BE(12); } } /// /// Gets or sets the time when the server clock was last set or corrected. /// /// /// Time when the server clock was last set or corrected or null when not specified. /// /// /// This Property is usually set only by servers. It usually lags server's current time by several minutes, /// so don't use this property for time synchronization. /// public DateTime? ReferenceTimestamp { get { return GetDateTime64(16); } } /// /// Gets or sets the time when the client sent its request. /// /// /// This property is null in request packets. /// In reply packets, it is the time when the client sent its request. /// Servers copy this value from /// that they find in received request packet. /// /// /// public DateTime? OriginTimestamp { get { return GetDateTime64(24); } } /// /// Gets or sets the time when the request was received by the server. /// /// /// This property is null in request packets. /// In reply packets, it is the time when the server received client request. /// /// /// public DateTime? ReceiveTimestamp { get { return GetDateTime64(32); } } /// /// Gets or sets the time when the packet was sent. /// /// /// Time when the packet was sent. It should never be null. /// Default value is . /// /// /// This property must be set by both clients and servers. /// /// /// public DateTime? TransmitTimestamp { get { return GetDateTime64(40); } private set { SetDateTime64(40, value); } } /// /// Gets or sets the time of reception of response SNTP packet on the client. /// /// /// Time of reception of response SNTP packet on the client. It is null in request packets. /// /// /// This property is not part of the protocol and has to be set when reply packet is received. /// /// /// public DateTime? DestinationTimestamp { get; private set; } /// /// Gets the round-trip time to the server. /// /// /// Time the request spent traveling to the server plus the time the reply spent traveling back. /// This is calculated from timestamps in the packet as (t1 - t0) + (t3 - t2) /// where t0 is , /// t1 is , /// t2 is , /// and t3 is . /// This property throws an exception in request packets. /// public TimeSpan RoundTripTime { get { CheckTimestamps(); return (ReceiveTimestamp.Value - OriginTimestamp.Value) + (DestinationTimestamp.Value - TransmitTimestamp.Value); } } /// /// Gets the offset that should be added to local time to synchronize it with server time. /// /// /// Time difference between server and client. It should be added to local time to get server time. /// It is calculated from timestamps in the packet as 0.5 * ((t1 - t0) - (t3 - t2)) /// where t0 is , /// t1 is , /// t2 is , /// and t3 is . /// This property throws an exception in request packets. /// public TimeSpan CorrectionOffset { get { CheckTimestamps(); return TimeSpan.FromTicks(((ReceiveTimestamp.Value - OriginTimestamp.Value) - (DestinationTimestamp.Value - TransmitTimestamp.Value)).Ticks / 2); } } /// /// Initializes default request packet. /// /// /// Properties and /// are set appropriately for request packet. Property /// is set to . /// public NtpPacket() : this(new byte[48]) { Mode = NtpMode.Client; VersionNumber = 4; TransmitTimestamp = DateTime.UtcNow; } /// /// Initializes packet from received data. /// internal NtpPacket(byte[] bytes) { if (bytes.Length < 48) throw new ArgumentException("SNTP reply packet must be at least 48 bytes long.", "bytes"); Bytes = bytes; } /// /// Initializes packet from data received from a server. /// /// Data received from the server. /// Utc time of reception of response SNTP packet on the client. /// public static NtpPacket FromServerResponse(byte[] bytes, DateTime destinationTimestamp) { return new NtpPacket(bytes) { DestinationTimestamp = destinationTimestamp }; } internal void ValidateRequest() { if (Mode != NtpMode.Client) throw new InvalidOperationException("This is not a request SNTP packet."); if (VersionNumber == 0) throw new InvalidOperationException("Protocol version of the request is not specified."); if (TransmitTimestamp == null) throw new InvalidOperationException("TransmitTimestamp must be set in request packet."); } internal void ValidateReply() { if (Mode != NtpMode.Server) throw new InvalidOperationException("This is not a reply SNTP packet."); if (VersionNumber == 0) throw new InvalidOperationException("Protocol version of the reply is not specified."); if (Stratum == 0) throw new InvalidOperationException(string.Format("Received Kiss-o'-Death SNTP packet with code 0x{0:x}.", ReferenceId)); if (LeapIndicator == NtpLeapIndicator.AlarmCondition) throw new InvalidOperationException("SNTP server has unsynchronized clock."); CheckTimestamps(); } private void CheckTimestamps() { if (OriginTimestamp == null) throw new InvalidOperationException("Origin timestamp is missing."); if (ReceiveTimestamp == null) throw new InvalidOperationException("Receive timestamp is missing."); if (TransmitTimestamp == null) throw new InvalidOperationException("Transmit timestamp is missing."); if (DestinationTimestamp == null) throw new InvalidOperationException("Destination timestamp is missing."); } private DateTime? GetDateTime64(int offset) { var field = GetUInt64BE(offset); if (field == 0) return null; return new DateTime(Epoch.Ticks + Convert.ToInt64(field * (1.0 / (1L << 32) * 10000000.0))); } private void SetDateTime64(int offset, DateTime? value) { SetUInt64BE(offset, value == null ? 0 : Convert.ToUInt64((value.Value.Ticks - Epoch.Ticks) * (0.0000001 * (1L << 32)))); } private TimeSpan GetTimeSpan32(int offset) { return TimeSpan.FromSeconds(GetInt32BE(offset) / (double)(1 << 16)); } private ulong GetUInt64BE(int offset) { return SwapEndianness(BitConverter.ToUInt64(Bytes, offset)); } private void SetUInt64BE(int offset, ulong value) { FastBitConverter.GetBytes(Bytes, offset, SwapEndianness(value)); } private int GetInt32BE(int offset) { return (int)GetUInt32BE(offset); } private uint GetUInt32BE(int offset) { return SwapEndianness(BitConverter.ToUInt32(Bytes, offset)); } private static uint SwapEndianness(uint x) { return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24); } private static ulong SwapEndianness(ulong x) { return ((ulong)SwapEndianness((uint)x) << 32) | SwapEndianness((uint)(x >> 32)); } } /// /// Represents leap second warning from the server that instructs the client to add or remove leap second. /// /// public enum NtpLeapIndicator { /// /// No leap second warning. No action required. /// NoWarning, /// /// Warns the client that the last minute of the current day has 61 seconds. /// LastMinuteHas61Seconds, /// /// Warns the client that the last minute of the current day has 59 seconds. /// LastMinuteHas59Seconds, /// /// Special value indicating that the server clock is unsynchronized and the returned time is unreliable. /// AlarmCondition } /// /// Describes SNTP packet mode, i.e. client or server. /// /// public enum NtpMode { /// /// Identifies client-to-server SNTP packet. /// Client = 3, /// /// Identifies server-to-client SNTP packet. /// Server = 4, } } ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NtpPacket.cs.meta ================================================ fileFormatVersion: 2 guid: 83e4dc419f776ab498b9af922a0a4b47 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NtpRequest.cs ================================================ using System; using System.Net; using System.Net.Sockets; #if NETSTANDARD || NETCOREAPP using System.Threading.Tasks; #endif namespace LiteNetLib.Utils { /// /// Make NTP request. /// /// 1. Create the object by method. /// /// /// 2. Use method to send requests. /// /// /// 3. Call to release the socket AFTER you have received the response or some timeout. /// If you close the socket too early, you may miss the response. /// /// public sealed class NtpRequest : INetSocketListener { public const int DefaultPort = 123; private readonly NetSocket _socket; private readonly Action _onRequestComplete; private readonly IPEndPoint _ntpEndPoint; /// /// Initialize object, open socket. /// /// NTP Server endpoint /// callback (called from other thread!) private NtpRequest(IPEndPoint endPoint, Action onRequestComplete) { _ntpEndPoint = endPoint; _onRequestComplete = onRequestComplete; // Create and start socket _socket = new NetSocket(this); _socket.Bind( IPAddress.Any, IPAddress.IPv6Any, 0, false, endPoint.AddressFamily == AddressFamily.InterNetworkV6 ? IPv6Mode.SeparateSocket : IPv6Mode.Disabled); } /// /// Create the requests for NTP server, open socket. /// /// NTP Server address. /// callback (called from other thread!) public static NtpRequest Create(IPEndPoint endPoint, Action onRequestComplete) { return new NtpRequest(endPoint, onRequestComplete); } /// /// Create the requests for NTP server (default port), open socket. /// /// NTP Server address. /// callback (called from other thread!) public static NtpRequest Create(IPAddress ipAddress, Action onRequestComplete) { IPEndPoint endPoint = new IPEndPoint(ipAddress, DefaultPort); return Create(endPoint, onRequestComplete); } /// /// Create the requests for NTP server, open socket. /// /// NTP Server address. /// port /// callback (called from other thread!) public static NtpRequest Create(string ntpServerAddress, int port, Action onRequestComplete) { IPEndPoint endPoint = NetUtils.MakeEndPoint(ntpServerAddress, port); return Create(endPoint, onRequestComplete); } /// /// Create the requests for NTP server (default port), open socket. /// /// NTP Server address. /// callback (called from other thread!) public static NtpRequest Create(string ntpServerAddress, Action onRequestComplete) { IPEndPoint endPoint = NetUtils.MakeEndPoint(ntpServerAddress, DefaultPort); return Create(endPoint, onRequestComplete); } #if NETSTANDARD || NETCOREAPP /// /// Requests asynchronously NTP server for time offset /// /// NTP Server address. /// Scheduled task public static async Task RequestAsync(string ntpServerAddress) { var t = new TaskCompletionSource(); await Task.Run(() => { NtpRequest request = null; request = Create(ntpServerAddress, (ntpPacket) => { request.Close(); t.SetResult(ntpPacket); }); request.Send(); }); return await t.Task; } #endif /// /// Send request to the NTP server calls callback (if success). /// In case of error the callback is called with null param. /// public void Send() { SocketError errorCode = 0; var packet = new NtpPacket(); packet.ValidateRequest(); // not necessary byte[] sendData = packet.Bytes; var sendCount = _socket.SendTo(sendData, 0, sendData.Length, _ntpEndPoint, ref errorCode); if (errorCode != 0 || sendCount != sendData.Length) { _onRequestComplete(null); } } /// /// Close socket. /// public void Close() { _socket.Close(false); } /// /// Handle received data: transform bytes to NtpPacket, close socket and call the callback. /// void INetSocketListener.OnMessageReceived(byte[] data, int length, SocketError errorCode, IPEndPoint remoteEndPoint) { DateTime destinationTimestamp = DateTime.UtcNow; if (!remoteEndPoint.Equals(_ntpEndPoint)) return; if (length < 48) { NetDebug.Write(NetLogLevel.Trace, "NTP response too short: {}", length); _onRequestComplete(null); return; } NtpPacket packet = NtpPacket.FromServerResponse(data, destinationTimestamp); try { packet.ValidateReply(); } catch (InvalidOperationException ex) { NetDebug.Write(NetLogLevel.Trace, "NTP response error: {}", ex.Message); packet = null; } _onRequestComplete(packet); } } } ================================================ FILE: Assets/Plugins/LiteNetLib/Utils/NtpRequest.cs.meta ================================================ fileFormatVersion: 2 guid: 2c10cb27cfa72e24ea36cc68ac7fd60d MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib/Utils.meta ================================================ fileFormatVersion: 2 guid: ebacf1eb87716ee49b6fcb20c0704ac6 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins/LiteNetLib.meta ================================================ fileFormatVersion: 2 guid: 25c77b3ad615d2d42a26744ab3ce1f23 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Plugins.meta ================================================ fileFormatVersion: 2 guid: 65324f827c8ba984c87f6eb3977105b3 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Prefabs/ClientPlayerPrefab.prefab ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1 &151283213529981653 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1797227722323354994} - component: {fileID: 7556605251219990148} m_Layer: 0 m_Name: ClientPlayerPrefab m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &1797227722323354994 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 151283213529981653} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 3046785166933838911} - {fileID: 9179342147788701958} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &7556605251219990148 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 151283213529981653} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 34c60df172a14e9ba8eeb93c4b0789c8, type: 3} m_Name: m_EditorClassIdentifier: _name: {fileID: 2752360525779543750} --- !u!1 &3814891849141129684 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 9179342147788701958} - component: {fileID: 2646037144284097107} - component: {fileID: 2752360525779543750} m_Layer: 0 m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &9179342147788701958 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3814891849141129684} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.27} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 1797227722323354994} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &2646037144284097107 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3814891849141129684} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 0 m_MotionVectors: 2 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 m_StaticBatchRoot: {fileID: 0} m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 --- !u!102 &2752360525779543750 TextMesh: serializedVersion: 3 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3814891849141129684} m_Text: UserName 1 m_OffsetZ: 0 m_CharacterSize: 0.07 m_LineSpacing: 1 m_Anchor: 4 m_Alignment: 0 m_TabSize: 4 m_FontSize: 0 m_FontStyle: 0 m_RichText: 0 m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_Color: serializedVersion: 2 rgba: 4294967040 --- !u!1 &4023587236576750559 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 3046785166933838911} - component: {fileID: 5424874948212989909} m_Layer: 0 m_Name: Sprite m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &3046785166933838911 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4023587236576750559} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 5, y: 5, z: 0} m_Children: [] m_Father: {fileID: 1797227722323354994} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!212 &5424874948212989909 SpriteRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4023587236576750559} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 m_StaticBatchRoot: {fileID: 0} m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 m_SelectedEditorRenderState: 0 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0} m_Color: {r: 0, g: 0.2466259, b: 1, a: 1} m_FlipX: 0 m_FlipY: 0 m_DrawMode: 0 m_Size: {x: 0.16, y: 0.16} m_AdaptiveModeThreshold: 0.5 m_SpriteTileMode: 0 m_WasSpriteAssigned: 1 m_MaskInteraction: 0 m_SpriteSortPoint: 0 ================================================ FILE: Assets/Prefabs/ClientPlayerPrefab.prefab.meta ================================================ fileFormatVersion: 2 guid: 4a0b43f6f6aa8d94885a8928324663ae PrefabImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Prefabs/RemotePlayerPrefab.prefab ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1 &151283213529981653 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 7536677238562662957} - component: {fileID: 4478346921506516055} - component: {fileID: 2657010345741789178} m_Layer: 0 m_Name: RemotePlayerPrefab m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &7536677238562662957 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 151283213529981653} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 5, y: 5, z: 5} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!212 &4478346921506516055 SpriteRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 151283213529981653} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 m_StaticBatchRoot: {fileID: 0} m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 m_SelectedEditorRenderState: 0 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} m_Color: {r: 1, g: 0, b: 0, a: 1} m_FlipX: 0 m_FlipY: 0 m_DrawMode: 0 m_Size: {x: 0.2, y: 0.2} m_AdaptiveModeThreshold: 0.5 m_SpriteTileMode: 0 m_WasSpriteAssigned: 1 m_MaskInteraction: 0 m_SpriteSortPoint: 0 --- !u!114 &2657010345741789178 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 151283213529981653} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 79ae69e7e2264cea80924512389f864d, type: 3} m_Name: m_EditorClassIdentifier: ================================================ FILE: Assets/Prefabs/RemotePlayerPrefab.prefab.meta ================================================ fileFormatVersion: 2 guid: 286a2aeb6d53b6140a311cbe617d000f PrefabImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Prefabs/ShootEffect.prefab ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1 &10134456475359289 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 9112191833029426742} - component: {fileID: 8921832712757828151} m_Layer: 0 m_Name: Trail m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &9112191833029426742 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 10134456475359289} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 8525329206789753032} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!120 &8921832712757828151 LineRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 10134456475359289} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 0 m_MotionVectors: 0 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - {fileID: 2100000, guid: 1d13efa0bfe6559499d9e8d00237ef50, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 m_StaticBatchRoot: {fileID: 0} m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 m_Positions: - {x: 0, y: 0, z: 0} - {x: 0, y: 0, z: 1} m_Parameters: serializedVersion: 3 widthMultiplier: 0.1 widthCurve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 1 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 colorGradient: serializedVersion: 2 key0: {r: 1, g: 1, b: 1, a: 1} key1: {r: 1, g: 1, b: 1, a: 1} key2: {r: 0, g: 0, b: 0, a: 0} key3: {r: 0, g: 0, b: 0, a: 0} key4: {r: 0, g: 0, b: 0, a: 0} key5: {r: 0, g: 0, b: 0, a: 0} key6: {r: 0, g: 0, b: 0, a: 0} key7: {r: 0, g: 0, b: 0, a: 0} ctime0: 0 ctime1: 65535 ctime2: 0 ctime3: 0 ctime4: 0 ctime5: 0 ctime6: 0 ctime7: 0 atime0: 0 atime1: 65535 atime2: 0 atime3: 0 atime4: 0 atime5: 0 atime6: 0 atime7: 0 m_Mode: 0 m_NumColorKeys: 2 m_NumAlphaKeys: 2 numCornerVertices: 0 numCapVertices: 0 alignment: 0 textureMode: 0 shadowBias: 0.5 generateLightingData: 0 m_UseWorldSpace: 1 m_Loop: 0 --- !u!1 &3604735914733021196 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 6589428951704469504} - component: {fileID: 2721550175456079499} m_Layer: 0 m_Name: From m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &6589428951704469504 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3604735914733021196} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 8525329206789753032} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!82 &2721550175456079499 AudioSource: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3604735914733021196} m_Enabled: 1 serializedVersion: 4 OutputAudioMixerGroup: {fileID: 0} m_audioClip: {fileID: 0} m_PlayOnAwake: 0 m_Volume: 0.802 m_Pitch: 1 Loop: 0 Mute: 0 Spatialize: 0 SpatializePostEffects: 0 Priority: 128 DopplerLevel: 1 MinDistance: 1 MaxDistance: 15 Pan2D: 0 rolloffMode: 1 BypassEffects: 0 BypassListenerEffects: 0 BypassReverbZones: 0 rolloffCustomCurve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 1 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 - serializedVersion: 3 time: 1 value: 0 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 panLevelCustomCurve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 1 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 spreadCustomCurve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 0 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 reverbZoneMixCustomCurve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 1 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 --- !u!1 &6888274765561601940 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 6218223324148723486} - component: {fileID: 7954143563232318346} m_Layer: 0 m_Name: To m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &6218223324148723486 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6888274765561601940} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 8525329206789753032} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!82 &7954143563232318346 AudioSource: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6888274765561601940} m_Enabled: 1 serializedVersion: 4 OutputAudioMixerGroup: {fileID: 0} m_audioClip: {fileID: 0} m_PlayOnAwake: 0 m_Volume: 0.802 m_Pitch: 1 Loop: 0 Mute: 0 Spatialize: 0 SpatializePostEffects: 0 Priority: 128 DopplerLevel: 1 MinDistance: 1 MaxDistance: 15 Pan2D: 0 rolloffMode: 1 BypassEffects: 0 BypassListenerEffects: 0 BypassReverbZones: 0 rolloffCustomCurve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 1 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 - serializedVersion: 3 time: 1 value: 0 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 panLevelCustomCurve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 1 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 spreadCustomCurve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 0 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 reverbZoneMixCustomCurve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 1 inSlope: 0 outSlope: 0 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 --- !u!1 &8525329206789753046 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 8525329206789753032} - component: {fileID: 8525329206789753033} m_Layer: 0 m_Name: ShootEffect m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &8525329206789753032 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8525329206789753046} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 6589428951704469504} - {fileID: 6218223324148723486} - {fileID: 9112191833029426742} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &8525329206789753033 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8525329206789753046} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 2b4d80dfb54119b48b368fd5b60c6ab6, type: 3} m_Name: m_EditorClassIdentifier: _trailRenderer: {fileID: 8921832712757828151} _source: {fileID: 2721550175456079499} _target: {fileID: 7954143563232318346} _shootClips: - {fileID: 8300000, guid: 55dee76dad95e3d4485c01b3bffe0cc3, type: 3} - {fileID: 8300000, guid: 8fa59a9269be9fb43992ae257a830baf, type: 3} - {fileID: 8300000, guid: c5fb28979014b6743a96f027422cd352, type: 3} _hitClips: - {fileID: 8300000, guid: 2db6c6b7d938d6445a1506241e464a60, type: 3} - {fileID: 8300000, guid: 7183751adb74f494d8c1760f20588846, type: 3} - {fileID: 8300000, guid: 71216075a33d8b747a2e1cf162174c02, type: 3} ================================================ FILE: Assets/Prefabs/ShootEffect.prefab.meta ================================================ fileFormatVersion: 2 guid: 34cd9b45af10c374daabd0023264392f PrefabImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Prefabs.meta ================================================ fileFormatVersion: 2 guid: 38a006a4ce1e703499c4c180ac138f0e folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Scenes/SampleScene.unity ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!29 &1 OcclusionCullingSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_OcclusionBakeSettings: smallestOccluder: 5 smallestHole: 0.25 backfaceThreshold: 100 m_SceneGUID: 00000000000000000000000000000000 m_OcclusionCullingData: {fileID: 0} --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 serializedVersion: 9 m_Fog: 0 m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} m_FogMode: 3 m_FogDensity: 0.01 m_LinearFogStart: 0 m_LinearFogEnd: 300 m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} m_AmbientIntensity: 1 m_AmbientMode: 3 m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} m_SkyboxMaterial: {fileID: 0} m_HaloStrength: 0.5 m_FlareStrength: 1 m_FlareFadeSpeed: 3 m_HaloTexture: {fileID: 0} m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} m_DefaultReflectionMode: 0 m_DefaultReflectionResolution: 128 m_ReflectionBounces: 1 m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 serializedVersion: 11 m_GIWorkflowMode: 1 m_GISettings: serializedVersion: 2 m_BounceScale: 1 m_IndirectOutputScale: 1 m_AlbedoBoost: 1 m_EnvironmentLightingMode: 0 m_EnableBakedLightmaps: 0 m_EnableRealtimeLightmaps: 0 m_LightmapEditorSettings: serializedVersion: 12 m_Resolution: 2 m_BakeResolution: 40 m_AtlasSize: 1024 m_AO: 0 m_AOMaxDistance: 1 m_CompAOExponent: 1 m_CompAOExponentDirect: 0 m_ExtractAmbientOcclusion: 0 m_Padding: 2 m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 m_TextureCompression: 1 m_FinalGather: 0 m_FinalGatherFiltering: 1 m_FinalGatherRayCount: 256 m_ReflectionCompression: 2 m_MixedBakeMode: 2 m_BakeBackend: 0 m_PVRSampling: 1 m_PVRDirectSampleCount: 32 m_PVRSampleCount: 500 m_PVRBounces: 2 m_PVREnvironmentSampleCount: 500 m_PVREnvironmentReferencePointCount: 2048 m_PVRFilteringMode: 2 m_PVRDenoiserTypeDirect: 0 m_PVRDenoiserTypeIndirect: 0 m_PVRDenoiserTypeAO: 0 m_PVRFilterTypeDirect: 0 m_PVRFilterTypeIndirect: 0 m_PVRFilterTypeAO: 0 m_PVREnvironmentMIS: 0 m_PVRCulling: 1 m_PVRFilteringGaussRadiusDirect: 1 m_PVRFilteringGaussRadiusIndirect: 5 m_PVRFilteringGaussRadiusAO: 2 m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 m_ExportTrainingData: 0 m_TrainingDataDestination: TrainingData m_LightProbeSampleCountMultiplier: 4 m_LightingDataAsset: {fileID: 0} m_UseShadowmask: 1 --- !u!196 &4 NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: serializedVersion: 2 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 agentSlope: 45 agentClimb: 0.4 ledgeDropHeight: 0 maxJumpAcrossDistance: 0 minRegionArea: 2 manualCellSize: 0 cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 accuratePlacement: 0 debug: m_Flags: 0 m_NavMeshData: {fileID: 0} --- !u!1 &519420028 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 519420032} - component: {fileID: 519420031} - component: {fileID: 519420029} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!81 &519420029 AudioListener: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 519420028} m_Enabled: 1 --- !u!20 &519420031 Camera: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 519420028} m_Enabled: 1 serializedVersion: 2 m_ClearFlags: 2 m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0} m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 y: 0 width: 1 height: 1 near clip plane: 0.3 far clip plane: 1000 field of view: 60 orthographic: 1 orthographic size: 5 m_Depth: -1 m_CullingMask: serializedVersion: 2 m_Bits: 4294967295 m_RenderingPath: -1 m_TargetTexture: {fileID: 0} m_TargetDisplay: 0 m_TargetEye: 0 m_HDR: 1 m_AllowMSAA: 0 m_AllowDynamicResolution: 0 m_ForceIntoRT: 0 m_OcclusionCulling: 0 m_StereoConvergence: 10 m_StereoSeparation: 0.022 --- !u!4 &519420032 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 519420028} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &574874987 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 574874990} - component: {fileID: 574874989} - component: {fileID: 574874988} m_Layer: 5 m_Name: DisconnectInfo m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &574874988 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 574874987} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 14 m_FontStyle: 0 m_BestFit: 0 m_MinSize: 10 m_MaxSize: 40 m_Alignment: 0 m_AlignByGeometry: 0 m_RichText: 1 m_HorizontalOverflow: 0 m_VerticalOverflow: 0 m_LineSpacing: 1 m_Text: --- !u!222 &574874989 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 574874987} m_CullTransparentMesh: 0 --- !u!224 &574874990 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 574874987} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 1169882547} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: 0, y: 68.39} m_SizeDelta: {x: 160, y: 30} m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &683264134 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 683264139} - component: {fileID: 683264138} - component: {fileID: 683264137} - component: {fileID: 683264136} - component: {fileID: 683264135} m_Layer: 5 m_Name: Canvas m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &683264135 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 683264134} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 637a36ad5bdc47f47a5e5c0de52df2fc, type: 3} m_Name: m_EditorClassIdentifier: _uiObject: {fileID: 1169882546} _clientLogic: {fileID: 1732463073} _serverLogic: {fileID: 892645884} _ipField: {fileID: 1804648706} _disconnectInfoField: {fileID: 574874988} --- !u!114 &683264136 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 683264134} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} m_Name: m_EditorClassIdentifier: m_IgnoreReversedGraphics: 1 m_BlockingObjects: 0 m_BlockingMask: serializedVersion: 2 m_Bits: 4294967295 --- !u!114 &683264137 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 683264134} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} m_Name: m_EditorClassIdentifier: m_UiScaleMode: 2 m_ReferencePixelsPerUnit: 100 m_ScaleFactor: 1 m_ReferenceResolution: {x: 800, y: 600} m_ScreenMatchMode: 0 m_MatchWidthOrHeight: 0 m_PhysicalUnit: 3 m_FallbackScreenDPI: 96 m_DefaultSpriteDPI: 96 m_DynamicPixelsPerUnit: 1 --- !u!223 &683264138 Canvas: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 683264134} m_Enabled: 1 serializedVersion: 3 m_RenderMode: 0 m_Camera: {fileID: 0} m_PlaneDistance: 100 m_PixelPerfect: 0 m_ReceivesEvents: 1 m_OverrideSorting: 0 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 m_AdditionalShaderChannelsFlag: 0 m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 --- !u!224 &683264139 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 683264134} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} m_Children: - {fileID: 1169882547} - {fileID: 2010902790} m_Father: {fileID: 0} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} --- !u!1 &777809101 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 777809102} - component: {fileID: 777809104} - component: {fileID: 777809103} m_Layer: 5 m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!224 &777809102 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 777809101} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 1804648705} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: -0.5} m_SizeDelta: {x: -20, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &777809103 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 777809101} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 14 m_FontStyle: 0 m_BestFit: 0 m_MinSize: 10 m_MaxSize: 40 m_Alignment: 0 m_AlignByGeometry: 0 m_RichText: 0 m_HorizontalOverflow: 1 m_VerticalOverflow: 0 m_LineSpacing: 1 m_Text: --- !u!222 &777809104 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 777809101} m_CullTransparentMesh: 0 --- !u!1 &892645883 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 892645885} - component: {fileID: 892645884} m_Layer: 0 m_Name: ServerObject m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &892645884 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 892645883} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 1e51c98f0b54146429c775c426760c8a, type: 3} m_Name: m_EditorClassIdentifier: --- !u!4 &892645885 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 892645883} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1021329754 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1021329755} - component: {fileID: 1021329758} - component: {fileID: 1021329757} - component: {fileID: 1021329756} m_Layer: 5 m_Name: ConnectButton m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!224 &1021329755 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1021329754} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 1331489039} m_Father: {fileID: 1169882547} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: 0, y: -51.4} m_SizeDelta: {x: 160, y: 30} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1021329756 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1021329754} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} m_SelectOnRight: {fileID: 0} m_Transition: 1 m_Colors: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 1021329757} m_OnClick: m_PersistentCalls: m_Calls: - m_Target: {fileID: 683264135} m_MethodName: OnConnectClick m_Mode: 1 m_Arguments: m_ObjectArgument: {fileID: 0} m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine m_IntArgument: 0 m_FloatArgument: 0 m_StringArgument: m_BoolArgument: 0 m_CallState: 2 --- !u!114 &1021329757 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1021329754} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 m_FillMethod: 4 m_FillAmount: 1 m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 --- !u!222 &1021329758 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1021329754} m_CullTransparentMesh: 0 --- !u!1 &1169882546 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1169882547} m_Layer: 5 m_Name: MainUI m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!224 &1169882547 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1169882546} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 2006599508} - {fileID: 1021329755} - {fileID: 1804648705} - {fileID: 574874990} m_Father: {fileID: 683264139} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 100, y: 100} m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1229896548 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1229896549} - component: {fileID: 1229896551} - component: {fileID: 1229896550} m_Layer: 5 m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!224 &1229896549 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1229896548} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 2006599508} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1229896550 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1229896548} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 14 m_FontStyle: 0 m_BestFit: 0 m_MinSize: 10 m_MaxSize: 40 m_Alignment: 4 m_AlignByGeometry: 0 m_RichText: 1 m_HorizontalOverflow: 0 m_VerticalOverflow: 0 m_LineSpacing: 1 m_Text: Host --- !u!222 &1229896551 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1229896548} m_CullTransparentMesh: 0 --- !u!1 &1306777468 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1306777472} - component: {fileID: 1306777471} - component: {fileID: 1306777469} - component: {fileID: 1306777470} m_Layer: 5 m_Name: DebugText m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &1306777469 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1306777468} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 14 m_FontStyle: 0 m_BestFit: 0 m_MinSize: 10 m_MaxSize: 40 m_Alignment: 0 m_AlignByGeometry: 0 m_RichText: 1 m_HorizontalOverflow: 0 m_VerticalOverflow: 1 m_LineSpacing: 1 m_Text: '1LINE 2LINE' --- !u!114 &1306777470 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1306777468} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} m_Name: m_EditorClassIdentifier: m_EffectColor: {r: 0, g: 0, b: 0, a: 0.5} m_EffectDistance: {x: 1, y: -1} m_UseGraphicAlpha: 1 --- !u!222 &1306777471 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1306777468} m_CullTransparentMesh: 0 --- !u!224 &1306777472 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1306777468} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 2010902790} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: -128, y: 1.9} m_SizeDelta: {x: 200.1, y: 78.3} m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1331489038 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1331489039} - component: {fileID: 1331489041} - component: {fileID: 1331489040} m_Layer: 5 m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!224 &1331489039 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1331489038} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 1021329755} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1331489040 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1331489038} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 14 m_FontStyle: 0 m_BestFit: 0 m_MinSize: 10 m_MaxSize: 40 m_Alignment: 4 m_AlignByGeometry: 0 m_RichText: 1 m_HorizontalOverflow: 0 m_VerticalOverflow: 0 m_LineSpacing: 1 m_Text: Connect --- !u!222 &1331489041 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1331489038} m_CullTransparentMesh: 0 --- !u!1 &1682712804 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1682712807} - component: {fileID: 1682712806} - component: {fileID: 1682712805} m_Layer: 0 m_Name: EventSystem m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &1682712805 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1682712804} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} m_Name: m_EditorClassIdentifier: m_HorizontalAxis: Horizontal m_VerticalAxis: Vertical m_SubmitButton: Submit m_CancelButton: Cancel m_InputActionsPerSecond: 10 m_RepeatDelay: 0.5 m_ForceModuleActive: 0 --- !u!114 &1682712806 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1682712804} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} m_Name: m_EditorClassIdentifier: m_FirstSelected: {fileID: 0} m_sendNavigationEvents: 1 m_DragThreshold: 10 --- !u!4 &1682712807 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1682712804} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1732463072 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1732463074} - component: {fileID: 1732463073} m_Layer: 0 m_Name: ClientObject m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &1732463073 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1732463072} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 4a77c76b7885899499706477e4e0eeb4, type: 3} m_Name: m_EditorClassIdentifier: _clientPlayerViewPrefab: {fileID: 7556605251219990148, guid: 4a0b43f6f6aa8d94885a8928324663ae, type: 3} _remotePlayerViewPrefab: {fileID: 2657010345741789178, guid: 286a2aeb6d53b6140a311cbe617d000f, type: 3} _debugText: {fileID: 1306777469} _shootEffectPrefab: {fileID: 8525329206789753033, guid: 34cd9b45af10c374daabd0023264392f, type: 3} --- !u!4 &1732463074 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1732463072} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1775097702 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1775097703} - component: {fileID: 1775097705} - component: {fileID: 1775097704} m_Layer: 5 m_Name: Placeholder m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!224 &1775097703 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1775097702} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 1804648705} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: -0.5} m_SizeDelta: {x: -20, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1775097704 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1775097702} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5} m_RaycastTarget: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 14 m_FontStyle: 2 m_BestFit: 0 m_MinSize: 10 m_MaxSize: 40 m_Alignment: 0 m_AlignByGeometry: 0 m_RichText: 1 m_HorizontalOverflow: 0 m_VerticalOverflow: 0 m_LineSpacing: 1 m_Text: Enter text... --- !u!222 &1775097705 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1775097702} m_CullTransparentMesh: 0 --- !u!1 &1804648704 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1804648705} - component: {fileID: 1804648708} - component: {fileID: 1804648707} - component: {fileID: 1804648706} m_Layer: 5 m_Name: IpField m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!224 &1804648705 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1804648704} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 1775097703} - {fileID: 777809102} m_Father: {fileID: 1169882547} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: 0, y: -102.9} m_SizeDelta: {x: 160, y: 30} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1804648706 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1804648704} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} m_SelectOnRight: {fileID: 0} m_Transition: 1 m_Colors: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 1804648707} m_TextComponent: {fileID: 777809103} m_Placeholder: {fileID: 1775097704} m_ContentType: 0 m_InputType: 0 m_AsteriskChar: 42 m_KeyboardType: 0 m_LineType: 0 m_HideMobileInput: 0 m_CharacterValidation: 0 m_CharacterLimit: 0 m_OnEndEdit: m_PersistentCalls: m_Calls: [] m_OnValueChanged: m_PersistentCalls: m_Calls: [] m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_CustomCaretColor: 0 m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} m_Text: m_CaretBlinkRate: 0.85 m_CaretWidth: 1 m_ReadOnly: 0 --- !u!114 &1804648707 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1804648704} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 m_FillMethod: 4 m_FillAmount: 1 m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 --- !u!222 &1804648708 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1804648704} m_CullTransparentMesh: 0 --- !u!1 &2006599507 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 2006599508} - component: {fileID: 2006599511} - component: {fileID: 2006599510} - component: {fileID: 2006599509} m_Layer: 5 m_Name: HostButton m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!224 &2006599508 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2006599507} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 1229896549} m_Father: {fileID: 1169882547} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: 0, y: 48.5} m_SizeDelta: {x: 160, y: 30} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &2006599509 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2006599507} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} m_SelectOnRight: {fileID: 0} m_Transition: 1 m_Colors: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 2006599510} m_OnClick: m_PersistentCalls: m_Calls: - m_Target: {fileID: 683264135} m_MethodName: OnHostClick m_Mode: 1 m_Arguments: m_ObjectArgument: {fileID: 0} m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine m_IntArgument: 0 m_FloatArgument: 0 m_StringArgument: m_BoolArgument: 0 m_CallState: 2 --- !u!114 &2006599510 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2006599507} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} m_Type: 1 m_PreserveAspect: 0 m_FillCenter: 1 m_FillMethod: 4 m_FillAmount: 1 m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 --- !u!222 &2006599511 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2006599507} m_CullTransparentMesh: 0 --- !u!1 &2010902789 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 2010902790} m_Layer: 5 m_Name: HUD m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!224 &2010902790 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2010902789} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 1306777472} m_Father: {fileID: 683264139} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0, y: 1} m_AnchoredPosition: {x: 263.9, y: -67} m_SizeDelta: {x: 475.9, y: 100} m_Pivot: {x: 0.5, y: 0.5} ================================================ FILE: Assets/Scenes/SampleScene.unity.meta ================================================ fileFormatVersion: 2 guid: 0ded2f7f4591ec7439b78ecd3eeabd99 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Scenes.meta ================================================ fileFormatVersion: 2 guid: 934d0155eefb63546bde5bf781f772f2 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds/death1.ogg.meta ================================================ fileFormatVersion: 2 guid: 13bf3bab902d9a64ba3936703ad369fe AudioImporter: externalObjects: {} serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 1 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds/death2.ogg.meta ================================================ fileFormatVersion: 2 guid: db647400cd797af43b96ed8a210e4caf AudioImporter: externalObjects: {} serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 1 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds/death3.ogg.meta ================================================ fileFormatVersion: 2 guid: 6f08295468d9a2f43a53f7826f09b3f2 AudioImporter: externalObjects: {} serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 1 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds/hit1.ogg.meta ================================================ fileFormatVersion: 2 guid: 2db6c6b7d938d6445a1506241e464a60 AudioImporter: externalObjects: {} serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 1 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds/hit2.ogg.meta ================================================ fileFormatVersion: 2 guid: 7183751adb74f494d8c1760f20588846 AudioImporter: externalObjects: {} serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 1 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds/hit3.ogg.meta ================================================ fileFormatVersion: 2 guid: 71216075a33d8b747a2e1cf162174c02 AudioImporter: externalObjects: {} serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 1 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds/shoot1.ogg.meta ================================================ fileFormatVersion: 2 guid: 55dee76dad95e3d4485c01b3bffe0cc3 AudioImporter: externalObjects: {} serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 1 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds/shoot2.ogg.meta ================================================ fileFormatVersion: 2 guid: 8fa59a9269be9fb43992ae257a830baf AudioImporter: externalObjects: {} serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 1 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds/shoot3.ogg.meta ================================================ fileFormatVersion: 2 guid: c5fb28979014b6743a96f027422cd352 AudioImporter: externalObjects: {} serializedVersion: 6 defaultSettings: loadType: 0 sampleRateSetting: 0 sampleRateOverride: 44100 compressionFormat: 1 quality: 1 conversionMode: 0 platformSettingOverrides: {} forceToMono: 1 normalize: 1 preloadAudioData: 1 loadInBackground: 0 ambisonic: 0 3D: 1 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Sounds.meta ================================================ fileFormatVersion: 2 guid: 660d458fea5aa5f458b37717489c7b36 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Ruslan Pyrch Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Packages/manifest.json ================================================ { "dependencies": { "com.unity.ide.rider": "3.0.15", "com.unity.ide.visualstudio": "2.0.16", "com.unity.ide.vscode": "1.2.5", "com.unity.ugui": "1.0.0", "com.veriorpies.parrelsync": "https://github.com/VeriorPies/ParrelSync.git?path=/ParrelSync", "com.unity.modules.audio": "1.0.0", "com.unity.modules.ui": "1.0.0" } } ================================================ FILE: Packages/packages-lock.json ================================================ { "dependencies": { "com.unity.ext.nunit": { "version": "1.0.6", "depth": 1, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.ide.rider": { "version": "3.0.15", "depth": 0, "source": "registry", "dependencies": { "com.unity.ext.nunit": "1.0.6" }, "url": "https://packages.unity.com" }, "com.unity.ide.visualstudio": { "version": "2.0.16", "depth": 0, "source": "registry", "dependencies": { "com.unity.test-framework": "1.1.9" }, "url": "https://packages.unity.com" }, "com.unity.ide.vscode": { "version": "1.2.5", "depth": 0, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.test-framework": { "version": "1.1.31", "depth": 1, "source": "registry", "dependencies": { "com.unity.ext.nunit": "1.0.6", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" }, "url": "https://packages.unity.com" }, "com.unity.ugui": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0" } }, "com.veriorpies.parrelsync": { "version": "https://github.com/VeriorPies/ParrelSync.git?path=/ParrelSync", "depth": 0, "source": "git", "dependencies": {}, "hash": "f45424822189ebd875d864a17d7f03b72eafbff7" }, "com.unity.modules.audio": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.imgui": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.jsonserialize": { "version": "1.0.0", "depth": 2, "source": "builtin", "dependencies": {} }, "com.unity.modules.ui": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} } } } ================================================ FILE: ProjectSettings/AudioManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!11 &1 AudioManager: m_ObjectHideFlags: 0 m_Volume: 1 Rolloff Scale: 1 Doppler Factor: 1 Default Speaker Mode: 2 m_SampleRate: 0 m_DSPBufferSize: 1024 m_VirtualVoiceCount: 512 m_RealVoiceCount: 32 m_SpatializerPlugin: m_AmbisonicDecoderPlugin: m_DisableAudio: 0 m_VirtualizeEffects: 1 ================================================ FILE: ProjectSettings/ClusterInputManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!236 &1 ClusterInputManager: m_ObjectHideFlags: 0 m_Inputs: [] ================================================ FILE: ProjectSettings/DynamicsManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!55 &1 PhysicsManager: m_ObjectHideFlags: 0 serializedVersion: 8 m_Gravity: {x: 0, y: -9.81, z: 0} m_DefaultMaterial: {fileID: 0} m_BounceThreshold: 2 m_SleepThreshold: 0.005 m_DefaultContactOffset: 0.01 m_DefaultSolverIterations: 6 m_DefaultSolverVelocityIterations: 1 m_QueriesHitBackfaces: 0 m_QueriesHitTriggers: 1 m_EnableAdaptiveForce: 0 m_ClothInterCollisionDistance: 0 m_ClothInterCollisionStiffness: 0 m_ContactsGeneration: 1 m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff m_AutoSimulation: 1 m_AutoSyncTransforms: 0 m_ReuseCollisionCallbacks: 1 m_ClothInterCollisionSettingsToggle: 0 m_ContactPairsMode: 0 m_BroadphaseType: 0 m_WorldBounds: m_Center: {x: 0, y: 0, z: 0} m_Extent: {x: 250, y: 250, z: 250} m_WorldSubdivisions: 8 ================================================ FILE: ProjectSettings/EditorBuildSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1045 &1 EditorBuildSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_Scenes: [] m_configObjects: {} ================================================ FILE: ProjectSettings/EditorSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!159 &1 EditorSettings: m_ObjectHideFlags: 0 serializedVersion: 9 m_ExternalVersionControlSupport: Visible Meta Files m_SerializationMode: 2 m_LineEndingsForNewScripts: 2 m_DefaultBehaviorMode: 1 m_PrefabRegularEnvironment: {fileID: 0} m_PrefabUIEnvironment: {fileID: 0} m_SpritePackerMode: 4 m_SpritePackerPaddingPower: 1 m_EtcTextureCompressorBehavior: 1 m_EtcTextureFastCompressor: 1 m_EtcTextureNormalCompressor: 2 m_EtcTextureBestCompressor: 4 m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref m_ProjectGenerationRootNamespace: m_CollabEditorSettings: inProgressEnabled: 1 m_EnableTextureStreamingInEditMode: 1 m_EnableTextureStreamingInPlayMode: 1 m_AsyncShaderCompilation: 1 m_EnterPlayModeOptionsEnabled: 0 m_EnterPlayModeOptions: 3 m_ShowLightmapResolutionOverlay: 1 m_UseLegacyProbeSampleCount: 1 m_AssetPipelineMode: 1 m_CacheServerMode: 0 m_CacheServerEndpoint: m_CacheServerNamespacePrefix: default m_CacheServerEnableDownload: 1 m_CacheServerEnableUpload: 1 ================================================ FILE: ProjectSettings/GraphicsSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!30 &1 GraphicsSettings: m_ObjectHideFlags: 0 serializedVersion: 13 m_Deferred: m_Mode: 1 m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} m_DeferredReflections: m_Mode: 1 m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} m_ScreenSpaceShadows: m_Mode: 1 m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} m_LegacyDeferred: m_Mode: 1 m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} m_DepthNormals: m_Mode: 1 m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} m_MotionVectors: m_Mode: 1 m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} m_LightHalo: m_Mode: 1 m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} m_LensFlare: m_Mode: 1 m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} m_AlwaysIncludedShaders: - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0} m_PreloadedShaders: [] m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} m_CustomRenderPipeline: {fileID: 0} m_TransparencySortMode: 0 m_TransparencySortAxis: {x: 0, y: 0, z: 1} m_DefaultRenderingPath: 1 m_DefaultMobileRenderingPath: 1 m_TierSettings: [] m_LightmapStripping: 0 m_FogStripping: 0 m_InstancingStripping: 0 m_LightmapKeepPlain: 1 m_LightmapKeepDirCombined: 1 m_LightmapKeepDynamicPlain: 1 m_LightmapKeepDynamicDirCombined: 1 m_LightmapKeepShadowMask: 1 m_LightmapKeepSubtractive: 1 m_FogKeepLinear: 1 m_FogKeepExp: 1 m_FogKeepExp2: 1 m_AlbedoSwatchInfos: [] m_LightsUseLinearIntensity: 0 m_LightsUseColorTemperature: 0 m_LogWhenShaderIsCompiled: 0 m_AllowEnlightenSupportForUpgradedProject: 1 ================================================ FILE: ProjectSettings/InputManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!13 &1 InputManager: m_ObjectHideFlags: 0 serializedVersion: 2 m_Axes: - serializedVersion: 3 m_Name: Horizontal descriptiveName: descriptiveNegativeName: negativeButton: left positiveButton: right altNegativeButton: a altPositiveButton: d gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 1 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Vertical descriptiveName: descriptiveNegativeName: negativeButton: down positiveButton: up altNegativeButton: s altPositiveButton: w gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 1 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire1 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: left ctrl altNegativeButton: altPositiveButton: mouse 0 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire2 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: left alt altNegativeButton: altPositiveButton: mouse 1 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire3 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: left shift altNegativeButton: altPositiveButton: mouse 2 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Jump descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: space altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Mouse X descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0 sensitivity: 0.1 snap: 0 invert: 0 type: 1 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Mouse Y descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0 sensitivity: 0.1 snap: 0 invert: 0 type: 1 axis: 1 joyNum: 0 - serializedVersion: 3 m_Name: Mouse ScrollWheel descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0 sensitivity: 0.1 snap: 0 invert: 0 type: 1 axis: 2 joyNum: 0 - serializedVersion: 3 m_Name: Horizontal descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0.19 sensitivity: 1 snap: 0 invert: 0 type: 2 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Vertical descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0.19 sensitivity: 1 snap: 0 invert: 1 type: 2 axis: 1 joyNum: 0 - serializedVersion: 3 m_Name: Fire1 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 0 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire2 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 1 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire3 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 2 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Jump descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 3 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Submit descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: return altNegativeButton: altPositiveButton: joystick button 0 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Submit descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: enter altNegativeButton: altPositiveButton: space gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Cancel descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: escape altNegativeButton: altPositiveButton: joystick button 1 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 ================================================ FILE: ProjectSettings/MemorySettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!387306366 &1 MemorySettings: m_ObjectHideFlags: 0 m_EditorMemorySettings: m_MainAllocatorBlockSize: -1 m_ThreadAllocatorBlockSize: -1 m_MainGfxBlockSize: -1 m_ThreadGfxBlockSize: -1 m_CacheBlockSize: -1 m_TypetreeBlockSize: -1 m_ProfilerBlockSize: -1 m_ProfilerEditorBlockSize: -1 m_BucketAllocatorGranularity: -1 m_BucketAllocatorBucketsCount: -1 m_BucketAllocatorBlockSize: -1 m_BucketAllocatorBlockCount: -1 m_ProfilerBucketAllocatorGranularity: -1 m_ProfilerBucketAllocatorBucketsCount: -1 m_ProfilerBucketAllocatorBlockSize: -1 m_ProfilerBucketAllocatorBlockCount: -1 m_TempAllocatorSizeMain: -1 m_JobTempAllocatorBlockSize: -1 m_BackgroundJobTempAllocatorBlockSize: -1 m_JobTempAllocatorReducedBlockSize: -1 m_TempAllocatorSizeGIBakingWorker: -1 m_TempAllocatorSizeNavMeshWorker: -1 m_TempAllocatorSizeAudioWorker: -1 m_TempAllocatorSizeCloudWorker: -1 m_TempAllocatorSizeGfx: -1 m_TempAllocatorSizeJobWorker: -1 m_TempAllocatorSizeBackgroundWorker: -1 m_TempAllocatorSizePreloadManager: -1 m_PlatformMemorySettings: {} ================================================ FILE: ProjectSettings/NavMeshAreas.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!126 &1 NavMeshProjectSettings: m_ObjectHideFlags: 0 serializedVersion: 2 areas: - name: Walkable cost: 1 - name: Not Walkable cost: 1 - name: Jump cost: 2 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 m_LastAgentTypeID: -887442657 m_Settings: - serializedVersion: 2 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 agentSlope: 45 agentClimb: 0.75 ledgeDropHeight: 0 maxJumpAcrossDistance: 0 minRegionArea: 2 manualCellSize: 0 cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 accuratePlacement: 0 debug: m_Flags: 0 m_SettingNames: - Humanoid ================================================ FILE: ProjectSettings/NetworkManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!149 &1 NetworkManager: m_ObjectHideFlags: 0 m_DebugLevel: 0 m_Sendrate: 15 m_AssetToPrefab: {} ================================================ FILE: ProjectSettings/PackageManagerSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: m_ObjectHideFlags: 61 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_EnablePreReleasePackages: 0 m_EnablePackageDependencies: 0 m_AdvancedSettingsExpanded: 1 m_ScopedRegistriesSettingsExpanded: 1 m_SeeAllPackageVersions: 0 oneTimeWarningShown: 0 m_Registries: - m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 m_Capabilities: 7 m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: m_Modified: 0 m_ErrorMessage: m_UserModificationsInstanceId: -832 m_OriginalInstanceId: -834 m_LoadAssets: 0 ================================================ FILE: ProjectSettings/Physics2DSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!19 &1 Physics2DSettings: m_ObjectHideFlags: 0 serializedVersion: 4 m_Gravity: {x: 0, y: -9.81} m_DefaultMaterial: {fileID: 0} m_VelocityIterations: 8 m_PositionIterations: 3 m_VelocityThreshold: 1 m_MaxLinearCorrection: 0.2 m_MaxAngularCorrection: 8 m_MaxTranslationSpeed: 100 m_MaxRotationSpeed: 360 m_BaumgarteScale: 0.2 m_BaumgarteTimeOfImpactScale: 0.75 m_TimeToSleep: 0.5 m_LinearSleepTolerance: 0.01 m_AngularSleepTolerance: 2 m_DefaultContactOffset: 0.01 m_JobOptions: serializedVersion: 2 useMultithreading: 0 useConsistencySorting: 0 m_InterpolationPosesPerJob: 100 m_NewContactsPerJob: 30 m_CollideContactsPerJob: 100 m_ClearFlagsPerJob: 200 m_ClearBodyForcesPerJob: 200 m_SyncDiscreteFixturesPerJob: 50 m_SyncContinuousFixturesPerJob: 50 m_FindNearestContactsPerJob: 100 m_UpdateTriggerContactsPerJob: 100 m_IslandSolverCostThreshold: 100 m_IslandSolverBodyCostScale: 1 m_IslandSolverContactCostScale: 10 m_IslandSolverJointCostScale: 10 m_IslandSolverBodiesPerJob: 50 m_IslandSolverContactsPerJob: 50 m_AutoSimulation: 1 m_QueriesHitTriggers: 1 m_QueriesStartInColliders: 1 m_CallbacksOnDisable: 1 m_ReuseCollisionCallbacks: 1 m_AutoSyncTransforms: 0 m_AlwaysShowColliders: 0 m_ShowColliderSleep: 1 m_ShowColliderContacts: 0 m_ShowColliderAABB: 0 m_ContactArrowScale: 0.2 m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ================================================ FILE: ProjectSettings/PresetManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1386491679 &1 PresetManager: m_ObjectHideFlags: 0 m_DefaultList: - type: m_NativeTypeID: 20 m_ManagedTypePPtr: {fileID: 0} m_ManagedTypeFallback: defaultPresets: - m_Preset: {fileID: 2655988077585873504, guid: bfcfc320427f8224bbb7a96f3d3aebad, type: 2} ================================================ FILE: ProjectSettings/ProjectSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 serializedVersion: 20 productGUID: a3fb00b1014b9a048905105fac7d033b AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 AndroidEnableSustainedPerformanceMode: 0 defaultScreenOrientation: 4 targetDevice: 2 useOnDemandResources: 0 accelerometerFrequency: 60 companyName: DefaultCompany productName: NetGameExample defaultCursor: {fileID: 0} cursorHotspot: {x: 0, y: 0} m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} m_ShowUnitySplashScreen: 1 m_ShowUnitySplashLogo: 1 m_SplashScreenOverlayOpacity: 1 m_SplashScreenAnimation: 1 m_SplashScreenLogoStyle: 1 m_SplashScreenDrawMode: 0 m_SplashScreenBackgroundAnimationZoom: 1 m_SplashScreenLogoAnimationZoom: 1 m_SplashScreenBackgroundLandscapeAspect: 1 m_SplashScreenBackgroundPortraitAspect: 1 m_SplashScreenBackgroundLandscapeUvs: serializedVersion: 2 x: 0 y: 0 width: 1 height: 1 m_SplashScreenBackgroundPortraitUvs: serializedVersion: 2 x: 0 y: 0 width: 1 height: 1 m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} m_HolographicTrackingLossScreen: {fileID: 0} defaultScreenWidth: 1024 defaultScreenHeight: 768 defaultScreenWidthWeb: 960 defaultScreenHeightWeb: 600 m_StereoRenderingPath: 0 m_ActiveColorSpace: 0 m_MTRendering: 1 m_StackTraceTypes: 010000000100000001000000010000000100000001000000 iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 iosUseCustomAppBackgroundBehavior: 0 iosAllowHTTPDownload: 1 allowedAutorotateToPortrait: 1 allowedAutorotateToPortraitUpsideDown: 1 allowedAutorotateToLandscapeRight: 1 allowedAutorotateToLandscapeLeft: 1 useOSAutorotation: 1 use32BitDisplayBuffer: 1 preserveFramebufferAlpha: 0 disableDepthAndStencilBuffers: 0 androidStartInFullscreen: 1 androidRenderOutsideSafeArea: 0 androidUseSwappy: 0 androidBlitType: 0 defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 1 captureSingleScreen: 0 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 Force IOS Speakers When Recording: 0 deferSystemGesturesMode: 0 hideHomeButton: 0 submitAnalytics: 1 usePlayerLog: 1 bakeCollisionMeshes: 0 forceSingleInstance: 0 useFlipModelSwapchain: 1 resizableWindow: 1 useMacAppStoreValidation: 0 macAppStoreCategory: public.app-category.games gpuSkinning: 0 xboxPIXTextureCapture: 0 xboxEnableAvatar: 0 xboxEnableKinect: 0 xboxEnableKinectAutoTracking: 0 xboxEnableFitness: 0 visibleInBackground: 1 allowFullscreenSwitch: 1 fullscreenMode: 3 xboxSpeechDB: 0 xboxEnableHeadOrientation: 0 xboxEnableGuest: 0 xboxEnablePIXSampling: 0 metalFramebufferOnly: 0 xboxOneResolution: 0 xboxOneSResolution: 0 xboxOneXResolution: 3 xboxOneMonoLoggingLevel: 0 xboxOneLoggingLevel: 1 xboxOneDisableEsram: 0 xboxOneEnableTypeOptimization: 0 xboxOnePresentImmediateThreshold: 0 switchQueueCommandMemory: 0 switchQueueControlMemory: 16384 switchQueueComputeMemory: 262144 switchNVNShaderPoolsGranularity: 33554432 switchNVNDefaultPoolsGranularity: 16777216 switchNVNOtherPoolsGranularity: 16777216 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 m_SupportedAspectRatios: 4:3: 1 5:4: 1 16:10: 1 16:9: 1 Others: 1 bundleVersion: 0.1 preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 m_HolographicPauseOnTrackingLoss: 1 xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: cardboard: depthFormat: 0 enableTransitionView: 0 daydream: depthFormat: 0 useSustainedPerformanceMode: 0 enableVideoLayer: 0 useProtectedVideoMemory: 0 minimumSupportedHeadTracking: 0 maximumSupportedHeadTracking: 1 hololens: depthFormat: 1 depthBufferSharingEnabled: 1 lumin: depthFormat: 0 frameTiming: 2 enableGLCache: 0 glCacheMaxBlobSize: 524288 glCacheMaxFileSize: 8388608 oculus: sharedDepthBuffer: 1 dashSupport: 1 lowOverheadMode: 0 protectedContext: 0 v2Signing: 1 enable360StereoCapture: 0 isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 useHDRDisplay: 0 D3DHDRBitDepth: 0 m_ColorGamuts: 00000000 targetPixelDensity: 30 resolutionScalingMode: 0 androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 applicationIdentifier: Standalone: com.Company.ProductName buildNumber: {} AndroidBundleVersionCode: 1 AndroidMinSdkVersion: 19 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 aotOptions: stripEngineCode: 1 iPhoneStrippingLevel: 0 iPhoneScriptCallOptimization: 0 ForceInternetPermission: 0 ForceSDCardPermission: 0 CreateWallpaper: 0 APKExpansionFiles: 0 keepLoadedShadersAlive: 0 StripUnusedMeshComponents: 1 VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 iOSTargetOSVersionString: 10.0 tvOSSdkVersion: 0 tvOSRequireExtendedGameController: 0 tvOSTargetOSVersionString: 10.0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 uIStatusBarHidden: 1 uIExitOnSuspend: 0 uIStatusBarStyle: 0 iPhoneSplashScreen: {fileID: 0} iPhoneHighResSplashScreen: {fileID: 0} iPhoneTallHighResSplashScreen: {fileID: 0} iPhone47inSplashScreen: {fileID: 0} iPhone55inPortraitSplashScreen: {fileID: 0} iPhone55inLandscapeSplashScreen: {fileID: 0} iPhone58inPortraitSplashScreen: {fileID: 0} iPhone58inLandscapeSplashScreen: {fileID: 0} iPadPortraitSplashScreen: {fileID: 0} iPadHighResPortraitSplashScreen: {fileID: 0} iPadLandscapeSplashScreen: {fileID: 0} iPadHighResLandscapeSplashScreen: {fileID: 0} iPhone65inPortraitSplashScreen: {fileID: 0} iPhone65inLandscapeSplashScreen: {fileID: 0} iPhone61inPortraitSplashScreen: {fileID: 0} iPhone61inLandscapeSplashScreen: {fileID: 0} appleTVSplashScreen: {fileID: 0} appleTVSplashScreen2x: {fileID: 0} tvOSSmallIconLayers: [] tvOSSmallIconLayers2x: [] tvOSLargeIconLayers: [] tvOSLargeIconLayers2x: [] tvOSTopShelfImageLayers: [] tvOSTopShelfImageLayers2x: [] tvOSTopShelfImageWideLayers: [] tvOSTopShelfImageWideLayers2x: [] iOSLaunchScreenType: 0 iOSLaunchScreenPortrait: {fileID: 0} iOSLaunchScreenLandscape: {fileID: 0} iOSLaunchScreenBackgroundColor: serializedVersion: 2 rgba: 0 iOSLaunchScreenFillPct: 100 iOSLaunchScreenSize: 100 iOSLaunchScreenCustomXibPath: iOSLaunchScreeniPadType: 0 iOSLaunchScreeniPadImage: {fileID: 0} iOSLaunchScreeniPadBackgroundColor: serializedVersion: 2 rgba: 0 iOSLaunchScreeniPadFillPct: 100 iOSLaunchScreeniPadSize: 100 iOSLaunchScreeniPadCustomXibPath: iOSUseLaunchScreenStoryboard: 0 iOSLaunchScreenCustomStoryboardPath: iOSDeviceRequirements: [] iOSURLSchemes: [] iOSBackgroundModes: 0 iOSMetalForceHardShadows: 0 metalEditorSupport: 1 metalAPIValidation: 1 iOSRenderExtraFrameOnPause: 0 appleDeveloperTeamID: iOSManualSigningProvisioningProfileID: tvOSManualSigningProvisioningProfileID: iOSManualSigningProvisioningProfileType: 0 tvOSManualSigningProvisioningProfileType: 0 appleEnableAutomaticSigning: 0 iOSRequireARKit: 0 iOSAutomaticallyDetectAndAddCapabilities: 1 appleEnableProMotion: 0 clonedFromGUID: 5f34be1353de5cf4398729fda238591b templatePackageId: com.unity.template.2d@1.3.0 templateDefaultScene: Assets/Scenes/SampleScene.unity AndroidTargetArchitectures: 5 AndroidSplashScreenScale: 0 androidSplashScreen: {fileID: 0} AndroidKeystoreName: '{inproject}: ' AndroidKeyaliasName: AndroidBuildApkPerCpuArchitecture: 0 AndroidTVCompatibility: 1 AndroidIsGame: 1 AndroidEnableTango: 0 androidEnableBanner: 1 androidUseLowAccuracyLocation: 0 androidUseCustomKeystore: 0 m_AndroidBanners: - width: 320 height: 180 banner: {fileID: 0} androidGamepadSupportLevel: 0 AndroidValidateAppBundleSize: 1 AndroidAppBundleSizeToValidate: 150 m_BuildTargetIcons: [] m_BuildTargetPlatformIcons: [] m_BuildTargetBatching: [] m_BuildTargetGraphicsJobs: - m_BuildTarget: MacStandaloneSupport m_GraphicsJobs: 0 - m_BuildTarget: Switch m_GraphicsJobs: 0 - m_BuildTarget: MetroSupport m_GraphicsJobs: 0 - m_BuildTarget: AppleTVSupport m_GraphicsJobs: 0 - m_BuildTarget: BJMSupport m_GraphicsJobs: 0 - m_BuildTarget: LinuxStandaloneSupport m_GraphicsJobs: 0 - m_BuildTarget: PS4Player m_GraphicsJobs: 0 - m_BuildTarget: iOSSupport m_GraphicsJobs: 0 - m_BuildTarget: WindowsStandaloneSupport m_GraphicsJobs: 0 - m_BuildTarget: XboxOnePlayer m_GraphicsJobs: 0 - m_BuildTarget: LuminSupport m_GraphicsJobs: 0 - m_BuildTarget: AndroidPlayer m_GraphicsJobs: 0 - m_BuildTarget: WebGLSupport m_GraphicsJobs: 0 m_BuildTargetGraphicsJobMode: - m_BuildTarget: PS4Player m_GraphicsJobMode: 0 - m_BuildTarget: XboxOnePlayer m_GraphicsJobMode: 0 m_BuildTargetGraphicsAPIs: [] m_BuildTargetVRSettings: [] openGLRequireES31: 0 openGLRequireES31AEP: 0 openGLRequireES32: 0 m_TemplateCustomTags: {} mobileMTRendering: Android: 1 iPhone: 1 tvOS: 1 m_BuildTargetGroupLightmapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 enableInternalProfiler: 0 logObjCUncaughtExceptions: 1 enableCrashReportAPI: 0 cameraUsageDescription: locationUsageDescription: microphoneUsageDescription: switchNetLibKey: switchSocketMemoryPoolSize: 6144 switchSocketAllocatorPoolSize: 128 switchSocketConcurrencyLimit: 14 switchScreenResolutionBehavior: 2 switchUseCPUProfiler: 0 switchApplicationID: 0x01004b9000490000 switchNSODependencies: switchTitleNames_0: switchTitleNames_1: switchTitleNames_2: switchTitleNames_3: switchTitleNames_4: switchTitleNames_5: switchTitleNames_6: switchTitleNames_7: switchTitleNames_8: switchTitleNames_9: switchTitleNames_10: switchTitleNames_11: switchTitleNames_12: switchTitleNames_13: switchTitleNames_14: switchPublisherNames_0: switchPublisherNames_1: switchPublisherNames_2: switchPublisherNames_3: switchPublisherNames_4: switchPublisherNames_5: switchPublisherNames_6: switchPublisherNames_7: switchPublisherNames_8: switchPublisherNames_9: switchPublisherNames_10: switchPublisherNames_11: switchPublisherNames_12: switchPublisherNames_13: switchPublisherNames_14: switchIcons_0: {fileID: 0} switchIcons_1: {fileID: 0} switchIcons_2: {fileID: 0} switchIcons_3: {fileID: 0} switchIcons_4: {fileID: 0} switchIcons_5: {fileID: 0} switchIcons_6: {fileID: 0} switchIcons_7: {fileID: 0} switchIcons_8: {fileID: 0} switchIcons_9: {fileID: 0} switchIcons_10: {fileID: 0} switchIcons_11: {fileID: 0} switchIcons_12: {fileID: 0} switchIcons_13: {fileID: 0} switchIcons_14: {fileID: 0} switchSmallIcons_0: {fileID: 0} switchSmallIcons_1: {fileID: 0} switchSmallIcons_2: {fileID: 0} switchSmallIcons_3: {fileID: 0} switchSmallIcons_4: {fileID: 0} switchSmallIcons_5: {fileID: 0} switchSmallIcons_6: {fileID: 0} switchSmallIcons_7: {fileID: 0} switchSmallIcons_8: {fileID: 0} switchSmallIcons_9: {fileID: 0} switchSmallIcons_10: {fileID: 0} switchSmallIcons_11: {fileID: 0} switchSmallIcons_12: {fileID: 0} switchSmallIcons_13: {fileID: 0} switchSmallIcons_14: {fileID: 0} switchManualHTML: switchAccessibleURLs: switchLegalInformation: switchMainThreadStackSize: 1048576 switchPresenceGroupId: switchLogoHandling: 0 switchReleaseVersion: 0 switchDisplayVersion: 1.0.0 switchStartupUserAccount: 0 switchTouchScreenUsage: 0 switchSupportedLanguagesMask: 0 switchLogoType: 0 switchApplicationErrorCodeCategory: switchUserAccountSaveDataSize: 0 switchUserAccountSaveDataJournalSize: 0 switchApplicationAttribute: 0 switchCardSpecSize: -1 switchCardSpecClock: -1 switchRatingsMask: 0 switchRatingsInt_0: 0 switchRatingsInt_1: 0 switchRatingsInt_2: 0 switchRatingsInt_3: 0 switchRatingsInt_4: 0 switchRatingsInt_5: 0 switchRatingsInt_6: 0 switchRatingsInt_7: 0 switchRatingsInt_8: 0 switchRatingsInt_9: 0 switchRatingsInt_10: 0 switchRatingsInt_11: 0 switchRatingsInt_12: 0 switchLocalCommunicationIds_0: switchLocalCommunicationIds_1: switchLocalCommunicationIds_2: switchLocalCommunicationIds_3: switchLocalCommunicationIds_4: switchLocalCommunicationIds_5: switchLocalCommunicationIds_6: switchLocalCommunicationIds_7: switchParentalControl: 0 switchAllowsScreenshot: 1 switchAllowsVideoCapturing: 1 switchAllowsRuntimeAddOnContentInstall: 0 switchDataLossConfirmation: 0 switchUserAccountLockEnabled: 0 switchSystemResourceMemory: 16777216 switchSupportedNpadStyles: 3 switchNativeFsCacheSize: 32 switchIsHoldTypeHorizontal: 0 switchSupportedNpadCount: 8 switchSocketConfigEnabled: 0 switchTcpInitialSendBufferSize: 32 switchTcpInitialReceiveBufferSize: 64 switchTcpAutoSendBufferSizeMax: 256 switchTcpAutoReceiveBufferSizeMax: 256 switchUdpSendBufferSize: 9 switchUdpReceiveBufferSize: 42 switchSocketBufferEfficiency: 4 switchSocketInitializeEnabled: 1 switchNetworkInterfaceManagerInitializeEnabled: 1 switchPlayerConnectionEnabled: 1 ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: ps4ParentalLevel: 11 ps4ContentID: ED1633-NPXX51362_00-0000000000000000 ps4Category: 0 ps4MasterVersion: 01.00 ps4AppVersion: 01.00 ps4AppType: 0 ps4ParamSfxPath: ps4VideoOutPixelFormat: 0 ps4VideoOutInitialWidth: 1920 ps4VideoOutBaseModeInitialWidth: 1920 ps4VideoOutReprojectionRate: 60 ps4PronunciationXMLPath: ps4PronunciationSIGPath: ps4BackgroundImagePath: ps4StartupImagePath: ps4StartupImagesFolder: ps4IconImagesFolder: ps4SaveDataImagePath: ps4SdkOverride: ps4BGMPath: ps4ShareFilePath: ps4ShareOverlayImagePath: ps4PrivacyGuardImagePath: ps4NPtitleDatPath: ps4RemotePlayKeyAssignment: -1 ps4RemotePlayKeyMappingDir: ps4PlayTogetherPlayerCount: 0 ps4EnterButtonAssignment: 1 ps4ApplicationParam1: 0 ps4ApplicationParam2: 0 ps4ApplicationParam3: 0 ps4ApplicationParam4: 0 ps4DownloadDataSize: 0 ps4GarlicHeapSize: 2048 ps4ProGarlicHeapSize: 2560 playerPrefsMaxSize: 32768 ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ ps4pnSessions: 1 ps4pnPresence: 1 ps4pnFriends: 1 ps4pnGameCustomData: 1 playerPrefsSupport: 0 enableApplicationExit: 0 resetTempFolder: 1 restrictedAudioUsageRights: 0 ps4UseResolutionFallback: 0 ps4ReprojectionSupport: 0 ps4UseAudio3dBackend: 0 ps4SocialScreenEnabled: 0 ps4ScriptOptimizationLevel: 0 ps4Audio3dVirtualSpeakerCount: 14 ps4attribCpuUsage: 0 ps4PatchPkgPath: ps4PatchLatestPkgPath: ps4PatchChangeinfoPath: ps4PatchDayOne: 0 ps4attribUserManagement: 0 ps4attribMoveSupport: 0 ps4attrib3DSupport: 0 ps4attribShareSupport: 0 ps4attribExclusiveVR: 0 ps4disableAutoHideSplash: 0 ps4videoRecordingFeaturesUsed: 0 ps4contentSearchFeaturesUsed: 0 ps4attribEyeToEyeDistanceSettingVR: 0 ps4IncludedModules: [] ps4attribVROutputEnabled: 0 monoEnv: splashScreenBackgroundSourceLandscape: {fileID: 0} splashScreenBackgroundSourcePortrait: {fileID: 0} blurSplashScreenBackground: 1 spritePackerPolicy: webGLMemorySize: 256 webGLExceptionSupport: 1 webGLNameFilesAsHashes: 0 webGLDataCaching: 1 webGLDebugSymbols: 0 webGLEmscriptenArgs: webGLModulesDirectory: webGLTemplate: APPLICATION:Default webGLAnalyzeBuildSize: 0 webGLUseEmbeddedResources: 0 webGLCompressionFormat: 1 webGLLinkerTarget: 1 webGLThreadsSupport: 0 webGLWasmStreaming: 0 scriptingDefineSymbols: {} platformArchitecture: {} scriptingBackend: {} il2cppCompilerConfiguration: {} managedStrippingLevel: {} incrementalIl2cppBuild: {} allowUnsafeCode: 0 additionalIl2CppArgs: scriptingRuntimeVersion: 1 gcIncremental: 0 gcWBarrierValidation: 0 apiCompatibilityLevelPerPlatform: {} m_RenderingPath: 1 m_MobileRenderingPath: 1 metroPackageName: Template_2D metroPackageVersion: metroCertificatePath: metroCertificatePassword: metroCertificateSubject: metroCertificateIssuer: metroCertificateNotAfter: 0000000000000000 metroApplicationDescription: Template_2D wsaImages: {} metroTileShortName: metroTileShowName: 0 metroMediumTileShowName: 0 metroLargeTileShowName: 0 metroWideTileShowName: 0 metroSupportStreamingInstall: 0 metroLastRequiredScene: 0 metroDefaultTileSize: 1 metroTileForegroundText: 2 metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} metroSplashScreenUseBackgroundColor: 0 platformCapabilities: {} metroTargetDeviceFamilies: {} metroFTAName: metroFTAFileTypes: [] metroProtocolName: XboxOneProductId: XboxOneUpdateKey: XboxOneSandboxId: XboxOneContentId: XboxOneTitleId: XboxOneSCId: XboxOneGameOsOverridePath: XboxOnePackagingOverridePath: XboxOneAppManifestOverridePath: XboxOneVersion: 1.0.0.0 XboxOnePackageEncryption: 0 XboxOnePackageUpdateGranularity: 2 XboxOneDescription: XboxOneLanguage: - enus XboxOneCapability: [] XboxOneGameRating: {} XboxOneIsContentPackage: 0 XboxOneEnableGPUVariability: 1 XboxOneSockets: {} XboxOneSplashScreen: {fileID: 0} XboxOneAllowedProductIds: [] XboxOnePersistentLocalStorageSize: 0 XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: vrEditorSettings: daydream: daydreamIconForeground: {fileID: 0} daydreamIconBackground: {fileID: 0} cloudServicesEnabled: UNet: 1 luminIcon: m_Name: m_ModelFolderPath: m_PortalFolderPath: luminCert: m_CertPath: m_SignPackage: 1 luminIsChannelApp: 0 luminVersion: m_VersionCode: 1 m_VersionName: apiCompatibilityLevel: 6 cloudProjectId: framebufferDepthMemorylessMode: 0 projectName: organizationId: cloudEnabled: 0 enableNativePlatformBackendsForNewInputSystem: 0 disableOldInputManagerSupport: 0 legacyClampBlendShapeWeights: 0 ================================================ FILE: ProjectSettings/ProjectVersion.txt ================================================ m_EditorVersion: 2021.3.8f1 m_EditorVersionWithRevision: 2021.3.8f1 (b30333d56e81) ================================================ FILE: ProjectSettings/QualitySettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!47 &1 QualitySettings: m_ObjectHideFlags: 0 serializedVersion: 5 m_CurrentQuality: 3 m_QualitySettings: - serializedVersion: 2 name: Very Low pixelLightCount: 0 shadows: 0 shadowResolution: 0 shadowProjection: 1 shadowCascades: 1 shadowDistance: 15 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 0 blendWeights: 1 textureQuality: 1 anisotropicTextures: 0 antiAliasing: 0 softParticles: 0 softVegetation: 0 realtimeReflectionProbes: 0 billboardsFaceCameraPosition: 0 vSyncCount: 0 lodBias: 0.3 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 4 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 excludedTargetPlatforms: [] - serializedVersion: 2 name: Low pixelLightCount: 0 shadows: 0 shadowResolution: 0 shadowProjection: 1 shadowCascades: 1 shadowDistance: 20 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 0 blendWeights: 2 textureQuality: 0 anisotropicTextures: 0 antiAliasing: 0 softParticles: 0 softVegetation: 0 realtimeReflectionProbes: 0 billboardsFaceCameraPosition: 0 vSyncCount: 0 lodBias: 0.4 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 16 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 excludedTargetPlatforms: [] - serializedVersion: 2 name: Medium pixelLightCount: 1 shadows: 0 shadowResolution: 0 shadowProjection: 1 shadowCascades: 1 shadowDistance: 20 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 0 blendWeights: 2 textureQuality: 0 anisotropicTextures: 0 antiAliasing: 0 softParticles: 0 softVegetation: 0 realtimeReflectionProbes: 0 billboardsFaceCameraPosition: 0 vSyncCount: 1 lodBias: 0.7 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 64 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 excludedTargetPlatforms: [] - serializedVersion: 2 name: High pixelLightCount: 2 shadows: 0 shadowResolution: 1 shadowProjection: 1 shadowCascades: 2 shadowDistance: 40 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 blendWeights: 2 textureQuality: 0 anisotropicTextures: 0 antiAliasing: 0 softParticles: 0 softVegetation: 1 realtimeReflectionProbes: 0 billboardsFaceCameraPosition: 0 vSyncCount: 1 lodBias: 1 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 256 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 excludedTargetPlatforms: [] - serializedVersion: 2 name: Very High pixelLightCount: 3 shadows: 0 shadowResolution: 2 shadowProjection: 1 shadowCascades: 2 shadowDistance: 70 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 blendWeights: 4 textureQuality: 0 anisotropicTextures: 0 antiAliasing: 0 softParticles: 0 softVegetation: 1 realtimeReflectionProbes: 0 billboardsFaceCameraPosition: 0 vSyncCount: 1 lodBias: 1.5 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 1024 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 excludedTargetPlatforms: [] - serializedVersion: 2 name: Ultra pixelLightCount: 4 shadows: 0 shadowResolution: 0 shadowProjection: 1 shadowCascades: 4 shadowDistance: 150 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 blendWeights: 4 textureQuality: 0 anisotropicTextures: 0 antiAliasing: 0 softParticles: 0 softVegetation: 1 realtimeReflectionProbes: 0 billboardsFaceCameraPosition: 0 vSyncCount: 1 lodBias: 2 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 4096 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 excludedTargetPlatforms: [] m_PerPlatformDefaultQuality: Android: 2 Nintendo 3DS: 5 Nintendo Switch: 5 PS4: 5 PSM: 5 PSP2: 2 Standalone: 5 Tizen: 2 WebGL: 3 WiiU: 5 Windows Store Apps: 5 XboxOne: 5 iPhone: 2 tvOS: 2 ================================================ FILE: ProjectSettings/TagManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!78 &1 TagManager: serializedVersion: 2 tags: [] layers: - Default - TransparentFX - Ignore Raycast - - Water - UI - - - - - - - - - - - - - - - - - - - - - - - - - - m_SortingLayers: - name: Default uniqueID: 0 locked: 0 ================================================ FILE: ProjectSettings/TimeManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!5 &1 TimeManager: m_ObjectHideFlags: 0 Fixed Timestep: 0.02 Maximum Allowed Timestep: 0.1 m_TimeScale: 1 Maximum Particle Timestep: 0.03 ================================================ FILE: ProjectSettings/UnityConnectSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!310 &1 UnityConnectSettings: m_ObjectHideFlags: 0 serializedVersion: 1 m_Enabled: 0 m_TestMode: 0 m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events m_EventUrl: https://cdp.cloud.unity3d.com/v1/events m_ConfigUrl: https://config.uca.cloud.unity3d.com m_TestInitMode: 0 CrashReportingSettings: m_EventUrl: https://perf-events.cloud.unity3d.com m_Enabled: 0 m_LogBufferSize: 10 m_CaptureEditorExceptions: 1 UnityPurchasingSettings: m_Enabled: 0 m_TestMode: 0 UnityAnalyticsSettings: m_Enabled: 0 m_TestMode: 0 m_InitializeOnStartup: 1 UnityAdsSettings: m_Enabled: 0 m_InitializeOnStartup: 1 m_TestMode: 0 m_IosGameId: m_AndroidGameId: m_GameIds: {} m_GameId: PerformanceReportingSettings: m_Enabled: 0 ================================================ FILE: ProjectSettings/VFXManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!937362698 &1 VFXManager: m_ObjectHideFlags: 0 m_IndirectShader: {fileID: 0} m_CopyBufferShader: {fileID: 0} m_SortShader: {fileID: 0} m_RenderPipeSettingsPath: m_FixedTimeStep: 0.016666668 m_MaxDeltaTime: 0.05 ================================================ FILE: ProjectSettings/VersionControlSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!890905787 &1 VersionControlSettings: m_ObjectHideFlags: 0 m_Mode: Visible Meta Files m_CollabEditorSettings: inProgressEnabled: 1 ================================================ FILE: ProjectSettings/XRSettings.asset ================================================ { "m_SettingKeys": [ "VR Device Disabled", "VR Device User Alert" ], "m_SettingValues": [ "False", "False" ] } ================================================ FILE: ProjectSettings/boot.config ================================================ ================================================ FILE: README.md ================================================ # NetGameExample Example simple game with LiteNetLib (work in progress) Top down 2d network shooter Done: * Authoritative server * Client-side input prediction * Buffered linear interpolation for remote players * Proper player disconnect * Shooting