ffi-enet 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +35 -0
- data/Rakefile +14 -0
- data/examples/echo.rb +60 -0
- data/lib/ffi-enet/renet/client.rb +59 -0
- data/lib/ffi-enet/renet/connection.rb +127 -0
- data/lib/ffi-enet/renet/server.rb +127 -0
- data/lib/ffi-enet/renet.rb +34 -0
- data/lib/ffi-enet/version.rb +5 -0
- data/lib/ffi-enet.rb +438 -0
- data/lib64/libenet.dll +0 -0
- data/lib64/libenet.so +0 -0
- data/sig/enet.rbs +4 -0
- metadata +78 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 3955bec45c5e95da5d98bd7517fe6957bcca3c2647dcfa3b3c44ecc53992f615
         | 
| 4 | 
            +
              data.tar.gz: 902b7cc535d3fe42fce86e50623132a20f1f3ea353f6a3abb98d36ac4cc4d8cd
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 77091b2a373cf343fa83a31dcf8fd9780335de0353626d812265e7135caa129976ce59594e64729813bbf328a7a98a71c3ab9f6d9f17958a15853cea8313ae54
         | 
| 7 | 
            +
              data.tar.gz: feb77626faae4c063ccb95256b6dd73343364f8a628fe042737832d42ce35985205149fb45382f5f8e0b16df83987a56928f01373a9ac7db0a5c28a778c56e15
         | 
    
        data/.standard.yml
    ADDED
    
    
    
        data/CHANGELOG.md
    ADDED
    
    
    
        data/LICENSE.txt
    ADDED
    
    | @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            The MIT License (MIT)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Copyright (c) 2023 Cyberarm
         | 
| 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 DEALINGS IN
         | 
| 21 | 
            +
            THE SOFTWARE.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # FFI-ENet
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            FFI interface to the [enet](http://sauerbraten.org/enet/) networking library.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Also includes a [rENet](https://github.com/Dahrkael/rENet/)-like API for easy usage.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ## Installation
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            Install the gem and add to the application's Gemfile by executing:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            If bundler is not being used to manage dependencies, install the gem by executing:
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## Usage
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            TODO: Write usage instructions here
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ## Development
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            ## Contributing
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            Bug reports and pull requests are welcome on GitHub at https://github.com/cyberarm/ffi-enet.
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            ## License
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "bundler/gem_tasks"
         | 
| 4 | 
            +
            require "rake/testtask"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Rake::TestTask.new(:test) do |t|
         | 
| 7 | 
            +
              t.libs << "test"
         | 
| 8 | 
            +
              t.libs << "lib"
         | 
| 9 | 
            +
              t.test_files = FileList["test/**/test_*.rb"]
         | 
| 10 | 
            +
            end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            require "standard/rake"
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            task default: %i[test standard]
         | 
    
        data/examples/echo.rb
    ADDED
    
    | @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            require_relative "../lib/ffi-enet"
         | 
| 2 | 
            +
            require_relative "../lib/ffi-enet/renet"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Thread.abort_on_exception = true
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            thread = Thread.new do
         | 
| 7 | 
            +
              @server = ENet::Server.new(host: "localhost", port: 3000, max_clients: 32, channels: 4, download_bandwidth: 0, upload_bandwidth: 0)
         | 
| 8 | 
            +
              @server.use_compression(true)
         | 
| 9 | 
            +
              # server.use_compression(false)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              def @server.on_connection(client)
         | 
| 12 | 
            +
                puts "[SERVER][ID #{client.id}] connected from #{client.address.host}:#{client.address.port}"
         | 
| 13 | 
            +
                send_packet(client, "Hello World", reliable: true, channel: 1)
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def @server.on_packet_received(client, data, channel)
         | 
| 17 | 
            +
                puts "[SERVER][ID #{client.id}][TOTAL PACKETS: #{client.total_sent_packets}] #{data}"
         | 
| 18 | 
            +
                send_packet(client, data, reliable: true, channel: 1)
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              def @server.on_disconnection(client)
         | 
| 22 | 
            +
                puts "[SERVER][ID #{client.id}] disconnected from #{client.address.host}"
         | 
| 23 | 
            +
                send_packet(client, "Goodbye World", reliable: true, channel: 1)
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              loop do
         | 
| 27 | 
            +
                @server.update(1_000)
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            sleep 0.5
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            connection = ENet::Connection.new(host: "localhost", port: 3000, channels: 4, download_bandwidth: 0, upload_bandwidth: 0)
         | 
| 34 | 
            +
            connection.use_compression(true)
         | 
| 35 | 
            +
            # connection.use_compression(false)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            def connection.on_connection
         | 
| 38 | 
            +
              puts "[CONNECTION] CONNECTED TO SERVER"
         | 
| 39 | 
            +
              send_packet("Hello World!", reliable: true, channel: 0)
         | 
| 40 | 
            +
            end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            def connection.on_packet_received(data, channel)
         | 
| 43 | 
            +
              puts "[CONNECTION][CHANNEL #{channel}]: #{data}"
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              send_packet(data, reliable: true, channel: channel)
         | 
| 46 | 
            +
            end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            def connection.on_disconnection
         | 
| 49 | 
            +
              puts "[CONNECTION] DISCONNECTED FROM SERVER"
         | 
| 50 | 
            +
            end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            connection.connect(5_000)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            while connection.online?
         | 
| 55 | 
            +
              connection.update(1_000)
         | 
| 56 | 
            +
              connection.disconnect(2_000)
         | 
| 57 | 
            +
              @server.shutdown
         | 
| 58 | 
            +
            end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            thread.join
         | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            module ENet
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                Address = Struct.new(:host, :port)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                attr_reader :_peer, :id, :address
         | 
| 6 | 
            +
                attr_reader :last_send_time, :last_receive_time, :last_round_trip_time, :round_trip_time, :packets_lost, :packet_loss,
         | 
| 7 | 
            +
                            :total_sent_packets, :total_sent_data, :total_received_packets, :total_received_data
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def self.enet_host_address_to_ipv4(address)
         | 
| 10 | 
            +
                  str_ptr = FFI::MemoryPointer.new(:char, 256)
         | 
| 11 | 
            +
                  result = LibENet.enet_address_get_host_ip(address, str_ptr, str_ptr.size)
         | 
| 12 | 
            +
                  raise "Failed to get IPv4 address of enet address" unless result.zero?
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  str_ptr.read_string
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def initialize(peer)
         | 
| 18 | 
            +
                  @_peer = peer
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  @id = @_peer[:connect_id]
         | 
| 21 | 
            +
                  @address = Address.new(Client.enet_host_address_to_ipv4(@_peer[:address]), @_peer[:address][:port])
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  @last_send_time = 0
         | 
| 24 | 
            +
                  @last_receive_time = 0
         | 
| 25 | 
            +
                  @last_round_trip_time = 0
         | 
| 26 | 
            +
                  @round_trip_time = 0
         | 
| 27 | 
            +
                  @packets_lost = 0
         | 
| 28 | 
            +
                  @packet_loss = 0
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  @total_sent_packets = 0
         | 
| 31 | 
            +
                  @total_sent_data = 0
         | 
| 32 | 
            +
                  @total_received_packets = 0
         | 
| 33 | 
            +
                  @total_received_data = 0
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  update_stats
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def update_stats
         | 
| 39 | 
            +
                  # enet Peer data
         | 
| 40 | 
            +
                  @last_send_time = @_peer[:last_send_time]
         | 
| 41 | 
            +
                  @last_receive_time = @_peer[:last_send_time]
         | 
| 42 | 
            +
                  @last_round_trip_time = @_peer[:last_round_trip_time]
         | 
| 43 | 
            +
                  @round_trip_time = @_peer[:round_trip_time]
         | 
| 44 | 
            +
                  @packets_lost = @_peer[:packets_lost]
         | 
| 45 | 
            +
                  @packet_loss = @_peer[:packet_loss] # .to_f / LibENet::ENET_PEER_PACKET_LOSS_SCALE
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  # enet Host data
         | 
| 48 | 
            +
                  @total_sent_packets += @_peer[:host][:total_sent_packets]
         | 
| 49 | 
            +
                  @total_sent_data += @_peer[:host][:total_sent_data]
         | 
| 50 | 
            +
                  @total_received_packets += @_peer[:host][:total_received_packets]
         | 
| 51 | 
            +
                  @total_received_data += @_peer[:host][:total_received_data]
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  @_peer[:host][:total_sent_packets] = 0
         | 
| 54 | 
            +
                  @_peer[:host][:total_sent_data] = 0
         | 
| 55 | 
            +
                  @_peer[:host][:total_received_packets] = 0
         | 
| 56 | 
            +
                  @_peer[:host][:total_received_data] = 0
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| @@ -0,0 +1,127 @@ | |
| 1 | 
            +
            module ENet
         | 
| 2 | 
            +
              class Connection
         | 
| 3 | 
            +
                attr_reader :client
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(host:, port:, channels: 8, download_bandwidth: 0, upload_bandwidth: 0)
         | 
| 6 | 
            +
                  @host = host
         | 
| 7 | 
            +
                  @port = port
         | 
| 8 | 
            +
                  @channels = channels
         | 
| 9 | 
            +
                  @download_bandwidth = download_bandwidth
         | 
| 10 | 
            +
                  @upload_bandwidth = upload_bandwidth
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  @enet_event = LibENet::ENetEvent.new
         | 
| 13 | 
            +
                  @online = false
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  ENet.init
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  @_address = LibENet::ENetAddress.new
         | 
| 18 | 
            +
                  if LibENet.enet_address_set_host(@_address, @host) != 0
         | 
| 19 | 
            +
                    raise "Failed to set host"
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                  @_address[:port] = @port
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  @_host = LibENet.enet_host_create(nil, 1, @channels, @download_bandwidth, @upload_bandwidth)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  raise "Failed to create host" if @_host.nil?
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  @client = nil
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def online?
         | 
| 31 | 
            +
                  @online
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def connect(timeout_ms)
         | 
| 35 | 
            +
                  @_connection = LibENet.enet_host_connect(@_host, @_address, @channels, 0)
         | 
| 36 | 
            +
                  raise "Cannot connect to remote host" if @_connection.nil?
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  result = LibENet.enet_host_service(@_host, @enet_event, timeout_ms)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  if result.positive? && @enet_event[:type] == :ENET_EVENT_TYPE_CONNECT
         | 
| 41 | 
            +
                    @online = true
         | 
| 42 | 
            +
                    @client = Client.new(@_connection)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    on_connection
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def disconnect(timeout_ms)
         | 
| 49 | 
            +
                  connected = online?
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  if connected
         | 
| 52 | 
            +
                    LibENet.enet_peer_disconnect(@_connection, 0)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    while (result = LibENet.enet_host_service(@_host, @enet_event, 0)) && result.positive?
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      case @enet_event[:type]
         | 
| 57 | 
            +
                      when :ENET_EVENT_TYPE_RECEIVE
         | 
| 58 | 
            +
                        LibENet.enet_packet_destroy(@enet_event[:packet])
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                      when :ENET_EVENT_TYPE_DISCONNECT
         | 
| 61 | 
            +
                        connected = false
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                      break unless connected
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    LibENet.enet_peer_disconnect_now(@_connection, 0) if connected
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  connected
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def send_packet(data, reliable:, channel:)
         | 
| 74 | 
            +
                  return unless online?
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  packet = LibENet.enet_packet_create(data, data.length, reliable ? 1 : 0)
         | 
| 77 | 
            +
                  LibENet.enet_peer_send(@_connection, channel, packet)
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                def send_queued_packets
         | 
| 81 | 
            +
                  LibENet.enet_host_flush(@_host) if online?
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                def flush
         | 
| 85 | 
            +
                  send_queued_packets
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                def update(timeout_ms)
         | 
| 89 | 
            +
                  result = LibENet.enet_host_service(@_host, @enet_event, timeout_ms)
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  if result.positive?
         | 
| 92 | 
            +
                    case @enet_event[:type]
         | 
| 93 | 
            +
                    when :ENET_EVENT_TYPE_RECEIVE
         | 
| 94 | 
            +
                      data = @enet_event[:packet][:data].read_string(@enet_event[:packet][:length])
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                      on_packet_received(data, @enet_event[:channel_id])
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                      LibENet.enet_packet_destroy(@enet_event[:packet])
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    when :ENET_EVENT_TYPE_DISCONNECT
         | 
| 101 | 
            +
                      @online = false
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                  elsif result.negative?
         | 
| 104 | 
            +
                    warn "Connection: An error occurred: #{result}"
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  @client.update_stats if online?
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                def use_compression(bool)
         | 
| 111 | 
            +
                  if bool
         | 
| 112 | 
            +
                    LibENet.enet_host_compress_with_range_coder(@_host)
         | 
| 113 | 
            +
                  else
         | 
| 114 | 
            +
                    LibENet.enet_host_compress(@_host, nil)
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def on_connection
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                def on_packet_received(data, channel)
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                def on_disconnection
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
            end
         | 
| @@ -0,0 +1,127 @@ | |
| 1 | 
            +
            module ENet
         | 
| 2 | 
            +
              class Server
         | 
| 3 | 
            +
                def self.release(pointer)
         | 
| 4 | 
            +
                end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                def initialize(host:, port:, max_clients: 16, channels: 8, download_bandwidth: 0, upload_bandwidth: 0)
         | 
| 7 | 
            +
                  @host = host
         | 
| 8 | 
            +
                  @port = port
         | 
| 9 | 
            +
                  @max_clients = max_clients
         | 
| 10 | 
            +
                  @channels = channels
         | 
| 11 | 
            +
                  @download_bandwidth = download_bandwidth
         | 
| 12 | 
            +
                  @upload_bandwidth = upload_bandwidth
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  @enet_event = LibENet::ENetEvent.new
         | 
| 15 | 
            +
                  @clients = {}
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  ENet.init
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  @_address = LibENet::ENetAddress.new
         | 
| 20 | 
            +
                  if LibENet.enet_address_set_host(@_address, @host) != 0
         | 
| 21 | 
            +
                    raise "Failed to set host"
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                  @_address[:port] = @port
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  @_host = FFI::AutoPointer.new(LibENet.enet_host_create(@_address, @max_clients, @channels, @download_bandwidth, @upload_bandwidth).to_ptr, Server.method(:release))
         | 
| 26 | 
            +
                  raise "Failed to create server" if @_host.nil?
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def disconnect_client(client)
         | 
| 30 | 
            +
                  LibENet.enet_peer_disconnect(client._peer, 0)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def disconnect_client!(client)
         | 
| 34 | 
            +
                  LibENet.enet_peer_disconnect_now(client._peer, 0)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  on_disconnection(client)
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def send_packet(client, data, reliable:, channel:)
         | 
| 40 | 
            +
                  packet = LibENet.enet_packet_create(data, data.length, reliable ? 1 : 0)
         | 
| 41 | 
            +
                  LibENet.enet_peer_send(client._peer, channel, packet)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  # pp [
         | 
| 44 | 
            +
                  #   client.packets_sent,
         | 
| 45 | 
            +
                  #   client.packets_received,
         | 
| 46 | 
            +
                  #   client.data_sent,
         | 
| 47 | 
            +
                  #   client.data_received,
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  #   client.packets_lost,
         | 
| 50 | 
            +
                  #   client.packet_loss,
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  #   client.round_trip_time,
         | 
| 53 | 
            +
                  #   client.last_round_trip_time,
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  #   client.last_receive_time,
         | 
| 56 | 
            +
                  #   client.last_send_time
         | 
| 57 | 
            +
                  # ]
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def broadcast_packet(data, reliable:, channel:)
         | 
| 61 | 
            +
                  packet = LibENet.enet_packet_create(data, data.length, reliable ? 1 : 0)
         | 
| 62 | 
            +
                  LibENet.enet_host_broadcast(@_host, channel, packet)
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def send_queued_packets
         | 
| 66 | 
            +
                  LibENet.enet_host_flush(@_host)
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def flush
         | 
| 70 | 
            +
                  send_queued_packets
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def update(timeout_ms)
         | 
| 74 | 
            +
                  result = LibENet.enet_host_service(@_host, @enet_event, timeout_ms)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  if result.positive?
         | 
| 77 | 
            +
                    case @enet_event[:type]
         | 
| 78 | 
            +
                    when :ENET_EVENT_TYPE_CONNECT
         | 
| 79 | 
            +
                      client = Client.new(@enet_event[:peer])
         | 
| 80 | 
            +
                      @clients[client] = @enet_event[:peer]
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                      on_connection(client)
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    when :ENET_EVENT_TYPE_RECEIVE
         | 
| 85 | 
            +
                      client = @clients.find { |k, peer| peer.to_ptr == @enet_event[:peer].to_ptr }.first
         | 
| 86 | 
            +
                      data = @enet_event[:packet][:data].read_string(@enet_event[:packet][:length])
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                      client.update_stats
         | 
| 89 | 
            +
                      on_packet_received(client, data, @enet_event[:channel_id])
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                      LibENet.enet_packet_destroy(@enet_event[:packet])
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    when :ENET_EVENT_TYPE_DISCONNECT
         | 
| 94 | 
            +
                      # FIXME: This might not work if peer pointer is invalid
         | 
| 95 | 
            +
                      client = @clients.find { |k, peer| peer.to_ptr == @enet_event[:peer].to_ptr }.first
         | 
| 96 | 
            +
                      @clients.delete(client)
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                      on_disconnection(client)
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
                  elsif result.negative?
         | 
| 101 | 
            +
                    warn "Server: An error occurred: #{result}"
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                def shutdown
         | 
| 106 | 
            +
                  LibENet.enet_host_flush(@_host)
         | 
| 107 | 
            +
                  LibENet.enet_host_destroy(@_host)
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                def use_compression(bool)
         | 
| 111 | 
            +
                  if bool
         | 
| 112 | 
            +
                    LibENet.enet_host_compress_with_range_coder(@_host)
         | 
| 113 | 
            +
                  else
         | 
| 114 | 
            +
                    LibENet.enet_host_compress(@_host, nil)
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def on_connection(client)
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                def on_packet_received(client, data, channel)
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                def on_disconnection(client)
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            require_relative "renet/server"
         | 
| 2 | 
            +
            require_relative "renet/connection"
         | 
| 3 | 
            +
            require_relative "renet/client"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module ENet
         | 
| 6 | 
            +
              @@initialized = false
         | 
| 7 | 
            +
              @@at_exit_handler = false
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def self.init
         | 
| 10 | 
            +
                return true if @@initialized
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                return false if LibENet.enet_initialize != 0
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                @@initialized = true
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                unless @@at_exit_handler
         | 
| 17 | 
            +
                  @@at_exit_handler = true
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  at_exit do
         | 
| 20 | 
            +
                    shutdown
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                true
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              def self.shutdown
         | 
| 28 | 
            +
                return unless @@initialized
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                LibENet.enet_deinitialize
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                @@initialized = false
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
    
        data/lib/ffi-enet.rb
    ADDED
    
    | @@ -0,0 +1,438 @@ | |
| 1 | 
            +
            require "ffi"
         | 
| 2 | 
            +
            require_relative "ffi-enet/version"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module LibENet
         | 
| 5 | 
            +
              extend FFI::Library
         | 
| 6 | 
            +
              ffi_lib [
         | 
| 7 | 
            +
                "#{File.expand_path(__dir__)}#{RUBY_PLATFORM.match?(/^x86_64-/) ? "/../lib64" : ""}/libenet.so",
         | 
| 8 | 
            +
                "#{File.expand_path(__dir__)}#{RUBY_PLATFORM.match?(/^x64-/) ? "/../lib64" : ""}/libenet.dll",
         | 
| 9 | 
            +
                "enet"
         | 
| 10 | 
            +
              ]
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              # "Constants"
         | 
| 13 | 
            +
              ENET_PROTOCOL_MINIMUM_MTU = 576
         | 
| 14 | 
            +
              ENET_PROTOCOL_MAXIMUM_MTU = 4096
         | 
| 15 | 
            +
              ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32
         | 
| 16 | 
            +
              ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096
         | 
| 17 | 
            +
              ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536
         | 
| 18 | 
            +
              ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1
         | 
| 19 | 
            +
              ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255
         | 
| 20 | 
            +
              ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF
         | 
| 21 | 
            +
              ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              ENET_BUFFER_MAXIMUM = (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024,
         | 
| 26 | 
            +
                ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024,
         | 
| 27 | 
            +
                ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000,
         | 
| 28 | 
            +
                ENET_HOST_DEFAULT_MTU = 1392,
         | 
| 29 | 
            +
                ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024,
         | 
| 30 | 
            +
                ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024,
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500,
         | 
| 33 | 
            +
                ENET_PEER_DEFAULT_PACKET_THROTTLE = 32,
         | 
| 34 | 
            +
                ENET_PEER_PACKET_THROTTLE_SCALE = 32,
         | 
| 35 | 
            +
                ENET_PEER_PACKET_THROTTLE_COUNTER = 7,
         | 
| 36 | 
            +
                ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2,
         | 
| 37 | 
            +
                ENET_PEER_PACKET_THROTTLE_DECELERATION = 2,
         | 
| 38 | 
            +
                ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000,
         | 
| 39 | 
            +
                ENET_PEER_PACKET_LOSS_SCALE = (1 << 16),
         | 
| 40 | 
            +
                ENET_PEER_PACKET_LOSS_INTERVAL = 10000,
         | 
| 41 | 
            +
                ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024,
         | 
| 42 | 
            +
                ENET_PEER_TIMEOUT_LIMIT = 32,
         | 
| 43 | 
            +
                ENET_PEER_TIMEOUT_MINIMUM = 5000,
         | 
| 44 | 
            +
                ENET_PEER_TIMEOUT_MAXIMUM = 30000,
         | 
| 45 | 
            +
                ENET_PEER_PING_INTERVAL = 500,
         | 
| 46 | 
            +
                ENET_PEER_UNSEQUENCED_WINDOWS = 64,
         | 
| 47 | 
            +
                ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024,
         | 
| 48 | 
            +
                ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32,
         | 
| 49 | 
            +
                ENET_PEER_RELIABLE_WINDOWS = 16,
         | 
| 50 | 
            +
                ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000,
         | 
| 51 | 
            +
                ENET_PEER_FREE_RELIABLE_WINDOWS = 8
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              # Enums
         | 
| 54 | 
            +
              ENetEventType = enum(
         | 
| 55 | 
            +
                :ENET_EVENT_TYPE_NONE,
         | 
| 56 | 
            +
                :ENET_EVENT_TYPE_CONNECT,
         | 
| 57 | 
            +
                :ENET_EVENT_TYPE_DISCONNECT,
         | 
| 58 | 
            +
                :ENET_EVENT_TYPE_RECEIVE
         | 
| 59 | 
            +
              )
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              ENetPeerState = enum(
         | 
| 62 | 
            +
                :ENET_PEER_STATE_DISCONNECTED,
         | 
| 63 | 
            +
                :ENET_PEER_STATE_CONNECTING,
         | 
| 64 | 
            +
                :ENET_PEER_STATE_ACKNOWLEDGING_CONNECT,
         | 
| 65 | 
            +
                :ENET_PEER_STATE_CONNECTION_PENDING,
         | 
| 66 | 
            +
                :ENET_PEER_STATE_CONNECTION_SUCCEEDED,
         | 
| 67 | 
            +
                :ENET_PEER_STATE_CONNECTED,
         | 
| 68 | 
            +
                :ENET_PEER_STATE_DISCONNECT_LATER,
         | 
| 69 | 
            +
                :ENET_PEER_STATE_DISCONNECTING,
         | 
| 70 | 
            +
                :ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT,
         | 
| 71 | 
            +
                :ENET_PEER_STATE_ZOMBIE
         | 
| 72 | 
            +
              )
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              # ENetSocketOption = enum(
         | 
| 75 | 
            +
              #   :ENET_SOCKOPT_NONBLOCK,
         | 
| 76 | 
            +
              #   :ENET_SOCKOPT_BROADCAST,
         | 
| 77 | 
            +
              #   :ENET_SOCKOPT_RCVBUF,
         | 
| 78 | 
            +
              #   :ENET_SOCKOPT_SNDBUF,
         | 
| 79 | 
            +
              #   :ENET_SOCKOPT_REUSEADDR,
         | 
| 80 | 
            +
              #   :ENET_SOCKOPT_RCVTIMEO,
         | 
| 81 | 
            +
              #   :ENET_SOCKOPT_SNDTIMEO,
         | 
| 82 | 
            +
              #   :ENET_SOCKOPT_ERROR,
         | 
| 83 | 
            +
              #   :ENET_SOCKOPT_NODELAY
         | 
| 84 | 
            +
              # )
         | 
| 85 | 
            +
             | 
| 86 | 
            +
              # ENetSocketType = enum(
         | 
| 87 | 
            +
              #   :ENET_SOCKET_TYPE_STREAM,
         | 
| 88 | 
            +
              #   :ENET_SOCKET_TYPE_DATAGRAM
         | 
| 89 | 
            +
              # )
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              # Structs
         | 
| 92 | 
            +
              class ENetAddress < FFI::Struct
         | 
| 93 | 
            +
                layout :host, :uint32,
         | 
| 94 | 
            +
                  :port, :ushort
         | 
| 95 | 
            +
              end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
              class ENetPacket < FFI::Struct
         | 
| 98 | 
            +
                layout :_ref_count, :size_t,
         | 
| 99 | 
            +
                  :flags, :uint32,
         | 
| 100 | 
            +
                  :data, :pointer,
         | 
| 101 | 
            +
                  :length, :short,
         | 
| 102 | 
            +
                  :callback, :pointer,
         | 
| 103 | 
            +
                  :user_data, :pointer
         | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
              # Why on earth would you do this? They're the same types...
         | 
| 107 | 
            +
              if FFI::Platform.windows?
         | 
| 108 | 
            +
                # Windows O_o
         | 
| 109 | 
            +
                class ENetBuffer < FFI::Struct
         | 
| 110 | 
            +
                  layout :length, :size_t,
         | 
| 111 | 
            +
                    :data, :pointer
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
              else
         | 
| 114 | 
            +
                # Unix o_O
         | 
| 115 | 
            +
                class ENetBuffer < FFI::Struct
         | 
| 116 | 
            +
                  layout :data, :pointer,
         | 
| 117 | 
            +
                    :length, :size_t
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
              end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
              class ENetCompressor < FFI::Struct
         | 
| 122 | 
            +
                layout :context, :pointer,
         | 
| 123 | 
            +
                  :compress, :pointer, # FIXME: http://sauerbraten.org/enet/structENetCompressor.html
         | 
| 124 | 
            +
                  :decompress, :pointer, # FIXME
         | 
| 125 | 
            +
                  :destroy, :pointer  # FIXME
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              class ENetListNode < FFI::Struct
         | 
| 129 | 
            +
                layout :nxt, ENetListNode.by_ref,
         | 
| 130 | 
            +
                  :previous, ENetListNode.by_ref
         | 
| 131 | 
            +
              end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
              class ENetList < FFI::Struct
         | 
| 134 | 
            +
                layout :sentinel, ENetListNode
         | 
| 135 | 
            +
              end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
              class ENetProtocolHeader < FFI::Struct
         | 
| 138 | 
            +
                layout :peer_id, :ushort,
         | 
| 139 | 
            +
                  :sent_time, :ushort
         | 
| 140 | 
            +
              end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
              class ENetProtocolCommandHeader < FFI::Struct
         | 
| 143 | 
            +
                layout :command, :uchar,
         | 
| 144 | 
            +
                  :channel_id, :uchar,
         | 
| 145 | 
            +
                  :reliable_sequence_number, :ushort
         | 
| 146 | 
            +
              end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
              class ENetProtocolAcknowledge < FFI::Struct
         | 
| 149 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 150 | 
            +
                  :received_reliable_sequence_number, :ushort,
         | 
| 151 | 
            +
                  :received_sent_time, :ushort
         | 
| 152 | 
            +
              end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
              class ENetProtocolConnect < FFI::Struct
         | 
| 155 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 156 | 
            +
                  :outgoingPeerID, :ushort,
         | 
| 157 | 
            +
                  :incomingSessionID, :uchar,
         | 
| 158 | 
            +
                  :outgoingSessionID, :uchar,
         | 
| 159 | 
            +
                  :mtu, :uint32,
         | 
| 160 | 
            +
                  :windowSize, :uint32,
         | 
| 161 | 
            +
                  :channelCount, :uint32,
         | 
| 162 | 
            +
                  :incomingBandwidth, :uint32,
         | 
| 163 | 
            +
                  :outgoingBandwidth, :uint32,
         | 
| 164 | 
            +
                  :packetThrottleInterval, :uint32,
         | 
| 165 | 
            +
                  :packetThrottleAcceleration, :uint32,
         | 
| 166 | 
            +
                  :packetThrottleDeceleration, :uint32,
         | 
| 167 | 
            +
                  :connectID, :uint32,
         | 
| 168 | 
            +
                  :data, :uint32
         | 
| 169 | 
            +
              end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
              class ENetProtocolVerifyConnect < FFI::Struct
         | 
| 172 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 173 | 
            +
                  :outgoingPeerID, :ushort,
         | 
| 174 | 
            +
                  :incomingSessionID, :uchar,
         | 
| 175 | 
            +
                  :outgoingSessionID, :uchar,
         | 
| 176 | 
            +
                  :mtu, :uint32,
         | 
| 177 | 
            +
                  :windowSize, :uint32,
         | 
| 178 | 
            +
                  :channelCount, :uint32,
         | 
| 179 | 
            +
                  :incomingBandwidth, :uint32,
         | 
| 180 | 
            +
                  :outgoingBandwidth, :uint32,
         | 
| 181 | 
            +
                  :packetThrottleInterval, :uint32,
         | 
| 182 | 
            +
                  :packetThrottleAcceleration, :uint32,
         | 
| 183 | 
            +
                  :packetThrottleDeceleration, :uint32,
         | 
| 184 | 
            +
                  :connectID, :uint32
         | 
| 185 | 
            +
              end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
              class ENetProtocolBandwidthLimit < FFI::Struct
         | 
| 188 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 189 | 
            +
                  :incomingBandwidth, :uint32,
         | 
| 190 | 
            +
                  :outgoingBandwidth, :uint32
         | 
| 191 | 
            +
              end
         | 
| 192 | 
            +
             | 
| 193 | 
            +
              class ENetProtocolThrottleConfigure < FFI::Struct
         | 
| 194 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 195 | 
            +
                  :packetThrottleInterval, :uint32,
         | 
| 196 | 
            +
                  :packetThrottleAcceleration, :uint32,
         | 
| 197 | 
            +
                  :packetThrottleDeceleration, :uint32
         | 
| 198 | 
            +
              end
         | 
| 199 | 
            +
             | 
| 200 | 
            +
              class ENetProtocolDisconnect < FFI::Struct
         | 
| 201 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 202 | 
            +
                  :data, :uint32
         | 
| 203 | 
            +
              end
         | 
| 204 | 
            +
             | 
| 205 | 
            +
              class ENetProtocolPing < FFI::Struct
         | 
| 206 | 
            +
                layout :header, ENetProtocolCommandHeader
         | 
| 207 | 
            +
              end
         | 
| 208 | 
            +
             | 
| 209 | 
            +
              class ENetProtocolSendReliable < FFI::Struct
         | 
| 210 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 211 | 
            +
                  :dataLength, :ushort
         | 
| 212 | 
            +
              end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
              class ENetProtocolSendUnreliable < FFI::Struct
         | 
| 215 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 216 | 
            +
                  :unreliableSequenceNumber, :ushort,
         | 
| 217 | 
            +
                  :dataLength, :ushort
         | 
| 218 | 
            +
              end
         | 
| 219 | 
            +
             | 
| 220 | 
            +
              class ENetProtocolSendUnsequenced < FFI::Struct
         | 
| 221 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 222 | 
            +
                  :unsequencedGroup, :ushort,
         | 
| 223 | 
            +
                  :dataLength, :ushort
         | 
| 224 | 
            +
              end
         | 
| 225 | 
            +
             | 
| 226 | 
            +
              class ENetProtocolSendFragment < FFI::Struct
         | 
| 227 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 228 | 
            +
                  :startSequenceNumber, :ushort,
         | 
| 229 | 
            +
                  :dataLength, :ushort,
         | 
| 230 | 
            +
                  :fragmentCount, :uint32,
         | 
| 231 | 
            +
                  :fragmentNumber, :uint32,
         | 
| 232 | 
            +
                  :totalLength, :uint32,
         | 
| 233 | 
            +
                  :fragmentOffset, :uint32
         | 
| 234 | 
            +
              end
         | 
| 235 | 
            +
             | 
| 236 | 
            +
              class ENetProtocol < FFI::Union
         | 
| 237 | 
            +
                layout :header, ENetProtocolCommandHeader,
         | 
| 238 | 
            +
                  :acknowledge, ENetProtocolAcknowledge,
         | 
| 239 | 
            +
                  :connect, ENetProtocolConnect,
         | 
| 240 | 
            +
                  :verifyConnect, ENetProtocolVerifyConnect,
         | 
| 241 | 
            +
                  :disconnect, ENetProtocolDisconnect,
         | 
| 242 | 
            +
                  :ping, ENetProtocolPing,
         | 
| 243 | 
            +
                  :sendReliable, ENetProtocolSendReliable,
         | 
| 244 | 
            +
                  :sendUnreliable, ENetProtocolSendUnreliable,
         | 
| 245 | 
            +
                  :sendUnsequenced, ENetProtocolSendUnsequenced,
         | 
| 246 | 
            +
                  :sendFragment, ENetProtocolSendFragment,
         | 
| 247 | 
            +
                  :bandwidthLimit, ENetProtocolBandwidthLimit,
         | 
| 248 | 
            +
                  :throttleConfigure, ENetProtocolThrottleConfigure
         | 
| 249 | 
            +
              end
         | 
| 250 | 
            +
             | 
| 251 | 
            +
              if FFI::Platform.windows?
         | 
| 252 | 
            +
                typedef(:uint64, :ENetSocket)
         | 
| 253 | 
            +
              else
         | 
| 254 | 
            +
                typedef(:int, :ENetSocket) # FIXME: Test this on Linux
         | 
| 255 | 
            +
              end
         | 
| 256 | 
            +
             | 
| 257 | 
            +
              class ENetHost < FFI::Struct
         | 
| 258 | 
            +
                layout :socket, :ENetSocket,
         | 
| 259 | 
            +
                  :address, ENetAddress,
         | 
| 260 | 
            +
                  :incoming_bandwidth, :uint32,
         | 
| 261 | 
            +
                  :outgoing_bandwidth, :uint32,
         | 
| 262 | 
            +
                  :bandwidth_throttle_epoch, :uint32,
         | 
| 263 | 
            +
                  :mtu, :uint32,
         | 
| 264 | 
            +
                  :random_seed, :uint32,
         | 
| 265 | 
            +
                  :recalculate_bandwidth_limits, :int,
         | 
| 266 | 
            +
                  :peers, :pointer, # Array of ENetPeer
         | 
| 267 | 
            +
                  :peer_count, :size_t,
         | 
| 268 | 
            +
                  :channel_limit, :size_t,
         | 
| 269 | 
            +
                  :service_time, :uint32,
         | 
| 270 | 
            +
                  :dispatch_queue, :pointer, # ENetList
         | 
| 271 | 
            +
                  :total_queued, :uint32,
         | 
| 272 | 
            +
                  :packet_size, :size_t,
         | 
| 273 | 
            +
                  :header_flags, :ushort,
         | 
| 274 | 
            +
                  :commands, [ENetProtocol, ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS],
         | 
| 275 | 
            +
                  :command_count, :size_t,
         | 
| 276 | 
            +
                  :buffers, [ENetBuffer, ENET_BUFFER_MAXIMUM],
         | 
| 277 | 
            +
                  :buffer_count, :size_t,
         | 
| 278 | 
            +
                  :checksum, :pointer, # ENetChecksumCallback
         | 
| 279 | 
            +
                  :compressor, ENetCompressor,
         | 
| 280 | 
            +
                  :packet_data, [:uchar, 2 * ENET_PROTOCOL_MAXIMUM_MTU], # NOTE: This may be wrong size/offset/length... using a 1d array here, instead of [2][ENET_PROTOCOL_MAXIMUM_MTU]; FFI docs don't seem to show how to do a multidimentional array.
         | 
| 281 | 
            +
                  :received_address, ENetAddress,
         | 
| 282 | 
            +
                  :received_data, :string,
         | 
| 283 | 
            +
                  :received_data_length, :size_t,
         | 
| 284 | 
            +
                  :total_sent_data, :uint32,
         | 
| 285 | 
            +
                  :total_sent_packets, :uint32,
         | 
| 286 | 
            +
                  :total_received_data, :uint32,
         | 
| 287 | 
            +
                  :total_received_packets, :uint32,
         | 
| 288 | 
            +
                  :intercept, :pointer, # ENetInterceptCallback
         | 
| 289 | 
            +
                  :connected_peers, :size_t,
         | 
| 290 | 
            +
                  :bandwidth_limited_peers, :size_t,
         | 
| 291 | 
            +
                  :duplicate_peers, :size_t,
         | 
| 292 | 
            +
                  :maximum_packet_size, :size_t,
         | 
| 293 | 
            +
                  :maximum_waiting_data, :size_t
         | 
| 294 | 
            +
              end
         | 
| 295 | 
            +
             | 
| 296 | 
            +
              # FIXME: Layout is wrong due to doxygen alphabetizing fields...
         | 
| 297 | 
            +
              class ENetPeer < FFI::Struct
         | 
| 298 | 
            +
                layout :dispatchList, ENetListNode,
         | 
| 299 | 
            +
                  :host, ENetHost,
         | 
| 300 | 
            +
                  :outgoing_peer_id, :ushort,
         | 
| 301 | 
            +
                  :incoming_peer_id, :ushort,
         | 
| 302 | 
            +
                  :connect_id, :uint32,
         | 
| 303 | 
            +
                  :outgoing_session_id, :uchar,
         | 
| 304 | 
            +
                  :incoming_session_id, :uchar,
         | 
| 305 | 
            +
                  :address, ENetAddress,
         | 
| 306 | 
            +
                  :data, :pointer,
         | 
| 307 | 
            +
                  :state, ENetPeerState,
         | 
| 308 | 
            +
                  :channels, :pointer, # ENetChannel
         | 
| 309 | 
            +
                  :channel_count, :size_t,
         | 
| 310 | 
            +
                  :incoming_bandwidth, :uint32,
         | 
| 311 | 
            +
                  :outgoing_bandwidth, :uint32,
         | 
| 312 | 
            +
                  :incoming_bandwidth_throttle_epoch, :uint32,
         | 
| 313 | 
            +
                  :outgoing_bandwidth_throttle_epoch, :uint32,
         | 
| 314 | 
            +
                  :incoming_data_total, :uint32,
         | 
| 315 | 
            +
                  :outgoing_data_total, :uint32,
         | 
| 316 | 
            +
                  :last_send_time, :uint32,
         | 
| 317 | 
            +
                  :last_receive_time, :uint32,
         | 
| 318 | 
            +
                  :next_timeout, :uint32,
         | 
| 319 | 
            +
                  :earliest_timeout, :uint32,
         | 
| 320 | 
            +
                  :packet_loss_epoch, :uint32,
         | 
| 321 | 
            +
                  :packets_sent, :uint32,
         | 
| 322 | 
            +
                  :packets_lost, :uint32,
         | 
| 323 | 
            +
                  :packet_loss, :uint32, # mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE
         | 
| 324 | 
            +
                  :packet_loss_variance, :uint32,
         | 
| 325 | 
            +
                  :packet_throttle, :uint32,
         | 
| 326 | 
            +
                  :packet_throttle_limit, :uint32,
         | 
| 327 | 
            +
                  :packet_throttle_counter, :uint32,
         | 
| 328 | 
            +
                  :packet_throttle_epoch, :uint32,
         | 
| 329 | 
            +
                  :packet_throttle_acceleration, :uint32,
         | 
| 330 | 
            +
                  :packet_throttle_deceleration, :uint32,
         | 
| 331 | 
            +
                  :packet_throttle_interval, :uint32,
         | 
| 332 | 
            +
                  :ping_interval, :uint32,
         | 
| 333 | 
            +
                  :timeout_limit, :uint32,
         | 
| 334 | 
            +
                  :timeout_minimum, :uint32,
         | 
| 335 | 
            +
                  :timeout_maximum, :uint32,
         | 
| 336 | 
            +
                  :last_round_trip_time, :uint32,
         | 
| 337 | 
            +
                  :lowest_round_trip_time, :uint32,
         | 
| 338 | 
            +
                  :last_round_trip_time_variance, :uint32,
         | 
| 339 | 
            +
                  :highest_round_trip_time_variance, :uint32,
         | 
| 340 | 
            +
                  :round_trip_time, :uint32, # mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement
         | 
| 341 | 
            +
                  :round_trip_time_variance, :uint32,
         | 
| 342 | 
            +
                  :mtu, :uint32,
         | 
| 343 | 
            +
                  :window_size, :uint32,
         | 
| 344 | 
            +
                  :reliable_data_in_transit, :uint32,
         | 
| 345 | 
            +
                  :outgoing_reliable_sequence_number, :ushort,
         | 
| 346 | 
            +
                  :acknowledgements, ENetList,
         | 
| 347 | 
            +
                  :sent_reliable_commands, ENetList,
         | 
| 348 | 
            +
                  :outgoing_send_reliable_commands, ENetList,
         | 
| 349 | 
            +
                  :outgoing_commands, ENetList,
         | 
| 350 | 
            +
                  :dispatched_commands, ENetList,
         | 
| 351 | 
            +
                  :flags, :ushort,
         | 
| 352 | 
            +
                  :reserved, :ushort,
         | 
| 353 | 
            +
                  :incoming_unsequenced_group, :ushort,
         | 
| 354 | 
            +
                  :outgoing_unsequenced_group, :ushort,
         | 
| 355 | 
            +
                  :unsequenced_window, [:uint32, ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32],
         | 
| 356 | 
            +
                  :event_data, :uint32,
         | 
| 357 | 
            +
                  :total_waiting_data, :size_t
         | 
| 358 | 
            +
              end
         | 
| 359 | 
            +
             | 
| 360 | 
            +
              class ENetEvent < FFI::Struct
         | 
| 361 | 
            +
                layout :type, ENetEventType,
         | 
| 362 | 
            +
                  :peer, ENetPeer.by_ref,
         | 
| 363 | 
            +
                  :channel_id, :uchar,
         | 
| 364 | 
            +
                  :data, :uint32,
         | 
| 365 | 
            +
                  :packet, ENetPacket.by_ref
         | 
| 366 | 
            +
              end
         | 
| 367 | 
            +
             | 
| 368 | 
            +
              # Global
         | 
| 369 | 
            +
              attach_function :enet_deinitialize, [], :void
         | 
| 370 | 
            +
              attach_function :enet_initialize, [], :void
         | 
| 371 | 
            +
              attach_function :enet_initialize_with_callbacks, [:uint32, :pointer], :void # FIXME
         | 
| 372 | 
            +
              attach_function :enet_linked_version, [], :int
         | 
| 373 | 
            +
             | 
| 374 | 
            +
              # Address
         | 
| 375 | 
            +
              attach_function :enet_address_get_host, [ENetAddress.by_ref, :pointer, :size_t], :int
         | 
| 376 | 
            +
              attach_function :enet_address_get_host_ip, [ENetAddress.by_ref, :pointer, :size_t], :int
         | 
| 377 | 
            +
              attach_function :enet_address_set_host, [ENetAddress.by_ref, :string], :int
         | 
| 378 | 
            +
              attach_function :enet_address_set_host_ip, [ENetAddress.by_ref, :string], :int
         | 
| 379 | 
            +
             | 
| 380 | 
            +
              # Host
         | 
| 381 | 
            +
              attach_function :enet_host_bandwidth_limit, [ENetHost, :uint32, :uint32], :void
         | 
| 382 | 
            +
              attach_function :enet_host_broadcast, [ENetHost, :uchar, :pointer], :void
         | 
| 383 | 
            +
              attach_function :enet_host_channel_limit, [ENetHost, :size_t], :void
         | 
| 384 | 
            +
              attach_function :enet_host_check_events, [ENetHost, ENetEvent.by_ref], :int
         | 
| 385 | 
            +
              attach_function :enet_host_compress, [ENetHost, ENetCompressor.by_ref], :int
         | 
| 386 | 
            +
              attach_function :enet_host_compress_with_range_coder, [ENetHost], :int
         | 
| 387 | 
            +
              attach_function :enet_host_connect, [ENetHost, ENetAddress.by_ref, :size_t, :uint32], ENetPeer.by_ref
         | 
| 388 | 
            +
              attach_function :enet_host_create, [ENetAddress.by_ref, :size_t, :size_t, :uint32, :uint32], ENetHost
         | 
| 389 | 
            +
              attach_function :enet_host_destroy, [ENetHost], :void
         | 
| 390 | 
            +
              attach_function :enet_host_flush, [ENetHost], :void
         | 
| 391 | 
            +
              attach_function :enet_host_service, [ENetHost, ENetEvent.by_ref, :uint32], :int, blocking: true
         | 
| 392 | 
            +
             | 
| 393 | 
            +
              # Packet
         | 
| 394 | 
            +
              attach_function :enet_crc32, [:pointer, :size_t], :uint32
         | 
| 395 | 
            +
              attach_function :enet_packet_create, [:string, :size_t, :uint32], ENetPacket.by_ref
         | 
| 396 | 
            +
              attach_function :enet_packet_destroy, [ENetPacket.by_ref], :void
         | 
| 397 | 
            +
              attach_function :enet_packet_resize, [ENetHost, :size_t], :int
         | 
| 398 | 
            +
             | 
| 399 | 
            +
              # Peer
         | 
| 400 | 
            +
              attach_function :enet_peer_disconnect, [ENetPeer.by_ref, :uint32], :void
         | 
| 401 | 
            +
              attach_function :enet_peer_disconnect_later, [ENetPeer.by_ref, :uint32], :void
         | 
| 402 | 
            +
              attach_function :enet_peer_disconnect_now, [ENetPeer.by_ref, :uint32], :void
         | 
| 403 | 
            +
              attach_function :enet_peer_ping, [ENetPeer.by_ref], :void
         | 
| 404 | 
            +
              attach_function :enet_peer_ping_interval, [ENetPeer.by_ref, :uint32], :void
         | 
| 405 | 
            +
              attach_function :enet_peer_receive, [ENetPeer.by_ref, :uchar], ENetPacket.by_ref # FIXME
         | 
| 406 | 
            +
              attach_function :enet_peer_reset, [ENetPeer.by_ref], :void
         | 
| 407 | 
            +
              attach_function :enet_peer_send, [ENetPeer.by_ref, :ushort, ENetPacket.by_ref], :int
         | 
| 408 | 
            +
              attach_function :enet_peer_throttle_configure, [ENetPeer.by_ref, :uint32, :uint32, :uint32], :void
         | 
| 409 | 
            +
              attach_function :enet_peer_timeout, [ENetPeer.by_ref, :uint32, :uint32, :uint32], :void
         | 
| 410 | 
            +
             | 
| 411 | 
            +
              # Range Coder
         | 
| 412 | 
            +
              attach_function :enet_range_coder_compress, [:pointer, ENetBuffer.by_ref, :size_t, :size_t, :uchar, :size_t], :size_t
         | 
| 413 | 
            +
              attach_function :enet_range_coder_create, [], :pointer
         | 
| 414 | 
            +
              attach_function :enet_range_coder_decompress, [:pointer, :ushort, :size_t, :ushort, :size_t], :size_t
         | 
| 415 | 
            +
              attach_function :enet_range_coder_destroy, [:pointer], :void
         | 
| 416 | 
            +
             | 
| 417 | 
            +
              # Socket
         | 
| 418 | 
            +
              # typedef(:int, :ENetSocket)
         | 
| 419 | 
            +
             | 
| 420 | 
            +
              # attach_function :enet_socket_accept, [:ENetSocket, ENetAddress.by_ref], :ENetSocket
         | 
| 421 | 
            +
              # attach_function :enet_socket_bind, [:ENetSocket, ENetAddress.by_ref], :int
         | 
| 422 | 
            +
              # attach_function :enet_socket_connect, [:ENetSocket, ENetAddress.by_ref], :int
         | 
| 423 | 
            +
              # attach_function :enet_socket_create, [:uint32], :ENetSocket
         | 
| 424 | 
            +
              # attach_function :enet_socket_destroy, [:ENetSocket], :void
         | 
| 425 | 
            +
              # attach_function :enet_socket_get_address, [:ENetSocket, ENetAddress.by_ref], :int
         | 
| 426 | 
            +
              # attach_function :enet_socket_get_option, [:ENetSocket, ENetSocketOption, :int], :int
         | 
| 427 | 
            +
              # attach_function :enet_socket_listen, [:ENetSocket, :int], :int
         | 
| 428 | 
            +
              # attach_function :enet_socket_receive, [:ENetSocket, ENetAddress.by_ref, ENetBuffer.by_ref, :size_t], :int
         | 
| 429 | 
            +
              # attach_function :enet_socket_send, [:ENetSocket, ENetAddress.by_ref, ENetBuffer.by_ref, :size_t], :int
         | 
| 430 | 
            +
              # attach_function :enet_socket_set_option, [:ENetSocket, ENetSocketOption, :pointer], :int
         | 
| 431 | 
            +
              # attach_function :enet_socket_shutdown, [:ENetSocket, ENetSocketShutdown], :int
         | 
| 432 | 
            +
              # attach_function :enet_socket_wait, [:ENetSocket, :pointer, :uint32], :int
         | 
| 433 | 
            +
              # attach_function :enet_socketset_select, [:ENetSocket, ENetSocketSet.by_ref, ENetSocketSet.by_ref, :uint32], :int
         | 
| 434 | 
            +
             | 
| 435 | 
            +
              # Time
         | 
| 436 | 
            +
              attach_function :enet_time_get, [], :uint32
         | 
| 437 | 
            +
              attach_function :enet_time_set, [:uint32], :void
         | 
| 438 | 
            +
            end
         | 
    
        data/lib64/libenet.dll
    ADDED
    
    | Binary file | 
    
        data/lib64/libenet.so
    ADDED
    
    | Binary file | 
    
        data/sig/enet.rbs
    ADDED
    
    
    
        metadata
    ADDED
    
    | @@ -0,0 +1,78 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: ffi-enet
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Cyberarm
         | 
| 8 | 
            +
            autorequire:
         | 
| 9 | 
            +
            bindir: exe
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2024-03-13 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: ffi
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '1.15'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '1.15'
         | 
| 27 | 
            +
            description: FFI interface to the enet networking library. Includes rENet-like API
         | 
| 28 | 
            +
              to simplify enet usage.
         | 
| 29 | 
            +
            email:
         | 
| 30 | 
            +
            - matthewlikesrobots@gmail.com
         | 
| 31 | 
            +
            executables: []
         | 
| 32 | 
            +
            extensions: []
         | 
| 33 | 
            +
            extra_rdoc_files: []
         | 
| 34 | 
            +
            files:
         | 
| 35 | 
            +
            - ".standard.yml"
         | 
| 36 | 
            +
            - CHANGELOG.md
         | 
| 37 | 
            +
            - LICENSE.txt
         | 
| 38 | 
            +
            - README.md
         | 
| 39 | 
            +
            - Rakefile
         | 
| 40 | 
            +
            - examples/echo.rb
         | 
| 41 | 
            +
            - lib/ffi-enet.rb
         | 
| 42 | 
            +
            - lib/ffi-enet/renet.rb
         | 
| 43 | 
            +
            - lib/ffi-enet/renet/client.rb
         | 
| 44 | 
            +
            - lib/ffi-enet/renet/connection.rb
         | 
| 45 | 
            +
            - lib/ffi-enet/renet/server.rb
         | 
| 46 | 
            +
            - lib/ffi-enet/version.rb
         | 
| 47 | 
            +
            - lib64/libenet.dll
         | 
| 48 | 
            +
            - lib64/libenet.so
         | 
| 49 | 
            +
            - sig/enet.rbs
         | 
| 50 | 
            +
            homepage: https://github.com/cyberarm/ffi-enet
         | 
| 51 | 
            +
            licenses:
         | 
| 52 | 
            +
            - MIT
         | 
| 53 | 
            +
            metadata:
         | 
| 54 | 
            +
              allowed_push_host: https://rubygems.org
         | 
| 55 | 
            +
              homepage_uri: https://github.com/cyberarm/ffi-enet
         | 
| 56 | 
            +
              source_code_uri: https://github.com/cyberarm/ffi-enet
         | 
| 57 | 
            +
              changelog_uri: https://github.com/cyberarm/ffi-enet/blob/master/CHANGELOG.md
         | 
| 58 | 
            +
            post_install_message:
         | 
| 59 | 
            +
            rdoc_options: []
         | 
| 60 | 
            +
            require_paths:
         | 
| 61 | 
            +
            - lib
         | 
| 62 | 
            +
            - lib64
         | 
| 63 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 64 | 
            +
              requirements:
         | 
| 65 | 
            +
              - - ">="
         | 
| 66 | 
            +
                - !ruby/object:Gem::Version
         | 
| 67 | 
            +
                  version: 3.0.0
         | 
| 68 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 69 | 
            +
              requirements:
         | 
| 70 | 
            +
              - - ">="
         | 
| 71 | 
            +
                - !ruby/object:Gem::Version
         | 
| 72 | 
            +
                  version: '0'
         | 
| 73 | 
            +
            requirements: []
         | 
| 74 | 
            +
            rubygems_version: 3.5.6
         | 
| 75 | 
            +
            signing_key:
         | 
| 76 | 
            +
            specification_version: 4
         | 
| 77 | 
            +
            summary: FFI interface to the enet networking library.
         | 
| 78 | 
            +
            test_files: []
         |