├── .gitignore ├── Makefile ├── README.md ├── docs └── index.md ├── enet-dev-1.rockspec ├── enet.c ├── examples ├── channels │ ├── client.lua │ └── server.lua ├── simple │ ├── client.lua │ └── server.lua └── unreliable │ ├── client.lua │ └── server.lua └── spec └── enet_spec.moon /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.o 3 | .*.swp 4 | /*.lua 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | enet.so: enet.c 2 | luarocks make --local enet-dev-1.rockspec 3 | 4 | enet.dll: enet.c 5 | gcc -O2 -shared -o $@ $< -lenet -llua5.1 -lws2_32 -lwinmm 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-enet 2 | 3 | Lua bindings for [ENet](http://enet.bespin.org/). 4 | See . 5 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | **lua-enet** is a binding to the [ENet](http://enet.bespin.org/) library for 2 | Lua. ENet is a thin network communication layer over UDP that provides high 3 | performance and reliable communication that is suitable for games. The 4 | interface exposes an asynchronous way of creating clients and servers that is 5 | easy to integrate into existing event loops. 6 | 7 | To get an idea of how ENet works and what it provides have a look at 8 | [Features and Achritecture](http://enet.bespin.org/Features.html) from the 9 | original documentation. 10 | 11 |
$index
12 | 13 | 14 | ## Download & Install 15 | 16 | ### Linux & OSX 17 | 18 | Before installing **lua-enet**, you must make sure you have 19 | [ENet](http://enet.bespin.org/) installed. Consult your system's package 20 | management. 21 | 22 | If you've got [Lua Rocks](http://www.luarocks.org/) then installation is very 23 | easy: 24 | 25 | ```bash 26 | $ luarocks install enet 27 | ``` 28 | 29 | Otherwise you can download the source can from GitHub: 30 | 31 | 32 | ### Windows 33 | 34 | Prebuilt binaries are provided: 35 | 36 | Download . 37 | Unzip and install alongside your Lua installation. 38 | 39 | 40 | ## Tutorial 41 | 42 | We'll need a client and a server. A server is a *host* bound to an address. 43 | A client is an unbound *host* that connects to an address. 44 | `enet.host_create` is used to create a new host. `host:service` is used to wait 45 | for events and send packets. It can optionally have a specified timeout. 46 | 47 | When `host:service` receives an event it returns an event object, which is a 48 | plain Lua table. The `type` entry holds the kind of event as a string, and the 49 | `peer` entry has the associated peer who triggered the event. 50 | 51 | Using this information, we can make a simple echo server: 52 | 53 | 54 | ```lua 55 | -- server.lua 56 | require "enet" 57 | local host = enet.host_create"localhost:6789" 58 | while true do 59 | local event = host:service(100) 60 | if event and event.type == "receive" then 61 | print("Got message: ", event.data, event.peer) 62 | event.peer:send(event.data) 63 | end 64 | end 65 | ``` 66 | 67 | 68 | Data, whether sent or received, is always a Lua string. Primitive types can be 69 | converted to a binary string to reduce the number of bytes sent by using a binary 70 | serialization library. 71 | 72 | The client for our server can be written like so: 73 | 74 | 75 | ```lua 76 | -- client.lua 77 | require "enet" 78 | local host = enet.host_create() 79 | local server = host:connect("localhost:6789") 80 | 81 | local done = false 82 | while not done do 83 | local event = host:service(100) 84 | if event then 85 | if event.type == "connect" then 86 | print("Connected to", event.peer) 87 | event.peer:send("hello world") 88 | elseif event.type == "receive" then 89 | print("Got message: ", event.data, event.peer) 90 | done = true 91 | end 92 | end 93 | end 94 | 95 | server:disconnect() 96 | host:flush() 97 | ``` 98 | 99 | Upon receiving the connect message we send `"hello world"` to the server, then 100 | wait for the response. 101 | 102 | When a client disconnects, make sure to call `disconnect` on the server object, 103 | and then tell the host to flush (unless `host:service` will be called again) 104 | otherwise the server will have to wait for the client to disconnect from timeout. 105 | 106 | 107 | ## Reference 108 | 109 | ### `enet.host_create([bind_address, peer_count, channel_count, in_bandwidth, out_bandwidth])` 110 | 111 | Returns a new host. All arguments are optional. 112 | 113 | A `bind_address` of `nil` makes a host that can not be connected to (typically 114 | a client). Otherwise the address can either be of the form 115 | `:`, `:`, or `*:`. 116 | 117 | Example addresses include `"127.0.0.1:8888"`, `"localhost:2232"`, and `"*:6767"`. 118 | 119 | Parameters: 120 | 121 | * `peer_count` max number of peers, defaults to `64` 122 | * `channel_count` max number of channels, defaults to `1` 123 | * `in_bandwidth` downstream bandwidth in bytes/sec, defaults to `0` 124 | (unlimited) 125 | * `out_bandwidth` upstream bandwidth in bytes/sec, defaults to `0` 126 | (unlimited) 127 | 128 | ### `host:connect(address [, channel_count, data])` 129 | 130 | Connects a host to a remote host. Returns peer object associated with remote 131 | host. The actual connection will not take place until the next `host:service` 132 | is done, in which a `"connect"` event will be generated. 133 | 134 | `channel_count` is the number of channels to allocate. It should be the same as 135 | the channel count on the server. Defaults to `1`. 136 | 137 | `data` is an integer value that can be associated with the connect event. 138 | Defaults to `0`. 139 | 140 | ### `host:destroy()` 141 | 142 | Destroys host, freeing up the port it's bound to. Automatically called when `host` is garbage collected. 143 | 144 | ### `host:service([timeout])` 145 | 146 | Wait for events, send and receive any ready packets. `timeout` is the max 147 | number of milliseconds to be waited for an event. By default `timeout` is `0`. 148 | Returns `nil` on timeout if no events occurred. 149 | 150 | If an event happens, an event table is returned. All events have a `type` entry, 151 | which is one of `"connect"`, `"disconnect"`, or `"receive"`. Events also have a 152 | `peer` entry which holds the peer object of who triggered the event. 153 | 154 | A `"receive"` event also has a `data` entry which is a Lua string containing the 155 | data received. 156 | 157 | ### `host:check_events()` 158 | 159 | Checks for any queued events and dispatches one if available. Returns the 160 | associated event if something was dispatched, otherwise `nil`. 161 | 162 | ### `host:compress_with_range_coder()` 163 | 164 | Enables an adaptive order-2 PPM range coder for the transmitted data of 165 | all pers. 166 | 167 | ### `host:flush()` 168 | 169 | Sends any queued packets. This is only required to send packets earlier than 170 | the next call to `host:service`, or if `host:service` will not be called again. 171 | 172 | ### `host:broadcast(data [, channel, flag])` 173 | 174 | Queues a packet to be sent to all connected peers. See 175 | [peer:send](#peersenddata__channel_flag) for arguments. 176 | 177 | ### `host:channel_limit(limit)` 178 | 179 | Sets the maximum number of channels allowed. If it is `0` then the system 180 | maximum allowable value is used. 181 | 182 | ### `host:bandwidth_limit(incoming, outgoing)` 183 | 184 | Sets the bandwidth limits of the host in bytes/sec. Set to `0` for unlimited. 185 | 186 | ### `host:total_sent_data()` 187 | 188 | Returns the number of bytes that were sent through the given host. 189 | 190 | ### `host:total_received_data()` 191 | 192 | Returns the number of bytes that were received by the given host. 193 | 194 | ### `host:service_time()` 195 | 196 | Returns the timestamp of the last call to host:service() or host:flush(). 197 | 198 | ### `host:peer_count()` 199 | Returns the number of peers that are allocated for the given host. This 200 | represents the maximum number of possible connections. 201 | 202 | ### `host:get_peer(index)` 203 | 204 | Returns the connected peer at the specified index (starting at 1). ENet 205 | stores all peers in an array of the corresponding host and re-uses unused 206 | peers for new connections. You can query the state of a peer using 207 | [peer:state](#peerstate). 208 | 209 | ### `host:get_socket_address()` 210 | 211 | Returns a string that describes the socket address of the given host. The 212 | string is formatted as "a.b.c.d:port", where "a.b.c.d" is the ip address of 213 | the used socket. 214 | 215 | ### `peer:connect_id()` 216 | 217 | Returns the field ENetPeer::connectID that is assigned for each 218 | connection. 219 | 220 | ### `peer:disconnect([data])` 221 | 222 | Requests a disconnection from the peer. The message is sent on the next 223 | `host:service` or `host:flush`. 224 | 225 | `data` is optional integer value to be associated with the disconnect. 226 | 227 | ### `peer:disconnect_now([data])` 228 | 229 | Force immediate disconnection from peer. Foreign peer not guaranteed to receive 230 | disconnect notification. 231 | 232 | `data` is optional integer value to be associated with the disconnect. 233 | 234 | ### `peer:disconnect_later([data])` 235 | 236 | Request a disconnection from peer, but only after all queued outgoing packets 237 | are sent. 238 | 239 | `data` is optional integer value to be associated with the disconnect. 240 | 241 | ### `peer:index()` 242 | 243 | Returns the index of the peer. All peers of an ENet host are kept in an 244 | array. This function finds and returns the index of the peer of its host 245 | structure. 246 | 247 | ### `peer:ping()` 248 | 249 | Send a ping request to peer, updates `round_trip_time`. This is called 250 | automatically at regular intervals. 251 | 252 | ### `peer:ping_interval(interval)` 253 | 254 | Specifies the interval in milliseconds that pings are sent to the other 255 | end of the connection (defaults to 500). 256 | 257 | ### `peer:reset()` 258 | 259 | Forcefully disconnects peer. The peer is not notified of the disconnection. 260 | 261 | ### `peer:send(data [, channel, flag])` 262 | 263 | Queues a packet to be sent to peer. `data` is the contents of the packet, it 264 | must be a Lua string. 265 | 266 | `channel` is the channel to send the packet on. Defaults to `0`. 267 | 268 | 269 | `flag` is one of `"reliable"`, `"unsequenced"`, or `"unreliable"`. Reliable 270 | packets are guaranteed to arrive, and arrive in the order in which they are sent. 271 | Unsequenced packets are unreliable and have no guarantee on the order they 272 | arrive. Defaults to reliable. 273 | 274 | 275 | ### `peer:state()` 276 | 277 | Returns the state of the peer as a string. This can be any of the 278 | following: 279 | 280 | * `"disconnected"` 281 | * `"connecting"` 282 | * `"acknowledging_connect"` 283 | * `"connection_pending"` 284 | * `"connection_succeeded"` 285 | * `"connected"` 286 | * `"disconnect_later"` 287 | * `"disconnecting"` 288 | * `"acknowledging_disconnect"` 289 | * `"zombie"` 290 | * `"unknown"` 291 | 292 | ### `peer:receive()` 293 | 294 | Attempts to dequeue an incoming packet for this peer. 295 | Returns `nil` if there are no packets waiting. Otherwise returns two values: 296 | the string representing the packet data, and the channel the packet came from. 297 | 298 | ### `peer:round_trip_time([value])` 299 | 300 | Returns or sets the current round trip time (i.e. ping). If value is nil 301 | the current value of the peer is returned. Otherwise the value roundTripTime 302 | is set to the specified value and returned. 303 | 304 | Enet performs some filtering on the round trip times and it takes some time 305 | until the parameters are accurate. 306 | 307 | ### `peer:last_round_trip_time([value])` 308 | 309 | Returns or sets the round trip time of the previous round trip time 310 | computation. If value is nil the current value of the peer is returned. 311 | Otherwise the value lastRoundTripTime is set to the specified value and 312 | returned. 313 | 314 | Enet performs some filtering on the round trip times and it takes 315 | some time until the parameters are accurate. To speed it up you can set 316 | the value of the last round trip time to a more accurate guess. 317 | 318 | ### `peer:throttle_configure(interval, acceleration, deceleration)` 319 | Changes the probability at which unreliable packets should not be dropped. 320 | 321 | Parameters: 322 | 323 | * `interval` interval in milliseconds to measure lowest mean RTT 324 | * `acceleration` rate at which to increase throttle probability as mean RTT 325 | declines 326 | * `deceleration` rate at which to decrease throttle probability as mean RTT 327 | increases 328 | 329 | ### `(limit, minimum, maximum) peer:timeout(limit, minimum, maximum)` 330 | 331 | Returns or sets the parameters when a timeout is detected. This is happens 332 | either after a fixed timeout or a variable timeout of time that takes the 333 | round trip time into account. The former is specified with the `maximum` 334 | parameter. 335 | 336 | Parameters: 337 | 338 | * `limit` a factor that is multiplied with a value that based on the 339 | average round trip time to compute the timeout limit 340 | * `minimum` timeout value in milliseconds that a reliable packet has to 341 | be acknowledged if the variable timeout limit was exceeded 342 | * `maximum` fixed timeout in milliseconds for which any packet has to be acknowledged 343 | 344 | See official ENet documentation for detailed description. 345 | 346 | 347 | ## License (MIT) 348 | 349 | Copyright (C) 2014 by Leaf Corcoran 350 | 351 | Permission is hereby granted, free of charge, to any person obtaining a copy 352 | of this software and associated documentation files (the "Software"), to deal 353 | in the Software without restriction, including without limitation the rights 354 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 355 | copies of the Software, and to permit persons to whom the Software is 356 | furnished to do so, subject to the following conditions: 357 | 358 | The above copyright notice and this permission notice shall be included in 359 | all copies or substantial portions of the Software. 360 | 361 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 362 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 363 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 364 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 365 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 366 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 367 | THE SOFTWARE. 368 | 369 | 370 | ## Contact 371 | 372 | Author: Leaf Corcoran ([leafo](http://github.com/leafo)) ([@moonscript](http://twitter.com/moonscript)) 373 | Email: 374 | Homepage: 375 | 376 | -------------------------------------------------------------------------------- /enet-dev-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "enet" 2 | version = "dev-1" 3 | 4 | source = { 5 | url = "git://github.com/leafo/lua-enet.git" 6 | } 7 | 8 | description = { 9 | summary = "A library for doing network communication in Lua", 10 | detailed = [[ 11 | Binding to ENet, network communication layer on top of UDP. 12 | ]], 13 | homepage = "http://leafo.net/lua-enet", 14 | license = "MIT" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | external_dependencies = { 22 | ENET = { 23 | header = "enet/enet.h" 24 | } 25 | } 26 | 27 | build = { 28 | type = "builtin", 29 | modules = { 30 | enet = { 31 | sources = {"enet.c"}, 32 | libraries = {"enet"}, 33 | incdirs = {"$(ENET_INCDIR)"}, 34 | libdirs = {"$(ENET_LIBDIR)"} 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /enet.c: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (C) 2014 by Leaf Corcoran 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 13 | * all 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 DEALING IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | // For lua5.2 support, instead we could replace all the luaL_register's with whatever 28 | // lua5.2's equivalent function is, but this is easier so whatever. 29 | #define LUA_COMPAT_MODULE 30 | #include "lua.h" 31 | #include "lualib.h" 32 | #include "lauxlib.h" 33 | #include 34 | 35 | #define check_host(l, idx)\ 36 | *(ENetHost**)luaL_checkudata(l, idx, "enet_host") 37 | 38 | #define check_peer(l, idx)\ 39 | *(ENetPeer**)luaL_checkudata(l, idx, "enet_peer") 40 | 41 | /** 42 | * Parse address string, eg: 43 | * *:5959 44 | * 127.0.0.1:* 45 | * website.com:8080 46 | */ 47 | static void parse_address(lua_State *l, const char *addr_str, ENetAddress *address) { 48 | int host_i = 0, port_i = 0; 49 | char host_str[128] = {0}; 50 | char port_str[32] = {0}; 51 | int scanning_port = 0; 52 | 53 | char *c = (char *)addr_str; 54 | 55 | while (*c != 0) { 56 | if (host_i >= 128 || port_i >= 32 ) luaL_error(l, "Hostname too long"); 57 | if (scanning_port) { 58 | port_str[port_i++] = *c; 59 | } else { 60 | if (*c == ':') { 61 | scanning_port = 1; 62 | } else { 63 | host_str[host_i++] = *c; 64 | } 65 | } 66 | c++; 67 | } 68 | host_str[host_i] = '\0'; 69 | port_str[port_i] = '\0'; 70 | 71 | if (host_i == 0) luaL_error(l, "Failed to parse address"); 72 | if (port_i == 0) luaL_error(l, "Missing port in address"); 73 | 74 | if (strcmp("*", host_str) == 0) { 75 | address->host = ENET_HOST_ANY; 76 | } else { 77 | if (enet_address_set_host(address, host_str) != 0) { 78 | luaL_error(l, "Failed to resolve host name"); 79 | } 80 | } 81 | 82 | if (strcmp("*", port_str) == 0) { 83 | address->port = ENET_PORT_ANY; 84 | } else { 85 | address->port = atoi(port_str); 86 | } 87 | } 88 | 89 | /** 90 | * Find the index of a given peer for which we only have the pointer. 91 | */ 92 | size_t find_peer_index (lua_State *l, ENetHost *enet_host, ENetPeer *peer) { 93 | size_t peer_index; 94 | for (peer_index = 0; peer_index < enet_host->peerCount; peer_index++) { 95 | if (peer == &(enet_host->peers[peer_index])) 96 | return peer_index; 97 | } 98 | 99 | luaL_error (l, "enet: could not find peer id!"); 100 | 101 | return peer_index; 102 | } 103 | 104 | static void push_peer(lua_State *l, ENetPeer *peer) { 105 | // try to find in peer table 106 | lua_getfield(l, LUA_REGISTRYINDEX, "enet_peers"); 107 | lua_pushlightuserdata(l, peer); 108 | lua_gettable(l, -2); 109 | 110 | if (lua_isnil(l, -1)) { 111 | // printf("creating new peer\n"); 112 | lua_pop(l, 1); 113 | 114 | *(ENetPeer**)lua_newuserdata(l, sizeof(void*)) = peer; 115 | luaL_getmetatable(l, "enet_peer"); 116 | lua_setmetatable(l, -2); 117 | 118 | lua_pushlightuserdata(l, peer); 119 | lua_pushvalue(l, -2); 120 | 121 | lua_settable(l, -4); 122 | } 123 | lua_remove(l, -2); // remove enet_peers 124 | } 125 | 126 | static void push_event(lua_State *l, ENetEvent *event) { 127 | lua_newtable(l); // event table 128 | 129 | if (event->peer) { 130 | push_peer(l, event->peer); 131 | lua_setfield(l, -2, "peer"); 132 | } 133 | 134 | switch (event->type) { 135 | case ENET_EVENT_TYPE_CONNECT: 136 | lua_pushinteger(l, event->data); 137 | lua_setfield(l, -2, "data"); 138 | 139 | lua_pushstring(l, "connect"); 140 | break; 141 | case ENET_EVENT_TYPE_DISCONNECT: 142 | lua_pushinteger(l, event->data); 143 | lua_setfield(l, -2, "data"); 144 | 145 | lua_pushstring(l, "disconnect"); 146 | break; 147 | case ENET_EVENT_TYPE_RECEIVE: 148 | lua_pushlstring(l, (const char *)event->packet->data, event->packet->dataLength); 149 | lua_setfield(l, -2, "data"); 150 | 151 | lua_pushinteger(l, event->channelID); 152 | lua_setfield(l, -2, "channel"); 153 | 154 | lua_pushstring(l, "receive"); 155 | 156 | enet_packet_destroy(event->packet); 157 | break; 158 | case ENET_EVENT_TYPE_NONE: 159 | lua_pushstring(l, "none"); 160 | break; 161 | } 162 | 163 | lua_setfield(l, -2, "type"); 164 | } 165 | 166 | /** 167 | * Read a packet off the stack as a string 168 | * idx is position of string 169 | */ 170 | static ENetPacket *read_packet(lua_State *l, int idx, enet_uint8 *channel_id) { 171 | size_t size; 172 | int argc = lua_gettop(l); 173 | const void *data = luaL_checklstring(l, idx, &size); 174 | ENetPacket *packet; 175 | 176 | enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE; 177 | *channel_id = 0; 178 | 179 | if (argc >= idx+2 && !lua_isnil(l, idx+2)) { 180 | const char *flag_str = luaL_checkstring(l, idx+2); 181 | if (strcmp("unsequenced", flag_str) == 0) { 182 | flags = ENET_PACKET_FLAG_UNSEQUENCED; 183 | } else if (strcmp("reliable", flag_str) == 0) { 184 | flags = ENET_PACKET_FLAG_RELIABLE; 185 | } else if (strcmp("unreliable", flag_str) == 0) { 186 | flags = 0; 187 | } else { 188 | luaL_error(l, "Unknown packet flag: %s", flag_str); 189 | } 190 | } 191 | 192 | if (argc >= idx+1 && !lua_isnil(l, idx+1)) { 193 | *channel_id = luaL_checkint(l, idx+1); 194 | } 195 | 196 | packet = enet_packet_create(data, size, flags); 197 | if (packet == NULL) { 198 | luaL_error(l, "Failed to create packet"); 199 | } 200 | 201 | return packet; 202 | } 203 | 204 | /** 205 | * Create a new host 206 | * Args: 207 | * address (nil for client) 208 | * [peer_count = 64] 209 | * [channel_count = 1] 210 | * [in_bandwidth = 0] 211 | * [out_bandwidth = 0] 212 | */ 213 | static int host_create(lua_State *l) { 214 | ENetHost *host; 215 | size_t peer_count = 64, channel_count = 1; 216 | enet_uint32 in_bandwidth = 0, out_bandwidth = 0; 217 | 218 | int have_address = 1; 219 | ENetAddress address; 220 | 221 | if (lua_gettop(l) == 0 || lua_isnil(l, 1)) { 222 | have_address = 0; 223 | } else { 224 | parse_address(l, luaL_checkstring(l, 1), &address); 225 | } 226 | 227 | switch (lua_gettop(l)) { 228 | case 5: 229 | if (!lua_isnil(l, 5)) out_bandwidth = luaL_checkint(l, 5); 230 | case 4: 231 | if (!lua_isnil(l, 4)) in_bandwidth = luaL_checkint(l, 4); 232 | case 3: 233 | if (!lua_isnil(l, 3)) channel_count = luaL_checkint(l, 3); 234 | case 2: 235 | if (!lua_isnil(l, 2)) peer_count = luaL_checkint(l, 2); 236 | } 237 | 238 | // printf("host create, peers=%d, channels=%d, in=%d, out=%d\n", 239 | // peer_count, channel_count, in_bandwidth, out_bandwidth); 240 | host = enet_host_create(have_address ? &address : NULL, peer_count, 241 | channel_count, in_bandwidth, out_bandwidth); 242 | 243 | if (host == NULL) { 244 | lua_pushnil (l); 245 | lua_pushstring(l, "enet: failed to create host (already listening?)"); 246 | return 2; 247 | } 248 | 249 | *(ENetHost**)lua_newuserdata(l, sizeof(void*)) = host; 250 | luaL_getmetatable(l, "enet_host"); 251 | lua_setmetatable(l, -2); 252 | 253 | return 1; 254 | } 255 | 256 | static int linked_version(lua_State *l) { 257 | lua_pushfstring(l, "%d.%d.%d", 258 | ENET_VERSION_GET_MAJOR(enet_linked_version()), 259 | ENET_VERSION_GET_MINOR(enet_linked_version()), 260 | ENET_VERSION_GET_PATCH(enet_linked_version())); 261 | return 1; 262 | } 263 | 264 | /** 265 | * Serice a host 266 | * Args: 267 | * timeout 268 | * 269 | * Return 270 | * nil on no event 271 | * an event table on event 272 | */ 273 | static int host_service(lua_State *l) { 274 | ENetHost *host = check_host(l, 1); 275 | if (!host) { 276 | return luaL_error(l, "Tried to index a nil host!"); 277 | } 278 | ENetEvent event; 279 | int timeout = 0, out; 280 | 281 | if (lua_gettop(l) > 1) 282 | timeout = luaL_checkint(l, 2); 283 | 284 | out = enet_host_service(host, &event, timeout); 285 | if (out == 0) return 0; 286 | if (out < 0) return luaL_error(l, "Error during service"); 287 | 288 | push_event(l, &event); 289 | return 1; 290 | } 291 | 292 | /** 293 | * Dispatch a single event if available 294 | */ 295 | static int host_check_events(lua_State *l) { 296 | ENetHost *host = check_host(l, 1); 297 | if (!host) { 298 | return luaL_error(l, "Tried to index a nil host!"); 299 | } 300 | ENetEvent event; 301 | int out = enet_host_check_events(host, &event); 302 | if (out == 0) return 0; 303 | if (out < 0) return luaL_error(l, "Error checking event"); 304 | 305 | push_event(l, &event); 306 | return 1; 307 | } 308 | 309 | /** 310 | * Enables an adaptive order-2 PPM range coder for the transmitted data of 311 | * all peers. 312 | */ 313 | static int host_compress_with_range_coder(lua_State *l) { 314 | ENetHost *host = check_host(l, 1); 315 | if (!host) { 316 | return luaL_error(l, "Tried to index a nil host!"); 317 | } 318 | 319 | int result = enet_host_compress_with_range_coder (host); 320 | if (result == 0) { 321 | lua_pushboolean (l, 1); 322 | } else { 323 | lua_pushboolean (l, 0); 324 | } 325 | 326 | return 1; 327 | } 328 | 329 | /** 330 | * Connect a host to an address 331 | * Args: 332 | * the address 333 | * [channel_count = 1] 334 | * [data = 0] 335 | */ 336 | static int host_connect(lua_State *l) { 337 | ENetHost *host = check_host(l, 1); 338 | if (!host) { 339 | return luaL_error(l, "Tried to index a nil host!"); 340 | } 341 | ENetAddress address; 342 | ENetPeer *peer; 343 | 344 | enet_uint32 data = 0; 345 | size_t channel_count = 1; 346 | 347 | parse_address(l, luaL_checkstring(l, 2), &address); 348 | 349 | switch (lua_gettop(l)) { 350 | case 4: 351 | if (!lua_isnil(l, 4)) data = luaL_checkint(l, 4); 352 | case 3: 353 | if (!lua_isnil(l, 3)) channel_count = luaL_checkint(l, 3); 354 | } 355 | 356 | // printf("host connect, channels=%d, data=%d\n", channel_count, data); 357 | peer = enet_host_connect(host, &address, channel_count, data); 358 | 359 | if (peer == NULL) { 360 | return luaL_error(l, "Failed to create peer"); 361 | } 362 | 363 | push_peer(l, peer); 364 | 365 | return 1; 366 | } 367 | 368 | static int host_flush(lua_State *l) { 369 | ENetHost *host = check_host(l, 1); 370 | if (!host) { 371 | return luaL_error(l, "Tried to index a nil host!"); 372 | } 373 | enet_host_flush(host); 374 | return 0; 375 | } 376 | 377 | static int host_broadcast(lua_State *l) { 378 | ENetHost *host = check_host(l, 1); 379 | if (!host) { 380 | return luaL_error(l, "Tried to index a nil host!"); 381 | } 382 | 383 | enet_uint8 channel_id; 384 | ENetPacket *packet = read_packet(l, 2, &channel_id); 385 | enet_host_broadcast(host, channel_id, packet); 386 | return 0; 387 | } 388 | 389 | // Args: limit:number 390 | static int host_channel_limit(lua_State *l) { 391 | ENetHost *host = check_host(l, 1); 392 | if (!host) { 393 | return luaL_error(l, "Tried to index a nil host!"); 394 | } 395 | int limit = luaL_checkint(l, 2); 396 | enet_host_channel_limit(host, limit); 397 | return 0; 398 | } 399 | 400 | static int host_bandwidth_limit(lua_State *l) { 401 | ENetHost *host = check_host(l, 1); 402 | if (!host) { 403 | return luaL_error(l, "Tried to index a nil host!"); 404 | } 405 | enet_uint32 in_bandwidth = luaL_checkint(l, 2); 406 | enet_uint32 out_bandwidth = luaL_checkint(l, 2); 407 | enet_host_bandwidth_limit(host, in_bandwidth, out_bandwidth); 408 | return 0; 409 | } 410 | 411 | static int host_get_socket_address(lua_State *l) { 412 | ENetHost *host = check_host(l, 1); 413 | if (!host) { 414 | return luaL_error(l, "Tried to index a nil host!"); 415 | } 416 | ENetAddress address; 417 | enet_socket_get_address (host->socket, &address); 418 | 419 | lua_pushfstring(l, "%d.%d.%d.%d:%d", 420 | ((address.host) & 0xFF), 421 | ((address.host >> 8) & 0xFF), 422 | ((address.host >> 16) & 0xFF), 423 | (address.host >> 24& 0xFF), 424 | address.port); 425 | 426 | return 1; 427 | } 428 | static int host_total_sent_data(lua_State *l) { 429 | ENetHost *host = check_host(l, 1); 430 | if (!host) { 431 | return luaL_error(l, "Tried to index a nil host!"); 432 | } 433 | 434 | lua_pushinteger (l, host->totalSentData); 435 | 436 | return 1; 437 | } 438 | 439 | static int host_total_received_data(lua_State *l) { 440 | ENetHost *host = check_host(l, 1); 441 | if (!host) { 442 | return luaL_error(l, "Tried to index a nil host!"); 443 | } 444 | 445 | lua_pushinteger (l, host->totalReceivedData); 446 | 447 | return 1; 448 | } 449 | static int host_service_time(lua_State *l) { 450 | ENetHost *host = check_host(l, 1); 451 | if (!host) { 452 | return luaL_error(l, "Tried to index a nil host!"); 453 | } 454 | 455 | lua_pushinteger (l, host->serviceTime); 456 | 457 | return 1; 458 | } 459 | 460 | static int host_peer_count(lua_State *l) { 461 | ENetHost *host = check_host(l, 1); 462 | if (!host) { 463 | return luaL_error(l, "Tried to index a nil host!"); 464 | } 465 | 466 | lua_pushinteger (l, host->peerCount); 467 | 468 | return 1; 469 | } 470 | 471 | static int host_get_peer(lua_State *l) { 472 | ENetHost *host = check_host(l, 1); 473 | if (!host) { 474 | return luaL_error(l, "Tried to index a nil host!"); 475 | } 476 | 477 | size_t peer_index = (size_t) luaL_checkint(l, 2) - 1; 478 | 479 | if (peer_index < 0 || peer_index >= host->peerCount) { 480 | luaL_argerror (l, 2, "Invalid peer index"); 481 | } 482 | 483 | ENetPeer *peer = &(host->peers[peer_index]); 484 | 485 | push_peer (l, peer); 486 | return 1; 487 | } 488 | 489 | static int host_gc(lua_State *l) { 490 | // We have to manually grab the userdata so that we can set it to NULL. 491 | ENetHost** host = luaL_checkudata(l, 1, "enet_host"); 492 | // We don't want to crash by destroying a non-existant host. 493 | if (*host) { 494 | enet_host_destroy(*host); 495 | } 496 | *host = NULL; 497 | return 0; 498 | } 499 | 500 | static int peer_tostring(lua_State *l) { 501 | ENetPeer *peer = check_peer(l, 1); 502 | char host_str[128]; 503 | enet_address_get_host_ip(&peer->address, host_str, 128); 504 | 505 | lua_pushstring(l, host_str); 506 | lua_pushstring(l, ":"); 507 | lua_pushinteger(l, peer->address.port); 508 | lua_concat(l, 3); 509 | return 1; 510 | } 511 | 512 | static int peer_ping(lua_State *l) { 513 | ENetPeer *peer = check_peer(l, 1); 514 | enet_peer_ping(peer); 515 | return 0; 516 | } 517 | 518 | static int peer_throttle_configure(lua_State *l) { 519 | ENetPeer *peer = check_peer(l, 1); 520 | 521 | enet_uint32 interval = luaL_checkint(l, 2); 522 | enet_uint32 acceleration = luaL_checkint(l, 3); 523 | enet_uint32 deceleration = luaL_checkint(l, 4); 524 | 525 | enet_peer_throttle_configure(peer, interval, acceleration, deceleration); 526 | return 0; 527 | } 528 | 529 | static int peer_round_trip_time(lua_State *l) { 530 | ENetPeer *peer = check_peer(l, 1); 531 | 532 | if (lua_gettop(l) > 1) { 533 | enet_uint32 round_trip_time = luaL_checkint(l, 2); 534 | peer->roundTripTime = round_trip_time; 535 | } 536 | 537 | lua_pushinteger (l, peer->roundTripTime); 538 | 539 | return 1; 540 | } 541 | 542 | static int peer_last_round_trip_time(lua_State *l) { 543 | ENetPeer *peer = check_peer(l, 1); 544 | 545 | if (lua_gettop(l) > 1) { 546 | enet_uint32 round_trip_time = luaL_checkint(l, 2); 547 | peer->lastRoundTripTime = round_trip_time; 548 | } 549 | lua_pushinteger (l, peer->lastRoundTripTime); 550 | 551 | return 1; 552 | } 553 | 554 | static int peer_ping_interval(lua_State *l) { 555 | ENetPeer *peer = check_peer(l, 1); 556 | 557 | if (lua_gettop(l) > 1) { 558 | enet_uint32 interval = luaL_checkint(l, 2); 559 | enet_peer_ping_interval (peer, interval); 560 | } 561 | 562 | lua_pushinteger (l, peer->pingInterval); 563 | 564 | return 1; 565 | } 566 | 567 | static int peer_timeout(lua_State *l) { 568 | ENetPeer *peer = check_peer(l, 1); 569 | 570 | enet_uint32 timeout_limit = 0; 571 | enet_uint32 timeout_minimum = 0; 572 | enet_uint32 timeout_maximum = 0; 573 | 574 | switch (lua_gettop(l)) { 575 | case 4: 576 | if (!lua_isnil(l, 4)) timeout_maximum = luaL_checkint(l, 4); 577 | case 3: 578 | if (!lua_isnil(l, 3)) timeout_minimum = luaL_checkint(l, 3); 579 | case 2: 580 | if (!lua_isnil(l, 2)) timeout_limit = luaL_checkint(l, 2); 581 | } 582 | 583 | enet_peer_timeout (peer, timeout_limit, timeout_minimum, timeout_maximum); 584 | 585 | lua_pushinteger (l, peer->timeoutLimit); 586 | lua_pushinteger (l, peer->timeoutMinimum); 587 | lua_pushinteger (l, peer->timeoutMaximum); 588 | 589 | return 3; 590 | } 591 | 592 | static int peer_disconnect(lua_State *l) { 593 | ENetPeer *peer = check_peer(l, 1); 594 | 595 | enet_uint32 data = lua_gettop(l) > 1 ? luaL_checkint(l, 2) : 0; 596 | enet_peer_disconnect(peer, data); 597 | return 0; 598 | } 599 | 600 | static int peer_disconnect_now(lua_State *l) { 601 | ENetPeer *peer = check_peer(l, 1); 602 | 603 | enet_uint32 data = lua_gettop(l) > 1 ? luaL_checkint(l, 2) : 0; 604 | enet_peer_disconnect_now(peer, data); 605 | return 0; 606 | } 607 | 608 | static int peer_disconnect_later(lua_State *l) { 609 | ENetPeer *peer = check_peer(l, 1); 610 | 611 | enet_uint32 data = lua_gettop(l) > 1 ? luaL_checkint(l, 2) : 0; 612 | enet_peer_disconnect_later(peer, data); 613 | return 0; 614 | } 615 | 616 | static int peer_index(lua_State *l) { 617 | ENetPeer *peer = check_peer(l, 1); 618 | 619 | size_t peer_index = find_peer_index (l, peer->host, peer); 620 | lua_pushinteger (l, peer_index + 1); 621 | 622 | return 1; 623 | } 624 | 625 | static int peer_state(lua_State *l) { 626 | ENetPeer *peer = check_peer(l, 1); 627 | 628 | switch (peer->state) { 629 | case (ENET_PEER_STATE_DISCONNECTED): 630 | lua_pushstring (l, "disconnected"); 631 | break; 632 | case (ENET_PEER_STATE_CONNECTING): 633 | lua_pushstring (l, "connecting"); 634 | break; 635 | case (ENET_PEER_STATE_ACKNOWLEDGING_CONNECT): 636 | lua_pushstring (l, "acknowledging_connect"); 637 | break; 638 | case (ENET_PEER_STATE_CONNECTION_PENDING): 639 | lua_pushstring (l, "connection_pending"); 640 | break; 641 | case (ENET_PEER_STATE_CONNECTION_SUCCEEDED): 642 | lua_pushstring (l, "connection_succeeded"); 643 | break; 644 | case (ENET_PEER_STATE_CONNECTED): 645 | lua_pushstring (l, "connected"); 646 | break; 647 | case (ENET_PEER_STATE_DISCONNECT_LATER): 648 | lua_pushstring (l, "disconnect_later"); 649 | break; 650 | case (ENET_PEER_STATE_DISCONNECTING): 651 | lua_pushstring (l, "disconnecting"); 652 | break; 653 | case (ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT): 654 | lua_pushstring (l, "acknowledging_disconnect"); 655 | break; 656 | case (ENET_PEER_STATE_ZOMBIE): 657 | lua_pushstring (l, "zombie"); 658 | break; 659 | default: 660 | lua_pushstring (l, "unknown"); 661 | } 662 | 663 | return 1; 664 | } 665 | 666 | static int peer_connect_id(lua_State *l) { 667 | ENetPeer *peer = check_peer(l, 1); 668 | 669 | lua_pushinteger (l, peer->connectID); 670 | 671 | return 1; 672 | } 673 | 674 | 675 | static int peer_reset(lua_State *l) { 676 | ENetPeer *peer = check_peer(l, 1); 677 | enet_peer_reset(peer); 678 | return 0; 679 | } 680 | 681 | static int peer_receive(lua_State *l) { 682 | ENetPeer *peer = check_peer(l, 1); 683 | ENetPacket *packet; 684 | enet_uint8 channel_id = 0; 685 | 686 | if (lua_gettop(l) > 1) { 687 | channel_id = luaL_checkint(l, 2); 688 | } 689 | 690 | packet = enet_peer_receive(peer, &channel_id); 691 | if (packet == NULL) return 0; 692 | 693 | lua_pushlstring(l, (const char *)packet->data, packet->dataLength); 694 | lua_pushinteger(l, channel_id); 695 | 696 | enet_packet_destroy(packet); 697 | return 2; 698 | } 699 | 700 | 701 | /** 702 | * Send a lua string to a peer 703 | * Args: 704 | * packet data, string 705 | * channel id 706 | * flags ["reliable", nil] 707 | * 708 | */ 709 | static int peer_send(lua_State *l) { 710 | ENetPeer *peer = check_peer(l, 1); 711 | 712 | enet_uint8 channel_id; 713 | ENetPacket *packet = read_packet(l, 2, &channel_id); 714 | 715 | // printf("sending, channel_id=%d\n", channel_id); 716 | enet_peer_send(peer, channel_id, packet); 717 | return 0; 718 | } 719 | 720 | static const struct luaL_Reg enet_funcs [] = { 721 | {"host_create", host_create}, 722 | {"linked_version", linked_version}, 723 | {NULL, NULL} 724 | }; 725 | 726 | static const struct luaL_Reg enet_host_funcs [] = { 727 | {"service", host_service}, 728 | {"check_events", host_check_events}, 729 | {"compress_with_range_coder", host_compress_with_range_coder}, 730 | {"connect", host_connect}, 731 | {"flush", host_flush}, 732 | {"broadcast", host_broadcast}, 733 | {"channel_limit", host_channel_limit}, 734 | {"bandwidth_limit", host_bandwidth_limit}, 735 | // Since ENetSocket isn't part of enet-lua, we should try to keep 736 | // naming conventions the same as the rest of the lib. 737 | {"get_socket_address", host_get_socket_address}, 738 | // We need this function to free up our ports when needed! 739 | {"destroy", host_gc}, 740 | 741 | // additional convenience functions (mostly accessors) 742 | {"total_sent_data", host_total_sent_data}, 743 | {"total_received_data", host_total_received_data}, 744 | {"service_time", host_service_time}, 745 | {"peer_count", host_peer_count}, 746 | {"get_peer", host_get_peer}, 747 | {NULL, NULL} 748 | }; 749 | 750 | static const struct luaL_Reg enet_peer_funcs [] = { 751 | {"disconnect", peer_disconnect}, 752 | {"disconnect_now", peer_disconnect_now}, 753 | {"disconnect_later", peer_disconnect_later}, 754 | {"reset", peer_reset}, 755 | {"ping", peer_ping}, 756 | {"receive", peer_receive}, 757 | {"send", peer_send}, 758 | {"throttle_configure", peer_throttle_configure}, 759 | {"ping_interval", peer_ping_interval}, 760 | {"timeout", peer_timeout}, 761 | 762 | // additional convenience functions to member variables 763 | {"index", peer_index}, 764 | {"state", peer_state}, 765 | {"connect_id", peer_connect_id}, 766 | {"round_trip_time", peer_round_trip_time}, 767 | {"last_round_trip_time", peer_last_round_trip_time}, 768 | {NULL, NULL} 769 | }; 770 | 771 | static const struct luaL_Reg enet_event_funcs [] = { 772 | {NULL, NULL} 773 | }; 774 | 775 | int luaopen_enet(lua_State *l) { 776 | enet_initialize(); 777 | atexit(enet_deinitialize); 778 | 779 | // create metatables 780 | luaL_newmetatable(l, "enet_host"); 781 | lua_newtable(l); // index 782 | luaL_register(l, NULL, enet_host_funcs); 783 | lua_setfield(l, -2, "__index"); 784 | lua_pushcfunction(l, host_gc); 785 | lua_setfield(l, -2, "__gc"); 786 | 787 | luaL_newmetatable(l, "enet_peer"); 788 | lua_newtable(l); 789 | luaL_register(l, NULL, enet_peer_funcs); 790 | lua_setfield(l, -2, "__index"); 791 | lua_pushcfunction(l, peer_tostring); 792 | lua_setfield(l, -2, "__tostring"); 793 | 794 | // set up peer table 795 | lua_newtable(l); 796 | 797 | lua_newtable(l); // metatable 798 | lua_pushstring(l, "v"); 799 | lua_setfield(l, -2, "__mode"); 800 | lua_setmetatable(l, -2); 801 | 802 | lua_setfield(l, LUA_REGISTRYINDEX, "enet_peers"); 803 | 804 | luaL_register(l, "enet", enet_funcs); 805 | return 1; 806 | } 807 | -------------------------------------------------------------------------------- /examples/channels/client.lua: -------------------------------------------------------------------------------- 1 | 2 | require "enet" 3 | 4 | local host = enet.host_create() 5 | local server = host:connect("localhost:7890", 2) 6 | 7 | local done = false 8 | while not done do 9 | local event = host:service(100) 10 | if event then 11 | if event.type == "connect" then 12 | server:send("wally world", 0) 13 | server:send("nut house", 1) 14 | elseif event.type == "receive" then 15 | print(event.type, event.data) 16 | done = true 17 | end 18 | end 19 | end 20 | 21 | server:disconnect() 22 | host:flush() 23 | 24 | print "done" 25 | -------------------------------------------------------------------------------- /examples/channels/server.lua: -------------------------------------------------------------------------------- 1 | 2 | require "enet" 3 | 4 | local channel_responders = { 5 | [0] = function(event) 6 | print("doing nothing") 7 | end, 8 | [1] = function(event) 9 | print("sending back...") 10 | event.peer:send(event.data, event.channel) 11 | end 12 | } 13 | 14 | local host = enet.host_create("localhost:7890", nil, 2) 15 | 16 | while true do 17 | local event = host:service(100) 18 | if event then 19 | if event.type == "receive" then 20 | print("receive, channel:", event.channel) 21 | channel_responders[event.channel](event) 22 | else 23 | print(event.type, event.peer) 24 | end 25 | end 26 | end 27 | 28 | -------------------------------------------------------------------------------- /examples/simple/client.lua: -------------------------------------------------------------------------------- 1 | 2 | require "enet" 3 | 4 | local host = enet.host_create() 5 | local server = host:connect("localhost:5678") 6 | 7 | local count = 0 8 | while count < 100 do 9 | local event = host:service(100) 10 | if event then 11 | if event.type == "receive" then 12 | print("Got message: ", event.data) 13 | else 14 | print("Got event", event.type) 15 | end 16 | end 17 | 18 | if count == 8 then 19 | print "sending message" 20 | server:send("hello world") 21 | end 22 | 23 | count = count + 1 24 | end 25 | 26 | server:disconnect() 27 | host:flush() 28 | 29 | print"done" 30 | 31 | -------------------------------------------------------------------------------- /examples/simple/server.lua: -------------------------------------------------------------------------------- 1 | require "enet" 2 | 3 | local host = enet.host_create"localhost:5678" 4 | 5 | while true do 6 | local event = host:service(100) 7 | if event then 8 | if event.type == "receive" then 9 | print("Got message: ", event.data, event.peer) 10 | event.peer:send("howdy back at ya") 11 | elseif event.type == "connect" then 12 | print("Connect:", event.peer) 13 | host:broadcast("new client connected") 14 | else 15 | print("Got event", event.type, event.peer) 16 | end 17 | 18 | end 19 | end -------------------------------------------------------------------------------- /examples/unreliable/client.lua: -------------------------------------------------------------------------------- 1 | 2 | require "enet" 3 | 4 | local host = enet.host_create() 5 | local server = host:connect("localhost:6789", 2) 6 | 7 | local done = false 8 | while not done do 9 | local event = host:service(200) 10 | if event then 11 | if event.type == "receive" then 12 | print(event.channel, event.data) 13 | end 14 | end 15 | end 16 | 17 | server:disconnect() 18 | host:flush() 19 | 20 | print "done" 21 | 22 | -------------------------------------------------------------------------------- /examples/unreliable/server.lua: -------------------------------------------------------------------------------- 1 | require "enet" 2 | 3 | local host = enet.host_create("localhost:6789", nil, 2) 4 | 5 | while true do 6 | local event = host:service(100) 7 | if event and event.type == "connect" then 8 | event.peer:send("hello world") 9 | for i = 0, 100 do 10 | event.peer:send(tostring(i), 1, "unreliable") 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/enet_spec.moon: -------------------------------------------------------------------------------- 1 | 2 | enet = require "enet" 3 | 4 | base_port = 112344 5 | 6 | moon = require "moon" 7 | 8 | describe "enet", -> 9 | it "should gc host", -> 10 | do 11 | client = enet.host_create() 12 | collectgarbage! 13 | 14 | it "should gc destroyed host", -> 15 | do 16 | client = enet.host_create() 17 | client\destroy! 18 | collectgarbage! 19 | 20 | it "should host and client", -> 21 | host = enet.host_create "localhost:#{base_port}" 22 | assert.truthy host 23 | 24 | client = enet.host_create() 25 | client\connect "localhost:#{base_port}" 26 | 27 | local host_event, client_event 28 | while not client_event or not host_event 29 | client_event = client\service(10) or client_event 30 | host_event = host\service(10) or host_event 31 | 32 | assert.same "connect", host_event.type 33 | assert.same "connect", client_event.type 34 | 35 | host_event.peer\send "Hello World" 36 | 37 | host_event, client_event = nil 38 | 39 | while not client_event 40 | client_event = client\service(10) or client_event 41 | host_event = host\service(10) or host_event 42 | 43 | assert.same "receive", client_event.type 44 | assert.same "Hello World", client_event.data 45 | 46 | it "should error on destroyed host", -> 47 | client = enet.host_create() 48 | client\destroy! 49 | assert.has_error -> 50 | client\connect "localhost:7889" 51 | --------------------------------------------------------------------------------