roominfo #112

Merged
jake merged 61 commits from roominfo into master 2 years ago
  1. 55
      src/ship/map/area.rs
  2. 31
      src/ship/packet/handler/lobby.rs
  3. 110
      src/ship/packet/handler/room.rs
  4. 8
      src/ship/ship.rs
  5. 69
      tests/test_rooms.rs

55
src/ship/map/area.rs

@ -3,6 +3,7 @@ use serde::{Serialize, Deserialize};
use std::collections::HashMap; use std::collections::HashMap;
use thiserror::Error; use thiserror::Error;
use crate::ship::room::Episode; use crate::ship::room::Episode;
use std::fmt;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum MapArea { pub enum MapArea {
@ -256,6 +257,60 @@ impl MapArea {
MapArea::SaintMillion => Episode::Four, MapArea::SaintMillion => Episode::Four,
} }
} }
pub fn as_string(&self) -> &str{
match self {
MapArea::Pioneer2Ep1 => "Pioneer 2",
MapArea::Forest1 => "Forest 1",
MapArea::Forest2 => "Forest 2",
MapArea::Caves1 => "Caves 1",
MapArea::Caves2 => "Caves 2",
MapArea::Caves3 => "Caves 3",
MapArea::Mines1 => "Mines 1",
MapArea::Mines2 => "Mines 2",
MapArea::Ruins1 => "Ruins 1",
MapArea::Ruins2 => "Ruins 2",
MapArea::Ruins3 => "Ruins 3",
MapArea::Dragon => "Dragon",
MapArea::DeRolLe => "De Rol Le",
MapArea::VolOpt => "Vol Opt",
MapArea::DarkFalz => "Dark Falz",
MapArea::Pioneer2Ep2 => "Pioneer 2",
MapArea::VrTempleAlpha => "Vr Temple Alpha",
MapArea::VrTempleBeta => "Vr Temple Beta",
MapArea::VrSpaceshipAlpha => "Vr Spaceship Alpha",
MapArea::VrSpaceshipBeta => "Vr Spaceship Beta",
MapArea::Cca => "CCA",
MapArea::JungleAreaNorth => "Jungle Area North",
MapArea::JungleAreaEast => "Jungle Area East",
MapArea::Mountain => "Mountain",
MapArea::Seaside => "Seaside",
MapArea::SeabedUpper => "Seabed Upper",
MapArea::SeabedLower => "Seabed Lower",
MapArea::GalGryphon => "Gal Gryphon",
MapArea::OlgaFlow => "Olga Flow",
MapArea::BarbaRay => "Barba Ray",
MapArea::GolDragon => "Gol Dragon",
MapArea::SeasideNight => "Seaside Night",
MapArea::Tower => "Tower",
MapArea::Pioneer2Ep4 => "Pioneer 2",
MapArea::CraterEast => "Crater East",
MapArea::CraterWest => "Crater West",
MapArea::CraterSouth => "Crater South",
MapArea::CraterNorth => "Crater North",
MapArea::CraterInterior => "Crater Interior",
MapArea::SubDesert1 => "Sub Desert 1",
MapArea::SubDesert2 => "Sub Desert 2",
MapArea::SubDesert3 => "Sub Desert 3",
MapArea::SaintMillion => "Saint Million",
}
}
}
impl fmt::Display for MapArea {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_string())
}
} }

31
src/ship/packet/handler/lobby.rs

@ -3,11 +3,11 @@ use crate::common::serverstate::ClientId;
use crate::common::leveltable::CharacterLevelTable; use crate::common::leveltable::CharacterLevelTable;
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms}; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms};
use crate::ship::character::{FullCharacterBytesBuilder}; use crate::ship::character::{FullCharacterBytesBuilder};
use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError};
//use crate::ship::items::;
use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId};
use crate::ship::packet; use crate::ship::packet;
use crate::ship::items::state::ItemState; use crate::ship::items::state::ItemState;
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use crate::ship::map::MapArea;
// this function needs a better home // this function needs a better home
pub fn block_selected(id: ClientId, pub fn block_selected(id: ClientId,
@ -129,3 +129,30 @@ pub fn remove_from_lobby(id: ClientId,
(n.client, leave_lobby_pkt.clone()) (n.client, leave_lobby_pkt.clone())
}).collect()) }).collect())
} }
pub fn get_room_tab_info(id: ClientId,
pkt: &MenuDetail,
client_location: &mut ClientLocation,
clients: &Clients,
rooms: &mut Rooms)
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
let room_id = RoomId(pkt.item as usize);
if let Some(_room) = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))? {
let mut room_info = String::new();
let clients_in_room = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
for client in clients_in_room {
let cs = clients.get(&client.client).ok_or(ShipError::ClientNotFound(client.client))?;
let gc = cs.user.guildcard;
let name = &cs.character.name;
let cc = cs.character.char_class;
let leveltable = CharacterLevelTable::default();
Review

wow is it really necessary to construct this here? out of scope for this pr but this should probably be some sort of global const

wow is it really necessary to construct this here? out of scope for this pr but this should probably be some sort of global const
let lv = leveltable.get_level_from_exp(cc, cs.character.exp);
let floor = cs.area.unwrap_or(MapArea::Pioneer2Ep1);
room_info += format!("{} Lv{} {}\n{} {}\n", gc,lv,name,cc,floor).as_str();
}
Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new(room_info)))])
} else {
Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new("Game is no longer active".into())))])
}
}

110
src/ship/packet/handler/room.rs

@ -86,62 +86,66 @@ pub fn join_room(id: ClientId,
-> Result<Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send>, ShipError> { -> Result<Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send>, ShipError> {
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
let level = level_table.get_level_from_exp(client.character.char_class, client.character.exp); let level = level_table.get_level_from_exp(client.character.char_class, client.character.exp);
let room = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))?.as_ref().unwrap(); // clippy look what you made me do
// let room = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))?.as_ref().unwrap(); // clippy look what you made me do
if let Some(room) = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))? {
match room.mode.difficulty() {
room::Difficulty::Ultimate => {
if level < 80 {
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto join Ultimate rooms.".into())))].into_iter()))
}
},
room::Difficulty::VeryHard => {
if level < 40 {
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 40 \nto join Very Hard rooms.".into())))].into_iter()))
}
},
room::Difficulty::Hard => {
if level < 20 {
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto join Hard rooms.".into())))].into_iter()))
}
},
_ => {},
};
let original_area = client_location.get_area(id).unwrap();
let original_neighbors = client_location.get_client_neighbors(id).unwrap();
if room.bursting {
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("player is bursting\nplease wait".into())))].into_iter()))
}
let room_id = RoomId(pkt.item as usize);
let original_room_clients = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
client_location.add_client_to_room(id, room_id).unwrap(); // TODO: show room full error or whatever
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
item_state.add_character_to_room(room_id, &client.character, area_client);
let leader = client_location.get_room_leader(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
let join_room = builder::room::join_room(id, clients, client_location, room_id, room)?;
let add_to = builder::room::add_to_room(id, client, &area_client, &leader, item_state, level_table, room_id)?;
let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap();
room.bursting = true;
let mut result: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = Box::new(
vec![(id, SendShipPacket::JoinRoom(join_room))]
.into_iter()
.chain(original_room_clients.into_iter()
.map(move |c| (c.client, SendShipPacket::AddToRoom(add_to.clone())))
));
match room.mode.difficulty() {
room::Difficulty::Ultimate => {
if level < 80 {
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto join Ultimate rooms.".into())))].into_iter()))
}
},
room::Difficulty::VeryHard => {
if level < 40 {
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 40 \nto join Very Hard rooms.".into())))].into_iter()))
}
},
room::Difficulty::Hard => {
if level < 20 {
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto join Hard rooms.".into())))].into_iter()))
}
},
_ => {},
};
if let Ok(leader) = client_location.get_area_leader(original_area) {
let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()));
result = Box::new(result.chain(original_neighbors.into_iter()
.map(move |c| (c.client, leave_lobby.clone()))))
let original_area = client_location.get_area(id).unwrap();
let original_neighbors = client_location.get_client_neighbors(id).unwrap();
if room.bursting {
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("player is bursting\nplease wait".into())))].into_iter()))
}
let room_id = RoomId(pkt.item as usize);
let original_room_clients = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
client_location.add_client_to_room(id, room_id).unwrap(); // TODO: show room full error or whatever
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
item_state.add_character_to_room(room_id, &client.character, area_client);
let leader = client_location.get_room_leader(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
let join_room = builder::room::join_room(id, clients, client_location, room_id, room)?;
let add_to = builder::room::add_to_room(id, client, &area_client, &leader, item_state, level_table, room_id)?;
let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap();
room.bursting = true;
let mut result: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = Box::new(
vec![(id, SendShipPacket::JoinRoom(join_room))]
.into_iter()
.chain(original_room_clients.into_iter()
.map(move |c| (c.client, SendShipPacket::AddToRoom(add_to.clone())))
));
if let Ok(leader) = client_location.get_area_leader(original_area) {
let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()));
result = Box::new(result.chain(original_neighbors.into_iter()
.map(move |c| (c.client, leave_lobby.clone()))))
}
Ok(result)
} else {
Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Game is no longer active".into())))].into_iter()))
} }
Ok(result)
} }
pub fn done_bursting(id: ClientId, pub fn done_bursting(id: ClientId,

8
src/ship/ship.rs

@ -205,6 +205,7 @@ pub enum SendShipPacket {
DirectMessage(DirectMessage), DirectMessage(DirectMessage),
PlayerChat(PlayerChat), PlayerChat(PlayerChat),
SmallDialog(SmallDialog), SmallDialog(SmallDialog),
SmallLeftDialog(SmallLeftDialog),
JoinRoom(JoinRoom), JoinRoom(JoinRoom),
AddToRoom(AddToRoom), AddToRoom(AddToRoom),
LeaveLobby(LeaveLobby), LeaveLobby(LeaveLobby),
@ -246,6 +247,7 @@ impl SendServerPacket for SendShipPacket {
SendShipPacket::DirectMessage(pkt) => pkt.as_bytes(), SendShipPacket::DirectMessage(pkt) => pkt.as_bytes(),
SendShipPacket::PlayerChat(pkt) => pkt.as_bytes(), SendShipPacket::PlayerChat(pkt) => pkt.as_bytes(),
SendShipPacket::SmallDialog(pkt) => pkt.as_bytes(), SendShipPacket::SmallDialog(pkt) => pkt.as_bytes(),
SendShipPacket::SmallLeftDialog(pkt) => pkt.as_bytes(),
SendShipPacket::JoinRoom(pkt) => pkt.as_bytes(), SendShipPacket::JoinRoom(pkt) => pkt.as_bytes(),
SendShipPacket::AddToRoom(pkt) => pkt.as_bytes(), SendShipPacket::AddToRoom(pkt) => pkt.as_bytes(),
SendShipPacket::LeaveLobby(pkt) => pkt.as_bytes(), SendShipPacket::LeaveLobby(pkt) => pkt.as_bytes(),
@ -665,9 +667,9 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
let block = self.blocks.with_client(id, &self.clients)?; let block = self.blocks.with_client(id, &self.clients)?;
handler::quest::player_chose_quest(id, questmenuselect, &mut self.clients, &block.client_location, &mut block.rooms)? handler::quest::player_chose_quest(id, questmenuselect, &mut self.clients, &block.client_location, &mut block.rooms)?
}, },
RecvShipPacket::MenuDetail(_menudetail) => {
//unreachable!();
Box::new(Vec::new().into_iter())
RecvShipPacket::MenuDetail(menudetail) => {
let block = self.blocks.with_client(id, &self.clients)?;
Box::new(handler::lobby::get_room_tab_info(id, menudetail, &mut block.client_location, &self.clients, &mut block.rooms)?.into_iter())
}, },
RecvShipPacket::RoomPasswordReq(room_password_req) => { RecvShipPacket::RoomPasswordReq(room_password_req) => {
let block = self.blocks.with_client(id, &self.clients)?; let block = self.blocks.with_client(id, &self.clients)?;

69
tests/test_rooms.rs

@ -156,4 +156,71 @@ async fn test_set_invalid_quest_group() {
}, },
_ => panic!("Wrong quest category"), _ => panic!("Wrong quest category"),
} }
}
}
#[async_std::test]
async fn test_get_room_info() {
let mut entity_gateway = InMemoryGateway::default();
let (_user1, mut _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
_char1.name = String::from("GODmar");
entity_gateway.save_character(&_char1).await.unwrap();
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
join_lobby(&mut ship, ClientId(2)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
let _expectedmsg = String::from("1 Lv1 GODmar\nHUmar Pioneer 2\n");
let packets = ship.handle(ClientId(2), &RecvShipPacket::MenuDetail(MenuDetail{menu: 3, item: 0})).await.unwrap().collect::<Vec<_>>();
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::SmallLeftDialog(SmallLeftDialog{
padding: [17664, 1157645568],
msg: _expectedmsg,
}))));
}
#[async_std::test]
async fn test_cannot_get_room_info_after_room_is_closed() {
let mut entity_gateway = InMemoryGateway::default();
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
join_lobby(&mut ship, ClientId(2)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
leave_room(&mut ship, ClientId(1)).await;
let _expectedmsg = String::from("Game is no longer active!\0");
let packets = ship.handle(ClientId(2), &RecvShipPacket::MenuDetail(MenuDetail{menu: 3, item: 0})).await.unwrap().collect::<Vec<_>>();
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::SmallLeftDialog(SmallLeftDialog{
padding: [17664, 1157645568],
msg: _expectedmsg,
}))));
}
#[async_std::test]
async fn test_cannot_join_room_after_its_closed() {
let mut entity_gateway = InMemoryGateway::default();
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
join_lobby(&mut ship, ClientId(2)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
leave_room(&mut ship, ClientId(1)).await;
let _expectedmsg = String::from("This room no longer exists!\0");
let packets = ship.handle(ClientId(2), &RecvShipPacket::MenuSelect(MenuSelect{menu: 3, item: 0})).await.unwrap().collect::<Vec<_>>();
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::SmallDialog(SmallDialog{
padding: [0,0],
msg: _expectedmsg, // wow yes cool rust is so great literally the best i can't put a String::from() directly in here.
}))));
}
Loading…
Cancel
Save