diff --git a/Cargo.lock b/Cargo.lock
index 6f1fa0e..adfa525 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -206,6 +206,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
[[package]]
name = "crunchy"
version = "0.2.3"
@@ -1061,7 +1067,7 @@ dependencies = [
"percent-encoding",
"portable-atomic",
"rand_core",
- "ringbuffer",
+ "ringbuf",
"serde",
"serde-json-core",
"sha1",
@@ -1137,6 +1143,15 @@ dependencies = [
"critical-section",
]
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
[[package]]
name = "precomputed-hash"
version = "0.1.1"
@@ -1257,10 +1272,15 @@ dependencies = [
]
[[package]]
-name = "ringbuffer"
-version = "0.15.0"
+name = "ringbuf"
+version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3df6368f71f205ff9c33c076d170dd56ebf68e8161c733c0caa07a7a5509ed53"
+checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c"
+dependencies = [
+ "crossbeam-utils",
+ "portable-atomic",
+ "portable-atomic-util",
+]
[[package]]
name = "rp-pac"
diff --git a/Cargo.toml b/Cargo.toml
index e2f829a..03c4eac 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,15 +4,16 @@ version = "0.1.0"
edition = "2024"
[features]
+json = ["dep:serde-json-core", "dep:serde"]
wifi-connect = [
"dep:serde-json-core",
"dep:serde",
] # you need to add a wifi.json file for this to work
dhcp = ["dep:dhcparse"]
dns = ["dep:dnsparse"]
-chat = ["dep:ringbuffer"]
-ttt = ["dep:serde-json-core", "dep:serde"]
-default = ["dhcp", "dns", "ttt"]
+chat = ["dep:ringbuf", "json"]
+ttt = ["json"]
+default = ["dhcp", "dns", "chat"]
[dependencies]
embassy-executor = { git = "https://github.com/embassy-rs/embassy", features = [
@@ -54,7 +55,9 @@ serde = { version = "*", optional = true, default-features = false, features = [
] }
dhcparse = { version = "*", default-features = false, optional = true }
dnsparse = { version = "*", optional = true }
-ringbuffer = { version = "*", default-features = false, optional = true }
+ringbuf = { version = "*", default-features = false, features = [
+ "portable-atomic",
+], optional = true }
percent-encoding = { version = "*", default-features = false }
sha1 = { version = "*", default-features = false }
base64 = { version = "*", default-features = false }
diff --git a/src/apps/chat.html b/src/apps/chat.html
index 3074e84..9afc45c 100644
--- a/src/apps/chat.html
+++ b/src/apps/chat.html
@@ -1,41 +1,11 @@
-
-
+
Chat
-
+
+
diff --git a/src/apps/chat.js b/src/apps/chat.js
new file mode 100644
index 0000000..75245e6
--- /dev/null
+++ b/src/apps/chat.js
@@ -0,0 +1,26 @@
+const id = 0;
+const username = "testName";
+if (id === undefined) {
+ throw "id is undefined!";
+}
+if (username === undefined) {
+ throw "username is undefined!";
+}
+const ws = new WebSocket("ws://192.254.0.2:" + (9000 + id) + "/" + username);
+
+ws.onmessage = (event) => {
+ if (typeof event.data == "string") {
+ let msg = JSON.parse(event.data);
+ if (event.data[0] == "[") {
+ let usernames = [];
+ for (u of msg) {
+ if (u.length() > 0) {
+ let un = document.createElement("div");
+ un.innerText = u;
+ usernames.push(un);
+ }
+ }
+ document.getElementById("usernames").replaceChildren(...usernames);
+ }
+ }
+};
diff --git a/src/apps/chat.rs b/src/apps/chat.rs
index c100e67..41c7f7d 100644
--- a/src/apps/chat.rs
+++ b/src/apps/chat.rs
@@ -1,30 +1,167 @@
-use core::fmt::Write;
-use dhcparse::dhcpv4::MAX_MESSAGE_SIZE;
+use core::str::from_utf8_unchecked;
+
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, mutex::Mutex};
-use heapless::{String, Vec};
+use embassy_time::Timer;
+use heapless::String;
use log::{info, warn};
-use percent_encoding::percent_decode_str;
-use pico_website::unwrap;
-use ringbuffer::{ConstGenericRingBuffer, RingBuffer};
+use pico_website::{unimplemented, unwrap, unwrap_opt};
+use serde::Serialize;
-use crate::socket::{HttpRequestType, HttpResCode};
+use crate::{apps::App, socket::ws::WsMsg};
-use super::App;
+// Must be <= u8::MAX-1;
+pub const USERS_LEN: u8 = 4;
-const MEMORY_SIZE: usize = 16;
-const USERNAME_MIN_SIZE: usize = 3;
-const USERNAME_SIZE: usize = 16;
-const MSG_SIZE: usize = 128;
+const MSG_MAX_SIZE: usize = 10;
+#[derive(Debug, Serialize)]
+struct Msg<'a> {
+ id: usize,
+ author: u8,
+ content: &'a str,
+}
+// {"id"=999999,"author"="","content"=""}
-static MESSAGES: Mutex = Mutex::new(Messages::new());
+const MSGS_SIZE: usize = 30;
+#[derive(Debug)]
+struct Msgs {
+ /// Memory layout with sizes in bytes : ...|content: len|len: 2|author+1: 1|...
+ /// `author=0` means theres no message, it's just padding and should be skipped.
+ /// No message is splitted
+ inner: [u8; MSGS_SIZE],
+ /// next byte index
+ head: usize,
+ next_msg: usize,
+}
+impl Msgs {
+ const fn default() -> Self {
+ Self {
+ inner: [0; _],
+ head: 0,
+ next_msg: 0,
+ }
+ }
+ fn push(&mut self, author: u8, content: &str) {
+ if self.head + content.len() + 3 >= MSGS_SIZE {
+ self.inner[self.head..].fill(0);
+ self.head = 0
+ }
+ self.inner[self.head..self.head + content.len()].copy_from_slice(content.as_bytes());
+ self.head += content.len();
+ self.inner[self.head..self.head + 2].copy_from_slice(&(content.len() as u16).to_le_bytes());
+ self.inner[self.head + 2] = author + 1;
+ self.head += 3;
+ }
+ // Iter messages from present to past
+ fn iter(&self) -> MsgsIter {
+ if self.head == 0 {
+ MsgsIter {
+ msgs: self,
+ head: 0,
+ current_id: 0,
+ finished: true,
+ }
+ } else {
+ MsgsIter {
+ msgs: self,
+ head: self.head,
+ current_id: self.next_msg - 1,
+ finished: false,
+ }
+ }
+ }
+}
+struct MsgsIter<'a> {
+ msgs: &'a Msgs,
+ /// next byte index
+ head: usize,
+ finished: bool,
+ current_id: usize,
+}
+impl<'a> Iterator for MsgsIter<'a> {
+ type Item = Msg<'a>;
+ /// We trust msgs.inner validity in this function, it might panic or do UB if msgs.inner is not valid
+ fn next(&mut self) -> Option {
+ if self.finished {
+ return None;
+ }
+ if self.head == 0 {
+ self.head = MSGS_SIZE;
+ }
+ let above = self.head > self.msgs.head;
+ if above && self.head < self.msgs.head + 3 {
+ self.finished = true;
+ return None;
+ }
+ let author = self.msgs.inner[self.head - 1];
+ self.head -= 1;
+ if author == 0 {
+ return self.next();
+ }
+ let author = author - 1;
+ let len = u16::from_le_bytes([
+ self.msgs.inner[self.head - 2],
+ self.msgs.inner[self.head - 1],
+ ]) as usize;
+ self.head -= 2;
+
+ let content =
+ unsafe { str::from_utf8_unchecked(&self.msgs.inner[self.head - len..self.head]) };
+ self.head -= len;
+ if above && self.head < self.msgs.head {
+ self.finished = true;
+ return None;
+ }
+ if self.current_id == 0 {
+ self.finished = true;
+ } else {
+ self.current_id -= 1;
+ }
+
+ Some(Msg {
+ id: self.current_id,
+ author,
+ content,
+ })
+ }
+}
+
+static MSGS: Mutex = Mutex::new(Msgs::default());
+const USERNAME_MAX_LEN: usize = 16;
+#[derive(Serialize)]
+pub struct Usernames([Option>; USERS_LEN as usize]);
+impl Usernames {
+ const fn default() -> Self {
+ Self([None, None, None, None])
+ }
+ pub fn get_id(&mut self, name: &str) -> Option {
+ for (i, un) in self.0.iter().enumerate() {
+ if let Some(n) = un {
+ if n.as_str() == name {
+ return Some(i as u8);
+ }
+ }
+ }
+ for (i, un) in self.0.iter_mut().enumerate() {
+ if *un == None {
+ *un = Some(String::new());
+ un.as_mut().unwrap().push_str(name).unwrap();
+ return Some(i as u8);
+ }
+ }
+ None
+ }
+}
+pub static USERNAMES: Mutex = Mutex::new(Usernames::default());
pub struct ChatApp {
- res_buf: String<1100>,
+ id: u8,
+ json_buf: [u8; 48 + USERNAME_MAX_LEN + MSG_MAX_SIZE],
}
impl ChatApp {
- pub fn new() -> Self {
+ pub fn new(id: u8) -> Self {
Self {
- res_buf: String::new(),
+ id,
+ json_buf: [0; _],
}
}
}
@@ -32,245 +169,106 @@ impl App for ChatApp {
fn socket_name(&self) -> &'static str {
"chat"
}
- async fn handle_request<'a>(
+ fn accept_ws(&self, path: &str) -> bool {
+ path.len() > 1 && path.len() <= 17
+ }
+ async fn handle_ws<'a, const BUF_SIZE: usize, const RES_HEAD_BUF_SIZE: usize>(
&'a mut self,
- path: &str,
- req_type: HttpRequestType,
- content: &str,
- ) -> (HttpResCode, &'static str, &'a str) {
- match (req_type, path) {
- (HttpRequestType::Get, "/" | "/index" | "/index.html" | "/chat" | "/chat.html") => {
- (HttpResCode::Ok, "html", include_str!("./chat.html"))
+ _path: &str,
+ mut ws: crate::socket::ws::Ws<'a, BUF_SIZE, RES_HEAD_BUF_SIZE>,
+ ) {
+ Timer::after_millis(500).await;
+ let r: Result<(), ()> = try {
+ // Send all messages at the beginning
+ {
+ let msgs = MSGS.lock().await;
+ for m in msgs.iter() {
+ let n = unwrap(serde_json_core::to_slice(&m, &mut self.json_buf)).await;
+ let json =
+ unsafe { from_utf8_unchecked(&unwrap_opt(self.json_buf.get(..n)).await) };
+ ws.send(WsMsg::Text(json)).await?;
+ }
+ }
+ {
+ let usernames = USERNAMES.lock().await;
+ let n = unwrap(serde_json_core::to_slice(&(*usernames), &mut self.json_buf)).await;
+ let json =
+ unsafe { from_utf8_unchecked(&unwrap_opt(self.json_buf.get(..n)).await) };
+ ws.send(WsMsg::Text(json)).await?;
}
- (_, path) => {
- let (path, args) = path.split_once('?').unwrap_or((path, ""));
- let mut load = None;
- let mut username = None;
- let mut msg_content = None;
- let mut poll = false;
- for arg in args.split('&').chain(content.split('&')) {
- match arg.split_once('=') {
- Some(("load", n)) => {
- let n: u16 = match n.parse() {
- Ok(v) => v,
- Err(_) => return (HttpResCode::BadRequest, "", ""),
- };
- if n > 0 {
- load = Some(n);
- }
- }
- Some(("username", u)) => {
- let mut name = String::::new();
- for c in percent_decode_str(u) {
- if let Err(_) = name.push(c as char) {
- return (HttpResCode::BadRequest, "", "");
- }
- }
- if u.len() < USERNAME_MIN_SIZE {
- return (HttpResCode::BadRequest, "", "");
- }
- username = Some(name);
- }
- Some(("msg", m)) => {
- let mut msg = Vec::::new();
- let mut i = 0;
- while i < m.len() {
- let c = if m.as_bytes()[i] == b'%' {
- let c = match m
- .get(i + 1..=i + 2)
- .map(|s| u8::from_str_radix(s, 16))
- {
- Some(Ok(c)) => c,
- _ => {
- warn!("Invalid percent encoding of msg argument");
- return (HttpResCode::BadRequest, "", "");
- }
- };
- i += 2;
- c
- } else {
- m.as_bytes()[i]
- };
- if let Err(_) = msg.push(c) {
- return (HttpResCode::BadRequest, "", "");
- }
- i += 1;
- }
- msg_content = Some(match String::from_utf8(msg) {
- Ok(msg) => msg,
- Err(_) => {
- warn!("Invalid utf8 msg argument");
- return (HttpResCode::BadRequest, "", "");
- }
- });
- }
- Some(("poll", "true")) => poll = true,
- _ => {}
+ loop {
+ Timer::after_secs(1).await;
+ {
+ let msgs = MSGS.lock().await;
+ info!("{:?}", msgs);
+ Timer::after_millis(100).await;
+ for m in msgs.iter() {
+ info!("{:?}", m);
+ Timer::after_millis(100).await;
}
}
- info!(
- "load:{:?} | username:{:?} | msg:{:?}",
- load, username, msg_content
- );
- if path == "/chat/connect" && username.is_some() {
- self.res_buf.clear();
- unwrap(write!(
- &mut self.res_buf,
- "
- \
- ",
- username.unwrap(),
- MEMORY_SIZE,
- MAX_MESSAGE_SIZE
- ))
- .await;
- return (HttpResCode::Ok, "html", &self.res_buf);
- } else if path == "/chat/send" && username.is_some() && msg_content.is_some() {
- let mut msgs = MESSAGES.lock().await;
- msgs.push(Message {
- author: username.unwrap(),
- content: msg_content.unwrap(),
- });
- self.res_buf.clear();
- unwrap(write!(
- &mut self.res_buf,
- "\
- ",
- MAX_MESSAGE_SIZE
- ))
- .await;
- return (HttpResCode::Ok, "html", &self.res_buf);
- } else if path.starts_with("/chat/message/") && path.len() > 14 {
- let msg_id: u16 = match path[14..].parse() {
- Ok(n) => n,
- Err(_) => return (HttpResCode::BadRequest, "", ""),
- };
- let msgs = MESSAGES.lock().await;
- if msg_id > msgs.next {
- return (HttpResCode::BadRequest, "", "");
- }
- self.res_buf.clear();
- unwrap(write!(&mut self.res_buf, " {
- unwrap(write!(
- &mut self.res_buf,
- ">{}: {}
",
- msg.author, msg.content
- ))
+ while let Some(r) = ws.rcv().await? {
+ info!("{:?}", r);
+ if let WsMsg::Text(r) = r {
+ if r.starts_with("send ") {
+ if r.len() > 5 + MSG_MAX_SIZE {
+ warn!("Message too long! (len={})", r.len() - 5);
+ return;
+ }
+ MSGS.lock()
.await
- }
- None => {
- if load.is_some() {
- if (msg_id as isize)
- == (msgs.next as isize - MEMORY_SIZE as isize - 1)
- {
- unwrap(write!(
- &mut self.res_buf,
- ">Older messages forgotten"
- ))
- .await;
- } else {
- unwrap(write!(
- &mut self.res_buf,
- " style=\"display: none;\">"
- ))
- .await;
- }
- } else {
- return (HttpResCode::NoContent, "", "");
- }
- }
- };
- };
+ .push(self.id, r.get(5..).unwrap_or_default());
+ }
+ // if r.starts_with("reqmsg ") {
+ // let Ok(msg_id) = r.get(7..).unwrap_or_default().parse::() else {
+ // warn!("Invalid requested message : {}", r);
+ // return;
+ // };
+ // // 0 is next msg
+ // let msg_rel_id = if msg_id >= 0 {
- return (HttpResCode::Ok, "html", &self.res_buf);
- } else {
- (HttpResCode::NotFound, "", "")
+ // }
+ // if msg_id < 0 {
+
+ // }
+ // }
+ }
}
}
+ };
+ if r.is_err() {
+ warn!(
+ "Socket {}: error in ws, terminating connection",
+ self.socket_name()
+ );
}
}
}
-struct Message {
- author: String,
- content: String,
-}
+// #[derive(Deserialize)]
+// enum ClientMsg {
+// ReqMsg(usize),
+// }
-struct Messages {
- inner: ConstGenericRingBuffer,
- next: u16,
-}
-impl Messages {
- const fn new() -> Self {
- Self {
- inner: ConstGenericRingBuffer::new(),
- next: 0,
- }
- }
- fn get_abs(&self, id: u16) -> Option<&Message> {
- if (id as isize) < (self.next as isize - MEMORY_SIZE as isize) {
- return None;
- }
- self.inner.get_signed((id as isize) - (self.next as isize))
- }
- fn push(&mut self, msg: Message) {
- info!("{}: {}", msg.author, msg.content);
- self.inner.push(msg);
- self.next += 1;
+pub async fn id_to_static_str(id: u8) -> &'static str {
+ match id {
+ 0 => "0",
+ 1 => "1",
+ 2 => "2",
+ 3 => "3",
+ 4 => "4",
+ 5 => "5",
+ 6 => "6",
+ 7 => "7",
+ 8 => "8",
+ 9 => "9",
+ 10 => "10",
+ 11 => "11",
+ 12 => "12",
+ 13 => "13",
+ 14 => "14",
+ 15 => "15",
+ _ => unimplemented().await,
}
}
diff --git a/src/apps/index.rs b/src/apps/index.rs
index 4f767ae..94e827a 100644
--- a/src/apps/index.rs
+++ b/src/apps/index.rs
@@ -1,8 +1,8 @@
-use heapless::Vec;
+use heapless::{LinearMap, Vec};
use pico_website::unwrap;
use crate::{
- apps::Content,
+ apps::{self, Content, chat::id_to_static_str},
socket::{HttpRequestType, HttpResCode},
};
@@ -39,52 +39,108 @@ impl App for IndexApp {
path => {
let (path, args) = path.split_once('?').unwrap_or((path, ""));
let mut team = None;
+ let mut name = None;
+ let mut id = None;
for arg in args.split('&') {
match arg.split_once('=') {
Some(("team", "0")) => team = Some("0"),
Some(("team", "1")) => team = Some("1"),
+ Some(("name", n)) => {
+ if n.len() >= 1 && n.len() <= 16 {
+ name = Some(n);
+ }
+ }
+ Some(("id", id)) => {
+ if let Ok(id) = id.parse::() {
+ if id < apps::chat::USERS_LEN {}
+ }
+ }
_ => {}
}
}
- if path == "/ttt" {
- let Some(team) = team else {
- return (HttpResCode::BadRequest, "", None);
- };
- #[cfg(debug_assertions)]
- let html = include_str!("ttt.html");
- #[cfg(not(debug_assertions))]
- let html = include_str!("../../static/ttt.min.html");
-
- let mut content = Vec::new();
- let r: Result<(), &str> = try {
- let (html1, html2) = html.split_once("/ttt.js").ok_or("")?;
-
- content.push(html1)?;
- content.push("/ttt.js?team=")?;
- content.push(team)?;
- content.push(html2)?;
- };
- unwrap(r).await;
- (HttpResCode::Ok, "html", Some(Content(content)))
- } else if path == "/ttt.js" {
- let Some(team) = team else {
- return (HttpResCode::BadRequest, "", None);
- };
-
- let mut content = Vec::new();
- let r: Result<(), &str> = try {
- content.push("const team = ")?;
- content.push(team)?;
- content.push(";")?;
+ match path {
+ "/ttt" => {
+ let Some(team) = team else {
+ return (HttpResCode::BadRequest, "", None);
+ };
#[cfg(debug_assertions)]
- content.push(include_str!("ttt.js"))?;
+ let html = include_str!("ttt.html");
#[cfg(not(debug_assertions))]
- content.push(include_str!("../../static/ttt.min.js"))?;
- };
- unwrap(r).await;
- (HttpResCode::Ok, "javascript", Some(Content(content)))
- } else {
- (HttpResCode::NotFound, "", None)
+ let html = include_str!("../../static/ttt.min.html");
+
+ let mut content = Vec::new();
+ let r: Result<(), &str> = try {
+ let (html1, html2) = html.split_once("/ttt.js").ok_or("")?;
+
+ content.push(html1)?;
+ content.push("/ttt.js?team=")?;
+ content.push(team)?;
+ content.push(html2)?;
+ };
+ unwrap(r).await;
+ (HttpResCode::Ok, "html", Some(Content(content)))
+ }
+ "/ttt.js" => {
+ let Some(team) = team else {
+ return (HttpResCode::BadRequest, "", None);
+ };
+
+ let mut content = Vec::new();
+ let r: Result<(), &str> = try {
+ content.push("const team = ")?;
+ content.push(team)?;
+ content.push(";")?;
+ #[cfg(debug_assertions)]
+ content.push(include_str!("ttt.js"))?;
+ #[cfg(not(debug_assertions))]
+ content.push(include_str!("../../static/ttt.min.js"))?;
+ };
+ unwrap(r).await;
+ (HttpResCode::Ok, "javascript", Some(Content(content)))
+ }
+ "/chat" => {
+ let Some(name) = name else {
+ return (HttpResCode::BadRequest, "", None);
+ };
+ // #[cfg(debug_assertions)]
+ let html = include_str!("chat.html");
+ // #[cfg(not(debug_assertions))]
+ // let html = include_str!("../../static/chat.min.html");
+ let Some(id) = apps::chat::USERNAMES.lock().await.get_id(name) else {
+ return (HttpResCode::NoContent, "", None);
+ };
+
+ let mut content = Vec::new();
+ let r: Result<(), &str> = try {
+ let (html1, html2) = html.split_once("/chat.js").ok_or("")?;
+
+ content.push(html1)?;
+ content.push("/chat.js?id=")?;
+ content.push(id_to_static_str(id).await)?;
+ content.push(html2)?;
+ };
+ unwrap(r).await;
+ (HttpResCode::Ok, "html", Some(Content(content)))
+ }
+ "/chat.js" => {
+ let Some(id) = id else {
+ return (HttpResCode::BadRequest, "", None);
+ };
+
+ let mut content = Vec::new();
+ let r: Result<(), &str> = try {
+ content.push("const team = ")?;
+ content.push(team)?;
+ content.push(";")?;
+ #[cfg(debug_assertions)]
+ content.push(include_str!("ttt.js"))?;
+ #[cfg(not(debug_assertions))]
+ content.push(include_str!("../../static/ttt.min.js"))?;
+ };
+ unwrap(r).await;
+ (HttpResCode::Ok, "javascript", Some(Content(content)))
+ }
+ _ => (HttpResCode::NotFound, "", None),
}
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 7e6dcf7..21609b8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -14,7 +14,7 @@ pub async fn unwrap(res: Result) -> T {
}
}
pub async fn unwrap_opt(opt: Option) -> T {
- unwrap(opt.ok_or(())).await
+ expect_opt("option unwraped", opt).await
}
pub async fn assert(condition: bool) {
@@ -24,6 +24,16 @@ pub async fn assert(condition: bool) {
}
}
+pub async fn expect_opt(msg: &str, opt: Option) -> T {
+ unwrap(opt.ok_or(msg)).await
+}
+
+pub async fn unimplemented() -> ! {
+ let err: Result<(), &str> = Err("unimplemented");
+ unwrap(err).await;
+ loop {}
+}
+
// TODO: make this log work
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
diff --git a/src/main.rs b/src/main.rs
index 885c0f2..b4dc43e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,6 +5,8 @@
#![feature(slice_split_once)]
#![feature(try_blocks)]
#![feature(impl_trait_in_bindings)]
+#![feature(generic_arg_infer)]
+#![feature(array_repeat)]
#[cfg(feature = "wifi-connect")]
use core::net::Ipv4Addr;
@@ -178,8 +180,8 @@ async fn main(spawner: Spawner) {
unwrap(spawner.spawn(socket::ttt_listen_task(stack, apps::ttt::Team::One, 8081))).await;
}
#[cfg(feature = "chat")]
- for _ in 0..4 {
- unwrap(spawner.spawn(socket::chat_listen_task(stack, 8082))).await;
+ for i in 0..4 {
+ unwrap(spawner.spawn(socket::chat_listen_task(stack, i, 9000 + i as u16))).await;
}
info!("All apps lauched!");
}
diff --git a/src/socket.rs b/src/socket.rs
index be147fd..f12cd7c 100644
--- a/src/socket.rs
+++ b/src/socket.rs
@@ -27,8 +27,8 @@ pub async fn index_listen_task(stack: embassy_net::Stack<'static>, port: u16) {
#[cfg(feature = "chat")]
#[embassy_executor::task(pool_size = 4)]
-pub async fn chat_listen_task(stack: embassy_net::Stack<'static>, port: u16) {
- listen_task(stack, apps::chat::ChatApp::new(), port).await
+pub async fn chat_listen_task(stack: embassy_net::Stack<'static>, id: u8, port: u16) {
+ listen_task(stack, apps::chat::ChatApp::new(id), port).await
}
pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps::App, port: u16) {
diff --git a/src/socket/ws.rs b/src/socket/ws.rs
index fe87cf5..71f2c5c 100644
--- a/src/socket/ws.rs
+++ b/src/socket/ws.rs
@@ -1,11 +1,11 @@
use core::str::from_utf8;
use embassy_net::tcp::{TcpReader, TcpSocket, TcpWriter};
-use embassy_time::Instant;
+use embassy_time::{Instant, Timer};
use embedded_io_async::{ErrorType, ReadReady, Write};
use heapless::Vec;
use log::{info, warn};
-use pico_website::{assert, unwrap, unwrap_opt};
+use pico_website::{assert, expect_opt, unwrap, unwrap_opt};
#[derive(Clone, Copy, Debug)]
pub enum WsMsg<'a> {
@@ -179,7 +179,7 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA
.iter_mut()
.enumerate()
{
- *x ^= unwrap_opt(mask_key.get(i & 0xff)).await;
+ *x ^= unwrap_opt(mask_key.get(i % 4)).await;
}
if n_after_length + 4 + (length as usize) < n {
self.rx.msg_in_buf = Some((