README.md

# enet
A complete re-implementation of the [ENet](http://enet.bespin.org/) protocol in Erlang/OTP.

*NOTE*: This is a friendly fork of https://github.com/flambard/enet. This fork is being 
tested against and adapted to the C implementation of ENet as used by the Godot engine. 

## API
The module `enet` presents the API of enet.

### Data Types
```erlang
port_number() = 0..65535

peer_count() = 1..255

channel_count() = 1..255

bytes_per_second() = non_neg_integer()

channels() = #{ non_neg_integer() := pid() }
```

### Functions

#### start_host/3
```erlang
start_host(Port, ConnectFun, Options) -> {ok, port_number()} | {error, term()}

    Port = port_number()
    ConnectFun = mfa() | fun((PeerInfo) -> ConnectFunResult)
    PeerInfo = map()
    ConnectFunResult = {ok, pid()} | {error, term()}
    Options = [Option]
    Option =
      {peer_limit, peer_count()} |
      {channel_limit, channel_count()} |
      {incoming_bandwidth, bytes_per_second()} |
      {outgoing_bandwidth, bytes_per_second()} |
      {compression_mode, atom()}
```
Start a new host. If `Port` set to `0`, the port will be dynamically assigned by the underlying operating system. The assigned port is returned.

The `ConnectFun` function or MFA tuple will be called when a new peer has started and a connection to a remote peer has been established. This function is expected to spawn a new process and return a pid to which all messages from the new peer will be sent.

#### stop_host/1
```erlang
stop_host(Port) -> ok

    Port = port_number()
```
Stop a host listening on `Port`.

#### connect_peer/4
```erlang
connect_peer(HostPort, IP, RemotePort, ChannelCount) -> {ok, Peer} | {error, atom()}

    HostPort = port_number()
    IP = string()
    RemotePort = port_number()
    ChannelCount = channel_count()
    Peer = pid()
```
Start a new peer on the host listening on `HostPort` connecting to a remote host on address `IP:RemotePort`. The peer process will call `ConnectFun` (given to start_host/3) when the handshake has been completed successfully.

#### disconnect_peer/1
```erlang
disconnect_peer(Peer) -> ok

    Peer = pid()
```
Disconnect `Peer`.

#### disconnect_peer_now/1
```erlang
disconnect_peer_now(Peer) -> ok

    Peer = pid()
```
Disconnect `Peer` immediately without waiting for an ACK from the remote peer.

#### send_unsequenced/2
```erlang
send_unsequenced(Channel, Data) -> ok

    Channel = pid()
    Data = iodata()
```
Send *unsequenced* data to the remote peer over `Channel`.

#### send_unreliable/2
```erlang
send_unreliable(Channel, Data) -> ok

    Channel = pid()
    Data = iodata()
```
Send *unreliable* data to the remote peer over `Channel`.

#### send_reliable/2
```erlang
send_reliable(Channel, Data) -> ok

    Channel = pid()
    Data = iodata()
```
Send *reliable* data to the remote peer over `Channel`.

#### broadcast_unsequenced/3
```erlang
broadcast_unsequenced(HostPort, ChannelID, Data) -> ok

    HostPort = port_number()
    ChannelID = integer()
    Data = iodata()
```
Broadcast *unsequenced* data to all peers connected to `HostPort` on `ChannelID`.

#### broadcast_unreliable/3
```erlang
broadcast_unreliable(HostPort, ChannelID, Data) -> ok

    HostPort = port_number()
    ChannelID = integer()
    Data = iodata()
```
Broadcast *unreliable* data to all peers connected to `HostPort` on `ChannelID`.

#### broadcast_reliable/3
```erlang
broadcast_reliable(HostPort, ChannelID, Data) -> ok

    HostPort = port_number()
    ChannelID = integer()
    Data = iodata()
```
Broadcast *reliable* data to all peers connected to `HostPort` on `ChannelID`.

## Examples
### Creating an ENet server
```erlang
ListeningPort = 1234,
ConnectFun = fun(PeerInfo) ->
                     server_supervisor:start_worker(PeerInfo)
             end,
Options = [{peer_limit, 8}, {channel_limit, 3}],
{ok, Host} = enet:start_host(ListeningPort, ConnectFun, Options),
...
...
...
enet:stop_host(Host).
```
### Creating an ENet client and connecting to a server
```erlang
ListeningPort = 0, %% Port will be dynamically assigned
ConnectFun = fun(PeerInfo) ->
                     client_supervisor:start_worker(PeerInfo)
             end,
Options = [{peer_limit, 1}, {channel_limit, 3}],
{ok, Host} = enet:start_host(ListeningPort, ConnectFun, Options),
...
...
RemoteHost = "...."
Port = 1234,
ChannelCount = 3
{ok, Peer} = enet:connect_peer(Host, RemoteHost, Port, ChannelCount),
...
...
enet:stop_host(Host).
```
### Sending packets to an ENet peer
```erlang
worker_loop(PeerInfo = #{channels := Channels}, State) ->
    ...
    ...
    {ok, Channel0} = maps:find(0, Channels),
    Data = <<"packet">>,
    enet:send_unsequenced(Channel0, Data),
    enet:send_unreliable(Channel0, Data),
    enet:send_reliable(Channel0, Data),
    ...
    ...
    worker_loop(PeerInfo, State).
```
### Receiving packets from an ENet peer
```erlang
worker_loop(PeerInfo, State) ->
    ...
    ...
    receive
        {enet, ChannelID, #unsequenced{ data = Packet }} ->
            %% Handle the Packet
            ok;
        {enet, ChannelID, #unreliable{ data = Packet }} ->
            %% Handle the Packet
            ok;
        {enet, ChannelID, #reliable{ data = Packet }} ->
            %% Handle the Packet
            ok
    end,
    ...
    ...
    worker_loop(PeerInfo, State).
```