Multiplayer continues after a player leaves: AI side takeover, background match while menus are open, match chat, five-tile fog start
This commit is contained in:
1 parent
2721638910
commit
dc22e27184
11 files changed
+436
-70
No files matched your search
+41
-8
@@ -58,7 +58,7 @@ pub struct MatchUi {
|
||||
pub turn: u32,
|
||||
pub error: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub chat: Vec<(String, String)>,
|
||||
pub chat: Vec<(Option<u8>, String, String)>,
|
||||
}
|
||||
|
||||
impl MatchUi {
|
||||
@@ -90,6 +90,7 @@ struct RoomInfo {
|
||||
code: String,
|
||||
sides: Vec<u8>,
|
||||
seats: Vec<SeatInfo>,
|
||||
ai: Vec<u8>,
|
||||
turn: u32,
|
||||
started: bool,
|
||||
paused: bool,
|
||||
@@ -107,9 +108,10 @@ pub struct MatchClient {
|
||||
local_side: Option<u8>,
|
||||
error: Option<String>,
|
||||
status: Option<String>,
|
||||
chat: Vec<(String, String)>,
|
||||
chat: Vec<(Option<u8>, String, String)>,
|
||||
chat_seen: usize,
|
||||
start: Option<StartInfo>,
|
||||
turns: Vec<(u32, Vec<Command>)>,
|
||||
turns: Vec<(u32, Vec<Command>, Vec<u8>)>,
|
||||
checks: Vec<(Option<u8>, u32, u64)>,
|
||||
}
|
||||
|
||||
@@ -126,6 +128,7 @@ impl MatchClient {
|
||||
error: None,
|
||||
status: None,
|
||||
chat: Vec::new(),
|
||||
chat_seen: 0,
|
||||
start: None,
|
||||
turns: Vec::new(),
|
||||
checks: Vec::new(),
|
||||
@@ -172,6 +175,7 @@ impl MatchClient {
|
||||
self.error = None;
|
||||
self.status = None;
|
||||
self.chat.clear();
|
||||
self.chat_seen = 0;
|
||||
self.outbox.push_back(json!({"t":"create","sides":sides,"map_w":map_w,"map_h":map_h,"difficulty":difficulty}).to_string());
|
||||
}
|
||||
|
||||
@@ -186,6 +190,8 @@ impl MatchClient {
|
||||
}
|
||||
self.error = None;
|
||||
self.status = None;
|
||||
self.chat.clear();
|
||||
self.chat_seen = 0;
|
||||
self.outbox.push_back(json!({"t":"join","code":code}).to_string());
|
||||
}
|
||||
|
||||
@@ -231,12 +237,13 @@ impl MatchClient {
|
||||
self.outbox.push_back(json!({"t":"leave"}).to_string());
|
||||
}
|
||||
|
||||
pub fn chat(&mut self, text: &str) {
|
||||
pub fn chat(&mut self, text: &str, team: bool) {
|
||||
let text: String = text.trim().chars().take(MAX_CHAT).collect();
|
||||
if !self.connected || text.is_empty() {
|
||||
return;
|
||||
}
|
||||
self.outbox.push_back(json!({"t":"chat","text":text}).to_string());
|
||||
let msg = if team { json!({"t":"chat","text":text,"channel":"team"}) } else { json!({"t":"chat","text":text,"channel":"all"}) };
|
||||
self.outbox.push_back(msg.to_string());
|
||||
}
|
||||
|
||||
pub fn send_commands(&mut self, cmds: &[Command]) {
|
||||
@@ -272,9 +279,13 @@ impl MatchClient {
|
||||
"chat" => {
|
||||
let name = v.get("name").and_then(|x| x.as_str()).unwrap_or("?").to_string();
|
||||
let text = v.get("text").and_then(|x| x.as_str()).unwrap_or("").to_string();
|
||||
self.chat.push((name, text));
|
||||
let side = v.get("side").and_then(|x| x.as_u64()).map(|x| x as u8);
|
||||
self.chat.push((side, name, text));
|
||||
while self.chat.len() > 40 {
|
||||
self.chat.remove(0);
|
||||
if self.chat_seen > 0 {
|
||||
self.chat_seen -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
"closed" => {
|
||||
@@ -284,6 +295,8 @@ impl MatchClient {
|
||||
self.local_side = None;
|
||||
self.start = None;
|
||||
self.turns.clear();
|
||||
self.chat.clear();
|
||||
self.chat_seen = 0;
|
||||
}
|
||||
"error" => {
|
||||
self.error = Some(v.get("why").and_then(|x| x.as_str()).unwrap_or("error").to_string());
|
||||
@@ -296,7 +309,7 @@ impl MatchClient {
|
||||
self.start.take()
|
||||
}
|
||||
|
||||
pub fn take_turns(&mut self) -> Vec<(u32, Vec<Command>)> {
|
||||
pub fn take_turns(&mut self) -> Vec<(u32, Vec<Command>, Vec<u8>)> {
|
||||
std::mem::take(&mut self.turns)
|
||||
}
|
||||
|
||||
@@ -304,6 +317,15 @@ impl MatchClient {
|
||||
std::mem::take(&mut self.checks)
|
||||
}
|
||||
|
||||
pub fn take_new_chat(&mut self) -> Vec<(Option<u8>, String, String)> {
|
||||
if self.chat_seen >= self.chat.len() {
|
||||
return Vec::new();
|
||||
}
|
||||
let out = self.chat[self.chat_seen..].to_vec();
|
||||
self.chat_seen = self.chat.len();
|
||||
out
|
||||
}
|
||||
|
||||
fn parse_rooms(&mut self, v: &Value) {
|
||||
let Some(arr) = v.get("rooms").and_then(|x| x.as_array()) else { return };
|
||||
self.rooms = arr
|
||||
@@ -335,6 +357,7 @@ impl MatchClient {
|
||||
if let Some(x) = v.get("turn").and_then(|x| x.as_u64()) {
|
||||
room.turn = x as u32;
|
||||
}
|
||||
room.ai = array_bytes(v.get("ai"));
|
||||
if let Some(x) = v.get("started").and_then(|x| x.as_bool()) {
|
||||
room.started = x;
|
||||
}
|
||||
@@ -381,12 +404,14 @@ impl MatchClient {
|
||||
let turn = v.get("turn").and_then(|x| x.as_u64()).unwrap_or(0) as u32;
|
||||
if let Some(r) = self.room.as_mut() {
|
||||
r.turn = turn;
|
||||
r.ai = array_bytes(v.get("ai"));
|
||||
}
|
||||
let cmds = match v.get("cmds").cloned() {
|
||||
Some(v) => serde_json::from_value::<Vec<Command>>(v).unwrap_or_default(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
self.turns.push((turn, cmds));
|
||||
let ai = array_bytes(v.get("ai"));
|
||||
self.turns.push((turn, cmds, ai));
|
||||
}
|
||||
|
||||
fn parse_sum(&mut self, v: &Value) {
|
||||
@@ -489,5 +514,13 @@ mod tests {
|
||||
client.feed(r#"{"t":"sum","from":2,"turn":1,"sum":42}"#);
|
||||
let checks = client.take_checks();
|
||||
assert_eq!(checks[0], (Some(2), 1, 42));
|
||||
|
||||
client.chat("hello", true);
|
||||
let req = pop(&mut client);
|
||||
assert_eq!(req["t"].as_str(), Some("chat"));
|
||||
assert_eq!(req["channel"].as_str(), Some("team"));
|
||||
client.feed(r#"{"t":"chat","name":"red","text":"hello","side":0,"channel":"team"}"#);
|
||||
let chat = client.take_new_chat();
|
||||
assert_eq!(chat[0], (Some(0), "red".to_string(), "hello".to_string()));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user