├── .gitignore ├── CHANGES.md ├── COMMON-MISTAKES.md ├── ENet-CSharp.nuspec ├── LICENSE ├── README.md └── Source ├── Managed ├── ENet-CSharp.csproj └── ENet.cs └── Native ├── CMakeLists.txt ├── build-ios.sh ├── enet.c ├── enet.h └── jni ├── Android.mk └── Application.mk /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Source/Native/build 3 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | The most notable changes that were made before 2.0.3 version 2 | -------- 3 | 4 | - Added functionality for easier bindings 5 | - Added monotonic time 6 | - Improved connection-related calculations 7 | - Improved compatibility with various compilers 8 | - Improved transmission statistics 9 | - Eliminated unnecessary memory allocations 10 | - Removed/replaced legacy functionality 11 | - Amalgamated code base into a single header 12 | - Cleaned and reorganized code base 13 | 14 | For other changes, check the [release section](https://github.com/nxrighthere/ENet-CSharp/releases). 15 | -------------------------------------------------------------------------------- /COMMON-MISTAKES.md: -------------------------------------------------------------------------------- 1 | A collection of common mistakes 2 | -------- 3 | 4 | 1. `Packet.Dispose()` called right after sending a packet. 5 | 6 | Never dispose a packet after enqueuing it for sending, ENet does that for you automatically, otherwise, a memory access failure will occur. 7 | 8 | 2. `Packet.Dispose()` called more than once per received packet. 9 | 10 | The `Packet` structure is a value type thus internal checks will fail for two different copies of a packet, and a memory access failure will occur. Try to keep processing flow under strict control. If the received packet will be sent further, you should not dispose it since ENet will use it as you normally enqueuing a packet for sending. 11 | 12 | 3. Channels limit didn't match at the endpoints. 13 | 14 | Always make sure that the channel limit set to the same value at the endpoints using `Host.Create()` and `Host.Connect()` functions, otherwise, packets will not be delivered on disabled channels. 15 | 16 | 4. Round-trip time is unstable even on localhost. 17 | 18 | This is the first indication that the `Host.Service()` is not called often enough. Make sure that the service and events are processed continuously and nothing prevents the ENet to shuttle packets across peers. 19 | 20 | 5. Latency gets higher relatively to a count of concurrent connections. 21 | 22 | Make sure that only the actual payload is sent and not a whole buffer, a packet should be created with the correct length using the `Packet.Create()` function. Check that the ENet is not overwhelmed with large [reliably fragmented](https://github.com/nxrighthere/ENet-CSharp#packetflags) packets. 23 | 24 | 6. A host is unable to accept multiple connections or degrades with many packets. 25 | 26 | Make sure that the service is processing as many events as possible and not only one event per frame/iteration. Put the service into a loop even within a game loop (but without a timeout to avoid blocking). If nothing helps, you can try to increase the socket buffer size of the [host](https://github.com/nxrighthere/ENet-CSharp#host) at creation up to one megabyte using the appropriate parameter at both ends. 27 | 28 | 7. A host is not flushed after the service is no longer in a loop. 29 | 30 | Always flush the host before the end of a session to ensure that all enqueued packets and protocol commands were sent to its designated peers. 31 | 32 | 8. Unreliable packets are dropped significantly under simulation of latency. 33 | 34 | If the simulated delay/ping is applied in the middle of the process, but not before connection establishment, then unreliable packets will be throttled if simulated latency exceeds the threshold (40 ms by default between service updates). See the description of `Peer.ConfigureThrottle()` for details and parameters tuning. 35 | -------------------------------------------------------------------------------- /ENet-CSharp.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ENet-CSharp 5 | 2.5.2 6 | ENet-CSharp 7 | Stanislav Denisov 8 | nxrighthere 9 | false 10 | license\license.txt 11 | https://github.com/nxrighthere/ENet-CSharp 12 | Reliable UDP networking library extended for the .NET environment 13 | 14 | 15 | (c) 2024 Stanislav Denisov 16 | networking udp protocol ipv4 ipv6 gamedev 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Stanislav Denisov (nxrighthere@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | alt logo 3 |

4 | 5 | [![GitHub release](https://img.shields.io/github/release/nxrighthere/ENet-CSharp.svg?style=flat-square)](https://github.com/nxrighthere/ENet-CSharp/releases) [![NuGet](https://img.shields.io/nuget/v/ENet-CSharp.svg?style=flat-square)](https://www.nuget.org/packages/ENet-CSharp/) 6 | 7 | This is an independent ENet implementation with a modified protocol for C, C++, C#, and other languages. 8 | 9 | Features: 10 | 11 | - Lightweight and straightforward 12 | - Low resource consumption 13 | - Dual-stack IPv4/IPv6 support 14 | - Connection management 15 | - Sequencing 16 | - Channels 17 | - Reliability 18 | - Fragmentation and reassembly 19 | - Aggregation 20 | - Adaptability 21 | - Portability 22 | 23 | Please, read [common mistakes](https://github.com/nxrighthere/ENet-CSharp/blob/master/COMMON-MISTAKES.md) to get an idea what can go wrong. 24 | 25 | Building 26 | -------- 27 | To build the native library appropriate software is required: 28 | 29 | For desktop platforms [CMake](https://cmake.org/download/) with GNU Make or Visual Studio. 30 | 31 | For mobile platforms [NDK](https://developer.android.com/ndk/downloads/) for Android and [Xcode](https://developer.apple.com/xcode/) for iOS. Make sure that all compiled libraries are assigned to appropriate platforms and CPU architectures. 32 | 33 | To build the library for Nintendo Switch, follow [this](https://pastebin.com/raw/rbjLgMV2) guide. 34 | 35 | A managed assembly can be built using any available compiling platform that supports C# 3.0 or higher. 36 | 37 | Compiled libraries 38 | -------- 39 | You can grab compiled libraries from the [release](https://github.com/nxrighthere/ENet-CSharp/releases) section or from [NuGet](https://www.nuget.org/packages/ENet-CSharp): 40 | 41 | `ENet-CSharp` contains compiled assembly with native libraries for the .NET environment (.NET Standard 2.1). 42 | 43 | `ENet-Unity` contains script with native libraries for the Unity. 44 | 45 | It's highly recommended to delete a folder with binaries instead of replacing it to upgrade. 46 | 47 | These packages are provided only for traditional platforms: Windows, Linux, and macOS (x64). 48 | 49 | Supported OS versions: 50 | - Windows 7 or higher 51 | - Linux 4.4 or higher 52 | - macOS 10.12 or higher 53 | 54 | Usage 55 | -------- 56 | Before starting to work, the library should be initialized using `ENet.Library.Initialize();` function. 57 | 58 | After the work is done, deinitialize the library using `ENet.Library.Deinitialize();` function. 59 | 60 | ### .NET environment 61 | ##### Start a new server 62 | ```c# 63 | using (Host server = new Host()) { 64 | Address address = new Address(); 65 | 66 | address.Port = port; 67 | server.Create(address, maxClients); 68 | 69 | Event netEvent; 70 | 71 | while (!Console.KeyAvailable) { 72 | bool polled = false; 73 | 74 | while (!polled) { 75 | if (server.CheckEvents(out netEvent) <= 0) { 76 | if (server.Service(15, out netEvent) <= 0) 77 | break; 78 | 79 | polled = true; 80 | } 81 | 82 | switch (netEvent.Type) { 83 | case EventType.None: 84 | break; 85 | 86 | case EventType.Connect: 87 | Console.WriteLine("Client connected - ID: " + netEvent.Peer.ID + ", IP: " + netEvent.Peer.IP); 88 | break; 89 | 90 | case EventType.Disconnect: 91 | Console.WriteLine("Client disconnected - ID: " + netEvent.Peer.ID + ", IP: " + netEvent.Peer.IP); 92 | break; 93 | 94 | case EventType.Timeout: 95 | Console.WriteLine("Client timeout - ID: " + netEvent.Peer.ID + ", IP: " + netEvent.Peer.IP); 96 | break; 97 | 98 | case EventType.Receive: 99 | Console.WriteLine("Packet received from - ID: " + netEvent.Peer.ID + ", IP: " + netEvent.Peer.IP + ", Channel ID: " + netEvent.ChannelID + ", Data length: " + netEvent.Packet.Length); 100 | netEvent.Packet.Dispose(); 101 | break; 102 | } 103 | } 104 | } 105 | 106 | server.Flush(); 107 | } 108 | ``` 109 | 110 | ##### Start a new client 111 | ```c# 112 | using (Host client = new Host()) { 113 | Address address = new Address(); 114 | 115 | address.SetHost(ip); 116 | address.Port = port; 117 | client.Create(); 118 | 119 | Peer peer = client.Connect(address); 120 | 121 | Event netEvent; 122 | 123 | while (!Console.KeyAvailable) { 124 | bool polled = false; 125 | 126 | while (!polled) { 127 | if (client.CheckEvents(out netEvent) <= 0) { 128 | if (client.Service(15, out netEvent) <= 0) 129 | break; 130 | 131 | polled = true; 132 | } 133 | 134 | switch (netEvent.Type) { 135 | case EventType.None: 136 | break; 137 | 138 | case EventType.Connect: 139 | Console.WriteLine("Client connected to server"); 140 | break; 141 | 142 | case EventType.Disconnect: 143 | Console.WriteLine("Client disconnected from server"); 144 | break; 145 | 146 | case EventType.Timeout: 147 | Console.WriteLine("Client connection timeout"); 148 | break; 149 | 150 | case EventType.Receive: 151 | Console.WriteLine("Packet received from server - Channel ID: " + netEvent.ChannelID + ", Data length: " + netEvent.Packet.Length); 152 | netEvent.Packet.Dispose(); 153 | break; 154 | } 155 | } 156 | } 157 | 158 | client.Flush(); 159 | } 160 | ``` 161 | 162 | ##### Create and send a new packet 163 | ```csharp 164 | Packet packet = default(Packet); 165 | byte[] data = new byte[64]; 166 | 167 | packet.Create(data); 168 | peer.Send(channelID, ref packet); 169 | ``` 170 | 171 | ##### Copy payload from a packet 172 | ```csharp 173 | byte[] buffer = new byte[1024]; 174 | 175 | netEvent.Packet.CopyTo(buffer); 176 | ``` 177 | 178 | ##### Integrate with a custom memory allocator 179 | ```csharp 180 | AllocCallback OnMemoryAllocate = (size) => { 181 | return Marshal.AllocHGlobal(size); 182 | }; 183 | 184 | FreeCallback OnMemoryFree = (memory) => { 185 | Marshal.FreeHGlobal(memory); 186 | }; 187 | 188 | NoMemoryCallback OnNoMemory = () => { 189 | throw new OutOfMemoryException(); 190 | }; 191 | 192 | Callbacks callbacks = new Callbacks(OnMemoryAllocate, OnMemoryFree, OnNoMemory); 193 | 194 | if (ENet.Library.Initialize(callbacks)) 195 | Console.WriteLine("ENet successfully initialized using a custom memory allocator"); 196 | ``` 197 | 198 | ### Unity 199 | Usage is almost the same as in the .NET environment, except that the console functions must be replaced with functions provided by Unity. If the `Host.Service()` will be called in a game loop, then make sure that the timeout parameter set to 0 which means non-blocking. Also, keep Unity run in background by enabling the appropriate option in the player settings. 200 | 201 | Multi-threading 202 | -------- 203 | ### Strategy 204 | The best-known strategy is to use ENet in an independent I/O thread and utilize [inter-thread messaging](http://www.1024cores.net/home/lock-free-algorithms/queues) techniques for transferring data across threads/tasks without any locks/mutexes. Non-blocking queues like [Ring Buffer](https://www.slideshare.net/trishagee/introduction-to-the-disruptor) were designed for such purposes. High-level abstractions and logic can be parallelized [using workers](https://forum.unity.com/threads/showcase-enet-unity-ecs-5000-real-time-player-simulation.605656/), then communicate with I/O thread and enqueue/dequeue messages to send/receive data across the network. 205 | 206 | ### Functionality 207 | In general, ENet is not thread-safe, but some of its functions can be used safely if the user is careful enough: 208 | 209 | `Packet` structure and its functions are safe until a packet is only moving across threads by value and a custom memory allocator is not used. 210 | 211 | `Peer.ID` as soon as a pointer to a peer was obtained from the native side, the ID will be cached in `Peer` structure for further actions with objects that assigned to that ID. `Peer` structure can be moved across threads by value, but its functions are not thread-safe because data in memory may change by the service in another thread. 212 | 213 | `Library.Time` utilizes atomic primitives internally for managing local monotonic time. 214 | 215 | API reference 216 | -------- 217 | ### Enumerations 218 | #### PacketFlags 219 | Definitions of a flags for `Peer.Send()` function: 220 | 221 | `PacketFlags.None` unreliable sequenced, delivery of packet is not guaranteed. 222 | 223 | `PacketFlags.Reliable` reliable sequenced, a packet must be received by the target peer and resend attempts should be made until the packet is delivered. 224 | 225 | `PacketFlags.Unsequenced` a packet will not be sequenced with other packets and may be delivered out of order. This flag makes delivery unreliable. 226 | 227 | `PacketFlags.NoAllocate` a packet will not allocate data, and the user must supply it instead. Packet lifetime should be tracked using the `PacketFreeCallback` callback. 228 | 229 | `PacketFlags.UnreliableFragmented` a packet will be unreliably fragmented if it exceeds the MTU. By default, unreliable packets that exceed the MTU are fragmented and transmitted reliably. This flag should be used to explicitly indicate packets that should remain unreliable. 230 | 231 | `PacketFlags.Instant` a packet will not be bundled with other packets at a next service iteration and sent instantly instead. This delivery type trades multiplexing efficiency in favor of latency. The same packet can't be used for multiple `Peer.Send()` calls. 232 | 233 | `PacketFlags.Unthrottled` a packet that was enqueued for sending unreliably should not be dropped due to throttling and sent if possible. 234 | 235 | `PacketFlags.Sent` a packet was sent from all queues it has entered. 236 | 237 | #### EventType 238 | Definitions of event types for `Event.Type` property: 239 | 240 | `EventType.None` no event occurred within the specified time limit. 241 | 242 | `EventType.Connect` a connection request initiated by `Peer.Connect()` function has completed. `Event.Peer` returns a peer which successfully connected. `Event.Data` returns the user-supplied data describing the connection or 0 if none is available. 243 | 244 | `EventType.Disconnect` a peer has disconnected. This event is generated on a successful completion of a disconnect initiated by `Peer.Disconnect()` function. `Event.Peer` returns a peer which disconnected. `Event.Data` returns the user-supplied data describing the disconnection or 0 if none is available. 245 | 246 | `EventType.Receive` a packet has been received from a peer. `Event.Peer` returns a peer which sent the packet. `Event.ChannelID` specifies the channel number upon which the packet was received. `Event.Packet` returns a packet that was received, and this packet must be destroyed using `Event.Packet.Dispose()` function after use. 247 | 248 | `EventType.Timeout` a peer has timed out. This event occurs if a peer has timed out or if a connection request initialized by `Peer.Connect()` has timed out. `Event.Peer` returns a peer which timed out. 249 | 250 | #### PeerState 251 | Definitions of peer states for `Peer.State` property: 252 | 253 | `PeerState.Uninitialized` a peer not initialized. 254 | 255 | `PeerState.Disconnected` a peer disconnected or timed out. 256 | 257 | `PeerState.Connecting` a peer connection in-progress. 258 | 259 | `PeerState.Connected` a peer successfully connected. 260 | 261 | `PeerState.Disconnecting` a peer disconnection in-progress. 262 | 263 | `PeerState.Zombie` a peer not properly disconnected. 264 | 265 | ### Delegates 266 | #### Memory callbacks 267 | Provides per application events. 268 | 269 | `AllocCallback(IntPtr size)` notifies when a memory is requested for allocation. Expects pointer to the newly allocated memory. A reference to the delegate should be preserved from being garbage collected. 270 | 271 | `FreeCallback(IntPtr memory)` notifies when the memory can be freed. A reference to the delegate should be preserved from being garbage collected. 272 | 273 | `NoMemoryCallback()` notifies when memory is not enough. A reference to the delegate should be preserved from being garbage collected. 274 | 275 | #### Packet callbacks 276 | Provides per packet events. 277 | 278 | `PacketFreeCallback(Packet packet)` notifies when a packet is being destroyed. Indicates if a reliable packet was acknowledged. A reference to the delegate should be preserved from being garbage collected. 279 | 280 | #### Host callbacks 281 | Provides per host events. 282 | 283 | `InterceptCallback(ref Event @event, ref Address address, IntPtr receivedData, int receivedDataLength)` notifies when a raw UDP packet is intercepted. Status code returned from this callback instructs ENet how the set event should be handled. Returning 1 indicates dispatching of the set event by the service. Returning 0 indicates that ENet subsystems should handle received data. Returning -1 indicates an error. A reference to the delegate should be preserved from being garbage collected. 284 | 285 | `ChecksumCallback(IntPtr buffers, int bufferCount)` notifies when a checksum should be computed for buffers at sending and receiving. A value returned from this callback is a 64-bit checksum. ENet automatically handles integrity verification of packets if a checksum mechanism is enabled at both ends. Can be used with `ENet.Library.CRC64()` function. A reference to the delegate should be preserved from being garbage collected. 286 | 287 | ### Structures 288 | #### Address 289 | Contains structure with anonymous host data and port number. 290 | 291 | `Address.Port` gets or sets a port number. 292 | 293 | `Address.GetIP()` gets an IP address. 294 | 295 | `Address.SetIP(string ip)` sets an IP address. To use IPv4 broadcast in the local network the address can be set to _255.255.255.255_ for a client. ENet will automatically respond to the broadcast and update the address to a server's actual IP. 296 | 297 | `Address.GetHost()` attempts to do a reverse lookup from the address. Returns a string with a resolved name or an IP address. 298 | 299 | `Address.SetHost(string hostName)` sets host name or an IP address. Should be used for binding to a network interface or for connection to a foreign host. Returns true on success or false on failure. 300 | 301 | #### Event 302 | Contains structure with the event type, managed pointer to the peer, channel ID, the user-supplied data, and managed pointer to the packet. 303 | 304 | `Event.Type` returns a type of the event. 305 | 306 | `Event.Peer` returns a peer that generated a connect, disconnect, receive or a timeout event. 307 | 308 | `Event.ChannelID` returns a channel ID on the peer that generated the event, if appropriate. 309 | 310 | `Event.Data` returns the user-supplied data, if appropriate. 311 | 312 | `Event.Packet` returns a packet associated with the event, if appropriate. 313 | 314 | #### Packet 315 | Contains a managed pointer to the packet. 316 | 317 | `Packet.Dispose()` destroys the packet. Should be called only when the packet was obtained from `EventType.Receive` event. 318 | 319 | `Packet.IsSet` returns a state of the managed pointer. 320 | 321 | `Packet.Data` returns a managed pointer to the packet data. 322 | 323 | `Packet.UserData` gets or sets the user-supplied data. 324 | 325 | `Packet.Length` returns a length of payload in the packet. 326 | 327 | `Packet.HasReferences` checks references to the packet. 328 | 329 | `Packet.SetFreeCallback(PacketFreeCallback callback)` sets the callback to notify when an appropriate packet is being destroyed. A pointer `IntPtr` to a callback can be used instead of a reference to a delegate. 330 | 331 | `Packet.Create(byte[] data, int offset, int length, PacketFlags flags)` creates a packet that may be sent to a peer. The offset parameter indicates the starting point of data in an array, the length is the ending point of data in an array. All parameters are optional. Multiple packet flags can be specified at once. A pointer `IntPtr` to a native buffer can be used instead of a reference to a byte array. 332 | 333 | `Packet.CopyTo(byte[] destination)` copies payload from the packet to the destination array. 334 | 335 | #### Peer 336 | Contains a managed pointer to the peer and cached ID. 337 | 338 | `Peer.IsSet` returns a state of the managed pointer. 339 | 340 | `Peer.ID` returns a peer ID. It's always zero on the client side. 341 | 342 | `Peer.IP` returns an IP address in a printable form. 343 | 344 | `Peer.Port` returns a port number. 345 | 346 | `Peer.MTU` returns an MTU. 347 | 348 | `Peer.State` returns a peer state described in the `PeerState` enumeration. 349 | 350 | `Peer.RoundTripTime` returns a round-trip time in milliseconds. 351 | 352 | `Peer.LastRoundTripTime` returns a round-trip time since the last acknowledgment in milliseconds. 353 | 354 | `Peer.LastSendTime` returns a last packet send time in milliseconds. 355 | 356 | `Peer.LastReceiveTime` returns a last packet receive time in milliseconds. 357 | 358 | `Peer.PacketsSent` returns a total number of packets sent during the connection. 359 | 360 | `Peer.PacketsLost` returns a total number of packets that considered lost during the connection based on retransmission logic. 361 | 362 | `Peer.PacketsThrottle` returns a ratio of packets throttle depending on conditions of the connection to the peer. 363 | 364 | `Peer.BytesSent` returns a total number of bytes sent during the connection. 365 | 366 | `Peer.BytesReceived` returns a total number of bytes received during the connection. 367 | 368 | `Peer.Data` gets or sets the user-supplied data. Should be used with an explicit cast to appropriate data type. 369 | 370 | `Peer.ConfigureThrottle(uint interval, uint acceleration, uint deceleration, uint threshold)` configures throttle parameter for a peer. Unreliable packets are dropped by ENet in response to the varying conditions of the connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. The lowest mean round-trip time from the sending of a reliable packet to the receipt of its acknowledgment is measured over an amount of time specified by the interval parameter in milliseconds. If a measured round-trip time happens to be significantly less than the mean round-trip time measured over the interval, then the throttle probability is increased to allow more traffic by an amount specified in the acceleration parameter, which is a ratio to the `Library.throttleScale` constant. If a measured round-trip time happens to be significantly greater than the mean round-trip time measured over the interval, then the throttle probability is decreased to limit traffic by an amount specified in the deceleration parameter, which is a ratio to the `Library.throttleScale` constant. When the throttle has a value of `Library.throttleScale`, no unreliable packets are dropped by ENet, and so 100% of all unreliable packets will be sent. When the throttle has a value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent. Intermediate values for the throttle represent intermediate probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits of the local and foreign hosts are taken into account to determine a sensible limit for the throttle probability above which it should not raise even in the best of conditions. To disable throttling the deceleration parameter should be set to zero. The threshold parameter can be used to reduce packet throttling relative to measured round-trip time in unstable network environments with high jitter and low average latency which is a common condition for Wi-Fi networks in crowded places. By default the threshold parameter set to `Library.throttleThreshold` in milliseconds. 371 | 372 | `Peer.Send(byte channelID, ref Packet packet)` queues a packet to be sent. Returns true on success or false on failure. 373 | 374 | `Peer.Receive(out byte channelID, out Packet packet)` attempts to dequeue any incoming queued packet. Returns true if a packet was dequeued or false if no packets available. 375 | 376 | `Peer.Ping()` sends a ping request to a peer. ENet automatically pings all connected peers at regular intervals, however, this function may be called to ensure more frequent ping requests. 377 | 378 | `Peer.PingInterval(uint interval)` sets an interval at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. 379 | 380 | `Peer.Timeout(uint timeoutLimit, uint timeoutMinimum, uint timeoutMaximum)` sets a timeout parameters for a peer. The timeout parameters control how and when a peer will timeout from a failure to acknowledge reliable traffic. Timeout values used in the semi-linear mechanism, where if a reliable packet is not acknowledged within an average round-trip time plus a variance tolerance until timeout reaches a set limit. If the timeout is thus at this limit and reliable packets have been sent but not acknowledged within a certain minimum time period, the peer will be disconnected. Alternatively, if reliable packets have been sent but not acknowledged for a certain maximum time period, the peer will be disconnected regardless of the current timeout limit value. 381 | 382 | `Peer.Disconnect(uint data)` requests a disconnection from a peer. 383 | 384 | `Peer.DisconnectNow(uint data)` forces an immediate disconnection from a peer. 385 | 386 | `Peer.DisconnectLater(uint data)` requests a disconnection from a peer, but only after all queued outgoing packets are sent. 387 | 388 | `Peer.Reset()` forcefully disconnects a peer. The foreign host represented by the peer is not notified of the disconnection and will timeout on its connection to the local host. 389 | 390 | ### Classes 391 | #### Host 392 | Contains a managed pointer to the host. 393 | 394 | `Host.Dispose()` destroys the host. 395 | 396 | `Host.IsSet` returns a state of the managed pointer. 397 | 398 | `Host.PeersCount` returns a number of connected peers. 399 | 400 | `Host.PacketsSent` returns a total number of packets sent during the session. 401 | 402 | `Host.PacketsReceived` returns a total number of packets received during the session. 403 | 404 | `Host.BytesSent` returns a total number of bytes sent during the session. 405 | 406 | `Host.BytesReceived` returns a total number of bytes received during the session. 407 | 408 | `Host.Create(Address? address, int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize)` creates a host for communicating with peers. The bandwidth parameters determine the window size of a connection which limits the number of reliable packets that may be in transit at any given time. ENet will strategically drop packets on specific sides of a connection between hosts to ensure the host's bandwidth is not overwhelmed. The buffer size parameter is used to set the socket buffer size for sending and receiving datagrams. All the parameters are optional except the address and peer limit in cases where the function is used to create a host which will listen for incoming connections. 409 | 410 | `Host.PreventConnections(bool state)` prevents access to the host for new incoming connections. This function makes the host completely invisible in network, any peer that attempts to connect to it will be timed out. 411 | 412 | `Host.Broadcast(byte channelID, ref Packet packet, Peer[] peers)` queues a packet to be sent to a range of peers or to all peers associated with the host if the optional peers parameter is not used. Any zeroed `Peer` structure in an array will be excluded from the broadcast. Instead of an array, a single `Peer` can be passed to function which will be excluded from the broadcast. 413 | 414 | `Host.CheckEvents(out Event @event)` checks for any queued events on the host and dispatches one if available. Returns > 0 if an event was dispatched, 0 if no events are available, < 0 on failure. 415 | 416 | `Host.Connect(Address address, int channelLimit, uint data)` initiates a connection to a foreign host. Returns a peer representing the foreign host on success or throws an exception on failure. The peer returned will not have completed the connection until `Host.Service()` notifies of an `EventType.Connect` event. The channel limit and the user-supplied data parameters are optional. 417 | 418 | `Host.Service(int timeout, out Event @event)` waits for events on the specified host and shuttles packets between the host and its peers. ENet uses a polled event model to notify the user of significant events. ENet hosts are polled for events with this function, where an optional timeout value in milliseconds may be specified to control how long ENet will poll. If a timeout of 0 is specified, this function will return immediately if there are no events to dispatch. Otherwise, it will return 1 if an event was dispatched within the specified timeout. This function should be regularly called to ensure packets are sent and received, otherwise, traffic spikes will occur leading to increased latency. The timeout parameter set to 0 means non-blocking which required for cases where the function is called in a game loop. 419 | 420 | `Host.SetBandwidthLimit(uint incomingBandwidth, uint outgoingBandwidth)` adjusts the bandwidth limits of a host in bytes per second. 421 | 422 | `Host.SetChannelLimit(int channelLimit)` limits the maximum allowed channels of future incoming connections. 423 | 424 | `Host.SetMaxDuplicatePeers(ushort number)` limits the maximum allowed duplicate peers from the same host and prevents connection if exceeded. By default set to `Library.maxPeers`, can't be less than one. 425 | 426 | `Host.SetInterceptCallback(InterceptCallback callback)` sets the callback to notify when a raw UDP packet is intercepted. A pointer `IntPtr` to a callback can be used instead of a reference to a delegate. 427 | 428 | `Host.SetChecksumCallback(ChecksumCallback callback)` sets the callback to notify when a checksum should be computed. A pointer `IntPtr` to a callback can be used instead of a reference to a delegate. 429 | 430 | `Host.Flush()` sends any queued packets on the specified host to its designated peers. 431 | 432 | #### Library 433 | Contains constant fields. 434 | 435 | `Library.maxChannelCount` the maximum possible number of channels. 436 | 437 | `Library.maxPeers` the maximum possible number of peers. 438 | 439 | `Library.maxPacketSize` the maximum size of a packet. 440 | 441 | `Library.version` the current compatibility version relative to the native library. 442 | 443 | `Library.Time` returns a current local monotonic time in milliseconds. It never reset while the application remains alive. 444 | 445 | `Library.Initialize(Callbacks callbacks)` initializes the native library. Callbacks parameter is optional and should be used only with a custom memory allocator. Should be called before starting the work. Returns true on success or false on failure. 446 | 447 | `Library.Deinitialize()` deinitializes the native library. Should be called after the work is done. 448 | 449 | `Library.CRC64(IntPtr buffers, int bufferCount)` computes a checksum for unmanaged buffers. 450 | 451 | Supporters 452 | -------- 453 | This project is sponsored by: 454 | 455 | [Flying Squirrel Entertainment](https://www.fsegames.eu)
456 | Flying Squirrel Entertainment 457 | 458 | [Square Root Studios](https://titanreach.com)
459 | Square Root Studios 460 | 461 | [Strange Loop Games](https://play.eco)
462 | Strange Loop Games 463 | -------------------------------------------------------------------------------- /Source/Managed/ENet-CSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | netstandard2.1;netcoreapp3.1;net8.0 6 | x64 7 | ENet 8 | 3 9 | 10 | 11 | 12 | false 13 | True 14 | 15 | 16 | 17 | true 18 | False 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Source/Managed/ENet.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Managed C# wrapper for an extended version of ENet 3 | * Copyright (c) 2013 James Bellinger 4 | * Copyright (c) 2016 Nate Shoffner 5 | * Copyright (c) 2018 Stanislav Denisov 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | using System; 27 | using System.Runtime.InteropServices; 28 | using System.Security; 29 | using System.Text; 30 | 31 | namespace ENet { 32 | [Flags] 33 | public enum PacketFlags { 34 | None = 0, 35 | Reliable = 1 << 0, 36 | Unsequenced = 1 << 1, 37 | NoAllocate = 1 << 2, 38 | UnreliableFragmented = 1 << 3, 39 | Instant = 1 << 4, 40 | Unthrottled = 1 << 5, 41 | Sent = 1 << 8 42 | } 43 | 44 | public enum EventType { 45 | None = 0, 46 | Connect = 1, 47 | Disconnect = 2, 48 | Receive = 3, 49 | Timeout = 4 50 | } 51 | 52 | public enum PeerState { 53 | Uninitialized = -1, 54 | Disconnected = 0, 55 | Connecting = 1, 56 | AcknowledgingConnect = 2, 57 | ConnectionPending = 3, 58 | ConnectionSucceeded = 4, 59 | Connected = 5, 60 | DisconnectLater = 6, 61 | Disconnecting = 7, 62 | AcknowledgingDisconnect = 8, 63 | Zombie = 9 64 | } 65 | 66 | [StructLayout(LayoutKind.Explicit, Size = 18)] 67 | internal struct ENetAddress { 68 | [FieldOffset(16)] 69 | public ushort port; 70 | } 71 | 72 | [StructLayout(LayoutKind.Sequential)] 73 | internal struct ENetEvent { 74 | public EventType type; 75 | public IntPtr peer; 76 | public byte channelID; 77 | public uint data; 78 | public IntPtr packet; 79 | } 80 | 81 | [StructLayout(LayoutKind.Sequential)] 82 | internal struct ENetCallbacks { 83 | public AllocCallback malloc; 84 | public FreeCallback free; 85 | public NoMemoryCallback noMemory; 86 | } 87 | 88 | public delegate IntPtr AllocCallback(IntPtr size); 89 | public delegate void FreeCallback(IntPtr memory); 90 | public delegate void NoMemoryCallback(); 91 | public delegate void PacketFreeCallback(Packet packet); 92 | public delegate int InterceptCallback(ref Event @event, ref Address address, IntPtr receivedData, int receivedDataLength); 93 | public delegate ulong ChecksumCallback(IntPtr buffers, int bufferCount); 94 | 95 | internal static class ArrayPool { 96 | [ThreadStatic] 97 | private static byte[] byteBuffer; 98 | [ThreadStatic] 99 | private static IntPtr[] pointerBuffer; 100 | 101 | public static byte[] GetByteBuffer() { 102 | if (byteBuffer == null) 103 | byteBuffer = new byte[64]; 104 | 105 | return byteBuffer; 106 | } 107 | 108 | public static IntPtr[] GetPointerBuffer() { 109 | if (pointerBuffer == null) 110 | pointerBuffer = new IntPtr[Library.maxPeers]; 111 | 112 | return pointerBuffer; 113 | } 114 | } 115 | 116 | public struct Address { 117 | private ENetAddress nativeAddress; 118 | 119 | internal ENetAddress NativeData { 120 | get { 121 | return nativeAddress; 122 | } 123 | 124 | set { 125 | nativeAddress = value; 126 | } 127 | } 128 | 129 | internal Address(ENetAddress address) { 130 | nativeAddress = address; 131 | } 132 | 133 | public ushort Port { 134 | get { 135 | return nativeAddress.port; 136 | } 137 | 138 | set { 139 | nativeAddress.port = value; 140 | } 141 | } 142 | 143 | public string GetIP() { 144 | StringBuilder ip = new StringBuilder(1025); 145 | 146 | if (Native.enet_address_get_ip(ref nativeAddress, ip, (IntPtr)ip.Capacity) != 0) 147 | return String.Empty; 148 | 149 | return ip.ToString(); 150 | } 151 | 152 | public bool SetIP(string ip) { 153 | if (ip == null) 154 | throw new ArgumentNullException("ip"); 155 | 156 | return Native.enet_address_set_ip(ref nativeAddress, ip) == 0; 157 | } 158 | 159 | public string GetHost() { 160 | StringBuilder hostName = new StringBuilder(1025); 161 | 162 | if (Native.enet_address_get_hostname(ref nativeAddress, hostName, (IntPtr)hostName.Capacity) != 0) 163 | return String.Empty; 164 | 165 | return hostName.ToString(); 166 | } 167 | 168 | public bool SetHost(string hostName) { 169 | if (hostName == null) 170 | throw new ArgumentNullException("hostName"); 171 | 172 | return Native.enet_address_set_hostname(ref nativeAddress, hostName) == 0; 173 | } 174 | } 175 | 176 | public struct Event { 177 | private ENetEvent nativeEvent; 178 | 179 | internal ENetEvent NativeData { 180 | get { 181 | return nativeEvent; 182 | } 183 | 184 | set { 185 | nativeEvent = value; 186 | } 187 | } 188 | 189 | internal Event(ENetEvent @event) { 190 | nativeEvent = @event; 191 | } 192 | 193 | public EventType Type { 194 | get { 195 | return nativeEvent.type; 196 | } 197 | } 198 | 199 | public Peer Peer { 200 | get { 201 | return new Peer(nativeEvent.peer); 202 | } 203 | } 204 | 205 | public byte ChannelID { 206 | get { 207 | return nativeEvent.channelID; 208 | } 209 | } 210 | 211 | public uint Data { 212 | get { 213 | return nativeEvent.data; 214 | } 215 | } 216 | 217 | public Packet Packet { 218 | get { 219 | return new Packet(nativeEvent.packet); 220 | } 221 | } 222 | } 223 | 224 | public class Callbacks { 225 | private ENetCallbacks nativeCallbacks; 226 | 227 | internal ENetCallbacks NativeData { 228 | get { 229 | return nativeCallbacks; 230 | } 231 | 232 | set { 233 | nativeCallbacks = value; 234 | } 235 | } 236 | 237 | public Callbacks(AllocCallback allocCallback, FreeCallback freeCallback, NoMemoryCallback noMemoryCallback) { 238 | nativeCallbacks.malloc = allocCallback; 239 | nativeCallbacks.free = freeCallback; 240 | nativeCallbacks.noMemory = noMemoryCallback; 241 | } 242 | } 243 | 244 | public struct Packet : IDisposable { 245 | private IntPtr nativePacket; 246 | 247 | internal IntPtr NativeData { 248 | get { 249 | return nativePacket; 250 | } 251 | 252 | set { 253 | nativePacket = value; 254 | } 255 | } 256 | 257 | internal Packet(IntPtr packet) { 258 | nativePacket = packet; 259 | } 260 | 261 | public void Dispose() { 262 | if (nativePacket != IntPtr.Zero) { 263 | Native.enet_packet_dispose(nativePacket); 264 | nativePacket = IntPtr.Zero; 265 | } 266 | } 267 | 268 | public bool IsSet { 269 | get { 270 | return nativePacket != IntPtr.Zero; 271 | } 272 | } 273 | 274 | public IntPtr Data { 275 | get { 276 | ThrowIfNotCreated(); 277 | 278 | return Native.enet_packet_get_data(nativePacket); 279 | } 280 | } 281 | 282 | public IntPtr UserData { 283 | get { 284 | ThrowIfNotCreated(); 285 | 286 | return Native.enet_packet_get_user_data(nativePacket); 287 | } 288 | 289 | set { 290 | ThrowIfNotCreated(); 291 | 292 | Native.enet_packet_set_user_data(nativePacket, value); 293 | } 294 | } 295 | 296 | public int Length { 297 | get { 298 | ThrowIfNotCreated(); 299 | 300 | return Native.enet_packet_get_length(nativePacket); 301 | } 302 | } 303 | 304 | public bool HasReferences { 305 | get { 306 | ThrowIfNotCreated(); 307 | 308 | return Native.enet_packet_check_references(nativePacket) != 0; 309 | } 310 | } 311 | 312 | internal void ThrowIfNotCreated() { 313 | if (nativePacket == IntPtr.Zero) 314 | throw new InvalidOperationException("Packet not created"); 315 | } 316 | 317 | public void SetFreeCallback(IntPtr callback) { 318 | ThrowIfNotCreated(); 319 | 320 | Native.enet_packet_set_free_callback(nativePacket, callback); 321 | } 322 | 323 | public void SetFreeCallback(PacketFreeCallback callback) { 324 | ThrowIfNotCreated(); 325 | 326 | Native.enet_packet_set_free_callback(nativePacket, Marshal.GetFunctionPointerForDelegate(callback)); 327 | } 328 | 329 | public void Create(byte[] data) { 330 | if (data == null) 331 | throw new ArgumentNullException("data"); 332 | 333 | Create(data, data.Length); 334 | } 335 | 336 | public void Create(byte[] data, int length) { 337 | Create(data, length, PacketFlags.None); 338 | } 339 | 340 | public void Create(byte[] data, PacketFlags flags) { 341 | Create(data, data.Length, flags); 342 | } 343 | 344 | public void Create(byte[] data, int length, PacketFlags flags) { 345 | if (data == null) 346 | throw new ArgumentNullException("data"); 347 | 348 | if (length < 0 || length > data.Length) 349 | throw new ArgumentOutOfRangeException("length"); 350 | 351 | nativePacket = Native.enet_packet_create(data, (IntPtr)length, flags); 352 | } 353 | 354 | public void Create(IntPtr data, int length, PacketFlags flags) { 355 | if (data == IntPtr.Zero) 356 | throw new ArgumentNullException("data"); 357 | 358 | if (length < 0) 359 | throw new ArgumentOutOfRangeException("length"); 360 | 361 | nativePacket = Native.enet_packet_create(data, (IntPtr)length, flags); 362 | } 363 | 364 | public void Create(byte[] data, int offset, int length, PacketFlags flags) { 365 | if (data == null) 366 | throw new ArgumentNullException("data"); 367 | 368 | if (offset < 0) 369 | throw new ArgumentOutOfRangeException("offset"); 370 | 371 | if (length < 0 || length > data.Length) 372 | throw new ArgumentOutOfRangeException("length"); 373 | 374 | nativePacket = Native.enet_packet_create_offset(data, (IntPtr)length, (IntPtr)offset, flags); 375 | } 376 | 377 | public void Create(IntPtr data, int offset, int length, PacketFlags flags) { 378 | if (data == IntPtr.Zero) 379 | throw new ArgumentNullException("data"); 380 | 381 | if (offset < 0) 382 | throw new ArgumentOutOfRangeException("offset"); 383 | 384 | if (length < 0) 385 | throw new ArgumentOutOfRangeException("length"); 386 | 387 | nativePacket = Native.enet_packet_create_offset(data, (IntPtr)length, (IntPtr)offset, flags); 388 | } 389 | 390 | public void CopyTo(byte[] destination) { 391 | if (destination == null) 392 | throw new ArgumentNullException("destination"); 393 | 394 | Marshal.Copy(Data, destination, 0, Length); 395 | } 396 | } 397 | 398 | public class Host : IDisposable { 399 | private IntPtr nativeHost; 400 | 401 | internal IntPtr NativeData { 402 | get { 403 | return nativeHost; 404 | } 405 | 406 | set { 407 | nativeHost = value; 408 | } 409 | } 410 | 411 | public void Dispose() { 412 | Dispose(true); 413 | GC.SuppressFinalize(this); 414 | } 415 | 416 | protected virtual void Dispose(bool disposing) { 417 | if (nativeHost != IntPtr.Zero) { 418 | Native.enet_host_destroy(nativeHost); 419 | nativeHost = IntPtr.Zero; 420 | } 421 | } 422 | 423 | ~Host() { 424 | Dispose(false); 425 | } 426 | 427 | public bool IsSet { 428 | get { 429 | return nativeHost != IntPtr.Zero; 430 | } 431 | } 432 | 433 | public uint PeersCount { 434 | get { 435 | ThrowIfNotCreated(); 436 | 437 | return Native.enet_host_get_peers_count(nativeHost); 438 | } 439 | } 440 | 441 | public uint PacketsSent { 442 | get { 443 | ThrowIfNotCreated(); 444 | 445 | return Native.enet_host_get_packets_sent(nativeHost); 446 | } 447 | } 448 | 449 | public uint PacketsReceived { 450 | get { 451 | ThrowIfNotCreated(); 452 | 453 | return Native.enet_host_get_packets_received(nativeHost); 454 | } 455 | } 456 | 457 | public uint BytesSent { 458 | get { 459 | ThrowIfNotCreated(); 460 | 461 | return Native.enet_host_get_bytes_sent(nativeHost); 462 | } 463 | } 464 | 465 | public uint BytesReceived { 466 | get { 467 | ThrowIfNotCreated(); 468 | 469 | return Native.enet_host_get_bytes_received(nativeHost); 470 | } 471 | } 472 | 473 | internal void ThrowIfNotCreated() { 474 | if (nativeHost == IntPtr.Zero) 475 | throw new InvalidOperationException("Host not created"); 476 | } 477 | 478 | private static void ThrowIfChannelsExceeded(int channelLimit) { 479 | if (channelLimit < 0 || channelLimit > Library.maxChannelCount) 480 | throw new ArgumentOutOfRangeException("channelLimit"); 481 | } 482 | 483 | public void Create() { 484 | Create(null, 1, 0); 485 | } 486 | 487 | public void Create(int bufferSize) { 488 | Create(null, 1, 0, 0, 0, bufferSize); 489 | } 490 | 491 | public void Create(Address? address, int peerLimit) { 492 | Create(address, peerLimit, 0); 493 | } 494 | 495 | public void Create(Address? address, int peerLimit, int channelLimit) { 496 | Create(address, peerLimit, channelLimit, 0, 0, 0); 497 | } 498 | 499 | public void Create(int peerLimit, int channelLimit) { 500 | Create(null, peerLimit, channelLimit, 0, 0, 0); 501 | } 502 | 503 | public void Create(int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth) { 504 | Create(null, peerLimit, channelLimit, incomingBandwidth, outgoingBandwidth, 0); 505 | } 506 | 507 | public void Create(Address? address, int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth) { 508 | Create(address, peerLimit, channelLimit, incomingBandwidth, outgoingBandwidth, 0); 509 | } 510 | 511 | public void Create(Address? address, int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize) { 512 | if (nativeHost != IntPtr.Zero) 513 | throw new InvalidOperationException("Host already created"); 514 | 515 | if (peerLimit < 0 || peerLimit > Library.maxPeers) 516 | throw new ArgumentOutOfRangeException("peerLimit"); 517 | 518 | ThrowIfChannelsExceeded(channelLimit); 519 | 520 | if (address != null) { 521 | var nativeAddress = address.Value.NativeData; 522 | 523 | nativeHost = Native.enet_host_create(ref nativeAddress, (IntPtr)peerLimit, (IntPtr)channelLimit, incomingBandwidth, outgoingBandwidth, bufferSize); 524 | } else { 525 | nativeHost = Native.enet_host_create(IntPtr.Zero, (IntPtr)peerLimit, (IntPtr)channelLimit, incomingBandwidth, outgoingBandwidth, bufferSize); 526 | } 527 | 528 | if (nativeHost == IntPtr.Zero) 529 | throw new InvalidOperationException("Host creation call failed"); 530 | } 531 | 532 | public void PreventConnections(bool state) { 533 | ThrowIfNotCreated(); 534 | 535 | Native.enet_host_prevent_connections(nativeHost, (byte)(state ? 1 : 0)); 536 | } 537 | 538 | public void Broadcast(byte channelID, ref Packet packet) { 539 | ThrowIfNotCreated(); 540 | 541 | packet.ThrowIfNotCreated(); 542 | Native.enet_host_broadcast(nativeHost, channelID, packet.NativeData); 543 | packet.NativeData = IntPtr.Zero; 544 | } 545 | 546 | public void Broadcast(byte channelID, ref Packet packet, Peer excludedPeer) { 547 | ThrowIfNotCreated(); 548 | 549 | packet.ThrowIfNotCreated(); 550 | Native.enet_host_broadcast_exclude(nativeHost, channelID, packet.NativeData, excludedPeer.NativeData); 551 | packet.NativeData = IntPtr.Zero; 552 | } 553 | 554 | public void Broadcast(byte channelID, ref Packet packet, Peer[] peers) { 555 | if (peers == null) 556 | throw new ArgumentNullException("peers"); 557 | 558 | ThrowIfNotCreated(); 559 | 560 | packet.ThrowIfNotCreated(); 561 | 562 | if (peers.Length > 0) { 563 | IntPtr[] nativePeers = ArrayPool.GetPointerBuffer(); 564 | int nativeCount = 0; 565 | 566 | for (int i = 0; i < peers.Length; i++) { 567 | if (peers[i].NativeData != IntPtr.Zero) { 568 | nativePeers[nativeCount] = peers[i].NativeData; 569 | nativeCount++; 570 | } 571 | } 572 | 573 | Native.enet_host_broadcast_selective(nativeHost, channelID, packet.NativeData, nativePeers, (IntPtr)nativeCount); 574 | packet.NativeData = IntPtr.Zero; 575 | } else { 576 | packet.Dispose(); 577 | 578 | throw new ArgumentOutOfRangeException("Peers array can't be empty"); 579 | } 580 | } 581 | 582 | public int CheckEvents(out Event @event) { 583 | ThrowIfNotCreated(); 584 | 585 | ENetEvent nativeEvent; 586 | 587 | var result = Native.enet_host_check_events(nativeHost, out nativeEvent); 588 | 589 | if (result <= 0) { 590 | @event = default(Event); 591 | 592 | return result; 593 | } 594 | 595 | @event = new Event(nativeEvent); 596 | 597 | return result; 598 | } 599 | 600 | public Peer Connect(Address address) { 601 | return Connect(address, 0, 0); 602 | } 603 | 604 | public Peer Connect(Address address, int channelLimit) { 605 | return Connect(address, channelLimit, 0); 606 | } 607 | 608 | public Peer Connect(Address address, int channelLimit, uint data) { 609 | ThrowIfNotCreated(); 610 | ThrowIfChannelsExceeded(channelLimit); 611 | 612 | var nativeAddress = address.NativeData; 613 | var peer = new Peer(Native.enet_host_connect(nativeHost, ref nativeAddress, (IntPtr)channelLimit, data)); 614 | 615 | if (peer.NativeData == IntPtr.Zero) 616 | throw new InvalidOperationException("Host connect call failed"); 617 | 618 | return peer; 619 | } 620 | 621 | public int Service(int timeout, out Event @event) { 622 | if (timeout < 0) 623 | throw new ArgumentOutOfRangeException("timeout"); 624 | 625 | ThrowIfNotCreated(); 626 | 627 | ENetEvent nativeEvent; 628 | 629 | var result = Native.enet_host_service(nativeHost, out nativeEvent, (uint)timeout); 630 | 631 | if (result <= 0) { 632 | @event = default(Event); 633 | 634 | return result; 635 | } 636 | 637 | @event = new Event(nativeEvent); 638 | 639 | return result; 640 | } 641 | 642 | public void SetBandwidthLimit(uint incomingBandwidth, uint outgoingBandwidth) { 643 | ThrowIfNotCreated(); 644 | 645 | Native.enet_host_bandwidth_limit(nativeHost, incomingBandwidth, outgoingBandwidth); 646 | } 647 | 648 | public void SetChannelLimit(int channelLimit) { 649 | ThrowIfNotCreated(); 650 | ThrowIfChannelsExceeded(channelLimit); 651 | 652 | Native.enet_host_channel_limit(nativeHost, (IntPtr)channelLimit); 653 | } 654 | 655 | public void SetMaxDuplicatePeers(ushort number) { 656 | ThrowIfNotCreated(); 657 | 658 | Native.enet_host_set_max_duplicate_peers(nativeHost, number); 659 | } 660 | 661 | public void SetInterceptCallback(IntPtr callback) { 662 | ThrowIfNotCreated(); 663 | 664 | Native.enet_host_set_intercept_callback(nativeHost, callback); 665 | } 666 | 667 | public void SetInterceptCallback(InterceptCallback callback) { 668 | ThrowIfNotCreated(); 669 | 670 | Native.enet_host_set_intercept_callback(nativeHost, Marshal.GetFunctionPointerForDelegate(callback)); 671 | } 672 | 673 | public void SetChecksumCallback(IntPtr callback) { 674 | ThrowIfNotCreated(); 675 | 676 | Native.enet_host_set_checksum_callback(nativeHost, callback); 677 | } 678 | 679 | public void SetChecksumCallback(ChecksumCallback callback) { 680 | ThrowIfNotCreated(); 681 | 682 | Native.enet_host_set_checksum_callback(nativeHost, Marshal.GetFunctionPointerForDelegate(callback)); 683 | } 684 | 685 | public void Flush() { 686 | ThrowIfNotCreated(); 687 | 688 | Native.enet_host_flush(nativeHost); 689 | } 690 | } 691 | 692 | public struct Peer { 693 | private IntPtr nativePeer; 694 | private uint nativeID; 695 | 696 | internal IntPtr NativeData { 697 | get { 698 | return nativePeer; 699 | } 700 | 701 | set { 702 | nativePeer = value; 703 | } 704 | } 705 | 706 | internal Peer(IntPtr peer) { 707 | nativePeer = peer; 708 | nativeID = nativePeer != IntPtr.Zero ? Native.enet_peer_get_id(nativePeer) : 0; 709 | } 710 | 711 | public bool IsSet { 712 | get { 713 | return nativePeer != IntPtr.Zero; 714 | } 715 | } 716 | 717 | public uint ID { 718 | get { 719 | return nativeID; 720 | } 721 | } 722 | 723 | public string IP { 724 | get { 725 | ThrowIfNotCreated(); 726 | 727 | byte[] ip = ArrayPool.GetByteBuffer(); 728 | 729 | if (Native.enet_peer_get_ip(nativePeer, ip, (IntPtr)ip.Length) == 0) 730 | return Encoding.ASCII.GetString(ip, 0, ip.StringLength()); 731 | else 732 | return String.Empty; 733 | } 734 | } 735 | 736 | public ushort Port { 737 | get { 738 | ThrowIfNotCreated(); 739 | 740 | return Native.enet_peer_get_port(nativePeer); 741 | } 742 | } 743 | 744 | public uint MTU { 745 | get { 746 | ThrowIfNotCreated(); 747 | 748 | return Native.enet_peer_get_mtu(nativePeer); 749 | } 750 | } 751 | 752 | public PeerState State { 753 | get { 754 | return nativePeer == IntPtr.Zero ? PeerState.Uninitialized : Native.enet_peer_get_state(nativePeer); 755 | } 756 | } 757 | 758 | public uint RoundTripTime { 759 | get { 760 | ThrowIfNotCreated(); 761 | 762 | return Native.enet_peer_get_rtt(nativePeer); 763 | } 764 | } 765 | 766 | public uint LastRoundTripTime { 767 | get { 768 | ThrowIfNotCreated(); 769 | 770 | return Native.enet_peer_get_last_rtt(nativePeer); 771 | } 772 | } 773 | 774 | public uint LastSendTime { 775 | get { 776 | ThrowIfNotCreated(); 777 | 778 | return Native.enet_peer_get_lastsendtime(nativePeer); 779 | } 780 | } 781 | 782 | public uint LastReceiveTime { 783 | get { 784 | ThrowIfNotCreated(); 785 | 786 | return Native.enet_peer_get_lastreceivetime(nativePeer); 787 | } 788 | } 789 | 790 | public ulong PacketsSent { 791 | get { 792 | ThrowIfNotCreated(); 793 | 794 | return Native.enet_peer_get_packets_sent(nativePeer); 795 | } 796 | } 797 | 798 | public ulong PacketsLost { 799 | get { 800 | ThrowIfNotCreated(); 801 | 802 | return Native.enet_peer_get_packets_lost(nativePeer); 803 | } 804 | } 805 | 806 | public float PacketsThrottle { 807 | get { 808 | ThrowIfNotCreated(); 809 | 810 | return Native.enet_peer_get_packets_throttle(nativePeer); 811 | } 812 | } 813 | 814 | public ulong BytesSent { 815 | get { 816 | ThrowIfNotCreated(); 817 | 818 | return Native.enet_peer_get_bytes_sent(nativePeer); 819 | } 820 | } 821 | 822 | public ulong BytesReceived { 823 | get { 824 | ThrowIfNotCreated(); 825 | 826 | return Native.enet_peer_get_bytes_received(nativePeer); 827 | } 828 | } 829 | 830 | public IntPtr Data { 831 | get { 832 | ThrowIfNotCreated(); 833 | 834 | return Native.enet_peer_get_data(nativePeer); 835 | } 836 | 837 | set { 838 | ThrowIfNotCreated(); 839 | 840 | Native.enet_peer_set_data(nativePeer, value); 841 | } 842 | } 843 | 844 | internal void ThrowIfNotCreated() { 845 | if (nativePeer == IntPtr.Zero) 846 | throw new InvalidOperationException("Peer not created"); 847 | } 848 | 849 | public void ConfigureThrottle(uint interval, uint acceleration, uint deceleration, uint threshold) { 850 | ThrowIfNotCreated(); 851 | 852 | Native.enet_peer_throttle_configure(nativePeer, interval, acceleration, deceleration, threshold); 853 | } 854 | 855 | public bool Send(byte channelID, ref Packet packet) { 856 | ThrowIfNotCreated(); 857 | 858 | packet.ThrowIfNotCreated(); 859 | 860 | return Native.enet_peer_send(nativePeer, channelID, packet.NativeData) == 0; 861 | } 862 | 863 | public bool Receive(out byte channelID, out Packet packet) { 864 | ThrowIfNotCreated(); 865 | 866 | IntPtr nativePacket = Native.enet_peer_receive(nativePeer, out channelID); 867 | 868 | if (nativePacket != IntPtr.Zero) { 869 | packet = new Packet(nativePacket); 870 | 871 | return true; 872 | } 873 | 874 | packet = default(Packet); 875 | 876 | return false; 877 | } 878 | 879 | public void Ping() { 880 | ThrowIfNotCreated(); 881 | 882 | Native.enet_peer_ping(nativePeer); 883 | } 884 | 885 | public void PingInterval(uint interval) { 886 | ThrowIfNotCreated(); 887 | 888 | Native.enet_peer_ping_interval(nativePeer, interval); 889 | } 890 | 891 | public void Timeout(uint timeoutLimit, uint timeoutMinimum, uint timeoutMaximum) { 892 | ThrowIfNotCreated(); 893 | 894 | Native.enet_peer_timeout(nativePeer, timeoutLimit, timeoutMinimum, timeoutMaximum); 895 | } 896 | 897 | public void Disconnect(uint data) { 898 | ThrowIfNotCreated(); 899 | 900 | Native.enet_peer_disconnect(nativePeer, data); 901 | } 902 | 903 | public void DisconnectNow(uint data) { 904 | ThrowIfNotCreated(); 905 | 906 | Native.enet_peer_disconnect_now(nativePeer, data); 907 | } 908 | 909 | public void DisconnectLater(uint data) { 910 | ThrowIfNotCreated(); 911 | 912 | Native.enet_peer_disconnect_later(nativePeer, data); 913 | } 914 | 915 | public void Reset() { 916 | ThrowIfNotCreated(); 917 | 918 | Native.enet_peer_reset(nativePeer); 919 | } 920 | } 921 | 922 | public static class Extensions { 923 | public static int StringLength(this byte[] data) { 924 | if (data == null) 925 | throw new ArgumentNullException("data"); 926 | 927 | int i; 928 | 929 | for (i = 0; i < data.Length && data[i] != 0; i++); 930 | 931 | return i; 932 | } 933 | } 934 | 935 | public static class Library { 936 | public const uint maxChannelCount = 0xFF; 937 | public const uint maxPeers = 0xFFF; 938 | public const uint maxPacketSize = 32 * 1024 * 1024; 939 | public const uint throttleThreshold = 40; 940 | public const uint throttleScale = 32; 941 | public const uint throttleAcceleration = 2; 942 | public const uint throttleDeceleration = 2; 943 | public const uint throttleInterval = 5000; 944 | public const uint timeoutLimit = 32; 945 | public const uint timeoutMinimum = 5000; 946 | public const uint timeoutMaximum = 30000; 947 | public const uint version = (2 << 16) | (5 << 8) | (2); 948 | 949 | public static uint Time { 950 | get { 951 | return Native.enet_time_get(); 952 | } 953 | } 954 | 955 | public static bool Initialize() { 956 | if (Native.enet_linked_version() != version) 957 | throw new InvalidOperationException("Incompatible version"); 958 | 959 | return Native.enet_initialize() == 0; 960 | } 961 | 962 | public static bool Initialize(Callbacks callbacks) { 963 | if (callbacks == null) 964 | throw new ArgumentNullException("callbacks"); 965 | 966 | if (Native.enet_linked_version() != version) 967 | throw new InvalidOperationException("Incompatible version"); 968 | 969 | ENetCallbacks nativeCallbacks = callbacks.NativeData; 970 | 971 | return Native.enet_initialize_with_callbacks(version, ref nativeCallbacks) == 0; 972 | } 973 | 974 | public static void Deinitialize() { 975 | Native.enet_deinitialize(); 976 | } 977 | 978 | public static ulong CRC64(IntPtr buffers, int bufferCount) { 979 | return Native.enet_crc64(buffers, bufferCount); 980 | } 981 | } 982 | 983 | [SuppressUnmanagedCodeSecurity] 984 | internal static class Native { 985 | #if __IOS__ || UNITY_IOS && !UNITY_EDITOR 986 | private const string nativeLibrary = "__Internal"; 987 | #else 988 | private const string nativeLibrary = "enet"; 989 | #endif 990 | 991 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 992 | internal static extern int enet_initialize(); 993 | 994 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 995 | internal static extern int enet_initialize_with_callbacks(uint version, ref ENetCallbacks inits); 996 | 997 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 998 | internal static extern void enet_deinitialize(); 999 | 1000 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1001 | internal static extern uint enet_linked_version(); 1002 | 1003 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1004 | internal static extern uint enet_time_get(); 1005 | 1006 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1007 | internal static extern ulong enet_crc64(IntPtr buffers, int bufferCount); 1008 | 1009 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1010 | internal static extern int enet_address_set_ip(ref ENetAddress address, string ip); 1011 | 1012 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1013 | internal static extern int enet_address_set_hostname(ref ENetAddress address, string hostName); 1014 | 1015 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1016 | internal static extern int enet_address_get_ip(ref ENetAddress address, StringBuilder ip, IntPtr ipLength); 1017 | 1018 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1019 | internal static extern int enet_address_get_hostname(ref ENetAddress address, StringBuilder hostName, IntPtr nameLength); 1020 | 1021 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1022 | internal static extern IntPtr enet_packet_create(byte[] data, IntPtr dataLength, PacketFlags flags); 1023 | 1024 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1025 | internal static extern IntPtr enet_packet_create(IntPtr data, IntPtr dataLength, PacketFlags flags); 1026 | 1027 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1028 | internal static extern IntPtr enet_packet_create_offset(byte[] data, IntPtr dataLength, IntPtr dataOffset, PacketFlags flags); 1029 | 1030 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1031 | internal static extern IntPtr enet_packet_create_offset(IntPtr data, IntPtr dataLength, IntPtr dataOffset, PacketFlags flags); 1032 | 1033 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1034 | internal static extern int enet_packet_check_references(IntPtr packet); 1035 | 1036 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1037 | internal static extern IntPtr enet_packet_get_data(IntPtr packet); 1038 | 1039 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1040 | internal static extern IntPtr enet_packet_get_user_data(IntPtr packet); 1041 | 1042 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1043 | internal static extern IntPtr enet_packet_set_user_data(IntPtr packet, IntPtr userData); 1044 | 1045 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1046 | internal static extern int enet_packet_get_length(IntPtr packet); 1047 | 1048 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1049 | internal static extern void enet_packet_set_free_callback(IntPtr packet, IntPtr callback); 1050 | 1051 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1052 | internal static extern void enet_packet_dispose(IntPtr packet); 1053 | 1054 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1055 | internal static extern IntPtr enet_host_create(ref ENetAddress address, IntPtr peerLimit, IntPtr channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize); 1056 | 1057 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1058 | internal static extern IntPtr enet_host_create(IntPtr address, IntPtr peerLimit, IntPtr channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize); 1059 | 1060 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1061 | internal static extern IntPtr enet_host_connect(IntPtr host, ref ENetAddress address, IntPtr channelCount, uint data); 1062 | 1063 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1064 | internal static extern void enet_host_broadcast(IntPtr host, byte channelID, IntPtr packet); 1065 | 1066 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1067 | internal static extern void enet_host_broadcast_exclude(IntPtr host, byte channelID, IntPtr packet, IntPtr excludedPeer); 1068 | 1069 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1070 | internal static extern void enet_host_broadcast_selective(IntPtr host, byte channelID, IntPtr packet, IntPtr[] peers, IntPtr peersLength); 1071 | 1072 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1073 | internal static extern int enet_host_service(IntPtr host, out ENetEvent @event, uint timeout); 1074 | 1075 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1076 | internal static extern int enet_host_check_events(IntPtr host, out ENetEvent @event); 1077 | 1078 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1079 | internal static extern void enet_host_channel_limit(IntPtr host, IntPtr channelLimit); 1080 | 1081 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1082 | internal static extern void enet_host_bandwidth_limit(IntPtr host, uint incomingBandwidth, uint outgoingBandwidth); 1083 | 1084 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1085 | internal static extern uint enet_host_get_peers_count(IntPtr host); 1086 | 1087 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1088 | internal static extern uint enet_host_get_packets_sent(IntPtr host); 1089 | 1090 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1091 | internal static extern uint enet_host_get_packets_received(IntPtr host); 1092 | 1093 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1094 | internal static extern uint enet_host_get_bytes_sent(IntPtr host); 1095 | 1096 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1097 | internal static extern uint enet_host_get_bytes_received(IntPtr host); 1098 | 1099 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1100 | internal static extern void enet_host_set_max_duplicate_peers(IntPtr host, ushort number); 1101 | 1102 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1103 | internal static extern void enet_host_set_intercept_callback(IntPtr host, IntPtr callback); 1104 | 1105 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1106 | internal static extern void enet_host_set_checksum_callback(IntPtr host, IntPtr callback); 1107 | 1108 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1109 | internal static extern void enet_host_flush(IntPtr host); 1110 | 1111 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1112 | internal static extern void enet_host_destroy(IntPtr host); 1113 | 1114 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1115 | internal static extern void enet_host_prevent_connections(IntPtr host, byte state); 1116 | 1117 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1118 | internal static extern void enet_peer_throttle_configure(IntPtr peer, uint interval, uint acceleration, uint deceleration, uint threshold); 1119 | 1120 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1121 | internal static extern uint enet_peer_get_id(IntPtr peer); 1122 | 1123 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1124 | internal static extern int enet_peer_get_ip(IntPtr peer, byte[] ip, IntPtr ipLength); 1125 | 1126 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1127 | internal static extern ushort enet_peer_get_port(IntPtr peer); 1128 | 1129 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1130 | internal static extern uint enet_peer_get_mtu(IntPtr peer); 1131 | 1132 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1133 | internal static extern PeerState enet_peer_get_state(IntPtr peer); 1134 | 1135 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1136 | internal static extern uint enet_peer_get_rtt(IntPtr peer); 1137 | 1138 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1139 | internal static extern uint enet_peer_get_last_rtt(IntPtr peer); 1140 | 1141 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1142 | internal static extern uint enet_peer_get_lastsendtime(IntPtr peer); 1143 | 1144 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1145 | internal static extern uint enet_peer_get_lastreceivetime(IntPtr peer); 1146 | 1147 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1148 | internal static extern ulong enet_peer_get_packets_sent(IntPtr peer); 1149 | 1150 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1151 | internal static extern ulong enet_peer_get_packets_lost(IntPtr peer); 1152 | 1153 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1154 | internal static extern float enet_peer_get_packets_throttle(IntPtr peer); 1155 | 1156 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1157 | internal static extern ulong enet_peer_get_bytes_sent(IntPtr peer); 1158 | 1159 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1160 | internal static extern ulong enet_peer_get_bytes_received(IntPtr peer); 1161 | 1162 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1163 | internal static extern IntPtr enet_peer_get_data(IntPtr peer); 1164 | 1165 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1166 | internal static extern void enet_peer_set_data(IntPtr peer, IntPtr data); 1167 | 1168 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1169 | internal static extern int enet_peer_send(IntPtr peer, byte channelID, IntPtr packet); 1170 | 1171 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1172 | internal static extern IntPtr enet_peer_receive(IntPtr peer, out byte channelID); 1173 | 1174 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1175 | internal static extern void enet_peer_ping(IntPtr peer); 1176 | 1177 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1178 | internal static extern void enet_peer_ping_interval(IntPtr peer, uint pingInterval); 1179 | 1180 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1181 | internal static extern void enet_peer_timeout(IntPtr peer, uint timeoutLimit, uint timeoutMinimum, uint timeoutMaximum); 1182 | 1183 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1184 | internal static extern void enet_peer_disconnect(IntPtr peer, uint data); 1185 | 1186 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1187 | internal static extern void enet_peer_disconnect_now(IntPtr peer, uint data); 1188 | 1189 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1190 | internal static extern void enet_peer_disconnect_later(IntPtr peer, uint data); 1191 | 1192 | [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] 1193 | internal static extern void enet_peer_reset(IntPtr peer); 1194 | } 1195 | } 1196 | -------------------------------------------------------------------------------- /Source/Native/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.9) 2 | project(enet C) 3 | 4 | set(ENET_DEBUG "0" CACHE BOOL "Enable debug functionality") 5 | 6 | if (WIN32) 7 | set(ENET_EXCLUDE_WINDOWS_H "0" CACHE BOOL "Exclude \"windows.h\" header") 8 | endif() 9 | 10 | set(ENET_STATIC "0" CACHE BOOL "Create a static library") 11 | set(ENET_SHARED "0" CACHE BOOL "Create a shared library") 12 | 13 | if (MSYS OR MINGW) 14 | set(CMAKE_C_FLAGS "-static") 15 | 16 | add_definitions(-DWINVER=0x0601) 17 | add_definitions(-D_WIN32_WINNT=0x0601) 18 | endif() 19 | 20 | if (ENET_DEBUG) 21 | add_definitions(-DENET_DEBUG) 22 | endif() 23 | 24 | if (ENET_STATIC) 25 | add_library(enet_static STATIC enet.c ${SOURCES}) 26 | 27 | if (NOT UNIX) 28 | target_link_libraries(enet_static winmm ws2_32) 29 | SET_TARGET_PROPERTIES(enet_static PROPERTIES PREFIX "") 30 | endif() 31 | endif() 32 | 33 | if (ENET_SHARED) 34 | if (APPLE) 35 | SET(CMAKE_OSX_ARCHITECTURES "arm64;x86_64") 36 | endif() 37 | add_definitions(-DENET_DLL) 38 | add_library(enet SHARED enet.c ${SOURCES}) 39 | 40 | if (NOT UNIX) 41 | target_link_libraries(enet winmm ws2_32) 42 | SET_TARGET_PROPERTIES(enet PROPERTIES PREFIX "") 43 | endif() 44 | endif() 45 | -------------------------------------------------------------------------------- /Source/Native/build-ios.sh: -------------------------------------------------------------------------------- 1 | # Point sysdir to iOS SDK 2 | export SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk 3 | 4 | # Create object files with iOS architecture 5 | gcc -c enet.c -fembed-bitcode -target arm64-apple-ios 6 | 7 | # Create static library 8 | libtool -static enet.o -o libenet.a 9 | 10 | rm enet.o -------------------------------------------------------------------------------- /Source/Native/enet.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ENet reliable UDP networking library 3 | * Copyright (c) 2018 Lee Salzman, Vladyslav Hrytsenko, Dominik Madarász, Stanislav Denisov 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | #define ENET_IMPLEMENTATION 25 | #include "enet.h" -------------------------------------------------------------------------------- /Source/Native/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_MODULE := libenet 5 | LOCAL_SRC_FILES := ../enet.c 6 | 7 | ifdef ENET_DEBUG 8 | LOCAL_CFLAGS += -DENET_DEBUG 9 | endif 10 | 11 | ifdef ENET_STATIC 12 | include $(BUILD_STATIC_LIBRARY) 13 | else 14 | include $(BUILD_SHARED_LIBRARY) 15 | endif 16 | -------------------------------------------------------------------------------- /Source/Native/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_PLATFORM := android-18 2 | APP_OPTIM := release 3 | APP_MODULES := libenet 4 | --------------------------------------------------------------------------------