Crate gns

Source
Expand description

§Rust wrapper for Valve GameNetworkingSockets.

Provides an abstraction over the low-level library. There are multiple advantage to use this abstraction:

  • Type safety: most of the low-level structures are wrapped and we leverage the type system to restrict the operations such that they are all safe.
  • High level: the library abstract most of the structure in such a way that you don’t have to deal with the low-level FFI plumbering required. The API is idiomatic, pure Rust.

§Example

use gns::{GnsGlobal, GnsSocket, IsCreated};
use std::net::Ipv6Addr;
use std::time::Duration;

// **uwrap** must be banned in production, we use it here to extract the most relevant part of the library.

// Initial the global networking state. Note that this instance must be unique per-process.
let gns_global = GnsGlobal::get().unwrap();

// Create a new [`GnsSocket`], the index type [`IsCreated`] is used to determine the state of the socket.
// The [`GnsSocket::new`] function is only available for the [`IsCreated`] state. This is the initial state of the socket.
let gns_socket = GnsSocket::<IsCreated>::new(gns_global.clone());

// Choose your own port
let port = 9001;

// We now do a transition from [`IsCreated`] to the [`IsClient`] state. The [`GnsSocket::connect`] operation does this transition for us.
// Since we are now using a client socket, we have access to a different set of operations.
let client = gns_socket.connect(Ipv6Addr::LOCALHOST.into(), port).unwrap();

// Now that we initiated a connection, there is three operation we must loop over:
// - polling for new messages
// - polling for connection status change
// - polling for callbacks (low-level callbacks required by the underlying library).
// Important to know, regardless of the type of socket, whether it is in [`IsClient`] or [`IsServer`] state, theses three operations are the same.
// The only difference is that polling for messages and status on the client only act on the client connection, while polling for messages and status on a server yield event for all connected clients.

loop {
  // Run the low-level callbacks.
  gns_global.poll_callbacks();

  // Receive a maximum of 100 messages on the client connection.
  // For each messages, print it's payload.
  let _actual_nb_of_messages_processed = client.poll_messages::<100>(|message| {
    println!("{}", core::str::from_utf8(message.payload()).unwrap());
  });

  // Don't do anything with events.
  // One would check the event for connection status, i.e. doing something when we are connected/disconnected from the server.
  let _actual_nb_of_events_processed = client.poll_event::<100>(|_| {
  });

  // Sleep a little bit.
  std::thread::sleep(Duration::from_millis(10))
}

§Note

Every instance of of GnsSocket has a dangling Weak<SegQueue<GnsConnectionEvent>> pointer associated due to how polling works. Polling is done globally and may buffer events for already destructed GnsSocket. We use a weak pointer as user data on client/server connections to push events on GnsGlobal::poll_callbacks, see the queue field of IsClient and IsServer. For simplicity (we may fix this later), every GnsSocket has it’s own queue and we accept this pretty small memory leak. If you only ever create one instance for the lifetime of your application, this will have no effect.

Re-exports§

pub use gns_sys as sys;

Structs§

GnsConnection
GnsConnectionEvent
GnsConnectionInfo
GnsConnectionRealTimeLaneStatus
GnsConnectionRealTimeStatus
GnsError
Wrapper around steam [sys::EResult]. The library ensure that the wrapped value is not [sys::EResult::k_EResultOK].
GnsGlobal
Wraps the initialization/destruction of the low-level GameNetworkingSockets and associated singletons.
GnsListenSocket
Opaque wrapper around the low-level [sys::HSteamListenSocket].
GnsNetworkMessage
Wrapper around the low-level equivalent. This type is used to implements a more type-safe version of messages.
GnsPollGroup
Opaque wrapper around the low-level [sys::HSteamNetPollGroup].
GnsSocket
GnsSocket is the most important structure of this library. This structure is used to create client (GnsSocket<IsClient>) and server (GnsSocket<IsServer>) sockets via the GnsSocket::connect and GnsSocket::listen functions. The drop implementation make sure that everything related to this structure is correctly freed, except the GnsGlobal instance and the user has a strong guarantee that all the available operations over the socket are safe.
GnsUtils
IsClient
State of a GnsSocket that has been determined to be a client, usually via the GnsSocket::connect call. In this state, the socket hold the data required to receive and send messages.
IsCreated
Initial state of a GnsSocket. This state represent a socket that has not been used as a Server or Client implementation. Consequently, the state is empty.
IsServer
State of a GnsSocket that has been determined to be a server, usually via the GnsSocket::listen call. In this state, the socket hold the data required to accept connections and poll them for messages.
ToReceive
ToSend

Enums§

GnsConfig
The configuration value used to define configure global variables in GnsUtils::set_global_config_value

Traits§

IsReady
Common functions available for any GnsSocket state that is implementing it. Regardless of being a client or server, a ready socket will allow us to query for connection events as well as receive messages.
MayDrop

Type Aliases§

GnsLane
A lane is represented by a Priority and a Weight
GnsLaneId
A lane Id.
GnsMessageNumber
A network message number. Simple alias for documentation.
GnsResult
Outcome of many functions from this library, basic type alias with steam [sys::EResult] as error. If the result is [sys::EResult::k_EResultOK], the value can safely be wrapped, otherwise we return the error.
Priority
Lane priority
Weight
Lane weight