ISteamNetworkingSockets¶
This allows using the Steam Networking Sockets API. It’s a protocol on top of UDP without the caveats of TCP (very slow) and optimized for the demands of video game traffic. Supports both reliable and unreliable traffic.
There are two ways to use it.
Classic Bind/connect to sockets and send/receive data over UDP. You are going to use IPs and ports as expected.
P2P using the the steam network. In this case, you are going to connect directly to other steam users. Valve will handle all verification/authentication/encryption/NAT-punching issues for you and carry the traffic through the steam network.
Read more at https://partner.steamgames.com/doc/features/multiplayer/steamdatagramrelay.
List of Functions¶
List of Callbacks¶
Function Reference¶
- networkingSockets.createListenSocketP2P()¶
- Parameters:
virtualPort (integer) – The virtual port to bind to. Use 0 if you don’t care about any particular port. Use values below 1000.
options (table) – Table with key value pairs to override default SteamNetworkingSockets options. See the config section at the bottom for possible values
- Returns:
(userdata) a socket object
- SteamWorks:
Accept P2P connections through the Steam Network. If you want to change any networking settings, you need pass these options on creation. Implement the
networkingSockets.onAuthenticationStatus()
callback to be notified about connection events.
Example:
local socket = Steam.networkingSockets.createListenSocketP2P(0)
- networkingSockets.connectP2P()¶
- Parameters:
steamID (uint64) – The steamID of the remote host to connect to. Get it from
Steam.user.getSteamID()
or convert a string withSteam.extra.parseUint64()
.virtualPort (integer) – The virtual port to connect to on the server.
options (table) – Table with key value pairs to override default SteamNetworkingSockets options. See the config section at the bottom for possible values
- Returns:
(int) a connection id
- SteamWorks:
Accept P2P connections through the Steam Network. If you want to change any networking settings, you need pass these options on creation. Implement the
networkingSockets.onAuthenticationStatus()
callback to be notified about connection events.
Example:
local socket = Steam.networkingSockets.connectP2P(Steam.extra.parseUint64("someID"), 0)
- networkingSockets.createListenSocketIP()¶
- Parameters:
localAdress (string) – The adress to bind to in string format. E.g. to bind to localhost on port 55556, use “[::]:55556”.
options (table) – Table with key value pairs to override default SteamNetworkingSockets options. See the config section at the bottom for possible values
- Returns:
(userdata) a socket object
- SteamWorks:
Bind to an adress and accept connections as a “server”. If you want to change any networking settings, you need pass these options on creation. Implement the callback TODO to be notified about connection events.
Example:
local socket = Steam.networkingSockets.createListenSocketIP("[::]:55556")
- networkingSockets.connectByIPAddress()¶
- Parameters:
localAdress (string) – The adress to connect to in string format. E.g. to bind to localhost on port 55556, use “127.0.0.1:55556”.
options (table) – Table with key value pairs to override default SteamNetworkingSockets options. See the config section at the bottom for possible values
- Returns:
(int) a connection id
- SteamWorks:
Connect to a given adress as a “client”. If you want to change any networking settings, you need pass these options on creation. Implement the callback
networkingSockets.onConnectionChanged()
to be notified about connection events.
Example:
local connection = Steam.networkingSockets.connectByIPAddress("127.0.0.1:55556")
- networkingSockets.acceptConnection()¶
- Parameters:
connection (int) – The id of the connection to accept
- Returns:
(string) result with the possible values
OK | InvalidParam | InvalidState
- SteamWorks:
Accept a connection that was received via the callback
networkingSockets.onConnectionChanged()
. This will move the connection from theConnecting
state to theConnected
state.Return values explanation
OK - The connection was accepted
InvalidParam - The connection id was invalid
InvalidState - The connection was not in the
Connecting
state
Example:
local result = Steam.networkingSockets.acceptConnection(connection)
- networkingSockets.closeConnection()¶
- Parameters:
connection (int) – The id of the connection to close
enableLinger (boolean) – If true, attempts to finish delivering any outbound messages. If you set it to true, you need to make sure that you keep calling the Steam callback long enough for this to actually happen.
- Returns:
nothing
- SteamWorks:
Disconnects from the remote host and invalidates the connection handle. Any unread data on the connection is discarded.
Example:
Steam.networkingSockets.closeConnection(connection)
- networkingSockets.closeListenSocket()¶
- Parameters:
socket (userdata) – The socket object you received when opening a socket with
networkingSockets.createListenSocketIP()
ornetworkingSockets.connectByIPAddress()
- Returns:
nothing
- SteamWorks:
Destroy the given listen socket. All the connections that were accepted on the listen socket are closed ungracefully. You should call this before closing your application for any sockets you created.
Example:
Steam.networkingSockets.closeListenSocket(socket)
- networkingSockets.sendMessageToConnection()¶
- Parameters:
connection (int) – The id of the connection to send a message to
message (string) – The message to send. Can be any length (up to configured SendBufferSize), splitting will be handled by the library
flag (int) – A flag to specify how the message should be sent. See below for explanation
- Returns:
(string) result with the possible values
OK | InvalidParam | InvalidState | NoConnection | Ignored | LimitExceeded
- SteamWorks:
Send a string message to the specified connection. Delivery method depends on the flag you pass. See <https://partner.steamgames.com/doc/api/steamnetworkingtypes> in the section Flags used for message sending for a detailed explanation
Steam.networkingSockets.flags.Send_Reliable - Message will be sent reliably (resend if necessary until acknowledged) and in order with other reliable messages
Steam.networkingSockets.flags.Send_ReliableNoNagle - Reliable without Nagle algorithm (don’t wait a short while for more messages before sending). As a rule of thumb, don’t use this unless you’re sure you know what you’re doing. Use
networkingSockets.flushMessagesOnConnection()
insteadSteam.networkingSockets.flags.Send_Unreliable - Message will be sent once only, might get lost on the way and arrive in any order
Steam.networkingSockets.flags.Send_UnreliableNoNagle - Unreliable without Nagle algorithm
Steam.networkingSockets.flags.Send_UnreliableNoDelay - Send unreliable and only if the message can be sent right now. If there is any delay in sending the message (bottleneck, network hiccup, …) this message will be dropped
Return values explanation
OK - The message was sent
InvalidParam - The connection id was invalid
InvalidState - The connection was not in the
Connected
stateNoConnection - The connection has ended
Ignored - The message was ignored because you used
Send_UnreliableNoDelay
and it wasn’t possible to send the message right nowLimitExceeded - The message was too large to send and or there are too many outgoing messages crowding the send buffer
Example:
local result = Steam.networkingSockets.sendMessageToConnection(connection, "Ping", Steam.networkingSockets.flags.Reliable)
- networkingSockets.sendMessages()¶
- Parameters:
n (int) – The number of messages you are going to send
messages (table) –
A number indexed table with all messages you want to send. Indices from 1..n need to exist. Each message is a table with the following keys:
conn - The id of the connection to send a message to
msg - The message to send. Can be any length (up to configured SendBufferSize), splitting will be handled by the library
flag - A flag to specify how the message should be sent. See
networkingSockets.sendMessageToConnection()
for details
- Returns:
(table) Result table with
true
orfalse
for each message, indexed 1..n, e.g.{ 1 = true, 2 = false }
- SteamWorks:
This is a more efficient way to send out messages. E.g. you could gather all messages generated in an update cycle and then send them out all in one go using this method instead of dispatching them individually with
networkingSockets.sendMessageToConnection()
.
Example:
local messages = {}
messages[1] = { conn = connection1, msg = "Hello!", flag = Steam.networkingSockets.flags.Send_Reliable }
messages[2] = { conn = connection2, msg = "World", flag = Steam.networkingSockets.flags.Send_Reliable }
local result = Steam.networkingSockets.sendMessages(#messages, messages)
- networkingSockets.flushMessagesOnConnection()¶
- Parameters:
connection (int) – The id of the connection to flush messages for
- Returns:
(string) The result. See the return value of
networkingSockets.sendMessageToConnection()
for a detailed description.- SteamWorks:
Flush out all messages left in the send buffer of the given connection and don’t wait for the nagle timer. You only ever want to do this at the end of your update cycle if you know that you are not going to send any more messages for a while. Calling this may improve or degrade your networking performance depending on your use case.
Example:
local result = Steam.networkingSockets.flushMessagesOnConnection()
- networkingSockets.receiveMessagesOnConnection()¶
- Parameters:
connection (int) – The id of the connection to receive messages from
- Returns:
(int, table) Returns two parameters
n - The number of messages received.
-1
if the passed poll group id is invalid (the table is nil in this case).messages - A 1..n index table of messages. Reliable messages are in order in relation to each other. Unreliable messages might be in any order inside the table
- SteamWorks:
Receive all the messages that are waiting on the given connection up to 32. Call this repeatedly until
n < 32
A result table might look like this:
{ 1 = "Some message", 2 = "Another message", 3 = "Yet another message" }
Iterate with ipairs to retain the order any reliable messages were received in.
Example:
local n, messages = Steam.networkingSockets.receiveMessagesOnConnection(connection)
- networkingSockets.initAuthentication()¶
- Returns:
(int) The possible values are same as
data.status
innetworkingSockets.onAuthenticationStatus()
- SteamWorks:
Indicate our desire to be ready participate in authenticated communications.
Example:
local result = Steam.networkingSockets.initAuthentication()
- networkingSockets.getAuthenticationStatus()¶
- Returns:
(int, string)
status: The possible values are same as
data.status
innetworkingSockets.onAuthenticationStatus()
msg: A human readable message for the current status
- SteamWorks:
Get the curren status of authentication
Example:
local result, msg = Steam.networkingSockets.getAuthenticationStatus()
- networkingSockets.getConnectionInfo()¶
- Parameters:
connection (int) – The connection to get info about
- Returns:
(code, info)
code See the return values of
networkingSockets.connectByIPAddress()
for code meanings.info Connection detail string
- SteamWorks:
Get basic information about the status of a connection.
Example:
local code, info = Steam.networkingSockets.getConnectionInfo(connection)
- networkingSockets.createPollGroup()¶
- Returns:
(int) id of the poll group
- SteamWorks:
Create a new poll group. You can use this to receive messages from multiple connections at once. You need to delete this if you don’t need it anymore with
networkingSockets.destroyPollGroup()
.
Example:
local pollGroup = Steam.networkingSockets.createPollGroup()
- networkingSockets.destroyPollGroup()¶
- Parameters:
pollGroup (int) – The poll group to destroy
- Returns:
(boolean)
true
if it was successful- SteamWorks:
Destroy a poll group. Do this before you shut down.
Example:
local success = Steam.networkingSockets.destroyPollGroup(pollGroup)
- networkingSockets.setConnectionPollGroup()¶
- Parameters:
connection (int) – The connection to add to the poll group
pollGroup (int) – The poll group
- Returns:
(boolean)
true
if it was successful- SteamWorks:
Assign a connection to a poll group. After that you can poll this connections messages through the poll group with
networkingSockets.receiveMessagesOnPollGroup()
.
Example:
local success = Steam.networkingSockets.setConnectionPollGroup(connection, pollGroup)
- networkingSockets.receiveMessagesOnPollGroup()¶
- Parameters:
pollGroup (int) – The poll group to receive messages from
- Returns:
(int, table) Returns two parameters
n - The number of messages received.
-1
if the passed poll group id is invalid (the table is nil in this case).messages - A 1..n index table of messages. Reliable messages are in order in relation to each other. Unreliable messages might be in any order inside the table. Each message is a table with a
conn
and amsg
field.
- SteamWorks:
This is the poll group equivalent to
networkingSockets.receiveMessagesOnConnection()
. The advantage of this version is that you can just get all messages to any connection assigned. If you expect larger number of connections, it’s much more efficient to create one or more poll groups and just poll the group instead of having to check messages for every connection individually.Receive all the messages that are waiting on the given poll group up to 32. Call this repeatedly until
n < 32
A result table might look like this:
{ 1 = { conn = 5235, msg = "A message" }, 2 = { conn = 5235, msg = "Another message" }, 3 = { conn = 5678, msg = "Yet another message" } }
Iterate with ipairs to retain the order any reliable messages were received in.
Example:
local n, messages = Steam.networkingSockets.receiveMessagesOnPollGroup(pollGroup)
Callbacks Reference¶
Warning
Remember callbacks are functions that you should override in order to receive the events, and not call directly.
Also, you must constantly call Steam.runCallbacks()
(preferably in your game loop) in order for your callbacks to be called.
- networkingSockets.onAuthenticationStatus(data)¶
- Parameters:
data (table) –
Basic information about the steam authentication state
data.status (string) a unique id representing this connection
data.debugMsg (string) Detailed human readable information in case of problems
- Returns:
nothing
- SteamWorks:
Posted whenever the authentication status with the Steam back-end changes. Possible values for data.status are
-102 (CannotTry): A dependent resource is missing, so this service is unavailable (e.g. we cannot talk to routers because Internet is down or we don’t have the network config).
-101 (Availability_Failed): We have tried for enough time that we would expect to have been successful by now. We have never been successful.
-100 (Availability_Previously): We tried and were successful at one time, but now it looks like we have a problem.
-10 (Retrying): We previously failed and are currently retrying.
1 (NeverTried): We don’t know because we haven’t ever checked/tried.
2 (Waiting): We’re waiting on a dependent resource to be acquired (e.g. we cannot obtain a cert until we are logged into Steam. We cannot measure latency to relays until we have the network config).
3 (Attempting): We’re actively trying now, but are not yet successful.
100 (Current): Resource is online/available.
Example:
function Steam.networkingSockets.onAuthenticationStatus(data)
print ('Authentication status has changed', data.status, data.debugMsg)
end
- networkingSockets.onConnectionChanged(data)¶
- Parameters:
data (table) –
Basic information about the changing connection
data.connection (string) a unique id representing this connection
data.state (string) the state this connection is now in
data.state_old (int) the previous state this connection was in
data.endReason (int) end reason error code
data.debug (string) humand readable debug information string
- Returns:
nothing
- SteamWorks:
- Posted whenever the state of a connection changes. For example
a client attempts a new connection
a server receives a new connection
a connection is established successfully (client or server)
a connection is closed (client or server)
- Possible values for data.state are:
None
Connecting
FindingRoute
Connected
ClosedByPeer
ProblemDetectedLocally
Example:
function Steam.networkingSockets.onConnectionChanged(data)
print ('Connection changed', data.connection, data.state, data.state_old, data.endReason, data.debug)
end
- networkingSockets.getIdentity()¶
- Returns:
(uint64) The SteamID of the current user/server or
nil
if not available (should only happen with a server that is not logged in yet).- SteamWorks:
Get the current identity. Will either return the SteamID or null. Useful mostly if you want to write code that works identically on server and client side.
Example:
local steamID = Steam.networkingSockets.getConnectionInfo(connection)
List of Config Options¶
The following network options can be passed as a table when initiating connections. Example:
{ Unencrypted = 2, IP_AllowWithoutAuth = 1, NagleTime = 0}
Be careful with the values and only change things if you know what you are doing. All values are integers.
TimeoutInitial: Timeout value (in ms) to use when first connecting
TimeoutConnected: Timeout value (in ms) to use after connection is established
SendBufferSize: Upper limit of buffered pending bytes to be sent, if this is reached SendMessage will return k_EResultLimitExceeded Default is 512k (524288 bytes)
SendRateMin: Minimum/maximum send rate clamp. Default is 0 (no-limit). This value will control the min/max allowed sending rate that bandwidth estimation is allowed to reach.
SendRateMax: See SendRateMin
NagleTime: Nagle time, in microseconds (how long to wait with send small packets to facilitate grouping). Default is 5000us (5ms)
IP_AllowWithoutAuth: Don’t automatically fail IP connections that don’t have strong auth.
On clients, this means we will attempt the connection even if we don’t know our identity or can’t get a cert.
On the server, it means that we won’t automatically reject a connection due to a failure to authenticate. (You can examine the incoming connection and decide whether to accept it.) This is a dev configuration value, and you should not let users modify it in production.
MTU_PacketSize: Do not send UDP packets with a payload of larger than N bytes. If you set this, k_ESteamNetworkingConfig_MTU_DataSize is automatically adjusted
Unencrypted: Allow unencrypted (and unauthenticated) communication.
0: Not allowed (the default)
1: Allowed, but prefer encrypted
2: Allowed, and preferred
3: Required. (Fail the connection if the peer requires encryption.)
This is a dev configuration value, since its purpose is to disable encryption. You should not let users modify it in production. (But note that it requires the peer to also modify their value in order for encryption to be disabled.)
SymmetricConnect: Set this to 1 on outbound connections and listen sockets, to enable “symmetric connect mode”, which is useful in the following common peer-to-peer use case: The two peers are “equal” to each other. (Neither is clearly the “client” or “server”.)
Either peer may initiate the connection, and indeed they may do this at the same time
The peers only desire a single connection to each other, and if both peers initiate connections simultaneously, a protocol is needed for them to resolve the conflict, so that we end up with a single connection.
LocalVirtualPort: For connection types that use “virtual ports”, this can be used to assign a local virtual port The local port is only relevant for symmetric connections, when determining if two connections “match.” In this case, if you need the local and remote port to differ, you can set this value.
Examples¶
These examples should run with any version of lua >= 5.1 or Löve
Sockets Client:
local Steam = require 'luasteam'
local connection = nil
Steam.init()
Steam.networkingSockets.initAuthentication()
function Steam.networkingSockets.onConnectionChanged(data)
print ('Connection changed', data.connection, data.state, data.state_old, data.endReason, data.debug)
end
function Steam.networkingSockets.onAuthenticationStatus(data)
if data.status == 100 and not connection then
connection = Steam.networkingSockets.connectByIPAddress("127.0.0.1:55556")
end
end
local run = true
local counter = 0
local msg_rec = false
while run do
Steam.runCallbacks()
if connection then
local n, messages = Steam.networkingSockets.receiveMessagesOnConnection(connection)
if n > 0 then
for j,m in ipairs(messages) do
print("received message", j, m, "from connection", connection, type(j))
Steam.networkingSockets.sendMessageToConnection(connection, "Hello server! This is the client! Thank you!", Steam.networkingSockets.flags.Send_Reliable)
msg_rec = true
end
end
end
if msg_rec then
counter = counter + 1
if counter > 10000 then -- you might have to increase this if the client shuts down faster than the message can be sent
run = false
end
end
end
if connection then
Steam.networkingSockets.closeConnection(connection)
end
Steam.shutdown()
Sockets Server:
local Steam = require 'luasteam'
local connections = {}
local server = nil
Steam.init()
Steam.networkingSockets.initAuthentication()
function Steam.networkingSockets.onConnectionChanged(data)
print ('Connection changed', data.connection, data.state, data.state_old, data.endReason, data.debug)
if data.state == "Connecting" then
Steam.networkingSockets.acceptConnection(data.connection)
end
if data.state == "Connected" then
connections[data.connection] = true
Steam.networkingSockets.sendMessageToConnection(data.connection, "Hello client! This is the server! Welcome!", Steam.networkingSockets.flags.Send_Reliable)
end
if data.state == "ClosedByPeer" or data.state == "ProblemDetectedLocally" then
print(data.connection, data.state, data.endReason, data.debug)
Steam.networkingSockets.closeConnection(data.connection)
connections[data.connection] = nil
end
end
function Steam.networkingSockets.onAuthenticationStatus(data)
if data.status == 100 and not connection then
server = Steam.networkingSockets.createListenSocketIP("[::]:55556")
print("Listening on port 55556")
end
end
local run = true
while run do
Steam.runCallbacks()
for conn,_ in pairs(connections) do
local n, messages = Steam.networkingSockets.receiveMessagesOnConnection(conn)
if n > 0 then
for j,m in ipairs(messages) do
print("received message", j, m, "from connection", conn, type(j))
run = false
end
end
end
end
for conn,_ in pairs(connections) do
Steam.networkingSockets.closeConnection(conn)
end
Steam.shutdown()