Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ napi = { version = "2.16.8", features = ["tokio_rt", "napi6", "serde-json"] }
napi-derive = "2.16.9"
lazy_static = "1"
tokio = { version = "1", features = ["sync", "time"] }
steamworks = { git = "https://github.com/Noxime/steamworks-rs/", rev = "fbb79635b06b4feea8261e5ca3e8ea3ef42facf9", features = ["serde"] }
steamworks = { git = "https://github.com/Noxime/steamworks-rs/", rev = "5fc8ef13d52e82068f031535446d786cf0bd60a8", features = ["serde"] }
serde = "1"
serde_json = "1"

Expand Down
79 changes: 78 additions & 1 deletion client.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export declare function init(appId?: number | undefined | null): void
export declare function init(appId?: number | undefined | null, networking?: boolean | undefined | null): void
export declare function restartAppIfNecessary(appId: number): boolean
export declare function runCallbacks(): void
export interface PlayerSteamId {
Expand Down Expand Up @@ -188,6 +188,83 @@ export declare namespace networking {
export function readP2PPacket(size: number): P2PPacket
export function acceptP2PSession(steamId64: bigint): void
}
export declare namespace networking_messages {
/** The method used to send a packet */
export const enum SendType {
/**
* Send the packet directly over udp.
*
* Can't be larger than 1200 bytes
*/
Unreliable = 0,
/**
* Like `Unreliable` but doesn't buffer packets
* sent before the connection has started.
*/
UnreliableNoDelay = 1,
/**
* Reliable packet sending.
*
* Can't be larger than 1 megabyte.
*/
Reliable = 2,
/**
* Like `Reliable` but applies the nagle
* algorithm to packets being sent
*/
ReliableWithBuffering = 3
}
export function sendMessageToUser(steamId64: bigint, sendType: SendType, data: Buffer, channel?: number | undefined | null): void
export interface Message {
data: Buffer
steamId?: PlayerSteamId
}
export function receiveMessagesOnChannel(channel: number, batchSize?: number | undefined | null): Array<Message>
}
export declare namespace networking_sockets {
export function createListenSocketP2P(localVirtualPort?: number | undefined | null): boolean
export function createListenSocketIp(localVirtualPort?: number | undefined | null): boolean
export function setAmIServer(isServer: boolean): void
export function setAcceptNewP2PRequests(accept: boolean): void
export function connectP2P(steamId64: bigint, remoteVirtualPort: number): boolean
export function processListenP2PEvents(): void
export function processListenIpEvents(): void
export interface P2PPacket {
data: Buffer
steamId: bigint
}
export function receiveP2PMessages(batchSize?: number | undefined | null): Array<P2PPacket>
/** The method used to send a packet */
export const enum SendType {
/**
* Send the packet directly over udp.
*
* Can't be larger than 1200 bytes
*/
Unreliable = 0,
/**
* Like `Unreliable` but doesn't buffer packets
* sent before the connection has started.
*/
UnreliableNoDelay = 1,
/**
* Reliable packet sending.
*
* Can't be larger than 1 megabyte.
*/
Reliable = 2,
/**
* Like `Reliable` but applies the nagle
* algorithm to packets being sent
*/
ReliableWithBuffering = 3
}
export function sendP2PMessage(steamId64: bigint, data: Buffer, sendType: SendType): boolean
}
export declare namespace networking_utils {
export function initRelayNetworkAccess(): void
export function detailedRelayNetworkStatus(): string
}
export declare namespace overlay {
export const enum Dialog {
Friends = 0,
Expand Down
2 changes: 1 addition & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function init(appId?: number): Omit<Client, "init" | "runCallbacks">;
export function init(appId?: number, networking?: boolean): Omit<Client, "init" | "runCallbacks">;
export function restartAppIfNecessary(appId: number): boolean;
export function electronEnableSteamOverlay(disableEachFrameInvalidation?: boolean): void;
export type Client = typeof import("./client.d");
Expand Down
43 changes: 30 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,49 @@ const { platform, arch } = process
/** @typedef {typeof import('./client.d')} Client */
/** @type {Client} */
let nativeBinding = undefined

if (platform === 'win32' && arch === 'x64') {
nativeBinding = require('./dist/win64/steamworksjs.win32-x64-msvc.node')
} else if (platform === 'linux' && arch === 'x64') {
nativeBinding = require('./dist/linux64/steamworksjs.linux-x64-gnu.node')
} else if (platform === 'darwin') {
if (arch === 'x64') {
nativeBinding = require('./dist/osx/steamworksjs.darwin-x64.node')
} else if (arch === 'arm64') {
nativeBinding = require('./dist/osx/steamworksjs.darwin-arm64.node')
if(process.env.IS_TESTING){
if (platform === 'win32' && arch === 'x64') {
nativeBinding = require('./dist/win64/steamworksjs.win32-x64-msvc.node')
} else if (platform === 'linux' && arch === 'x64') {
nativeBinding = require('./dist/linux64/steamworksjs.linux-x64-gnu.node')
} else if (platform === 'darwin') {
if (arch === 'x64') {
nativeBinding = require('./dist/osx/steamworksjs.darwin-x64.node')
} else if (arch === 'arm64') {
nativeBinding = require('./dist/osx/steamworksjs.darwin-arm64.node')
}
} else {
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
}
} else {
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
if (platform === 'win32' && arch === 'x64') {
nativeBinding = require('steamworks.js/dist/win64/steamworksjs.win32-x64-msvc.node')
} else if (platform === 'linux' && arch === 'x64') {
nativeBinding = require('steamworks.js/dist/linux64/steamworksjs.linux-x64-gnu.node')
} else if (platform === 'darwin') {
if (arch === 'x64') {
nativeBinding = require('steamworks.js/dist/osx/steamworksjs.darwin-x64.node')
} else if (arch === 'arm64') {
nativeBinding = require('steamworks.js/dist/osx/steamworksjs.darwin-arm64.node')
}
} else {
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
}
}


let runCallbacksInterval = undefined

/**
* Initialize the steam client or throw an error if it fails
* @param {number} [appId] - App ID of the game to load, if undefined, will search for a steam_appid.txt file
* @param {boolean} [networking] - Enable networking
* @returns {Omit<Client, 'init' | 'runCallbacks'>}
*/
module.exports.init = (appId) => {
module.exports.init = (appId, networking) => {
const { init: internalInit, runCallbacks, restartAppIfNecessary, ...api } = nativeBinding

internalInit(appId)
internalInit(appId, networking)

clearInterval(runCallbacksInterval)
runCallbacksInterval = setInterval(runCallbacks, 1000 / 30)
Expand Down
10 changes: 5 additions & 5 deletions src/api/callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ pub mod callback {
where
C: steamworks::Callback + serde::Serialize,
{
let client = crate::client::get_client();
client.register_callback(move |value: C| {
let value = serde_json::to_value(&value).unwrap();
threadsafe_handler.call(value, ThreadsafeFunctionCallMode::Blocking);
})
let client = crate::client::get_client();
client.register_callback(move |value: C| {
let value = serde_json::to_value(&value).unwrap();
threadsafe_handler.call(value, ThreadsafeFunctionCallMode::Blocking);
})
}
}
3 changes: 3 additions & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pub mod input;
pub mod localplayer;
pub mod matchmaking;
pub mod networking;
pub mod networking_messages;
pub mod networking_sockets;
pub mod networking_utils;
pub mod overlay;
pub mod stats;
pub mod utils;
Expand Down
99 changes: 99 additions & 0 deletions src/api/networking_messages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use napi_derive::napi;
// some credit here goes to https://github.com/apxapob as this was heavily inspired by his work

#[napi]
pub mod networking_messages {
use napi::{
bindgen_prelude::{BigInt, Buffer},
Error
};
use steamworks::{
SteamId,
SteamError,
ClientManager,
networking_types::{
SendFlags,
NetworkingIdentity,
NetworkingMessage,
},
networking_messages::SessionRequest,
};

use crate::api::localplayer::PlayerSteamId;

fn err(steam_err:SteamError) -> Result<(), Error> {
Err(Error::new(
napi::Status::GenericFailure,
steam_err.to_string()
))
}

#[napi]
/// The method used to send a packet
pub enum SendType {
/// Send the packet directly over udp.
///
/// Can't be larger than 1200 bytes
Unreliable,
/// Like `Unreliable` but doesn't buffer packets
/// sent before the connection has started.
UnreliableNoDelay,
/// Reliable packet sending.
///
/// Can't be larger than 1 megabyte.
Reliable,
/// Like `Reliable` but applies the nagle
/// algorithm to packets being sent
ReliableWithBuffering,
}

#[napi]
pub fn send_message_to_user(
steam_id64: BigInt,
send_type: SendType,
data: Buffer,
channel: Option<u32>
) -> Result<(), Error> {
let client = crate::client::get_client();
let steam_id = SteamId::from_raw(steam_id64.get_u64().1);
let identity = NetworkingIdentity::new_steam_id(steam_id);

client.networking_messages().send_message_to_user(
identity,
match send_type {
SendType::Unreliable => SendFlags::UNRELIABLE,
SendType::UnreliableNoDelay => SendFlags::UNRELIABLE_NO_DELAY,
SendType::ReliableWithBuffering => SendFlags::RELIABLE, // nagle is the new default
SendType::Reliable => SendFlags::RELIABLE_NO_NAGLE,
} & SendFlags::AUTO_RESTART_BROKEN_SESSION,
&data,
channel.unwrap_or(0)
).or_else(err)
}

#[napi(object)]
pub struct Message {
pub data: Buffer,
pub steam_id: Option<PlayerSteamId>,
}

#[napi]
pub fn receive_messages_on_channel(
channel: u32,
batch_size: Option<u32>
) -> Vec<Message> {
let client = crate::client::get_client();

client
.networking_messages()
.receive_messages_on_channel(channel, batch_size.unwrap_or(10).try_into().unwrap())
.iter().map(|m:&NetworkingMessage<ClientManager>| {
let steam_id = m.identity_peer().steam_id();

return Message {
data: m.data().into(),
steam_id: steam_id.map(|id| PlayerSteamId::from_steamid(id))
}
}).collect()
}
}
Loading