mirror of
				https://github.com/DerTyp7/defrain-shooter-unity.git
				synced 2025-10-31 13:37:08 +01:00 
			
		
		
		
	CHANGED TO MIRROR
This commit is contained in:
		| @@ -0,0 +1,21 @@ | ||||
| MIT License | ||||
|  | ||||
| Copyright (c) 2021 Mirror Networking (vis2k, FakeByte) | ||||
|  | ||||
| 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. | ||||
| @@ -0,0 +1,7 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: a857d4e863bbf4a7dba70bc2cd1b5949 | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,8 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 6b7f3f8e8fa16475bbe48a8e9fbe800b | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,3 @@ | ||||
| using System.Runtime.CompilerServices; | ||||
|  | ||||
| [assembly: InternalsVisibleTo("where-allocations.Tests")] | ||||
| @@ -0,0 +1,3 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 158a96a7489b450485a8b06a13328871 | ||||
| timeCreated: 1622356221 | ||||
| @@ -0,0 +1,58 @@ | ||||
| using System.Net; | ||||
| using System.Net.Sockets; | ||||
|  | ||||
| namespace WhereAllocation | ||||
| { | ||||
|     public static class Extensions | ||||
|     { | ||||
|         // always pass the same IPEndPointNonAlloc instead of allocating a new | ||||
|         // one each time. | ||||
|         // | ||||
|         // use IPEndPointNonAlloc.temp to get the latest SocketAdddress written | ||||
|         // by ReceiveFrom_Internal! | ||||
|         // | ||||
|         // IMPORTANT: .temp will be overwritten in next call! | ||||
|         //            hash or manually copy it if you need to store it, e.g. | ||||
|         //            when adding a new connection. | ||||
|         public static int ReceiveFrom_NonAlloc( | ||||
|             this Socket socket, | ||||
|             byte[] buffer, | ||||
|             int offset, | ||||
|             int size, | ||||
|             SocketFlags socketFlags, | ||||
|             IPEndPointNonAlloc remoteEndPoint) | ||||
|         { | ||||
|             // call ReceiveFrom with IPEndPointNonAlloc. | ||||
|             // need to wrap this in ReceiveFrom_NonAlloc because it's not | ||||
|             // obvious that IPEndPointNonAlloc.Create does NOT create a new | ||||
|             // IPEndPoint. it saves the result in IPEndPointNonAlloc.temp! | ||||
|             EndPoint casted = remoteEndPoint; | ||||
|             return  socket.ReceiveFrom(buffer, offset, size, socketFlags, ref casted); | ||||
|         } | ||||
|  | ||||
|         // same as above, different parameters | ||||
|         public static int ReceiveFrom_NonAlloc(this Socket socket, byte[] buffer, IPEndPointNonAlloc remoteEndPoint) | ||||
|         { | ||||
|             EndPoint casted = remoteEndPoint; | ||||
|             return socket.ReceiveFrom(buffer, ref casted); | ||||
|         } | ||||
|  | ||||
|         // SendTo allocates too: | ||||
|         // https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L2240 | ||||
|         // -> the allocation is in EndPoint.Serialize() | ||||
|         // NOTE: technically this function isn't necessary. | ||||
|         //       could just pass IPEndPointNonAlloc. | ||||
|         //       still good for strong typing. | ||||
|         public static int SendTo_NonAlloc( | ||||
|             this Socket socket, | ||||
|             byte[] buffer, | ||||
|             int offset, | ||||
|             int size, | ||||
|             SocketFlags socketFlags, | ||||
|             IPEndPointNonAlloc remoteEndPoint) | ||||
|         { | ||||
|             EndPoint casted = remoteEndPoint; | ||||
|             return socket.SendTo(buffer, offset, size, socketFlags, casted); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 9e801942544d44d65808fb250623fe25 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,208 @@ | ||||
| using System; | ||||
| using System.Net; | ||||
| using System.Net.Sockets; | ||||
|  | ||||
| namespace WhereAllocation | ||||
| { | ||||
|     public class IPEndPointNonAlloc : IPEndPoint | ||||
|     { | ||||
|         // Two steps to remove allocations in ReceiveFrom_Internal: | ||||
|         // | ||||
|         // 1.) remoteEndPoint.Serialize(): | ||||
|         //     https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1733 | ||||
|         //     -> creates an EndPoint for ReceiveFrom_Internal to write into | ||||
|         //     -> it's never read from: | ||||
|         //        ReceiveFrom_Internal passes it to native: | ||||
|         //          https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1885 | ||||
|         //        native recv populates 'sockaddr* from' with the remote address: | ||||
|         //          https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom | ||||
|         //     -> can NOT be null. bricks both Unity and Unity Hub otherwise. | ||||
|         //     -> it seems as if Serialize() is only called to avoid allocating | ||||
|         //        a 'new SocketAddress' in ReceiveFrom. it's up to the EndPoint. | ||||
|         // | ||||
|         // 2.) EndPoint.Create(SocketAddress): | ||||
|         //     https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1761 | ||||
|         //     -> SocketAddress is the remote's address that we want to return | ||||
|         //     -> to avoid 'new EndPoint(SocketAddress), it seems up to the user | ||||
|         //        to decide how to create a new EndPoint via .Create | ||||
|         //     -> SocketAddress is the object that was returned by Serialize() | ||||
|         // | ||||
|         // in other words, all we need is an extra SocketAddress field that we | ||||
|         // can pass to ReceiveFrom_Internal to write the result into. | ||||
|         // => callers can then get the result from the extra field! | ||||
|         // => no allocations | ||||
|         // | ||||
|         // IMPORTANT: remember that IPEndPointNonAlloc is always the same object | ||||
|         //            and never changes. only the helper field is changed. | ||||
|         public SocketAddress temp; | ||||
|  | ||||
|         // constructors simply create the field once by calling the base method. | ||||
|         // (our overwritten method would create anything new) | ||||
|         public IPEndPointNonAlloc(long address, int port) : base(address, port) | ||||
|         { | ||||
|             temp = base.Serialize(); | ||||
|         } | ||||
|         public IPEndPointNonAlloc(IPAddress address, int port) : base(address, port) | ||||
|         { | ||||
|             temp = base.Serialize(); | ||||
|         } | ||||
|  | ||||
|         // Serialize simply returns it | ||||
|         public override SocketAddress Serialize() => temp; | ||||
|  | ||||
|         // Create doesn't need to create anything. | ||||
|         // SocketAddress object is already the one we returned in Serialize(). | ||||
|         // ReceiveFrom_Internal simply wrote into it. | ||||
|         public override EndPoint Create(SocketAddress socketAddress) | ||||
|         { | ||||
|             // original IPEndPoint.Create validates: | ||||
|             if (socketAddress.Family != AddressFamily) | ||||
|                 throw new ArgumentException($"Unsupported socketAddress.AddressFamily: {socketAddress.Family}. Expected: {AddressFamily}"); | ||||
|             if (socketAddress.Size < 8) | ||||
|                 throw new ArgumentException($"Unsupported socketAddress.Size: {socketAddress.Size}. Expected: <8"); | ||||
|  | ||||
|             // double check to guarantee that ReceiveFrom actually did write | ||||
|             // into our 'temp' field. just in case that's ever changed. | ||||
|             if (socketAddress != temp) | ||||
|             { | ||||
|                 // well this is fun. | ||||
|                 // in the latest mono from the above github links, | ||||
|                 // the result of Serialize() is passed as 'ref' so ReceiveFrom | ||||
|                 // does in fact write into it. | ||||
|                 // | ||||
|                 // in Unity 2019 LTS's mono version, it does create a new one | ||||
|                 // each time. this is from ILSpy Receive_From: | ||||
|                 // | ||||
|                 //     SocketPal.CheckDualModeReceiveSupport(this); | ||||
|                 //     ValidateBlockingMode(); | ||||
|                 //     if (NetEventSource.IsEnabled) | ||||
|                 //     { | ||||
|                 //         NetEventSource.Info(this, $"SRC{LocalEndPoint} size:{size} remoteEP:{remoteEP}", "ReceiveFrom"); | ||||
|                 //     } | ||||
|                 //     EndPoint remoteEP2 = remoteEP; | ||||
|                 //     System.Net.Internals.SocketAddress socketAddress = SnapshotAndSerialize(ref remoteEP2); | ||||
|                 //     System.Net.Internals.SocketAddress socketAddress2 = IPEndPointExtensions.Serialize(remoteEP2); | ||||
|                 //     int bytesTransferred; | ||||
|                 //     SocketError socketError = SocketPal.ReceiveFrom(_handle, buffer, offset, size, socketFlags, socketAddress.Buffer, ref socketAddress.InternalSize, out bytesTransferred); | ||||
|                 //     SocketException ex = null; | ||||
|                 //     if (socketError != 0) | ||||
|                 //     { | ||||
|                 //         ex = new SocketException((int)socketError); | ||||
|                 //         UpdateStatusAfterSocketError(ex); | ||||
|                 //         if (NetEventSource.IsEnabled) | ||||
|                 //         { | ||||
|                 //             NetEventSource.Error(this, ex, "ReceiveFrom"); | ||||
|                 //         } | ||||
|                 //         if (ex.SocketErrorCode != SocketError.MessageSize) | ||||
|                 //         { | ||||
|                 //             throw ex; | ||||
|                 //         } | ||||
|                 //     } | ||||
|                 //     if (!socketAddress2.Equals(socketAddress)) | ||||
|                 //     { | ||||
|                 //         try | ||||
|                 //         { | ||||
|                 //             remoteEP = remoteEP2.Create(socketAddress); | ||||
|                 //         } | ||||
|                 //         catch | ||||
|                 //         { | ||||
|                 //         } | ||||
|                 //         if (_rightEndPoint == null) | ||||
|                 //         { | ||||
|                 //             _rightEndPoint = remoteEP2; | ||||
|                 //         } | ||||
|                 //     } | ||||
|                 //     if (ex != null) | ||||
|                 //     { | ||||
|                 //         throw ex; | ||||
|                 //     } | ||||
|                 //     if (NetEventSource.IsEnabled) | ||||
|                 //     { | ||||
|                 //         NetEventSource.DumpBuffer(this, buffer, offset, size, "ReceiveFrom"); | ||||
|                 //         NetEventSource.Exit(this, bytesTransferred, "ReceiveFrom"); | ||||
|                 //     } | ||||
|                 //     return bytesTransferred; | ||||
|                 // | ||||
|  | ||||
|                 // so until they upgrade their mono version, we are stuck with | ||||
|                 // some allocations. | ||||
|                 // | ||||
|                 // for now, let's pass the newly created on to our temp so at | ||||
|                 // least we reuse it next time. | ||||
|                 temp = socketAddress; | ||||
|  | ||||
|                 // SocketAddress.GetHashCode() depends on SocketAddress.m_changed. | ||||
|                 // ReceiveFrom only sets the buffer, it does not seem to set m_changed. | ||||
|                 // we need to reset m_changed for two reasons: | ||||
|                 // * if m_changed is false, GetHashCode() returns the cahced m_hash | ||||
|                 //   which is '0'. that would be a problem. | ||||
|                 //   https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/SocketAddress.cs#L262 | ||||
|                 // * if we have a cached m_hash, but ReceiveFrom modified the buffer | ||||
|                 //   then the GetHashCode() should change too. so we need to reset | ||||
|                 //   either way. | ||||
|                 // | ||||
|                 // the only way to do that is by _actually_ modifying the buffer: | ||||
|                 // https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/SocketAddress.cs#L99 | ||||
|                 // so let's do that. | ||||
|                 // -> unchecked in case it's byte.Max | ||||
|                 unchecked | ||||
|                 { | ||||
|                     temp[0] += 1; | ||||
|                     temp[0] -= 1; | ||||
|                 } | ||||
|  | ||||
|                 // make sure this worked. | ||||
|                 // at least throw an Exception to make it obvious if the trick does | ||||
|                 // not work anymore, in case ReceiveFrom is ever changed. | ||||
|                 if (temp.GetHashCode() == 0) | ||||
|                     throw new Exception($"SocketAddress GetHashCode() is 0 after ReceiveFrom. Does the m_changed trick not work anymore?"); | ||||
|  | ||||
|                 // in the future, enable this again: | ||||
|                 //throw new Exception($"Socket.ReceiveFrom(): passed SocketAddress={socketAddress} but expected {temp}. This should never happen. Did ReceiveFrom() change?"); | ||||
|             } | ||||
|  | ||||
|             // ReceiveFrom sets seed_endpoint to the result of Create(): | ||||
|             // https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1764 | ||||
|             // so let's return ourselves at least. | ||||
|             // (seed_endpoint only seems to matter for BeginSend etc.) | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         // we need to overwrite GetHashCode() for two reasons. | ||||
|         // https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPEndPoint.cs#L160 | ||||
|         // * it uses m_Address. but our true SocketAddress is in m_temp. | ||||
|         //   m_Address might not be set at all. | ||||
|         // * m_Address.GetHashCode() allocates: | ||||
|         //   https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPAddress.cs#L699 | ||||
|         public override int GetHashCode() => temp.GetHashCode(); | ||||
|  | ||||
|         // helper function to create an ACTUAL new IPEndPoint from this. | ||||
|         // server needs it to store new connections as unique IPEndPoints. | ||||
|         public IPEndPoint DeepCopyIPEndPoint() | ||||
|         { | ||||
|             // we need to create a new IPEndPoint from 'temp' SocketAddress. | ||||
|             // there is no 'new IPEndPoint(SocketAddress) constructor. | ||||
|             // so we need to be a bit creative... | ||||
|  | ||||
|             // allocate a placeholder IPAddress to copy | ||||
|             // our SocketAddress into. | ||||
|             // -> needs to be the same address family. | ||||
|             IPAddress ipAddress; | ||||
|             if (temp.Family == AddressFamily.InterNetworkV6) | ||||
|                 ipAddress = IPAddress.IPv6Any; | ||||
|             else if (temp.Family == AddressFamily.InterNetwork) | ||||
|                 ipAddress = IPAddress.Any; | ||||
|             else | ||||
|                 throw new Exception($"Unexpected SocketAddress family: {temp.Family}"); | ||||
|  | ||||
|             // allocate a placeholder IPEndPoint | ||||
|             // with the needed size form IPAddress. | ||||
|             // (the real class. not NonAlloc) | ||||
|             IPEndPoint placeholder = new IPEndPoint(ipAddress, 0); | ||||
|  | ||||
|             // the real IPEndPoint's .Create function can create a new IPEndPoint | ||||
|             // copy from a SocketAddress. | ||||
|             return (IPEndPoint)placeholder.Create(temp); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: af0279d15e39b484792394f1d3cad4d9 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,13 @@ | ||||
| { | ||||
|     "name": "where-allocations", | ||||
|     "references": [], | ||||
|     "includePlatforms": [], | ||||
|     "excludePlatforms": [], | ||||
|     "allowUnsafeCode": false, | ||||
|     "overrideReferences": false, | ||||
|     "precompiledReferences": [], | ||||
|     "autoReferenced": true, | ||||
|     "defineConstraints": [], | ||||
|     "versionDefines": [], | ||||
|     "noEngineReferences": false | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: 63c380d6dae6946209ed0832388a657c | ||||
| AssemblyDefinitionImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
| @@ -0,0 +1,2 @@ | ||||
| V0.1 [2021-06-01] | ||||
| - initial release | ||||
| @@ -0,0 +1,7 @@ | ||||
| fileFormatVersion: 2 | ||||
| guid: f1256cadc037546ccb66071784fce137 | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
		Reference in New Issue
	
	Block a user
	 DerTyp187
					DerTyp187