/// <summary> /// Sets timeouts for a response from the remote computer acknowledging its receipt of a reliable packet. /// When the timeouts are exceeded, a disconnect event occurs. /// </summary> /// <param name="retryLimit"> /// The number of retries to make before considering the minimum timeout time. /// The default value is 5. /// </param> /// <param name="retryMinimumTime"> /// The minimum time in milliseconds to allow for retrying, or 0 to use the default (5000). /// </param> /// <param name="maximumTime"> /// The maximum time in milliseconds to wait regardless of the number of retries, or 0 to use the default (30000). /// </param> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="retryLimit"/> is negative or greater than 20, /// <paramref name="retryMinimumTime"/> is negative, and/or /// <paramref name="maximumTime"/> is negative. /// </exception> /// <exception cref="InvalidOperationException">The peer is not initialized.</exception> /// <exception cref="NotSupportedException">This method requires ENet 1.3.4 or newer.</exception> public void SetTimeouts(int retryLimit, int retryMinimumTime, int maximumTime) { CheckInitialized(); if (retryLimit < 0 || retryLimit > 20) { throw new ArgumentOutOfRangeException("retryLimit"); } if (retryMinimumTime < 0) { throw new ArgumentOutOfRangeException("retryMinimumTime"); } if (maximumTime < 0) { throw new ArgumentOutOfRangeException("maximumTime"); } try { ENetApi.enet_peer_timeout(NativeData, 1U << retryLimit, (uint)retryMinimumTime, (uint)maximumTime); } catch (EntryPointNotFoundException e) { throw new NotSupportedException("This method requires ENet 1.3.4 or newer.", e); } }
/// <summary> /// Initialize a host that will accept connections on a particular address, or not accept connections. /// </summary> /// <param name="address">The address to listen on, or null to not accept connections.</param> /// <param name="peerLimit">The maximum number of peers for this host.</param> /// <param name="channelLimit">The maximum number of channels, or 0 to use the maximum possible (255).</param> /// <param name="incomingBandwidth">The maximum incoming rate of transfer, or 0 for no limit.</param> /// <param name="outgoingBandwidth">The maximum outgoing rate of transfer, or 0 for no limit.</param> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="peerLimit"/> is less than 0 or greater than 4095, /// <paramref name="channelLimit"/> is less than 0 or greater than 255, /// <paramref name="incomingBandwidth"/> is less than 0, and/or /// <paramref name="outgoingBandwidth"/> is less than 0. /// </exception> /// <exception cref="InvalidOperationException">The host is already initialized.</exception> /// <exception cref="ENetException">Failed to initialize the host.</exception> public void Initialize(IPEndPoint address, int peerLimit, int channelLimit = 0, int incomingBandwidth = 0, int outgoingBandwidth = 0) { if (NativeData != null) { throw new InvalidOperationException("Already initialized."); } if (peerLimit < 0 || peerLimit > ENetApi.ENET_PROTOCOL_MAXIMUM_PEER_ID) { throw new ArgumentOutOfRangeException("peerLimit"); } CheckChannelLimit(channelLimit); CheckBandwidthLimit(incomingBandwidth, outgoingBandwidth); if (address != null) { ENetAddress nativeAddress = (ENetAddress)address; NativeData = ENetApi.enet_host_create(ref nativeAddress, (IntPtr)peerLimit, (IntPtr)channelLimit, (uint)incomingBandwidth, (uint)outgoingBandwidth); } else { NativeData = ENetApi.enet_host_create(null, (IntPtr)peerLimit, (IntPtr)channelLimit, (uint)incomingBandwidth, (uint)outgoingBandwidth); } if (NativeData == null) { throw new ENetException("Host creation call failed."); } }
/// <summary> /// Destroys the host. /// </summary> public void Dispose() { if (NativeData != null) { ENetApi.enet_host_destroy(NativeData); NativeData = null; } }
/// <summary> /// Enqueues a packet for sending. /// </summary> /// <param name="channelID">The ID of the channel to send on.</param> /// <param name="packet">The packet to send.</param> /// <returns>True if the packet was enqueued successfully, or false if an error occured.</returns> /// <exception cref="InvalidOperationException">The peer is not initialized.</exception> /// <exception cref="ENetException">An error occured.</exception> public void Send(byte channelID, Packet packet) { CheckInitialized(); packet.CheckInitialized(); int ret = ENetApi.enet_peer_send(NativeData, channelID, packet.NativeData); if (ret < 0) { throw new ENetException("An error occured sending to the peer."); } }
/// <summary> /// Dequeue a received packet. /// </summary> /// <param name="channelID">The ID of the channel the packet was sent on.</param> /// <param name="packet">The received packet.</param> /// <returns>True if a packet was dequeued, or false if there are no more packets.</returns> /// <exception cref="InvalidOperationException">The peer is not initialized.</exception> public bool Receive(out byte channelID, out Packet packet) { CheckInitialized(); ENetPacket *nativePacket; nativePacket = ENetApi.enet_peer_receive(NativeData, out channelID); if (nativePacket == null) { packet = new Packet(); return(false); } packet = new Packet(nativePacket); return(true); }
/// <summary> /// Enables compression using the range encoder. /// </summary> /// <exception cref="InvalidOperationException">The host is not initialized.</exception> /// <exception cref="ENetException">Failed to create range encoder. This is likely due to low memory.</exception> public void CompressWithRangeEncoder() { CheckInitialized(); int ret = ENetApi.enet_host_compress_with_range_encoder(NativeData); if (ret < 0) { throw new ENetException("Failed to create range encoder."); } }
/// <summary> /// Destroys the ENet packet. /// </summary> public void Dispose() { if (NativeData != null) { if (NativeData->referenceCount == IntPtr.Zero) { ENetApi.enet_packet_destroy(NativeData); } NativeData = null; } }
/// <summary> /// Broadcast a packet to all peers. /// </summary> /// <param name="channelID">The ID of the channel</param> /// <param name="packet">The packet to send.</param> /// <remarks>ENet takes ownership of the packet. Do not call methods on it afterwards.</remarks> /// <exception cref="InvalidOperationException">The host is not initialized.</exception> public void Broadcast(byte channelID, ref Packet packet) { CheckInitialized(); packet.CheckInitialized(); bool clear = packet.ReferenceCount == 0; ENetApi.enet_host_broadcast(NativeData, channelID, packet.NativeData); if (clear) { packet.NativeData = null; } // Broadcast may automatically free in this case. }
/// <summary> /// Resizes the packet. /// </summary> /// <param name="length">The new packet length.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> is negative.</exception> /// <exception cref="InvalidOperationException">The packet is not initialized.</exception> public void Resize(int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length"); } CheckInitialized(); int ret = ENetApi.enet_packet_resize(NativeData, (IntPtr)length); if (ret < 0) { throw new OutOfMemoryException("Packet resizing failed."); } }
/// <summary> /// Initializes a new packet from data, of the given length, and with the given flags. /// </summary> /// <param name="data">A pointer to the first byte of data.</param> /// <param name="length">The length of the data.</param> /// <param name="flags">The flags the packet will use.</param> /// <exception cref="InvalidOperationException">The packet is already initialized.</exception> /// <exception cref="ENetException">Packet creation failed.</exception> public void Initialize(IntPtr data, int length, PacketFlags flags) { if (NativeData != null) { throw new InvalidOperationException("Already initialized."); } NativeData = ENetApi.enet_packet_create(data, (IntPtr)length, flags); if (NativeData == null) { throw new ENetException("Packet creation call failed."); } }
/// <summary> /// Checks for queued events. /// </summary> /// <param name="event">The dequeued event.</param> /// <returns>True if an event was dequeued, otherwise false.</returns> /// <exception cref="InvalidOperationException">The host is not initialized.</exception> /// <exception cref="ENetException">An error occured while checking events.</exception> public bool CheckEvents(out Event @event) { CheckInitialized(); ENetEvent nativeEvent; int ret = ENetApi.enet_host_check_events(NativeData, out nativeEvent); if (ret < 0) { throw new ENetException("Error while checking for events."); } if (ret == 0) { @event = new Event(); return(false); } @event = new Event(nativeEvent); return(true); }
/// <summary> /// Sets the interval between pings. /// ENet will automatically send pings when it hasn't received anything from the remote computer. /// </summary> /// <param name="interval"> /// The interval in milliseconds between pings, or 0 to use the default (500). /// </param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="interval"/> is negative.</exception> /// <exception cref="InvalidOperationException">The peer is not initialized.</exception> /// <exception cref="NotSupportedException">This method requires ENet 1.3.4 or newer.</exception> public void SetPingInterval(int interval) { CheckInitialized(); if (interval < 0) { throw new ArgumentOutOfRangeException("interval"); } try { ENetApi.enet_peer_ping_interval(NativeData, (uint)interval); } catch (EntryPointNotFoundException e) { throw new NotSupportedException("This method requires ENet 1.3.4 or newer.", e); } }
/// <summary> /// Configures throttling. ENet measures lag over an interval, and alters its throttle parameter /// based on individual packet round-trip times versus the mean. This parameter controls the probability /// ENet will drop an unreliable packet. If a packet has a smaller round-trip time than average, the parameter /// is increased by the acceleration term, causing less packets to be dropped. If a packet has a larger /// round-trip time than average, the parameter is decreased by the deceleration term, causing more packets /// to be dropped. /// </summary> /// <param name="interval">The interval in milliseconds over which to measure. The default is 5000.</param> /// <param name="acceleration">Acceleration rate. The default value is 2, and the limit is 32.</param> /// <param name="deceleration">Deceleration rate. The default value is 2, and the limit is 32.</param> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="interval"/> is negative, /// <paramref name="acceleration"/> is less than 0 or greater than 32, and/or /// <paramref name="deceleration"/> is less than 0 or greater than 32. /// </exception> /// <exception cref="InvalidOperationException">The peer is not initialized.</exception> public void ConfigureThrottle(int interval, int acceleration, int deceleration) { CheckInitialized(); if (interval < 0) { throw new ArgumentOutOfRangeException("interval"); } if (acceleration < 0 || acceleration > 32) { throw new ArgumentOutOfRangeException("acceleration"); } if (deceleration < 0 || deceleration > 32) { throw new ArgumentOutOfRangeException("deceleration"); } ENetApi.enet_peer_throttle_configure(NativeData, (uint)interval, (uint)acceleration, (uint)deceleration); }
/// <summary> /// Connects to a remote computer at the given address. /// </summary> /// <param name="address">The address to connect to.</param> /// <param name="data">Data to send along with the connect packet.</param> /// <param name="channelLimit">The maximum number of channels, or 0 to use the maximum possible (255).</param> /// <returns> /// The new peer. This method does not block: the connection will be established /// when you receive a <see cref="EventType.Connect"/> event. /// </returns> /// <exception cref="ArgumentNullException"><paramref name="address"/> is null.</exception> /// <exception cref="ArgumentException"><paramref name="address"/> is not IPv4.</exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="address"/>'s port is less than 0 or greater than 65535, and/or /// <paramref name="channelLimit"/> is less than 0 or greater than 255. /// </exception> /// <exception cref="InvalidOperationException">The host is not initialized.</exception> /// <exception cref="ENetException">An error occured.</exception> public Peer Connect(IPEndPoint address, int data, int channelLimit = 0) { CheckInitialized(); ENetAddress nativeAddress = (ENetAddress)address; CheckChannelLimit(channelLimit); // For consistency with Connect() and SetChannelLimit(), if (channelLimit == 0) { channelLimit = (int)ENetApi.ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; } Peer peer = new Peer(ENetApi.enet_host_connect(NativeData, ref nativeAddress, (IntPtr)channelLimit, (uint)data)); if (peer.NativeData == null) { throw new ENetException("Host connect failed."); } return(peer); }
/// <summary> /// Sends queued outgoing packets, receives incoming packets, and handles connection events. /// </summary> /// <param name="timeout">Timeout in milliseconds to wait for an event. For polling, use 0.</param> /// <param name="event">The event.</param> /// <returns>True if an event occured, otherwise false.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="timeout"/> is negative.</exception> /// <exception cref="InvalidOperationException">The host is not initialized.</exception> /// <exception cref="ENetException">An error occured.</exception> public bool Service(int timeout, out Event @event) { if (timeout < 0) { throw new ArgumentOutOfRangeException("timeout"); } CheckInitialized(); ENetEvent nativeEvent = new ENetEvent(); // As of 1.3.6, ENet is not signal safe, and Mono uses signals for garbage collection. // // So, there's really nothing better we can do than retry. Luckily the cases that return -1 // are cases that return immediately or this could cause lockups. // // The entire situation is still very dicey with MonoDevelop. // A proper fix really has to be done in the ENet native library. // If you want to eliminate this entirely and don't care about these spurious // failures of enet_host_service, try/catch the ENetException and just ignore it. // That's essentially what I am doing here, except with an upper limit so real errors // can get through... int ret = -1; for (int nretries = 0; nretries < 1000 && ret == -1; nretries++) { ret = ENetApi.enet_host_service(NativeData, out nativeEvent, (uint)timeout); } if (ret < 0) { throw new ENetException(string.Format("Service failed (native data {0}).", (IntPtr)NativeData)); } if (ret == 0) { @event = new Event(); return(false); } @event = new Event(nativeEvent); return(true); }
/// <summary> /// Sends queued packets immediately. Normally they are sent when you call <see cref="Service(int, out Event)"/>. /// </summary> /// <exception cref="InvalidOperationException">The host is not initialized.</exception> public void Flush() { CheckInitialized(); ENetApi.enet_host_flush(NativeData); }
/// <summary> /// Immediately disconnects from the remote computer. /// A disconnect packet is sent unreliably. No event occurs. /// </summary> /// <param name="data">Data to send along with the disconnect packet.</param> /// <exception cref="InvalidOperationException">The peer is not initialized.</exception> public void DisconnectNow(int data) { CheckInitialized(); ENetApi.enet_peer_disconnect_now(NativeData, (uint)data); }
/// <summary> /// Disables compression. /// </summary> /// <exception cref="InvalidOperationException">The host is not initialized.</exception> public void DoNotCompress() { CheckInitialized(); ENetApi.enet_host_compress(NativeData, null); }
/// <summary> /// Sends a ping to the remote computer. /// </summary> /// <exception cref="InvalidOperationException">The peer is not initialized.</exception> public void Ping() { CheckInitialized(); ENetApi.enet_peer_ping(NativeData); }
/// <summary> /// Resets the connection to the remote computer. /// No disconnect packets are sent, and no event occurs. /// </summary> /// <exception cref="InvalidOperationException">The peer is not initialized.</exception> public void Reset() { CheckInitialized(); ENetApi.enet_peer_reset(NativeData); }
/// <summary> /// Set the bandwidth limit. /// </summary> /// <param name="incomingBandwidth">The maximum incoming rate of transfer, or 0 for no limit.</param> /// <param name="outgoingBandwidth">The maximum outgoing rate of transfer, or 0 for no limit.</param> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="incomingBandwidth"/> is less than 0, and/or /// <paramref name="outgoingBandwidth"/> is less than 0. /// </exception> /// <exception cref="InvalidOperationException">The host is not initialized.</exception> public void SetBandwidthLimit(int incomingBandwidth, int outgoingBandwidth) { CheckInitialized(); CheckBandwidthLimit(incomingBandwidth, outgoingBandwidth); ENetApi.enet_host_bandwidth_limit(NativeData, (uint)incomingBandwidth, (uint)outgoingBandwidth); }
/// <summary> /// Set the channel limit. /// </summary> /// <param name="channelLimit">The maximum number of channels, or 0 to use the maximum possible (255).</param> /// <exception cref="InvalidOperationException">The host is not initialized.</exception> public void SetChannelLimit(int channelLimit) { CheckChannelLimit(channelLimit); CheckInitialized(); ENetApi.enet_host_channel_limit(NativeData, (IntPtr)channelLimit); }
/// <summary> /// Throws an exception if the ENet native library cannot be loaded. /// ENet is now automatically initialized, so it is no longer strictly /// necessary to call this function. /// </summary> /// <exception cref="ENetException">The native library cannot be loaded.</exception> public static void Initialize() { ENetApi.enet_time_get(); }