mirror of
https://github.com/IntQuant/noita_entangled_worlds.git
synced 2025-10-19 07:03:16 +00:00
WIP steam networking
This commit is contained in:
parent
7a0efd4a9c
commit
07c98b4f57
7 changed files with 614 additions and 33 deletions
|
@ -37,9 +37,14 @@ function net.init()
|
|||
}
|
||||
end
|
||||
elseif string.byte(msg, 1, 1) == 1 then
|
||||
local peer_id_l, peer_id_h = string.byte(msg, 2, 3)
|
||||
local peer_id = peer_id_l + peer_id_h * 256
|
||||
local msg_l = string.sub(msg, 4)
|
||||
local peer_id_b = {string.byte(msg, 2, 2+8-1)}
|
||||
local mult = 1
|
||||
local peer_id = 0
|
||||
for _, b in ipairs(peer_id_b) do
|
||||
peer_id = peer_id + b * mult
|
||||
mult = mult * 256
|
||||
end
|
||||
local msg_l = string.sub(msg, 2+8)
|
||||
local success, item = pcall(bitser.loads, msg_l)
|
||||
if success then
|
||||
msg_decoded = {
|
||||
|
|
|
@ -30,7 +30,7 @@ local function send_pending()
|
|||
table.insert(will_send, packet)
|
||||
end
|
||||
if #will_send > 0 then
|
||||
-- GamePrint(#will_send.." "..total_len)
|
||||
-- GamePrint(#pending_send_wd.." "..#will_send.." "..total_len)
|
||||
ctx.lib.net.send_world_data(will_send)
|
||||
end
|
||||
end
|
||||
|
|
84
noita-proxy/Cargo.lock
generated
84
noita-proxy/Cargo.lock
generated
|
@ -90,7 +90,7 @@ version = "3.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"clipboard-win 5.3.1",
|
||||
"log",
|
||||
"objc2 0.5.1",
|
||||
"objc2-app-kit",
|
||||
|
@ -313,6 +313,28 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7"
|
||||
dependencies = [
|
||||
"clipboard-win 2.2.0",
|
||||
"objc",
|
||||
"objc-foundation",
|
||||
"objc_id",
|
||||
"x11-clipboard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.3.1"
|
||||
|
@ -1225,10 +1247,13 @@ name = "noita-proxy"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitcode",
|
||||
"clipboard",
|
||||
"crossbeam",
|
||||
"eframe",
|
||||
"lz4_flex",
|
||||
"rand",
|
||||
"serde",
|
||||
"steamworks",
|
||||
"tangled",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
|
@ -1284,6 +1309,17 @@ dependencies = [
|
|||
"malloc_buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc-foundation"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
|
||||
dependencies = [
|
||||
"block",
|
||||
"objc",
|
||||
"objc_id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc-sys"
|
||||
version = "0.3.3"
|
||||
|
@ -1355,6 +1391,15 @@ dependencies = [
|
|||
"objc2 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc_id"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
|
||||
dependencies = [
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
|
@ -1787,6 +1832,24 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "steamworks"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a79d6f059322f73a4586cc2d0ca595ce1583104b2b1574ae1bb87f2c05bf4c67"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"lazy_static",
|
||||
"steamworks-sys",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "steamworks-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef6b00f8fe8eaaaff22cb9b70822a48c1a5d772bc682c202a57c0b438175845"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.60"
|
||||
|
@ -2563,6 +2626,15 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11-clipboard"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea"
|
||||
dependencies = [
|
||||
"xcb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
version = "2.21.0"
|
||||
|
@ -2595,6 +2667,16 @@ version = "0.13.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34"
|
||||
|
||||
[[package]]
|
||||
name = "xcb"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e917a3f24142e9ff8be2414e36c649d47d6cc2ba81f16201cdef96e533e02de"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xcursor"
|
||||
version = "0.3.5"
|
||||
|
|
|
@ -18,6 +18,9 @@ serde = { version = "1.0.199", features = ["serde_derive"] }
|
|||
bitcode = "0.6.0"
|
||||
lz4_flex = { version = "0.11.3", default_features = false, features = ["std"]}
|
||||
rand = "0.8.5"
|
||||
steamworks = "0.11.0"
|
||||
crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] }
|
||||
clipboard = "0.5.0"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||
use std::{fmt::Display, net::SocketAddr, sync::Arc, thread, time::Duration};
|
||||
|
||||
use bitcode::{Decode, Encode};
|
||||
use clipboard::{ClipboardContext, ClipboardProvider};
|
||||
use eframe::egui::{self, Color32};
|
||||
use serde::de::Error;
|
||||
use steamworks::{LobbyId, SteamAPIInitError};
|
||||
use tangled::Peer;
|
||||
use tracing::info;
|
||||
|
||||
pub mod messages;
|
||||
|
||||
|
@ -17,10 +21,30 @@ pub mod net;
|
|||
enum AppState {
|
||||
Init,
|
||||
Netman { netman: Arc<net::NetManager> },
|
||||
Error { message: String },
|
||||
}
|
||||
|
||||
struct SteamState {
|
||||
pub client: steamworks::Client,
|
||||
}
|
||||
|
||||
impl SteamState {
|
||||
fn new() -> Result<Self, SteamAPIInitError> {
|
||||
let (client, single) = steamworks::Client::init_app(480)?;
|
||||
thread::spawn(move || {
|
||||
info!("Spawned steam callback thread");
|
||||
loop {
|
||||
single.run_callbacks();
|
||||
thread::sleep(Duration::from_millis(3));
|
||||
}
|
||||
});
|
||||
Ok(SteamState { client })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
state: AppState,
|
||||
steam_state: Result<SteamState, SteamAPIInitError>,
|
||||
addr: String,
|
||||
debug_mode: bool,
|
||||
use_constant_seed: bool,
|
||||
|
@ -30,23 +54,52 @@ impl App {
|
|||
fn start_server(&mut self) {
|
||||
let bind_addr = "0.0.0.0:5123".parse().unwrap();
|
||||
let peer = Peer::host(bind_addr, None).unwrap();
|
||||
let netman = net::NetManager::new(peer);
|
||||
{
|
||||
let netman = net::NetManager::new(net::PeerVariant::Tangled(peer));
|
||||
self.set_netman_settings(&netman);
|
||||
netman.clone().start();
|
||||
self.state = AppState::Netman { netman };
|
||||
}
|
||||
|
||||
fn set_netman_settings(&mut self, netman: &Arc<net::NetManager>) {
|
||||
let mut settings = netman.settings.lock().unwrap();
|
||||
settings.debug_mode = self.debug_mode;
|
||||
if !self.use_constant_seed {
|
||||
settings.seed = rand::random();
|
||||
}
|
||||
}
|
||||
netman
|
||||
.accept_local
|
||||
.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
netman.clone().start();
|
||||
self.state = AppState::Netman { netman };
|
||||
}
|
||||
fn start_connect(&mut self, addr: SocketAddr) {
|
||||
let peer = Peer::connect(addr, None).unwrap();
|
||||
let netman = net::NetManager::new(peer);
|
||||
let netman = net::NetManager::new(net::PeerVariant::Tangled(peer));
|
||||
netman.clone().start();
|
||||
self.state = AppState::Netman { netman };
|
||||
}
|
||||
|
||||
fn start_steam_host(&mut self) {
|
||||
let peer = net::steam_networking::SteamPeer::new_host(
|
||||
steamworks::LobbyType::Private,
|
||||
self.steam_state.as_ref().unwrap().client.clone(),
|
||||
);
|
||||
let netman = net::NetManager::new(net::PeerVariant::Steam(peer));
|
||||
self.set_netman_settings(&netman);
|
||||
netman.clone().start();
|
||||
self.state = AppState::Netman { netman };
|
||||
}
|
||||
|
||||
fn notify_error(&mut self, error: impl Display) {
|
||||
self.state = AppState::Error {
|
||||
message: error.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn start_steam_connect(&mut self, id: LobbyId) {
|
||||
let peer = net::steam_networking::SteamPeer::new_connect(
|
||||
id,
|
||||
self.steam_state.as_ref().unwrap().client.clone(),
|
||||
);
|
||||
let netman = net::NetManager::new(net::PeerVariant::Steam(peer));
|
||||
netman.clone().start();
|
||||
self.state = AppState::Netman { netman };
|
||||
}
|
||||
|
@ -54,11 +107,13 @@ impl App {
|
|||
|
||||
impl Default for App {
|
||||
fn default() -> Self {
|
||||
info!("Creating the app...");
|
||||
Self {
|
||||
state: AppState::Init,
|
||||
addr: "192.168.1.168:5123".to_string(),
|
||||
debug_mode: false,
|
||||
use_constant_seed: false,
|
||||
steam_state: SteamState::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +140,32 @@ impl eframe::App for App {
|
|||
}
|
||||
}
|
||||
});
|
||||
ui.separator();
|
||||
ui.heading("Steam networking");
|
||||
match &self.steam_state {
|
||||
Ok(_) => {
|
||||
if ui.button("Create lobby").clicked() {
|
||||
self.start_steam_host();
|
||||
}
|
||||
if ui.button("Connect to lobby in clipboard").clicked() {
|
||||
let id = ClipboardProvider::new()
|
||||
.and_then(|mut ctx: ClipboardContext| ctx.get_contents());
|
||||
match id {
|
||||
Ok(id) => {
|
||||
let id = id.parse().map(LobbyId::from_raw);
|
||||
match id {
|
||||
Ok(id) => self.start_steam_connect(id),
|
||||
Err(error) => self.notify_error(error),
|
||||
}
|
||||
}
|
||||
Err(error) => self.notify_error(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
ui.label(format!("Could not init steam networking: {}", err));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
AppState::Netman { netman } => {
|
||||
|
@ -105,13 +186,33 @@ impl eframe::App for App {
|
|||
ui.label("Not yet ready");
|
||||
}
|
||||
ui.separator();
|
||||
if let Some(id) = netman.peer.lobby_id() {
|
||||
if ui.button("Save lobby id to clipboard").clicked() {
|
||||
let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
|
||||
let _ = ctx.set_contents(id.raw().to_string());
|
||||
}
|
||||
} else {
|
||||
ui.label("Lobby id not available");
|
||||
}
|
||||
ui.heading("Current users");
|
||||
for peer in netman.peer.iter_peer_ids() {
|
||||
ui.label(peer.0.to_string());
|
||||
ui.label(peer.to_string());
|
||||
}
|
||||
ui.label(format!("Peer state: {}", netman.peer.state()));
|
||||
});
|
||||
}
|
||||
AppState::Error { message } => {
|
||||
if egui::CentralPanel::default()
|
||||
.show(ctx, |ui| {
|
||||
ui.heading("An error occured:");
|
||||
ui.label(message);
|
||||
ui.button("Back").clicked()
|
||||
})
|
||||
.inner
|
||||
{
|
||||
self.state = AppState::Init;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,15 +6,16 @@ use std::{
|
|||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
use steamworks::{LobbyId, SteamId};
|
||||
use tracing::debug;
|
||||
|
||||
use tangled::{PeerId, Reliability};
|
||||
use tangled::{PeerId, PeerState, Reliability};
|
||||
use tracing::{error, info, warn};
|
||||
use tungstenite::{accept, WebSocket};
|
||||
|
||||
use crate::{messages::NetMsg, GameSettings};
|
||||
|
||||
static HOST: PeerId = PeerId(0);
|
||||
pub mod steam_networking;
|
||||
|
||||
pub(crate) fn ws_encode_proxy(key: &'static str, value: impl Display) -> tungstenite::Message {
|
||||
let mut buf = Vec::new();
|
||||
|
@ -23,7 +24,7 @@ pub(crate) fn ws_encode_proxy(key: &'static str, value: impl Display) -> tungste
|
|||
tungstenite::Message::Binary(buf)
|
||||
}
|
||||
|
||||
pub(crate) fn ws_encode_mod(peer: PeerId, data: &[u8]) -> tungstenite::Message {
|
||||
pub(crate) fn ws_encode_mod(peer: OmniPeerId, data: &[u8]) -> tungstenite::Message {
|
||||
let mut buf = Vec::new();
|
||||
buf.push(1u8);
|
||||
buf.extend_from_slice(&peer.0.to_le_bytes());
|
||||
|
@ -46,8 +47,136 @@ impl NetInnerState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct OmniPeerId(u64);
|
||||
|
||||
impl From<PeerId> for OmniPeerId {
|
||||
fn from(value: PeerId) -> Self {
|
||||
Self(value.0.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SteamId> for OmniPeerId {
|
||||
fn from(value: SteamId) -> Self {
|
||||
Self(value.raw())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OmniPeerId> for PeerId {
|
||||
fn from(value: OmniPeerId) -> Self {
|
||||
Self(
|
||||
value
|
||||
.0
|
||||
.try_into()
|
||||
.expect("Assuming PeerId was stored here, so conversion should succeed"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OmniPeerId> for SteamId {
|
||||
fn from(value: OmniPeerId) -> Self {
|
||||
Self::from_raw(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for OmniPeerId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum OmniNetworkEvent {
|
||||
PeerConnected(OmniPeerId),
|
||||
PeerDisconnected(OmniPeerId),
|
||||
Message { src: OmniPeerId, data: Vec<u8> },
|
||||
}
|
||||
|
||||
impl From<tangled::NetworkEvent> for OmniNetworkEvent {
|
||||
fn from(value: tangled::NetworkEvent) -> Self {
|
||||
match value {
|
||||
tangled::NetworkEvent::PeerConnected(id) => Self::PeerConnected(id.into()),
|
||||
tangled::NetworkEvent::PeerDisconnected(id) => Self::PeerDisconnected(id.into()),
|
||||
tangled::NetworkEvent::Message(msg) => Self::Message {
|
||||
src: msg.src.into(),
|
||||
data: msg.data,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PeerVariant {
|
||||
Tangled(tangled::Peer),
|
||||
Steam(steam_networking::SteamPeer),
|
||||
}
|
||||
|
||||
impl PeerVariant {
|
||||
fn send(
|
||||
&self,
|
||||
peer: OmniPeerId,
|
||||
msg: Vec<u8>,
|
||||
reliability: Reliability,
|
||||
) -> Result<(), tangled::NetError> {
|
||||
match self {
|
||||
PeerVariant::Tangled(p) => p.send(peer.into(), msg, reliability),
|
||||
PeerVariant::Steam(p) => {
|
||||
p.send_message(peer.into(), &msg, reliability);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast(&self, msg: Vec<u8>, reliability: Reliability) -> Result<(), tangled::NetError> {
|
||||
match self {
|
||||
PeerVariant::Tangled(p) => p.broadcast(msg, reliability),
|
||||
PeerVariant::Steam(p) => Ok(p.broadcast_message(&msg, reliability)),
|
||||
}
|
||||
}
|
||||
|
||||
fn my_id(&self) -> Option<OmniPeerId> {
|
||||
match self {
|
||||
PeerVariant::Tangled(p) => p.my_id().map(OmniPeerId::from),
|
||||
PeerVariant::Steam(p) => Some(p.my_id().into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_peer_ids(&self) -> Vec<OmniPeerId> {
|
||||
match self {
|
||||
PeerVariant::Tangled(p) => p.iter_peer_ids().map(OmniPeerId::from).collect(),
|
||||
PeerVariant::Steam(p) => p.get_peer_ids().into_iter().map(OmniPeerId::from).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn recv(&self) -> Vec<OmniNetworkEvent> {
|
||||
match self {
|
||||
PeerVariant::Tangled(p) => p.recv().map(OmniNetworkEvent::from).collect(),
|
||||
PeerVariant::Steam(p) => p.recv(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state(&self) -> PeerState {
|
||||
match self {
|
||||
PeerVariant::Tangled(p) => p.state(),
|
||||
PeerVariant::Steam(p) => p.state(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_id(&self) -> OmniPeerId {
|
||||
match self {
|
||||
PeerVariant::Tangled(_) => PeerId::HOST.into(),
|
||||
PeerVariant::Steam(p) => p.host_id().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lobby_id(&self) -> Option<LobbyId> {
|
||||
match self {
|
||||
PeerVariant::Tangled(_) => None,
|
||||
PeerVariant::Steam(p) => p.lobby_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NetManager {
|
||||
pub(crate) peer: tangled::Peer,
|
||||
pub(crate) peer: PeerVariant,
|
||||
pub(crate) settings: Mutex<GameSettings>,
|
||||
pub(crate) continue_running: AtomicBool, // TODO stop on drop
|
||||
pub(crate) accept_local: AtomicBool,
|
||||
|
@ -55,7 +184,7 @@ pub struct NetManager {
|
|||
}
|
||||
|
||||
impl NetManager {
|
||||
pub fn new(peer: tangled::Peer) -> Arc<Self> {
|
||||
pub fn new(peer: PeerVariant) -> Arc<Self> {
|
||||
Self {
|
||||
peer,
|
||||
settings: Mutex::new(GameSettings {
|
||||
|
@ -70,7 +199,7 @@ impl NetManager {
|
|||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn send(&self, peer: tangled::PeerId, msg: &NetMsg, reliability: Reliability) {
|
||||
pub(crate) fn send(&self, peer: OmniPeerId, msg: &NetMsg, reliability: Reliability) {
|
||||
let encoded = bitcode::encode(msg);
|
||||
self.peer.send(peer, encoded.clone(), reliability).ok(); // TODO log
|
||||
}
|
||||
|
@ -135,9 +264,9 @@ impl NetManager {
|
|||
}
|
||||
for net_event in self.peer.recv() {
|
||||
match net_event {
|
||||
tangled::NetworkEvent::PeerConnected(id) => {
|
||||
OmniNetworkEvent::PeerConnected(id) => {
|
||||
info!("Peer connected");
|
||||
if self.peer.my_id() == Some(HOST) {
|
||||
if self.peer.my_id() == Some(self.peer.host_id()) {
|
||||
info!("Sending start game message");
|
||||
self.send(
|
||||
id,
|
||||
|
@ -149,11 +278,11 @@ impl NetManager {
|
|||
}
|
||||
state.try_ws_write(ws_encode_proxy("join", id));
|
||||
}
|
||||
tangled::NetworkEvent::PeerDisconnected(id) => {
|
||||
OmniNetworkEvent::PeerDisconnected(id) => {
|
||||
state.try_ws_write(ws_encode_proxy("leave", id));
|
||||
}
|
||||
tangled::NetworkEvent::Message(msg) => {
|
||||
let Ok(net_msg) = bitcode::decode::<NetMsg>(&msg.data) else {
|
||||
OmniNetworkEvent::Message { src, data } => {
|
||||
let Ok(net_msg) = bitcode::decode::<NetMsg>(&data) else {
|
||||
continue;
|
||||
};
|
||||
match net_msg {
|
||||
|
@ -164,12 +293,12 @@ impl NetManager {
|
|||
.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
NetMsg::ModRaw { data } => {
|
||||
state.try_ws_write(ws_encode_mod(msg.src, &data));
|
||||
state.try_ws_write(ws_encode_mod(src, &data));
|
||||
}
|
||||
NetMsg::ModCompressed { data } => {
|
||||
if let Ok(decompressed) = lz4_flex::decompress_size_prepended(&data)
|
||||
{
|
||||
state.try_ws_write(ws_encode_mod(msg.src, &decompressed));
|
||||
state.try_ws_write(ws_encode_mod(src, &decompressed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -251,7 +380,7 @@ impl NetManager {
|
|||
}
|
||||
|
||||
fn is_host(&self) -> bool {
|
||||
self.peer.my_id() == Some(PeerId::HOST)
|
||||
self.peer.my_id() == Some(self.peer.host_id())
|
||||
}
|
||||
|
||||
pub(crate) fn handle_message_to_proxy(&self, msg: &[u8]) {
|
||||
|
|
261
noita-proxy/src/net/steam_networking.rs
Normal file
261
noita-proxy/src/net/steam_networking.rs
Normal file
|
@ -0,0 +1,261 @@
|
|||
use std::sync::Mutex;
|
||||
|
||||
use crossbeam::channel;
|
||||
use steamworks::{
|
||||
CallbackHandle, LobbyChatUpdate, LobbyId, LobbyType, P2PSessionRequest, SendType, SteamError,
|
||||
SteamId,
|
||||
};
|
||||
use tangled::{PeerState, Reliability};
|
||||
use tracing::{info, warn};
|
||||
|
||||
use super::OmniNetworkEvent;
|
||||
|
||||
enum SteamEvent {
|
||||
LobbyCreated(LobbyId),
|
||||
LobbyError(SteamError),
|
||||
LobbyJoinError,
|
||||
PeerConnected(SteamId),
|
||||
PeerDisconnected(SteamId),
|
||||
PeerStateChanged,
|
||||
AcceptConnection(SteamId),
|
||||
}
|
||||
|
||||
pub struct InnerState {
|
||||
lobby_id: Option<LobbyId>,
|
||||
host_id: SteamId,
|
||||
remote_peers: Vec<SteamId>,
|
||||
state: PeerState,
|
||||
}
|
||||
|
||||
pub struct SteamPeer {
|
||||
client: steamworks::Client,
|
||||
events: channel::Receiver<SteamEvent>,
|
||||
sender: channel::Sender<SteamEvent>,
|
||||
my_id: SteamId,
|
||||
is_host: bool,
|
||||
inner: Mutex<InnerState>,
|
||||
_cbs: Vec<CallbackHandle>,
|
||||
}
|
||||
|
||||
impl SteamPeer {
|
||||
pub fn new_host(lobby_type: LobbyType, client: steamworks::Client) -> Self {
|
||||
let (sender, events) = channel::unbounded();
|
||||
let matchmaking = client.matchmaking();
|
||||
{
|
||||
let sender = sender.clone();
|
||||
matchmaking.create_lobby(lobby_type, 20, move |lobby| {
|
||||
let event = match lobby {
|
||||
Ok(id) => SteamEvent::LobbyCreated(id),
|
||||
Err(err) => SteamEvent::LobbyError(err),
|
||||
};
|
||||
sender.send(event).ok();
|
||||
});
|
||||
}
|
||||
|
||||
let my_id = client.user().steam_id();
|
||||
let _cbs = make_callbacks(&sender, &client);
|
||||
Self {
|
||||
my_id,
|
||||
client,
|
||||
|
||||
events,
|
||||
sender,
|
||||
is_host: true,
|
||||
inner: Mutex::new(InnerState {
|
||||
lobby_id: None,
|
||||
host_id: my_id,
|
||||
remote_peers: Vec::new(),
|
||||
state: PeerState::PendingConnection,
|
||||
}),
|
||||
_cbs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_connect(lobby: LobbyId, client: steamworks::Client) -> Self {
|
||||
let (sender, events) = channel::unbounded();
|
||||
let matchmaking = client.matchmaking();
|
||||
{
|
||||
let sender = sender.clone();
|
||||
matchmaking.join_lobby(lobby, move |lobby| {
|
||||
let event = match lobby {
|
||||
Ok(id) => SteamEvent::LobbyCreated(id),
|
||||
Err(_) => SteamEvent::LobbyJoinError,
|
||||
};
|
||||
sender.send(event).ok();
|
||||
});
|
||||
}
|
||||
|
||||
let my_id = client.user().steam_id();
|
||||
let _cbs = make_callbacks(&sender, &client);
|
||||
Self {
|
||||
my_id,
|
||||
client,
|
||||
|
||||
events,
|
||||
sender,
|
||||
is_host: false,
|
||||
inner: Mutex::new(InnerState {
|
||||
lobby_id: None,
|
||||
remote_peers: Vec::new(),
|
||||
host_id: my_id,
|
||||
state: PeerState::PendingConnection,
|
||||
}),
|
||||
_cbs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_message(&self, peer: SteamId, msg: &[u8], reliability: Reliability) -> bool {
|
||||
let send_type = if reliability == Reliability::Reliable || msg.len() > 1200 {
|
||||
SendType::Reliable
|
||||
} else {
|
||||
SendType::Unreliable
|
||||
};
|
||||
let networking = self.client.networking();
|
||||
let res = networking.send_p2p_packet(peer, send_type.clone(), msg);
|
||||
// info!("Sent a packet to {:?}, st {:?}", peer, send_type);
|
||||
if !res {
|
||||
warn!("Couldn't send a packet to {:?}, st {:?}", peer, send_type)
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn broadcast_message(&self, msg: &[u8], reliability: Reliability) {
|
||||
let peers = self.inner.lock().unwrap().remote_peers.clone();
|
||||
for peer in peers {
|
||||
self.send_message(peer, msg, reliability);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv(&self) -> Vec<OmniNetworkEvent> {
|
||||
let mut returned_events = Vec::new();
|
||||
for event in self.events.try_iter() {
|
||||
match event {
|
||||
SteamEvent::LobbyCreated(id) => {
|
||||
info!("Lobby ready");
|
||||
self.inner.lock().unwrap().lobby_id = Some(id);
|
||||
if !self.is_host {
|
||||
let host_id = self.client.matchmaking().lobby_owner(id);
|
||||
self.inner.lock().unwrap().host_id = host_id;
|
||||
info!("Got host id: {:?}", host_id)
|
||||
}
|
||||
self.update_remote_peers();
|
||||
self.inner.lock().unwrap().state = PeerState::Connected;
|
||||
}
|
||||
SteamEvent::LobbyError(err) => {
|
||||
warn!("Could not create lobby: {}", err);
|
||||
self.inner.lock().unwrap().state = PeerState::Disconnected;
|
||||
}
|
||||
SteamEvent::LobbyJoinError => {
|
||||
warn!("Could not join lobby");
|
||||
self.inner.lock().unwrap().state = PeerState::Disconnected;
|
||||
}
|
||||
SteamEvent::PeerConnected(id) => {
|
||||
returned_events.push(OmniNetworkEvent::PeerConnected(id.into()))
|
||||
}
|
||||
SteamEvent::PeerDisconnected(id) => {
|
||||
returned_events.push(OmniNetworkEvent::PeerDisconnected(id.into()))
|
||||
}
|
||||
SteamEvent::PeerStateChanged => self.update_remote_peers(),
|
||||
SteamEvent::AcceptConnection(id) => {
|
||||
info!("p2p accepted");
|
||||
self.client.networking().accept_p2p_session(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
let networking = self.client.networking();
|
||||
while let Some(size) = networking.is_p2p_packet_available() {
|
||||
info!("Got packet {}", size);
|
||||
let mut empty_array = vec![0; size];
|
||||
let mut buffer = empty_array.as_mut_slice();
|
||||
if let Some((sender, _)) = networking.read_p2p_packet(&mut buffer) {
|
||||
returned_events.push(OmniNetworkEvent::Message {
|
||||
src: sender.into(),
|
||||
data: empty_array,
|
||||
})
|
||||
}
|
||||
}
|
||||
returned_events
|
||||
}
|
||||
|
||||
fn update_remote_peers(&self) {
|
||||
let matchmaking = self.client.matchmaking();
|
||||
let lobby = self.inner.lock().unwrap().lobby_id;
|
||||
let mut peers = match lobby {
|
||||
Some(lobby_id) => matchmaking.lobby_members(lobby_id),
|
||||
None => Vec::new(),
|
||||
};
|
||||
peers.retain(|x| *x != self.my_id);
|
||||
peers.sort();
|
||||
let current_peers = &mut self.inner.lock().unwrap().remote_peers;
|
||||
|
||||
// TODO: could be done more efficiently
|
||||
for peer in &peers {
|
||||
if !current_peers.contains(&peer) {
|
||||
self.sender.send(SteamEvent::PeerConnected(*peer)).ok();
|
||||
}
|
||||
}
|
||||
for peer in &mut *current_peers {
|
||||
if !peers.contains(&peer) {
|
||||
self.sender.send(SteamEvent::PeerDisconnected(*peer)).ok();
|
||||
}
|
||||
}
|
||||
|
||||
*current_peers = peers;
|
||||
}
|
||||
|
||||
pub fn get_peer_ids(&self) -> Vec<SteamId> {
|
||||
// let matchmaking = self.client.matchmaking();
|
||||
// let lobby = self.inner.lock().unwrap().lobby_id;
|
||||
// match lobby {
|
||||
// Some(lobby_id) => matchmaking.lobby_members(lobby_id),
|
||||
// None => Vec::new(),
|
||||
// }
|
||||
let mut peers = self.inner.lock().unwrap().remote_peers.clone();
|
||||
peers.push(self.my_id);
|
||||
peers
|
||||
}
|
||||
|
||||
pub fn my_id(&self) -> SteamId {
|
||||
self.my_id
|
||||
}
|
||||
|
||||
pub fn host_id(&self) -> SteamId {
|
||||
if self.is_host {
|
||||
self.my_id
|
||||
} else {
|
||||
self.inner.lock().unwrap().host_id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lobby_id(&self) -> Option<LobbyId> {
|
||||
self.inner.lock().unwrap().lobby_id
|
||||
}
|
||||
|
||||
pub fn state(&self) -> PeerState {
|
||||
self.inner.lock().unwrap().state
|
||||
}
|
||||
}
|
||||
|
||||
fn make_callbacks(
|
||||
sender: &channel::Sender<SteamEvent>,
|
||||
client: &steamworks::Client,
|
||||
) -> Vec<CallbackHandle> {
|
||||
let cb_req = {
|
||||
let sender = sender.clone();
|
||||
client.register_callback(move |request: P2PSessionRequest| {
|
||||
info!("Accepting connection with {:?}", request.remote);
|
||||
sender
|
||||
.send(SteamEvent::AcceptConnection(request.remote))
|
||||
.ok();
|
||||
})
|
||||
};
|
||||
let cb_ch = {
|
||||
let sender = sender.clone();
|
||||
client.register_callback(move |update: LobbyChatUpdate| {
|
||||
info!("User state changed {:?}", update);
|
||||
sender.send(SteamEvent::PeerStateChanged).ok();
|
||||
})
|
||||
};
|
||||
vec![cb_ch, cb_req]
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue